Controls UI - Support RangeTemplate and TemperatureControlTemplate
Support RangeTemplate within ToggleRangeSupport by removing toggle functionality. Add back the ability to drag on a device that is off, as the recently defined behavior specifies that it should turn the device on. Fully support sub templates of TemperatureControlTemplate. Bug: 155494213 Test: mock app, use AC_UNIT and BLINDS Change-Id: Ifd8ee67956325358de7b035dfce27faab9fe49ce
This commit is contained in:
@@ -30,6 +30,9 @@ interface Behavior {
|
||||
|
||||
/**
|
||||
* Will be invoked on every update provided to the Control
|
||||
*
|
||||
* @param cws ControlWithState, as loaded from favorites and/or the application
|
||||
* @param colorOffset An additional flag to control rendering color. See [RenderInfo]
|
||||
*/
|
||||
fun bind(cws: ControlWithState)
|
||||
fun bind(cws: ControlWithState, colorOffset: Int = 0)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.service.controls.Control
|
||||
import android.service.controls.DeviceTypes
|
||||
import android.service.controls.actions.ControlAction
|
||||
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.ToggleRangeTemplate
|
||||
@@ -69,6 +70,25 @@ class ControlViewHolder(
|
||||
|
||||
const val MIN_LEVEL = 0
|
||||
const val MAX_LEVEL = 10000
|
||||
|
||||
fun findBehaviorClass(
|
||||
status: Int,
|
||||
template: ControlTemplate,
|
||||
deviceType: Int
|
||||
): KClass<out Behavior> {
|
||||
return when {
|
||||
status == Control.STATUS_UNKNOWN -> StatusBehavior::class
|
||||
status == Control.STATUS_ERROR -> StatusBehavior::class
|
||||
status == Control.STATUS_NOT_FOUND -> StatusBehavior::class
|
||||
deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
|
||||
template is ToggleTemplate -> ToggleBehavior::class
|
||||
template is StatelessTemplate -> TouchBehavior::class
|
||||
template is ToggleRangeTemplate -> ToggleRangeBehavior::class
|
||||
template is RangeTemplate -> ToggleRangeBehavior::class
|
||||
template is TemperatureControlTemplate -> TemperatureControlBehavior::class
|
||||
else -> DefaultBehavior::class
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val toggleBackgroundIntensity: Float = layout.context.resources
|
||||
@@ -123,18 +143,7 @@ class ControlViewHolder(
|
||||
})
|
||||
}
|
||||
|
||||
val clazz = findBehavior(controlStatus, template, deviceType)
|
||||
if (behavior == null || behavior!!::class != clazz) {
|
||||
// Behavior changes can signal a change in template from the app or
|
||||
// first time setup
|
||||
behavior = clazz.java.newInstance()
|
||||
behavior?.initialize(this)
|
||||
|
||||
// let behaviors define their own, if necessary, and clear any existing ones
|
||||
layout.setAccessibilityDelegate(null)
|
||||
}
|
||||
|
||||
behavior?.bind(cws)
|
||||
behavior = bindBehavior(behavior, findBehaviorClass(controlStatus, template, deviceType))
|
||||
|
||||
layout.setContentDescription("${title.text} ${subtitle.text} ${status.text}")
|
||||
}
|
||||
@@ -190,25 +199,30 @@ class ControlViewHolder(
|
||||
|
||||
fun usePanel(): Boolean = deviceType in ControlViewHolder.FORCE_PANEL_DEVICES
|
||||
|
||||
private fun findBehavior(
|
||||
status: Int,
|
||||
template: ControlTemplate,
|
||||
deviceType: Int
|
||||
): KClass<out Behavior> {
|
||||
return when {
|
||||
status == Control.STATUS_UNKNOWN -> StatusBehavior::class
|
||||
status == Control.STATUS_ERROR -> StatusBehavior::class
|
||||
status == Control.STATUS_NOT_FOUND -> StatusBehavior::class
|
||||
deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
|
||||
template is ToggleTemplate -> ToggleBehavior::class
|
||||
template is StatelessTemplate -> TouchBehavior::class
|
||||
template is ToggleRangeTemplate -> ToggleRangeBehavior::class
|
||||
template is TemperatureControlTemplate -> TemperatureControlBehavior::class
|
||||
else -> DefaultBehavior::class
|
||||
fun bindBehavior(
|
||||
existingBehavior: Behavior?,
|
||||
clazz: KClass<out Behavior>,
|
||||
offset: Int = 0
|
||||
): Behavior {
|
||||
val behavior = if (existingBehavior == null || existingBehavior!!::class != clazz) {
|
||||
// Behavior changes can signal a change in template from the app or
|
||||
// first time setup
|
||||
val newBehavior = clazz.java.newInstance()
|
||||
newBehavior.initialize(this)
|
||||
|
||||
// let behaviors define their own, if necessary, and clear any existing ones
|
||||
layout.setAccessibilityDelegate(null)
|
||||
newBehavior
|
||||
} else {
|
||||
existingBehavior
|
||||
}
|
||||
|
||||
return behavior.also {
|
||||
it.bind(cws, offset)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun applyRenderInfo(enabled: Boolean, offset: Int = 0, animated: Boolean = true) {
|
||||
internal fun applyRenderInfo(enabled: Boolean, offset: Int, animated: Boolean = true) {
|
||||
setEnabled(enabled)
|
||||
|
||||
val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)
|
||||
|
||||
@@ -23,8 +23,8 @@ class DefaultBehavior : Behavior {
|
||||
this.cvh = cvh
|
||||
}
|
||||
|
||||
override fun bind(cws: ControlWithState) {
|
||||
override fun bind(cws: ControlWithState, colorOffset: Int) {
|
||||
cvh.status.setText(cws.control?.getStatusText() ?: "")
|
||||
cvh.applyRenderInfo(false)
|
||||
cvh.applyRenderInfo(false, colorOffset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class StatusBehavior : Behavior {
|
||||
this.cvh = cvh
|
||||
}
|
||||
|
||||
override fun bind(cws: ControlWithState) {
|
||||
override fun bind(cws: ControlWithState, colorOffset: Int) {
|
||||
val status = cws.control?.status ?: Control.STATUS_UNKNOWN
|
||||
val msg = when (status) {
|
||||
Control.STATUS_ERROR -> R.string.controls_error_generic
|
||||
@@ -35,6 +35,6 @@ class StatusBehavior : Behavior {
|
||||
else -> com.android.internal.R.string.loading
|
||||
}
|
||||
cvh.status.setText(cvh.context.getString(msg))
|
||||
cvh.applyRenderInfo(false)
|
||||
cvh.applyRenderInfo(false, colorOffset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.controls.ui
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.templates.ControlTemplate
|
||||
import android.service.controls.templates.TemperatureControlTemplate
|
||||
|
||||
import com.android.systemui.R
|
||||
@@ -29,17 +30,13 @@ class TemperatureControlBehavior : Behavior {
|
||||
lateinit var clipLayer: Drawable
|
||||
lateinit var control: Control
|
||||
lateinit var cvh: ControlViewHolder
|
||||
lateinit var template: TemperatureControlTemplate
|
||||
var subBehavior: Behavior? = null
|
||||
|
||||
override fun initialize(cvh: ControlViewHolder) {
|
||||
this.cvh = cvh
|
||||
|
||||
cvh.layout.setOnClickListener { _ ->
|
||||
cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(cws: ControlWithState) {
|
||||
override fun bind(cws: ControlWithState, colorOffset: Int) {
|
||||
this.control = cws.control!!
|
||||
|
||||
cvh.status.setText(control.getStatusText())
|
||||
@@ -47,11 +44,32 @@ class TemperatureControlBehavior : Behavior {
|
||||
val ld = cvh.layout.getBackground() as LayerDrawable
|
||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
||||
|
||||
template = control.getControlTemplate() as TemperatureControlTemplate
|
||||
|
||||
val template = control.getControlTemplate() as TemperatureControlTemplate
|
||||
val activeMode = template.getCurrentActiveMode()
|
||||
val enabled = activeMode != 0 && activeMode != TemperatureControlTemplate.MODE_OFF
|
||||
clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
|
||||
cvh.applyRenderInfo(enabled, activeMode)
|
||||
val subTemplate = template.getTemplate()
|
||||
if (subTemplate == ControlTemplate.getNoTemplateObject() ||
|
||||
subTemplate == ControlTemplate.getErrorTemplate()) {
|
||||
// No sub template is specified, apply a default look with basic touch interaction.
|
||||
// Treat an error as no template.
|
||||
val enabled = activeMode != 0 && activeMode != TemperatureControlTemplate.MODE_OFF
|
||||
clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
|
||||
cvh.applyRenderInfo(enabled, activeMode)
|
||||
|
||||
cvh.layout.setOnClickListener { _ ->
|
||||
cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||
}
|
||||
} else {
|
||||
// A sub template has been specified, use this as the default behavior for user
|
||||
// interactions (touch, range)
|
||||
subBehavior = cvh.bindBehavior(
|
||||
subBehavior,
|
||||
ControlViewHolder.findBehaviorClass(
|
||||
control.status,
|
||||
subTemplate,
|
||||
control.deviceType
|
||||
),
|
||||
activeMode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ package com.android.systemui.controls.ui
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.templates.TemperatureControlTemplate
|
||||
import android.service.controls.templates.ToggleTemplate
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
|
||||
@@ -39,17 +41,25 @@ class ToggleBehavior : Behavior {
|
||||
})
|
||||
}
|
||||
|
||||
override fun bind(cws: ControlWithState) {
|
||||
override fun bind(cws: ControlWithState, colorOffset: Int) {
|
||||
this.control = cws.control!!
|
||||
|
||||
cvh.status.setText(control.getStatusText())
|
||||
template = control.getControlTemplate() as ToggleTemplate
|
||||
val controlTemplate = control.getControlTemplate()
|
||||
template = when (controlTemplate) {
|
||||
is ToggleTemplate -> controlTemplate
|
||||
is TemperatureControlTemplate -> controlTemplate.getTemplate() as ToggleTemplate
|
||||
else -> {
|
||||
Log.e(ControlsUiController.TAG, "Unsupported template type: $controlTemplate")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val ld = cvh.layout.getBackground() as LayerDrawable
|
||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
||||
clipLayer.level = MAX_LEVEL
|
||||
|
||||
val checked = template.isChecked()
|
||||
cvh.applyRenderInfo(checked)
|
||||
cvh.applyRenderInfo(checked, colorOffset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Bundle
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.actions.FloatAction
|
||||
import android.service.controls.templates.ControlTemplate
|
||||
import android.service.controls.templates.RangeTemplate
|
||||
import android.service.controls.templates.TemperatureControlTemplate
|
||||
import android.service.controls.templates.ToggleRangeTemplate
|
||||
import android.util.Log
|
||||
import android.util.MathUtils
|
||||
@@ -44,10 +46,14 @@ import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
|
||||
import java.util.IllegalFormatException
|
||||
|
||||
/**
|
||||
* Supports [ToggleRangeTemplate] and [RangeTemplate], as well as when one of those templates is
|
||||
* defined as the subtemplate in [TemperatureControlTemplate].
|
||||
*/
|
||||
class ToggleRangeBehavior : Behavior {
|
||||
private var rangeAnimator: ValueAnimator? = null
|
||||
lateinit var clipLayer: Drawable
|
||||
lateinit var template: ToggleRangeTemplate
|
||||
lateinit var templateId: String
|
||||
lateinit var control: Control
|
||||
lateinit var cvh: ControlViewHolder
|
||||
lateinit var rangeTemplate: RangeTemplate
|
||||
@@ -55,6 +61,9 @@ class ToggleRangeBehavior : Behavior {
|
||||
lateinit var context: Context
|
||||
var currentStatusText: CharSequence = ""
|
||||
var currentRangeValue: String = ""
|
||||
var isChecked: Boolean = false
|
||||
var isToggleable: Boolean = false
|
||||
var colorOffset: Int = 0
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_FORMAT = "%.1f"
|
||||
@@ -65,7 +74,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
status = cvh.status
|
||||
context = status.getContext()
|
||||
|
||||
cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
|
||||
cvh.applyRenderInfo(false /* enabled */, colorOffset, false /* animated */)
|
||||
|
||||
val gestureListener = ToggleRangeGestureListener(cvh.layout)
|
||||
val gestureDetector = GestureDetector(context, gestureListener)
|
||||
@@ -86,8 +95,40 @@ class ToggleRangeBehavior : Behavior {
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(cws: ControlWithState) {
|
||||
private fun setup(template: ToggleRangeTemplate) {
|
||||
rangeTemplate = template.getRange()
|
||||
isToggleable = true
|
||||
isChecked = template.isChecked()
|
||||
}
|
||||
|
||||
private fun setup(template: RangeTemplate) {
|
||||
rangeTemplate = template
|
||||
|
||||
// only show disabled state when value is at the minimum
|
||||
isChecked = rangeTemplate.currentValue != rangeTemplate.minValue
|
||||
}
|
||||
|
||||
private fun setupTemplate(template: ControlTemplate): Boolean {
|
||||
return when (template) {
|
||||
is ToggleRangeTemplate -> {
|
||||
setup(template)
|
||||
true
|
||||
}
|
||||
is RangeTemplate -> {
|
||||
setup(template)
|
||||
true
|
||||
}
|
||||
is TemperatureControlTemplate -> setupTemplate(template.getTemplate())
|
||||
else -> {
|
||||
Log.e(ControlsUiController.TAG, "Unsupported template type: $template")
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(cws: ControlWithState, colorOffset: Int) {
|
||||
this.control = cws.control!!
|
||||
this.colorOffset = colorOffset
|
||||
|
||||
currentStatusText = control.getStatusText()
|
||||
status.setText(currentStatusText)
|
||||
@@ -99,13 +140,14 @@ class ToggleRangeBehavior : Behavior {
|
||||
val ld = cvh.layout.getBackground() as LayerDrawable
|
||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
||||
|
||||
template = control.getControlTemplate() as ToggleRangeTemplate
|
||||
rangeTemplate = template.getRange()
|
||||
val template = control.getControlTemplate()
|
||||
if (!setupTemplate(template)) return
|
||||
templateId = template.getTemplateId()
|
||||
|
||||
val checked = template.isChecked()
|
||||
updateRange(rangeToLevelValue(rangeTemplate.currentValue), checked, /* isDragging */ false)
|
||||
updateRange(rangeToLevelValue(rangeTemplate.currentValue), isChecked,
|
||||
/* isDragging */ false)
|
||||
|
||||
cvh.applyRenderInfo(checked)
|
||||
cvh.applyRenderInfo(isChecked, colorOffset)
|
||||
|
||||
/*
|
||||
* This is custom widget behavior, so add a new accessibility delegate to
|
||||
@@ -141,9 +183,12 @@ class ToggleRangeBehavior : Behavior {
|
||||
): Boolean {
|
||||
val handled = when (action) {
|
||||
AccessibilityNodeInfo.ACTION_CLICK -> {
|
||||
cvh.controlActionCoordinator.toggle(cvh, template.getTemplateId(),
|
||||
template.isChecked())
|
||||
true
|
||||
if (!isToggleable) {
|
||||
false
|
||||
} else {
|
||||
cvh.controlActionCoordinator.toggle(cvh, templateId, isChecked)
|
||||
true
|
||||
}
|
||||
}
|
||||
AccessibilityNodeInfo.ACTION_LONG_CLICK -> {
|
||||
cvh.controlActionCoordinator.longPress(cvh)
|
||||
@@ -157,7 +202,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
val value = arguments.getFloat(
|
||||
AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)
|
||||
val level = rangeToLevelValue(value)
|
||||
updateRange(level, template.isChecked(), /* isDragging */ true)
|
||||
updateRange(level, isChecked, /* isDragging */ true)
|
||||
endUpdateRange()
|
||||
true
|
||||
}
|
||||
@@ -182,7 +227,14 @@ class ToggleRangeBehavior : Behavior {
|
||||
}
|
||||
|
||||
fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) {
|
||||
val newLevel = if (checked) Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, level)) else MIN_LEVEL
|
||||
val newLevel = Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, level))
|
||||
|
||||
// If the current level is at the minimum and the user is dragging, set the control to
|
||||
// the enabled state to indicate their intention to enable the device. This will update
|
||||
// control colors to support dragging.
|
||||
if (clipLayer.level == MIN_LEVEL && newLevel > MIN_LEVEL) {
|
||||
cvh.applyRenderInfo(checked, colorOffset, false /* animated */)
|
||||
}
|
||||
|
||||
rangeAnimator?.cancel()
|
||||
if (isDragging) {
|
||||
@@ -282,7 +334,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
if (isDragging) {
|
||||
return
|
||||
}
|
||||
cvh.controlActionCoordinator.longPress(this@ToggleRangeBehavior.cvh)
|
||||
cvh.controlActionCoordinator.longPress(cvh)
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
@@ -291,26 +343,21 @@ class ToggleRangeBehavior : Behavior {
|
||||
xDiff: Float,
|
||||
yDiff: Float
|
||||
): Boolean {
|
||||
if (!template.isChecked) {
|
||||
return false
|
||||
}
|
||||
if (!isDragging) {
|
||||
v.getParent().requestDisallowInterceptTouchEvent(true)
|
||||
this@ToggleRangeBehavior.beginUpdateRange()
|
||||
beginUpdateRange()
|
||||
isDragging = true
|
||||
}
|
||||
|
||||
val ratioDiff = -xDiff / v.width
|
||||
val changeAmount = ((MAX_LEVEL - MIN_LEVEL) * ratioDiff).toInt()
|
||||
this@ToggleRangeBehavior.updateRange(clipLayer.level + changeAmount,
|
||||
checked = true, isDragging = true)
|
||||
updateRange(clipLayer.level + changeAmount, checked = true, isDragging = true)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||
val th = this@ToggleRangeBehavior
|
||||
cvh.controlActionCoordinator.toggle(th.cvh, th.template.getTemplateId(),
|
||||
th.template.isChecked())
|
||||
if (!isToggleable) return false
|
||||
cvh.controlActionCoordinator.toggle(cvh, templateId, isChecked)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class TouchBehavior : Behavior {
|
||||
})
|
||||
}
|
||||
|
||||
override fun bind(cws: ControlWithState) {
|
||||
override fun bind(cws: ControlWithState, colorOffset: Int) {
|
||||
this.control = cws.control!!
|
||||
cvh.status.setText(control.getStatusText())
|
||||
template = control.getControlTemplate()
|
||||
@@ -53,6 +53,6 @@ class TouchBehavior : Behavior {
|
||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
||||
clipLayer.setLevel(MIN_LEVEL)
|
||||
|
||||
cvh.applyRenderInfo(false)
|
||||
cvh.applyRenderInfo(false, colorOffset)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user