diff --git a/packages/SystemUI/res/color/camera_foreground.xml b/packages/SystemUI/res/color/camera_foreground.xml
new file mode 100644
index 0000000000000..6e039cc1df1ef
--- /dev/null
+++ b/packages/SystemUI/res/color/camera_foreground.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c51e0bf2c31ba..afbe9b85b9f91 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -257,6 +257,8 @@
#FF8B66
@color/GM2_blue_300
@color/GM2_blue_300
+ #33000000
+ @*android:color/black
#F28B82
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a9601336c6e21..3a5d607e92ef2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1269,6 +1269,10 @@
2dp
32dp
+ - 2.0
+ - 2.0
+ - 2.0
+
@dimen/control_base_item_margin
- 15
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 0e4f68431c160..9d92a40beec47 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -37,6 +37,7 @@ import android.service.controls.templates.ControlTemplate
import android.service.controls.templates.RangeTemplate
import android.service.controls.templates.StatelessTemplate
import android.service.controls.templates.TemperatureControlTemplate
+import android.service.controls.templates.ThumbnailTemplate
import android.service.controls.templates.ToggleRangeTemplate
import android.service.controls.templates.ToggleTemplate
import android.util.MathUtils
@@ -87,8 +88,11 @@ class ControlViewHolder(
): KClass {
return when {
status != Control.STATUS_OK -> StatusBehavior::class
- deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
template == ControlTemplate.NO_TEMPLATE -> TouchBehavior::class
+ template is ThumbnailTemplate -> ThumbnailBehavior::class
+
+ // Required for legacy support, or where cameras do not use the new template
+ deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
template is ToggleTemplate -> ToggleBehavior::class
template is StatelessTemplate -> TouchBehavior::class
template is ToggleRangeTemplate -> ToggleRangeBehavior::class
@@ -105,7 +109,7 @@ class ControlViewHolder(
private var statusAnimator: Animator? = null
private val baseLayer: GradientDrawable
val icon: ImageView = layout.requireViewById(R.id.icon)
- private val status: TextView = layout.requireViewById(R.id.status)
+ val status: TextView = layout.requireViewById(R.id.status)
private var nextStatusText: CharSequence = ""
val title: TextView = layout.requireViewById(R.id.title)
val subtitle: TextView = layout.requireViewById(R.id.subtitle)
@@ -132,7 +136,6 @@ class ControlViewHolder(
val ld = layout.getBackground() as LayerDrawable
ld.mutate()
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
- clipLayer.alpha = ALPHA_DISABLED
baseLayer = ld.findDrawableByLayerId(R.id.background) as GradientDrawable
// needed for marquee to start
status.setSelected(true)
@@ -315,7 +318,9 @@ class ControlViewHolder(
)
}
- (clipLayer.getDrawable() as GradientDrawable).apply {
+ clipLayer.getDrawable().apply {
+ clipLayer.alpha = ALPHA_DISABLED
+
val newBaseColor = if (behavior is ToggleRangeBehavior) {
ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity)
} else {
@@ -323,15 +328,25 @@ class ControlViewHolder(
}
stateAnimator?.cancel()
if (animated) {
- val oldColor = color?.defaultColor ?: newClipColor
+ val oldColor = if (this is GradientDrawable) {
+ this.color?.defaultColor ?: newClipColor
+ } else {
+ newClipColor
+ }
val oldBaseColor = baseLayer.color?.defaultColor ?: newBaseColor
val oldAlpha = layout.alpha
+
+ // Animate both alpha and background colors. Only animate colors for
+ // GradientDrawables and not static images as used for the ThumbnailTemplate.
stateAnimator = ValueAnimator.ofInt(clipLayer.alpha, newAlpha).apply {
addUpdateListener {
alpha = it.animatedValue as Int
- setColor(ColorUtils.blendARGB(oldColor, newClipColor, it.animatedFraction))
- baseLayer.setColor(ColorUtils.blendARGB(oldBaseColor,
- newBaseColor, it.animatedFraction))
+ if (this is GradientDrawable) {
+ this.setColor(ColorUtils.blendARGB(oldColor, newClipColor,
+ it.animatedFraction))
+ }
+ baseLayer.setColor(ColorUtils.blendARGB(oldBaseColor, newBaseColor,
+ it.animatedFraction))
layout.alpha = MathUtils.lerp(oldAlpha, 1f, it.animatedFraction)
}
addListener(object : AnimatorListenerAdapter() {
@@ -345,7 +360,9 @@ class ControlViewHolder(
}
} else {
alpha = newAlpha
- setColor(newClipColor)
+ if (this is GradientDrawable) {
+ this.setColor(newClipColor)
+ }
baseLayer.setColor(newBaseColor)
layout.alpha = 1f
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index 3f17a4fefd7e8..ad2b785ea5c56 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -92,7 +92,9 @@ private val deviceColorMap = mapOf>(
(THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to
Pair(R.color.thermo_cool_foreground, R.color.control_enabled_thermo_cool_background),
DeviceTypes.TYPE_LIGHT
- to Pair(R.color.light_foreground, R.color.control_enabled_light_background)
+ to Pair(R.color.light_foreground, R.color.control_enabled_light_background),
+ DeviceTypes.TYPE_CAMERA
+ to Pair(R.color.camera_foreground, R.color.control_enabled_default_background)
).withDefault {
Pair(R.color.control_foreground, R.color.control_enabled_default_background)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
new file mode 100644
index 0000000000000..c2168aa8d9d9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.graphics.BlendMode
+import android.graphics.BlendModeColorFilter
+import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.LayerDrawable
+import android.view.View
+import android.service.controls.Control
+import android.service.controls.templates.ThumbnailTemplate
+import android.util.TypedValue
+
+import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
+import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
+
+/**
+ * Supports display of static images on the background of the tile. When marked active, the title
+ * and subtitle will not be visible. To be used with {@link Thumbnailtemplate} only.
+ */
+class ThumbnailBehavior : Behavior {
+ lateinit var template: ThumbnailTemplate
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ private var shadowOffsetX: Float = 0f
+ private var shadowOffsetY: Float = 0f
+ private var shadowRadius: Float = 0f
+ private var shadowColor: Int = 0
+
+ private val enabled: Boolean
+ get() = template.isActive()
+
+ override fun initialize(cvh: ControlViewHolder) {
+ this.cvh = cvh
+
+ val outValue = TypedValue()
+ cvh.context.resources.getValue(R.dimen.controls_thumbnail_shadow_x, outValue, true)
+ shadowOffsetX = outValue.getFloat()
+
+ cvh.context.resources.getValue(R.dimen.controls_thumbnail_shadow_y, outValue, true)
+ shadowOffsetY = outValue.getFloat()
+
+ cvh.context.resources.getValue(R.dimen.controls_thumbnail_shadow_radius, outValue, true)
+ shadowRadius = outValue.getFloat()
+
+ shadowColor = cvh.context.resources.getColor(R.color.control_thumbnail_shadow_color)
+ cvh.layout.setOnClickListener(View.OnClickListener() {
+ cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
+ })
+ }
+
+ override fun bind(cws: ControlWithState, colorOffset: Int) {
+ this.control = cws.control!!
+ cvh.setStatusText(control.getStatusText())
+ template = control.getControlTemplate() as ThumbnailTemplate
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ val clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+
+ clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
+
+ if (template.isActive()) {
+ cvh.title.visibility = View.INVISIBLE
+ cvh.subtitle.visibility = View.INVISIBLE
+ cvh.status.setShadowLayer(shadowOffsetX, shadowOffsetY, shadowRadius, shadowColor)
+
+ cvh.bgExecutor.execute {
+ val drawable = template.getThumbnail().loadDrawable(cvh.context)
+ cvh.uiExecutor.execute {
+ val radius = cvh.context.getResources()
+ .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
+ clipLayer.setDrawable(CornerDrawable(drawable, radius))
+ clipLayer.setColorFilter(BlendModeColorFilter(cvh.context.resources
+ .getColor(R.color.control_thumbnail_tint), BlendMode.LUMINOSITY))
+ cvh.applyRenderInfo(enabled, colorOffset)
+ }
+ }
+ } else {
+ cvh.title.visibility = View.VISIBLE
+ cvh.subtitle.visibility = View.VISIBLE
+ cvh.status.setShadowLayer(0f, 0f, 0f, shadowColor)
+ }
+
+ cvh.applyRenderInfo(enabled, colorOffset)
+ }
+}