Merge "Controls UI - Loading animations" into rvc-dev
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 17e4234..f979bbb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -18,10 +18,15 @@
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
@@ -34,6 +39,7 @@
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
@@ -63,6 +69,8 @@
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
@@ -94,9 +102,11 @@
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()
@@ -105,6 +115,7 @@
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 }
@@ -144,6 +155,7 @@
})
}
+ isLoading = false
behavior = bindBehavior(behavior, findBehaviorClass(controlStatus, template, deviceType))
updateContentDescription()
}
@@ -189,11 +201,11 @@
val previousText = status.getText()
cancelUpdate = uiExecutor.executeDelayed({
- status.setText(previousText)
- updateContentDescription()
- }, UPDATE_DELAY_IN_MILLIS)
+ setStatusText(previousText)
+ updateContentDescription()
+ }, UPDATE_DELAY_IN_MILLIS)
- status.setText(tempStatus)
+ setStatusText(tempStatus)
updateContentDescription()
}
@@ -231,18 +243,50 @@
}
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(
@@ -251,21 +295,6 @@
)
}
- 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)
@@ -303,6 +332,77 @@
}
}
+ 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)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
index 722ade9..0c8e3ff 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
@@ -24,7 +24,7 @@
}
override fun bind(cws: ControlWithState, colorOffset: Int) {
- cvh.status.setText(cws.control?.getStatusText() ?: "")
+ cvh.setStatusText(cws.control?.getStatusText() ?: "")
cvh.applyRenderInfo(false, colorOffset)
}
}
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 124df32..ba331f4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -97,6 +97,8 @@
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
@@ -108,13 +110,9 @@
}
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,
@@ -130,7 +128,7 @@
),
(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,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
index d8dceba..bf3835d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
@@ -32,9 +32,12 @@
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)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index 2795c7a..a7dc09b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -39,7 +39,7 @@
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)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index c432c09..dc7247c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -34,7 +34,6 @@
override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
- cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
cvh.layout.setOnClickListener(View.OnClickListener() {
cvh.controlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
@@ -44,7 +43,7 @@
override fun bind(cws: ControlWithState, colorOffset: Int) {
this.control = cws.control!!
- cvh.status.setText(control.getStatusText())
+ cvh.setStatusText(control.getStatusText())
val controlTemplate = control.getControlTemplate()
template = when (controlTemplate) {
is ToggleTemplate -> controlTemplate
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index a09ed09..1f0ca9b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -31,7 +31,6 @@
import android.service.controls.templates.ToggleRangeTemplate
import android.util.Log
import android.util.MathUtils
-import android.util.TypedValue
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
@@ -39,7 +38,6 @@
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
-import android.widget.TextView
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
@@ -57,7 +55,6 @@
lateinit var control: Control
lateinit var cvh: ControlViewHolder
lateinit var rangeTemplate: RangeTemplate
- lateinit var status: TextView
lateinit var context: Context
var currentStatusText: CharSequence = ""
var currentRangeValue: String = ""
@@ -71,10 +68,7 @@
override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
- status = cvh.status
- context = status.getContext()
-
- cvh.applyRenderInfo(false /* enabled */, colorOffset, false /* animated */)
+ context = cvh.context
val gestureListener = ToggleRangeGestureListener(cvh.layout)
val gestureDetector = GestureDetector(context, gestureListener)
@@ -131,7 +125,6 @@
this.colorOffset = colorOffset
currentStatusText = control.getStatusText()
- status.setText(currentStatusText)
// ControlViewHolder sets a long click listener, but we want to handle touch in
// here instead, otherwise we'll have state conflicts.
@@ -222,7 +215,7 @@
}
fun beginUpdateRange() {
- status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ cvh.setStatusTextSize(context.getResources()
.getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
}
@@ -261,14 +254,13 @@
val newValue = levelToRangeValue(newLevel)
currentRangeValue = format(rangeTemplate.getFormatString().toString(),
DEFAULT_FORMAT, newValue)
- val text = if (isDragging) {
- currentRangeValue
+ if (isDragging) {
+ cvh.setStatusText(currentRangeValue, /* immediately */ true)
} else {
- "$currentStatusText $currentRangeValue"
+ cvh.setStatusText("$currentStatusText $currentRangeValue")
}
- status.setText(text)
} else {
- status.setText(currentStatusText)
+ cvh.setStatusText(currentStatusText)
}
}
@@ -296,9 +288,9 @@
}
fun endUpdateRange() {
- status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ cvh.setStatusTextSize(context.getResources()
.getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
- status.setText("$currentStatusText $currentRangeValue")
+ cvh.setStatusText("$currentStatusText $currentRangeValue", /* immediately */ true)
cvh.action(FloatAction(rangeTemplate.getTemplateId(),
findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
index 8ce2e61..48f9458 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
@@ -23,6 +23,7 @@
import android.service.controls.templates.ControlTemplate
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
/**
@@ -37,7 +38,6 @@
override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
- cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
cvh.layout.setOnClickListener(View.OnClickListener() {
cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
@@ -46,13 +46,14 @@
override fun bind(cws: ControlWithState, colorOffset: Int) {
this.control = cws.control!!
- cvh.status.setText(control.getStatusText())
+ cvh.setStatusText(control.getStatusText())
template = control.getControlTemplate()
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
- clipLayer.setLevel(MIN_LEVEL)
- cvh.applyRenderInfo(false, colorOffset)
+ val enabled = if (colorOffset > 0) true else false
+ clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
+ cvh.applyRenderInfo(enabled, colorOffset)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 8e1854a..6274467 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -2221,7 +2221,7 @@
mBackgroundDrawable.setAlpha(0);
float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "transitionAlpha", 0f, 1f);
+ ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f);
alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
alphaAnimator.setDuration(183);
alphaAnimator.addUpdateListener((animation) -> {
@@ -2234,8 +2234,8 @@
ObjectAnimator xAnimator =
ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
- alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- alphaAnimator.setDuration(350);
+ xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ xAnimator.setDuration(350);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(alphaAnimator, xAnimator);
@@ -2247,7 +2247,7 @@
dismissWithAnimation(() -> {
mContainer.setTranslationX(0);
ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "transitionAlpha", 1f, 0f);
+ ObjectAnimator.ofFloat(mContainer, "alpha", 1f, 0f);
alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
alphaAnimator.setDuration(233);
alphaAnimator.addUpdateListener((animation) -> {
@@ -2261,8 +2261,8 @@
float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
ObjectAnimator xAnimator =
ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset);
- alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- alphaAnimator.setDuration(350);
+ xAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ xAnimator.setDuration(350);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(alphaAnimator, xAnimator);