Loading packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +124 −24 Original line number Diff line number Diff line Loading @@ -18,10 +18,15 @@ package com.android.systemui.controls.ui import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.animation.ValueAnimator import android.annotation.ColorRes import android.app.Dialog import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.ClipDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control Loading @@ -34,6 +39,7 @@ import android.service.controls.templates.TemperatureControlTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate import android.util.MathUtils import android.util.TypedValue import android.view.View import android.view.ViewGroup import android.widget.ImageView Loading Loading @@ -63,6 +69,8 @@ class ControlViewHolder( private const val UPDATE_DELAY_IN_MILLIS = 3000L private const val ALPHA_ENABLED = 255 private const val ALPHA_DISABLED = 0 private const val STATUS_ALPHA_ENABLED = 1f private const val STATUS_ALPHA_DIMMED = 0.45f private val FORCE_PANEL_DEVICES = setOf( DeviceTypes.TYPE_THERMOSTAT, DeviceTypes.TYPE_CAMERA Loading Loading @@ -94,9 +102,11 @@ class ControlViewHolder( private val toggleBackgroundIntensity: Float = layout.context.resources .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1) private var stateAnimator: ValueAnimator? = null private var statusAnimator: Animator? = null private val baseLayer: GradientDrawable val icon: ImageView = layout.requireViewById(R.id.icon) val status: TextView = layout.requireViewById(R.id.status) private 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) val context: Context = layout.getContext() Loading @@ -105,6 +115,7 @@ class ControlViewHolder( var cancelUpdate: Runnable? = null var behavior: Behavior? = null var lastAction: ControlAction? = null var isLoading = false private var lastChallengeDialog: Dialog? = null private val onDialogCancel: () -> Unit = { lastChallengeDialog = null } Loading Loading @@ -144,6 +155,7 @@ class ControlViewHolder( }) } isLoading = false behavior = bindBehavior(behavior, findBehaviorClass(controlStatus, template, deviceType)) updateContentDescription() } Loading Loading @@ -189,11 +201,11 @@ class ControlViewHolder( val previousText = status.getText() cancelUpdate = uiExecutor.executeDelayed({ status.setText(previousText) setStatusText(previousText) updateContentDescription() }, UPDATE_DELAY_IN_MILLIS) status.setText(tempStatus) setStatusText(tempStatus) updateContentDescription() } Loading Loading @@ -231,18 +243,50 @@ class ControlViewHolder( } internal fun applyRenderInfo(enabled: Boolean, offset: Int, animated: Boolean = true) { setEnabled(enabled) val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset) val fg = context.resources.getColorStateList(ri.foreground, context.theme) val newText = nextStatusText nextStatusText = "" val control = cws.control var shouldAnimate = animated if (newText == status.text) { shouldAnimate = false } animateStatusChange(shouldAnimate) { updateStatusRow(enabled, newText, ri.icon, fg, control) } animateBackgroundChange(shouldAnimate, enabled, ri.enabledBackground) } fun getStatusText() = status.text fun setStatusTextSize(textSize: Float) = status.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) fun setStatusText(text: CharSequence, immediately: Boolean = false) { if (immediately) { status.alpha = STATUS_ALPHA_ENABLED status.text = text nextStatusText = "" } else { nextStatusText = text } } private fun animateBackgroundChange( animated: Boolean, enabled: Boolean, @ColorRes bgColor: Int ) { val bg = context.resources.getColor(R.color.control_default_background, context.theme) var (newClipColor, newAlpha) = if (enabled) { // allow color overrides for the enabled state only val color = cws.control?.getCustomColor()?.let { val state = intArrayOf(android.R.attr.state_enabled) it.getColorForState(state, it.getDefaultColor()) } ?: context.resources.getColor(ri.enabledBackground, context.theme) } ?: context.resources.getColor(bgColor, context.theme) listOf(color, ALPHA_ENABLED) } else { listOf( Loading @@ -251,21 +295,6 @@ class ControlViewHolder( ) } status.setTextColor(fg) cws.control?.getCustomIcon()?.let { // do not tint custom icons, assume the intended icon color is correct icon.imageTintList = null icon.setImageIcon(it) } ?: run { icon.setImageDrawable(ri.icon) // do not color app icons if (deviceType != DeviceTypes.TYPE_ROUTINE) { icon.imageTintList = fg } } (clipLayer.getDrawable() as GradientDrawable).apply { val newBaseColor = if (behavior is ToggleRangeBehavior) { ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity) Loading Loading @@ -303,6 +332,77 @@ class ControlViewHolder( } } private fun animateStatusChange(animated: Boolean, statusRowUpdater: () -> Unit) { statusAnimator?.cancel() if (!animated) { statusRowUpdater.invoke() return } if (isLoading) { statusRowUpdater.invoke() statusAnimator = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_DIMMED).apply { repeatMode = ValueAnimator.REVERSE repeatCount = ValueAnimator.INFINITE duration = 500L interpolator = Interpolators.LINEAR startDelay = 900L start() } } else { val fadeOut = ObjectAnimator.ofFloat(status, "alpha", 0f).apply { duration = 200L interpolator = Interpolators.LINEAR addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { statusRowUpdater.invoke() } }) } val fadeIn = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_ENABLED).apply { duration = 200L interpolator = Interpolators.LINEAR } statusAnimator = AnimatorSet().apply { playSequentially(fadeOut, fadeIn) addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { status.alpha = STATUS_ALPHA_ENABLED statusAnimator = null } }) start() } } } private fun updateStatusRow( enabled: Boolean, text: CharSequence, drawable: Drawable, color: ColorStateList, control: Control? ) { setEnabled(enabled) status.text = text status.setTextColor(color) control?.getCustomIcon()?.let { // do not tint custom icons, assume the intended icon color is correct icon.imageTintList = null icon.setImageIcon(it) } ?: run { icon.setImageDrawable(drawable) // do not color app icons if (deviceType != DeviceTypes.TYPE_ROUTINE) { icon.imageTintList = color } } } private fun setEnabled(enabled: Boolean) { status.setEnabled(enabled) icon.setEnabled(enabled) Loading packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ class DefaultBehavior : Behavior { } override fun bind(cws: ControlWithState, colorOffset: Int) { cvh.status.setText(cws.control?.getStatusText() ?: "") cvh.setStatusText(cws.control?.getStatusText() ?: "") cvh.applyRenderInfo(false, colorOffset) } } packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +4 −6 Original line number Diff line number Diff line Loading @@ -97,6 +97,8 @@ private const val BUCKET_SIZE = 1000 private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE private val deviceColorMap = mapOf<Int, Pair<Int, Int>>( (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to Pair(R.color.control_default_foreground, R.color.control_default_background), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to Pair(R.color.thermo_heat_foreground, R.color.control_enabled_thermo_heat_background), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to Loading @@ -108,13 +110,9 @@ private val deviceColorMap = mapOf<Int, Pair<Int, Int>>( } private val deviceIconMap = mapOf<Int, IconState>( THERMOSTAT_RANGE to IconState( R.drawable.ic_device_thermostat_off, R.drawable.ic_device_thermostat_on ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to IconState( R.drawable.ic_device_thermostat_off, R.drawable.ic_device_thermostat_on R.drawable.ic_device_thermostat_off ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to IconState( R.drawable.ic_device_thermostat_off, Loading @@ -130,7 +128,7 @@ private val deviceIconMap = mapOf<Int, IconState>( ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_ECO) to IconState( R.drawable.ic_device_thermostat_off, R.drawable.ic_device_thermostat_on R.drawable.ic_device_thermostat_off ), DeviceTypes.TYPE_THERMOSTAT to IconState( R.drawable.ic_device_thermostat_off, Loading packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt +5 −2 Original line number Diff line number Diff line Loading @@ -32,9 +32,12 @@ class StatusBehavior : Behavior { val msg = when (status) { Control.STATUS_ERROR -> R.string.controls_error_generic Control.STATUS_NOT_FOUND -> R.string.controls_error_removed else -> com.android.internal.R.string.loading else -> { cvh.isLoading = true com.android.internal.R.string.loading } cvh.status.setText(cvh.context.getString(msg)) } cvh.setStatusText(cvh.context.getString(msg)) cvh.applyRenderInfo(false, colorOffset) } } packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt +1 −1 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ class TemperatureControlBehavior : Behavior { override fun bind(cws: ControlWithState, colorOffset: Int) { this.control = cws.control!! cvh.status.setText(control.getStatusText()) cvh.setStatusText(control.getStatusText()) val ld = cvh.layout.getBackground() as LayerDrawable clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) Loading Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +124 −24 Original line number Diff line number Diff line Loading @@ -18,10 +18,15 @@ package com.android.systemui.controls.ui import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.animation.ValueAnimator import android.annotation.ColorRes import android.app.Dialog import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.ClipDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control Loading @@ -34,6 +39,7 @@ import android.service.controls.templates.TemperatureControlTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate import android.util.MathUtils import android.util.TypedValue import android.view.View import android.view.ViewGroup import android.widget.ImageView Loading Loading @@ -63,6 +69,8 @@ class ControlViewHolder( private const val UPDATE_DELAY_IN_MILLIS = 3000L private const val ALPHA_ENABLED = 255 private const val ALPHA_DISABLED = 0 private const val STATUS_ALPHA_ENABLED = 1f private const val STATUS_ALPHA_DIMMED = 0.45f private val FORCE_PANEL_DEVICES = setOf( DeviceTypes.TYPE_THERMOSTAT, DeviceTypes.TYPE_CAMERA Loading Loading @@ -94,9 +102,11 @@ class ControlViewHolder( private val toggleBackgroundIntensity: Float = layout.context.resources .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1) private var stateAnimator: ValueAnimator? = null private var statusAnimator: Animator? = null private val baseLayer: GradientDrawable val icon: ImageView = layout.requireViewById(R.id.icon) val status: TextView = layout.requireViewById(R.id.status) private 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) val context: Context = layout.getContext() Loading @@ -105,6 +115,7 @@ class ControlViewHolder( var cancelUpdate: Runnable? = null var behavior: Behavior? = null var lastAction: ControlAction? = null var isLoading = false private var lastChallengeDialog: Dialog? = null private val onDialogCancel: () -> Unit = { lastChallengeDialog = null } Loading Loading @@ -144,6 +155,7 @@ class ControlViewHolder( }) } isLoading = false behavior = bindBehavior(behavior, findBehaviorClass(controlStatus, template, deviceType)) updateContentDescription() } Loading Loading @@ -189,11 +201,11 @@ class ControlViewHolder( val previousText = status.getText() cancelUpdate = uiExecutor.executeDelayed({ status.setText(previousText) setStatusText(previousText) updateContentDescription() }, UPDATE_DELAY_IN_MILLIS) status.setText(tempStatus) setStatusText(tempStatus) updateContentDescription() } Loading Loading @@ -231,18 +243,50 @@ class ControlViewHolder( } internal fun applyRenderInfo(enabled: Boolean, offset: Int, animated: Boolean = true) { setEnabled(enabled) val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset) val fg = context.resources.getColorStateList(ri.foreground, context.theme) val newText = nextStatusText nextStatusText = "" val control = cws.control var shouldAnimate = animated if (newText == status.text) { shouldAnimate = false } animateStatusChange(shouldAnimate) { updateStatusRow(enabled, newText, ri.icon, fg, control) } animateBackgroundChange(shouldAnimate, enabled, ri.enabledBackground) } fun getStatusText() = status.text fun setStatusTextSize(textSize: Float) = status.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) fun setStatusText(text: CharSequence, immediately: Boolean = false) { if (immediately) { status.alpha = STATUS_ALPHA_ENABLED status.text = text nextStatusText = "" } else { nextStatusText = text } } private fun animateBackgroundChange( animated: Boolean, enabled: Boolean, @ColorRes bgColor: Int ) { val bg = context.resources.getColor(R.color.control_default_background, context.theme) var (newClipColor, newAlpha) = if (enabled) { // allow color overrides for the enabled state only val color = cws.control?.getCustomColor()?.let { val state = intArrayOf(android.R.attr.state_enabled) it.getColorForState(state, it.getDefaultColor()) } ?: context.resources.getColor(ri.enabledBackground, context.theme) } ?: context.resources.getColor(bgColor, context.theme) listOf(color, ALPHA_ENABLED) } else { listOf( Loading @@ -251,21 +295,6 @@ class ControlViewHolder( ) } status.setTextColor(fg) cws.control?.getCustomIcon()?.let { // do not tint custom icons, assume the intended icon color is correct icon.imageTintList = null icon.setImageIcon(it) } ?: run { icon.setImageDrawable(ri.icon) // do not color app icons if (deviceType != DeviceTypes.TYPE_ROUTINE) { icon.imageTintList = fg } } (clipLayer.getDrawable() as GradientDrawable).apply { val newBaseColor = if (behavior is ToggleRangeBehavior) { ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity) Loading Loading @@ -303,6 +332,77 @@ class ControlViewHolder( } } private fun animateStatusChange(animated: Boolean, statusRowUpdater: () -> Unit) { statusAnimator?.cancel() if (!animated) { statusRowUpdater.invoke() return } if (isLoading) { statusRowUpdater.invoke() statusAnimator = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_DIMMED).apply { repeatMode = ValueAnimator.REVERSE repeatCount = ValueAnimator.INFINITE duration = 500L interpolator = Interpolators.LINEAR startDelay = 900L start() } } else { val fadeOut = ObjectAnimator.ofFloat(status, "alpha", 0f).apply { duration = 200L interpolator = Interpolators.LINEAR addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { statusRowUpdater.invoke() } }) } val fadeIn = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_ENABLED).apply { duration = 200L interpolator = Interpolators.LINEAR } statusAnimator = AnimatorSet().apply { playSequentially(fadeOut, fadeIn) addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { status.alpha = STATUS_ALPHA_ENABLED statusAnimator = null } }) start() } } } private fun updateStatusRow( enabled: Boolean, text: CharSequence, drawable: Drawable, color: ColorStateList, control: Control? ) { setEnabled(enabled) status.text = text status.setTextColor(color) control?.getCustomIcon()?.let { // do not tint custom icons, assume the intended icon color is correct icon.imageTintList = null icon.setImageIcon(it) } ?: run { icon.setImageDrawable(drawable) // do not color app icons if (deviceType != DeviceTypes.TYPE_ROUTINE) { icon.imageTintList = color } } } private fun setEnabled(enabled: Boolean) { status.setEnabled(enabled) icon.setEnabled(enabled) Loading
packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ class DefaultBehavior : Behavior { } override fun bind(cws: ControlWithState, colorOffset: Int) { cvh.status.setText(cws.control?.getStatusText() ?: "") cvh.setStatusText(cws.control?.getStatusText() ?: "") cvh.applyRenderInfo(false, colorOffset) } }
packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +4 −6 Original line number Diff line number Diff line Loading @@ -97,6 +97,8 @@ private const val BUCKET_SIZE = 1000 private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE private val deviceColorMap = mapOf<Int, Pair<Int, Int>>( (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to Pair(R.color.control_default_foreground, R.color.control_default_background), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to Pair(R.color.thermo_heat_foreground, R.color.control_enabled_thermo_heat_background), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to Loading @@ -108,13 +110,9 @@ private val deviceColorMap = mapOf<Int, Pair<Int, Int>>( } private val deviceIconMap = mapOf<Int, IconState>( THERMOSTAT_RANGE to IconState( R.drawable.ic_device_thermostat_off, R.drawable.ic_device_thermostat_on ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to IconState( R.drawable.ic_device_thermostat_off, R.drawable.ic_device_thermostat_on R.drawable.ic_device_thermostat_off ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to IconState( R.drawable.ic_device_thermostat_off, Loading @@ -130,7 +128,7 @@ private val deviceIconMap = mapOf<Int, IconState>( ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_ECO) to IconState( R.drawable.ic_device_thermostat_off, R.drawable.ic_device_thermostat_on R.drawable.ic_device_thermostat_off ), DeviceTypes.TYPE_THERMOSTAT to IconState( R.drawable.ic_device_thermostat_off, Loading
packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt +5 −2 Original line number Diff line number Diff line Loading @@ -32,9 +32,12 @@ class StatusBehavior : Behavior { val msg = when (status) { Control.STATUS_ERROR -> R.string.controls_error_generic Control.STATUS_NOT_FOUND -> R.string.controls_error_removed else -> com.android.internal.R.string.loading else -> { cvh.isLoading = true com.android.internal.R.string.loading } cvh.status.setText(cvh.context.getString(msg)) } cvh.setStatusText(cvh.context.getString(msg)) cvh.applyRenderInfo(false, colorOffset) } }
packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt +1 −1 Original line number Diff line number Diff line Loading @@ -39,7 +39,7 @@ class TemperatureControlBehavior : Behavior { override fun bind(cws: ControlWithState, colorOffset: Int) { this.control = cws.control!! cvh.status.setText(control.getStatusText()) cvh.setStatusText(control.getStatusText()) val ld = cvh.layout.getBackground() as LayerDrawable clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) Loading