Loading packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +22 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,28 @@ interface ControlActionCoordinator { */ fun drag(isEdge: Boolean) /** * Send a request to update the value of a device using the [FloatAction]. * * @param cvh [ControlViewHolder] for the control * @param templateId id of the control's template, as given by the service * @param newValue value to set for the device */ fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) /** * Actions may have been put on hold while the device is unlocked. Invoke this action if * present. */ fun runPendingAction(controlId: String) /** * User interaction with a control may be blocked for a period of time while actions are being * executed by the application. When the response returns, run this method to enable further * user interaction. */ fun enableActionOnTouch(controlId: String) /** * All long presses will be shown in a 3/4 height bottomsheet panel, in order for the user to * retain context with their favorited controls in the power menu. Loading packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +61 −16 Original line number Diff line number Diff line Loading @@ -21,11 +21,13 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.annotation.MainThread import android.os.Vibrator import android.os.VibrationEffect import android.service.controls.Control import android.service.controls.actions.BooleanAction import android.service.controls.actions.CommandAction import android.service.controls.actions.FloatAction import android.util.Log import android.view.HapticFeedbackConstants import com.android.systemui.dagger.qualifiers.Main Loading @@ -49,7 +51,12 @@ class ControlActionCoordinatorImpl @Inject constructor( ) : ControlActionCoordinator { private var dialog: Dialog? = null private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator private var lastAction: (() -> Unit)? = null private var pendingAction: Action? = null private var actionsInProgress = mutableSetOf<String>() companion object { private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L } override fun closeDialogs() { dialog?.dismiss() Loading @@ -57,54 +64,84 @@ class ControlActionCoordinatorImpl @Inject constructor( } override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) { bouncerOrRun { bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) cvh.action(BooleanAction(templateId, !isChecked)) } }, true /* blockable */)) } override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) { bouncerOrRun { val blockable = cvh.usePanel() bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) if (cvh.usePanel()) { showDialog(cvh, control.getAppIntent().getIntent()) } else { cvh.action(CommandAction(templateId)) } } }, blockable)) } override fun drag(isEdge: Boolean) { bouncerOrRun { if (isEdge) { vibrate(Vibrations.rangeEdgeEffect) } else { vibrate(Vibrations.rangeMiddleEffect) } } override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) { bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.action(FloatAction(templateId, newValue)) }, true /* blockable */)) } override fun longPress(cvh: ControlViewHolder) { bouncerOrRun { bouncerOrRun(Action(cvh.cws.ci.controlId, { // Long press snould only be called when there is valid control state, otherwise ignore cvh.cws.control?.let { cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) showDialog(cvh, it.getAppIntent().getIntent()) } }, false /* blockable */)) } override fun runPendingAction(controlId: String) { if (pendingAction?.controlId == controlId) { pendingAction?.invoke() pendingAction = null } } private fun bouncerOrRun(f: () -> Unit) { @MainThread override fun enableActionOnTouch(controlId: String) { actionsInProgress.remove(controlId) } private fun shouldRunAction(controlId: String) = if (actionsInProgress.add(controlId)) { uiExecutor.executeDelayed({ actionsInProgress.remove(controlId) }, RESPONSE_TIMEOUT_IN_MILLIS) true } else { false } private fun bouncerOrRun(action: Action) { if (!keyguardStateController.isUnlocked()) { context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) // pending actions will only run after the control state has been refreshed pendingAction = action activityStarter.dismissKeyguardThenExecute({ Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action") globalActionsComponent.handleShowGlobalActionsMenu() f() true }, null, true) }, { pendingAction = null }, true /* afterKeyguardGone */) } else { f() action.invoke() } } Loading Loading @@ -133,4 +170,12 @@ class ControlActionCoordinatorImpl @Inject constructor( } } } inner class Action(val controlId: String, val f: () -> Unit, val blockable: Boolean) { fun invoke() { if (!blockable || shouldRunAction(controlId)) { f.invoke() } } } } packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +4 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,8 @@ class ControlViewHolder( controlActionCoordinator.longPress(this@ControlViewHolder) true }) controlActionCoordinator.runPendingAction(cws.ci.controlId) } isLoading = false Loading @@ -164,6 +166,8 @@ class ControlViewHolder( } fun actionResponse(@ControlAction.ResponseResult response: Int) { controlActionCoordinator.enableActionOnTouch(cws.ci.controlId) // OK responses signal normal behavior, and the app will provide control updates val failedAttempt = lastChallengeDialog != null when (response) { Loading packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +2 −3 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.graphics.drawable.Drawable 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 Loading Loading @@ -293,8 +292,8 @@ class ToggleRangeBehavior : Behavior { cvh.setStatusTextSize(context.getResources() .getDimensionPixelSize(R.dimen.control_status_normal).toFloat()) cvh.setStatusText("$currentStatusText $currentRangeValue", /* immediately */ true) cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue(clipLayer.getLevel())))) cvh.controlActionCoordinator.setValue(cvh, rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue(clipLayer.getLevel()))) } fun findNearestStep(value: Float): Float { Loading Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +22 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,28 @@ interface ControlActionCoordinator { */ fun drag(isEdge: Boolean) /** * Send a request to update the value of a device using the [FloatAction]. * * @param cvh [ControlViewHolder] for the control * @param templateId id of the control's template, as given by the service * @param newValue value to set for the device */ fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) /** * Actions may have been put on hold while the device is unlocked. Invoke this action if * present. */ fun runPendingAction(controlId: String) /** * User interaction with a control may be blocked for a period of time while actions are being * executed by the application. When the response returns, run this method to enable further * user interaction. */ fun enableActionOnTouch(controlId: String) /** * All long presses will be shown in a 3/4 height bottomsheet panel, in order for the user to * retain context with their favorited controls in the power menu. Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +61 −16 Original line number Diff line number Diff line Loading @@ -21,11 +21,13 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.annotation.MainThread import android.os.Vibrator import android.os.VibrationEffect import android.service.controls.Control import android.service.controls.actions.BooleanAction import android.service.controls.actions.CommandAction import android.service.controls.actions.FloatAction import android.util.Log import android.view.HapticFeedbackConstants import com.android.systemui.dagger.qualifiers.Main Loading @@ -49,7 +51,12 @@ class ControlActionCoordinatorImpl @Inject constructor( ) : ControlActionCoordinator { private var dialog: Dialog? = null private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator private var lastAction: (() -> Unit)? = null private var pendingAction: Action? = null private var actionsInProgress = mutableSetOf<String>() companion object { private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L } override fun closeDialogs() { dialog?.dismiss() Loading @@ -57,54 +64,84 @@ class ControlActionCoordinatorImpl @Inject constructor( } override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) { bouncerOrRun { bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) cvh.action(BooleanAction(templateId, !isChecked)) } }, true /* blockable */)) } override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) { bouncerOrRun { val blockable = cvh.usePanel() bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) if (cvh.usePanel()) { showDialog(cvh, control.getAppIntent().getIntent()) } else { cvh.action(CommandAction(templateId)) } } }, blockable)) } override fun drag(isEdge: Boolean) { bouncerOrRun { if (isEdge) { vibrate(Vibrations.rangeEdgeEffect) } else { vibrate(Vibrations.rangeMiddleEffect) } } override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) { bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.action(FloatAction(templateId, newValue)) }, true /* blockable */)) } override fun longPress(cvh: ControlViewHolder) { bouncerOrRun { bouncerOrRun(Action(cvh.cws.ci.controlId, { // Long press snould only be called when there is valid control state, otherwise ignore cvh.cws.control?.let { cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) showDialog(cvh, it.getAppIntent().getIntent()) } }, false /* blockable */)) } override fun runPendingAction(controlId: String) { if (pendingAction?.controlId == controlId) { pendingAction?.invoke() pendingAction = null } } private fun bouncerOrRun(f: () -> Unit) { @MainThread override fun enableActionOnTouch(controlId: String) { actionsInProgress.remove(controlId) } private fun shouldRunAction(controlId: String) = if (actionsInProgress.add(controlId)) { uiExecutor.executeDelayed({ actionsInProgress.remove(controlId) }, RESPONSE_TIMEOUT_IN_MILLIS) true } else { false } private fun bouncerOrRun(action: Action) { if (!keyguardStateController.isUnlocked()) { context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) // pending actions will only run after the control state has been refreshed pendingAction = action activityStarter.dismissKeyguardThenExecute({ Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action") globalActionsComponent.handleShowGlobalActionsMenu() f() true }, null, true) }, { pendingAction = null }, true /* afterKeyguardGone */) } else { f() action.invoke() } } Loading Loading @@ -133,4 +170,12 @@ class ControlActionCoordinatorImpl @Inject constructor( } } } inner class Action(val controlId: String, val f: () -> Unit, val blockable: Boolean) { fun invoke() { if (!blockable || shouldRunAction(controlId)) { f.invoke() } } } }
packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +4 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,8 @@ class ControlViewHolder( controlActionCoordinator.longPress(this@ControlViewHolder) true }) controlActionCoordinator.runPendingAction(cws.ci.controlId) } isLoading = false Loading @@ -164,6 +166,8 @@ class ControlViewHolder( } fun actionResponse(@ControlAction.ResponseResult response: Int) { controlActionCoordinator.enableActionOnTouch(cws.ci.controlId) // OK responses signal normal behavior, and the app will provide control updates val failedAttempt = lastChallengeDialog != null when (response) { Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +2 −3 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.graphics.drawable.Drawable 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 Loading Loading @@ -293,8 +292,8 @@ class ToggleRangeBehavior : Behavior { cvh.setStatusTextSize(context.getResources() .getDimensionPixelSize(R.dimen.control_status_normal).toFloat()) cvh.setStatusText("$currentStatusText $currentRangeValue", /* immediately */ true) cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue(clipLayer.getLevel())))) cvh.controlActionCoordinator.setValue(cvh, rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue(clipLayer.getLevel()))) } fun findNearestStep(value: Float): Float { Loading