Merge "Controls Ui - Handle error scenarios" into rvc-dev am: c046b5959c
Change-Id: I996ed955c6d9c81d29f1d2ea57d2d3f5c0361aeb
This commit is contained in:
@@ -26,7 +26,6 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp"
|
android:minHeight="48dp"
|
||||||
android:hint="@string/controls_pin_instructions"
|
|
||||||
android:inputType="numberPassword" />
|
android:inputType="numberPassword" />
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/controls_pin_use_alpha"
|
android:id="@+id/controls_pin_use_alpha"
|
||||||
|
|||||||
@@ -2714,6 +2714,8 @@
|
|||||||
<string name="controls_pin_use_alphanumeric">PIN contains letters or symbols</string>
|
<string name="controls_pin_use_alphanumeric">PIN contains letters or symbols</string>
|
||||||
<!-- Controls PIN entry dialog, title [CHAR LIMIT=30] -->
|
<!-- Controls PIN entry dialog, title [CHAR LIMIT=30] -->
|
||||||
<string name="controls_pin_verify">Verify <xliff:g id="device" example="Backdoor lock">%s</xliff:g></string>
|
<string name="controls_pin_verify">Verify <xliff:g id="device" example="Backdoor lock">%s</xliff:g></string>
|
||||||
|
<!-- Controls PIN entry dialog, title when 1st attempt failed [CHAR LIMIT=30] -->
|
||||||
|
<string name="controls_pin_wrong">Wrong PIN</string>
|
||||||
<!-- Controls PIN entry dialog, waiting to verify [CHAR LIMIT=30] -->
|
<!-- Controls PIN entry dialog, waiting to verify [CHAR LIMIT=30] -->
|
||||||
<string name="controls_pin_verifying">Verifying\u2026</string>
|
<string name="controls_pin_verifying">Verifying\u2026</string>
|
||||||
<!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] -->
|
<!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] -->
|
||||||
@@ -2737,6 +2739,13 @@
|
|||||||
|
|
||||||
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
|
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
|
||||||
<string name="controls_error_timeout">Inactive, check app</string>
|
<string name="controls_error_timeout">Inactive, check app</string>
|
||||||
|
<!-- Error message indicating that an unspecified error occurred while getting the status, and
|
||||||
|
a retry will be attempted [CHAR LIMIT=30] -->
|
||||||
|
<string name="controls_error_retryable">Error, retrying\u2026</string>
|
||||||
|
<!-- Error message indicating that the control is no longer available in the application [CHAR LIMIT=30] -->
|
||||||
|
<string name="controls_error_removed">Device removed</string>
|
||||||
|
<!-- Error message indicating that an unspecified error occurred while getting the status [CHAR LIMIT=30] -->
|
||||||
|
<string name="controls_error_generic">Can\u2019t load status</string>
|
||||||
<!-- Error message indicating that a control action failed [CHAR_LIMIT=30] -->
|
<!-- Error message indicating that a control action failed [CHAR_LIMIT=30] -->
|
||||||
<string name="controls_error_failed">Error, try again</string>
|
<string name="controls_error_failed">Error, try again</string>
|
||||||
<!-- Stateless control message informing the user that a routine has started [CHAR_LIMIT=30] -->
|
<!-- Stateless control message informing the user that a routine has started [CHAR_LIMIT=30] -->
|
||||||
|
|||||||
@@ -47,16 +47,31 @@ object ChallengeDialogs {
|
|||||||
* [ControlAction#RESPONSE_CHALLENGE_PIN] responses, decided by the useAlphaNumeric
|
* [ControlAction#RESPONSE_CHALLENGE_PIN] responses, decided by the useAlphaNumeric
|
||||||
* parameter.
|
* parameter.
|
||||||
*/
|
*/
|
||||||
fun createPinDialog(cvh: ControlViewHolder, useAlphaNumeric: Boolean): Dialog? {
|
fun createPinDialog(
|
||||||
|
cvh: ControlViewHolder,
|
||||||
|
useAlphaNumeric: Boolean,
|
||||||
|
useRetryStrings: Boolean
|
||||||
|
): Dialog? {
|
||||||
val lastAction = cvh.lastAction
|
val lastAction = cvh.lastAction
|
||||||
if (lastAction == null) {
|
if (lastAction == null) {
|
||||||
Log.e(ControlsUiController.TAG,
|
Log.e(ControlsUiController.TAG,
|
||||||
"PIN Dialog attempted but no last action is set. Will not show")
|
"PIN Dialog attempted but no last action is set. Will not show")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
val res = cvh.context.resources
|
||||||
|
val (title, instructions) = if (useRetryStrings) {
|
||||||
|
Pair(
|
||||||
|
res.getString(R.string.controls_pin_wrong),
|
||||||
|
R.string.controls_pin_instructions_retry
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Pair(
|
||||||
|
res.getString(R.string.controls_pin_verify, cvh.title.getText()),
|
||||||
|
R.string.controls_pin_instructions
|
||||||
|
)
|
||||||
|
}
|
||||||
val builder = AlertDialog.Builder(cvh.context, STYLE).apply {
|
val builder = AlertDialog.Builder(cvh.context, STYLE).apply {
|
||||||
val res = cvh.context.resources
|
setTitle(title)
|
||||||
setTitle(res.getString(R.string.controls_pin_verify, cvh.title.getText()))
|
|
||||||
setView(R.layout.controls_dialog_pin)
|
setView(R.layout.controls_dialog_pin)
|
||||||
setPositiveButton(
|
setPositiveButton(
|
||||||
android.R.string.ok,
|
android.R.string.ok,
|
||||||
@@ -81,6 +96,7 @@ object ChallengeDialogs {
|
|||||||
}
|
}
|
||||||
setOnShowListener(DialogInterface.OnShowListener { _ ->
|
setOnShowListener(DialogInterface.OnShowListener { _ ->
|
||||||
val editText = requireViewById<EditText>(R.id.controls_pin_input)
|
val editText = requireViewById<EditText>(R.id.controls_pin_input)
|
||||||
|
editText.setHint(instructions)
|
||||||
val useAlphaCheckBox = requireViewById<CheckBox>(R.id.controls_pin_use_alpha)
|
val useAlphaCheckBox = requireViewById<CheckBox>(R.id.controls_pin_use_alpha)
|
||||||
useAlphaCheckBox.setChecked(useAlphaNumeric)
|
useAlphaCheckBox.setChecked(useAlphaNumeric)
|
||||||
setInputType(editText, useAlphaCheckBox.isChecked())
|
setInputType(editText, useAlphaCheckBox.isChecked())
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.systemui.controls.ui
|
|||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.ClipDrawable
|
import android.graphics.drawable.ClipDrawable
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
@@ -82,6 +83,8 @@ class ControlViewHolder(
|
|||||||
var cancelUpdate: Runnable? = null
|
var cancelUpdate: Runnable? = null
|
||||||
var behavior: Behavior? = null
|
var behavior: Behavior? = null
|
||||||
var lastAction: ControlAction? = null
|
var lastAction: ControlAction? = null
|
||||||
|
private var lastChallengeDialog: Dialog? = null
|
||||||
|
|
||||||
val deviceType: Int
|
val deviceType: Int
|
||||||
get() = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType
|
get() = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType
|
||||||
var dimmed: Boolean = false
|
var dimmed: Boolean = false
|
||||||
@@ -140,7 +143,37 @@ class ControlViewHolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun actionResponse(@ControlAction.ResponseResult response: Int) {
|
fun actionResponse(@ControlAction.ResponseResult response: Int) {
|
||||||
// TODO: b/150931809 - handle response codes
|
// OK responses signal normal behavior, and the app will provide control updates
|
||||||
|
val failedAttempt = lastChallengeDialog != null
|
||||||
|
when (response) {
|
||||||
|
ControlAction.RESPONSE_OK ->
|
||||||
|
lastChallengeDialog = null
|
||||||
|
ControlAction.RESPONSE_UNKNOWN -> {
|
||||||
|
lastChallengeDialog = null
|
||||||
|
setTransientStatus(context.resources.getString(R.string.controls_error_failed))
|
||||||
|
}
|
||||||
|
ControlAction.RESPONSE_FAIL -> {
|
||||||
|
lastChallengeDialog = null
|
||||||
|
setTransientStatus(context.resources.getString(R.string.controls_error_failed))
|
||||||
|
}
|
||||||
|
ControlAction.RESPONSE_CHALLENGE_PIN -> {
|
||||||
|
lastChallengeDialog = ChallengeDialogs.createPinDialog(this, false, failedAttempt)
|
||||||
|
lastChallengeDialog?.show()
|
||||||
|
}
|
||||||
|
ControlAction.RESPONSE_CHALLENGE_PASSPHRASE -> {
|
||||||
|
lastChallengeDialog = ChallengeDialogs.createPinDialog(this, true, failedAttempt)
|
||||||
|
lastChallengeDialog?.show()
|
||||||
|
}
|
||||||
|
ControlAction.RESPONSE_CHALLENGE_ACK -> {
|
||||||
|
lastChallengeDialog = ChallengeDialogs.createConfirmationDialog(this)
|
||||||
|
lastChallengeDialog?.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismiss() {
|
||||||
|
lastChallengeDialog?.dismiss()
|
||||||
|
lastChallengeDialog = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTransientStatus(tempStatus: String) {
|
fun setTransientStatus(tempStatus: String) {
|
||||||
@@ -166,7 +199,9 @@ class ControlViewHolder(
|
|||||||
deviceType: Int
|
deviceType: Int
|
||||||
): KClass<out Behavior> {
|
): KClass<out Behavior> {
|
||||||
return when {
|
return when {
|
||||||
status == Control.STATUS_UNKNOWN -> UnknownBehavior::class
|
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
|
deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
|
||||||
template is ToggleTemplate -> ToggleBehavior::class
|
template is ToggleTemplate -> ToggleBehavior::class
|
||||||
template is StatelessTemplate -> TouchBehavior::class
|
template is StatelessTemplate -> TouchBehavior::class
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import android.animation.Animator
|
|||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
@@ -32,7 +31,6 @@ import android.graphics.drawable.LayerDrawable
|
|||||||
import android.os.Process
|
import android.os.Process
|
||||||
import android.os.Vibrator
|
import android.os.Vibrator
|
||||||
import android.service.controls.Control
|
import android.service.controls.Control
|
||||||
import android.service.controls.actions.ControlAction
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.ContextThemeWrapper
|
import android.view.ContextThemeWrapper
|
||||||
@@ -101,7 +99,6 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
private lateinit var parent: ViewGroup
|
private lateinit var parent: ViewGroup
|
||||||
private lateinit var lastItems: List<SelectionItem>
|
private lateinit var lastItems: List<SelectionItem>
|
||||||
private var popup: ListPopupWindow? = null
|
private var popup: ListPopupWindow? = null
|
||||||
private var activeDialog: Dialog? = null
|
|
||||||
private var hidden = true
|
private var hidden = true
|
||||||
private lateinit var dismissGlobalActions: Runnable
|
private lateinit var dismissGlobalActions: Runnable
|
||||||
|
|
||||||
@@ -537,7 +534,11 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
Log.d(ControlsUiController.TAG, "hide()")
|
Log.d(ControlsUiController.TAG, "hide()")
|
||||||
hidden = true
|
hidden = true
|
||||||
popup?.dismissImmediate()
|
popup?.dismissImmediate()
|
||||||
activeDialog?.dismiss()
|
|
||||||
|
controlViewsById.forEach {
|
||||||
|
it.value.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
ControlActionCoordinator.closeDialog()
|
ControlActionCoordinator.closeDialog()
|
||||||
|
|
||||||
controlsController.get().unsubscribe()
|
controlsController.get().unsubscribe()
|
||||||
@@ -551,7 +552,6 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
|
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
|
||||||
Log.d(ControlsUiController.TAG, "onRefreshState()")
|
|
||||||
controls.forEach { c ->
|
controls.forEach { c ->
|
||||||
controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
|
controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
|
||||||
Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId())
|
Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId())
|
||||||
@@ -569,23 +569,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
|
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
|
||||||
val key = ControlKey(componentName, controlId)
|
val key = ControlKey(componentName, controlId)
|
||||||
uiExecutor.execute {
|
uiExecutor.execute {
|
||||||
controlViewsById.get(key)?.let { cvh ->
|
controlViewsById.get(key)?.actionResponse(response)
|
||||||
when (response) {
|
|
||||||
ControlAction.RESPONSE_CHALLENGE_PIN -> {
|
|
||||||
activeDialog = ChallengeDialogs.createPinDialog(cvh, false)
|
|
||||||
activeDialog?.show()
|
|
||||||
}
|
|
||||||
ControlAction.RESPONSE_CHALLENGE_PASSPHRASE -> {
|
|
||||||
activeDialog = ChallengeDialogs.createPinDialog(cvh, true)
|
|
||||||
activeDialog?.show()
|
|
||||||
}
|
|
||||||
ControlAction.RESPONSE_CHALLENGE_ACK -> {
|
|
||||||
activeDialog = ChallengeDialogs.createConfirmationDialog(cvh)
|
|
||||||
activeDialog?.show()
|
|
||||||
}
|
|
||||||
else -> cvh.actionResponse(response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,11 @@
|
|||||||
|
|
||||||
package com.android.systemui.controls.ui
|
package com.android.systemui.controls.ui
|
||||||
|
|
||||||
class UnknownBehavior : Behavior {
|
import android.service.controls.Control
|
||||||
|
|
||||||
|
import com.android.systemui.R
|
||||||
|
|
||||||
|
class StatusBehavior : Behavior {
|
||||||
lateinit var cvh: ControlViewHolder
|
lateinit var cvh: ControlViewHolder
|
||||||
|
|
||||||
override fun initialize(cvh: ControlViewHolder) {
|
override fun initialize(cvh: ControlViewHolder) {
|
||||||
@@ -24,7 +28,13 @@ class UnknownBehavior : Behavior {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun bind(cws: ControlWithState) {
|
override fun bind(cws: ControlWithState) {
|
||||||
cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading))
|
val status = cws.control?.status ?: Control.STATUS_UNKNOWN
|
||||||
|
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
|
||||||
|
}
|
||||||
|
cvh.status.setText(cvh.context.getString(msg))
|
||||||
cvh.applyRenderInfo(false)
|
cvh.applyRenderInfo(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user