Merge "Controls UI - Part 1 - Thumbnail support"

This commit is contained in:
TreeHugger Robot
2020-12-10 15:04:09 +00:00
committed by Android (Google) Code Review
6 changed files with 159 additions and 10 deletions

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright 2010, 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.
*/
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
<item android:color="@color/GM2_grey_100" />
</selector>

View File

@@ -257,6 +257,8 @@
<color name="control_enabled_heat_foreground">#FF8B66</color>
<color name="control_enabled_default_foreground">@color/GM2_blue_300</color>
<color name="control_enabled_cool_foreground">@color/GM2_blue_300</color>
<color name="control_thumbnail_tint">#33000000</color>
<color name="control_thumbnail_shadow_color">@*android:color/black</color>
<!-- Docked misalignment message -->
<color name="misalignment_text_color">#F28B82</color>

View File

@@ -1269,6 +1269,10 @@
<dimen name="controls_app_divider_height">2dp</dimen>
<dimen name="controls_app_divider_side_margin">32dp</dimen>
<item name="controls_thumbnail_shadow_x" type="dimen" format="float">2.0</item>
<item name="controls_thumbnail_shadow_y" type="dimen" format="float">2.0</item>
<item name="controls_thumbnail_shadow_radius" type="dimen" format="float">2.0</item>
<dimen name="controls_card_margin">@dimen/control_base_item_margin</dimen>
<item name="control_card_elevation" type="dimen" format="float">15</item>

View File

@@ -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<out Behavior> {
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
}

View File

@@ -92,7 +92,9 @@ private val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
(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)
}

View File

@@ -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)
}
}