Merge "Controls - Prevent action while locked" into sc-dev
This commit is contained in:
@@ -30,6 +30,7 @@ import android.service.controls.actions.CommandAction
|
||||
import android.service.controls.actions.FloatAction
|
||||
import android.util.Log
|
||||
import android.view.HapticFeedbackConstants
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
@@ -71,7 +72,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
}
|
||||
|
||||
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
|
||||
bouncerOrRun(Action(cvh.cws.ci.controlId, {
|
||||
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
|
||||
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
|
||||
cvh.action(BooleanAction(templateId, !isChecked))
|
||||
}, true /* blockable */))
|
||||
@@ -79,7 +80,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
|
||||
override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
|
||||
val blockable = cvh.usePanel()
|
||||
bouncerOrRun(Action(cvh.cws.ci.controlId, {
|
||||
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
|
||||
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
|
||||
if (cvh.usePanel()) {
|
||||
showDialog(cvh, control.getAppIntent().getIntent())
|
||||
@@ -98,13 +99,13 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
}
|
||||
|
||||
override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
|
||||
bouncerOrRun(Action(cvh.cws.ci.controlId, {
|
||||
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
|
||||
cvh.action(FloatAction(templateId, newValue))
|
||||
}, false /* blockable */))
|
||||
}
|
||||
|
||||
override fun longPress(cvh: ControlViewHolder) {
|
||||
bouncerOrRun(Action(cvh.cws.ci.controlId, {
|
||||
bouncerOrRun(createAction(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)
|
||||
@@ -114,6 +115,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
}
|
||||
|
||||
override fun runPendingAction(controlId: String) {
|
||||
if (!keyguardStateController.isUnlocked()) return
|
||||
if (pendingAction?.controlId == controlId) {
|
||||
pendingAction?.invoke()
|
||||
pendingAction = null
|
||||
@@ -135,7 +137,8 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
false
|
||||
}
|
||||
|
||||
private fun bouncerOrRun(action: Action) {
|
||||
@VisibleForTesting
|
||||
fun bouncerOrRun(action: Action) {
|
||||
if (keyguardStateController.isShowing()) {
|
||||
var closeDialog = !keyguardStateController.isUnlocked()
|
||||
if (closeDialog) {
|
||||
@@ -190,6 +193,10 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun createAction(controlId: String, f: () -> Unit, blockable: Boolean) =
|
||||
Action(controlId, f, blockable)
|
||||
|
||||
inner class Action(val controlId: String, val f: () -> Unit, val blockable: Boolean) {
|
||||
fun invoke() {
|
||||
if (!blockable || shouldRunAction(controlId)) {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.controls.ui
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.globalactions.GlobalActionsComponent
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.wm.shell.TaskViewFactory
|
||||
import dagger.Lazy
|
||||
import java.util.Optional
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Answers
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.reset
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class ControlActionCoordinatorImplTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var uiController: ControlsUiController
|
||||
@Mock
|
||||
private lateinit var lazyUiController: Lazy<ControlsUiController>
|
||||
@Mock
|
||||
private lateinit var keyguardStateController: KeyguardStateController
|
||||
@Mock
|
||||
private lateinit var bgExecutor: DelayableExecutor
|
||||
@Mock
|
||||
private lateinit var uiExecutor: DelayableExecutor
|
||||
@Mock
|
||||
private lateinit var activityStarter: ActivityStarter
|
||||
@Mock
|
||||
private lateinit var globalActionsComponent: GlobalActionsComponent
|
||||
@Mock
|
||||
private lateinit var taskViewFactory: Optional<TaskViewFactory>
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private lateinit var cvh: ControlViewHolder
|
||||
|
||||
companion object {
|
||||
fun <T> any(): T = Mockito.any<T>()
|
||||
|
||||
private val ID = "id"
|
||||
}
|
||||
|
||||
private lateinit var coordinator: ControlActionCoordinatorImpl
|
||||
private lateinit var action: ControlActionCoordinatorImpl.Action
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
coordinator = spy(ControlActionCoordinatorImpl(
|
||||
mContext,
|
||||
bgExecutor,
|
||||
uiExecutor,
|
||||
activityStarter,
|
||||
keyguardStateController,
|
||||
globalActionsComponent,
|
||||
taskViewFactory,
|
||||
getFakeBroadcastDispatcher(),
|
||||
lazyUiController
|
||||
))
|
||||
|
||||
`when`(cvh.cws.ci.controlId).thenReturn(ID)
|
||||
action = spy(coordinator.Action(ID, {}, false))
|
||||
doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToggleRunsWhenUnlocked() {
|
||||
`when`(keyguardStateController.isShowing()).thenReturn(false)
|
||||
|
||||
coordinator.toggle(cvh, "", true)
|
||||
verify(coordinator).bouncerOrRun(action)
|
||||
verify(action).invoke()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToggleDoesNotRunWhenLockedThenRunsWhenUnlocked() {
|
||||
`when`(keyguardStateController.isShowing()).thenReturn(true)
|
||||
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
|
||||
|
||||
coordinator.toggle(cvh, "", true)
|
||||
verify(coordinator).bouncerOrRun(action)
|
||||
verify(activityStarter).dismissKeyguardThenExecute(any(), any(), anyBoolean())
|
||||
verify(action, never()).invoke()
|
||||
|
||||
// Simulate a refresh call from a Publisher, which will trigger a call to runPendingAction
|
||||
reset(action)
|
||||
coordinator.runPendingAction(ID)
|
||||
verify(action, never()).invoke()
|
||||
|
||||
`when`(keyguardStateController.isUnlocked()).thenReturn(true)
|
||||
reset(action)
|
||||
coordinator.runPendingAction(ID)
|
||||
verify(action).invoke()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user