Pass Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS
Add an EXTRA for passing the value of this setting to the activities when the TaskView is started. That way they can respect it when showing content. Also, make sure that we create the pending intent for the correct user Test: atest ControlsUiControllerImplTest Test: manual with test app Fixes: 259953724 Change-Id: I44bb8191f8412eb098c3cc654dea2e8badd6fabd
This commit is contained in:
@@ -68,6 +68,18 @@ public abstract class ControlsProviderService extends Service {
|
||||
public static final String META_DATA_PANEL_ACTIVITY =
|
||||
"android.service.controls.META_DATA_PANEL_ACTIVITY";
|
||||
|
||||
/**
|
||||
* Boolean extra containing the value of
|
||||
* {@link android.provider.Settings.Secure#LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS}.
|
||||
*
|
||||
* This is passed with the intent when the panel specified by {@link #META_DATA_PANEL_ACTIVITY}
|
||||
* is launched.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS =
|
||||
"android.service.controls.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.ControlsProviderService
|
||||
import android.util.Log
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
@@ -48,6 +49,7 @@ import com.android.systemui.Dumpable
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.controls.ControlsMetricsLogger
|
||||
import com.android.systemui.controls.ControlsServiceInfo
|
||||
import com.android.systemui.controls.ControlsSettingsRepository
|
||||
import com.android.systemui.controls.CustomIconCache
|
||||
import com.android.systemui.controls.controller.ControlsController
|
||||
import com.android.systemui.controls.controller.StructureInfo
|
||||
@@ -96,6 +98,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
private val userFileManager: UserFileManager,
|
||||
private val userTracker: UserTracker,
|
||||
private val taskViewFactory: Optional<TaskViewFactory>,
|
||||
private val controlsSettingsRepository: ControlsSettingsRepository,
|
||||
dumpManager: DumpManager
|
||||
) : ControlsUiController, Dumpable {
|
||||
|
||||
@@ -354,7 +357,6 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
} else {
|
||||
items[0]
|
||||
}
|
||||
|
||||
maybeUpdateSelectedItem(selectionItem)
|
||||
|
||||
createControlsSpaceFrame()
|
||||
@@ -374,11 +376,20 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
}
|
||||
|
||||
private fun createPanelView(componentName: ComponentName) {
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
val setting = controlsSettingsRepository
|
||||
.allowActionOnTrivialControlsInLockscreen.value
|
||||
val pendingIntent = PendingIntent.getActivityAsUser(
|
||||
context,
|
||||
0,
|
||||
Intent().setComponent(componentName),
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
Intent()
|
||||
.setComponent(componentName)
|
||||
.putExtra(
|
||||
ControlsProviderService.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
|
||||
setting
|
||||
),
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
null,
|
||||
userTracker.userHandle
|
||||
)
|
||||
|
||||
parent.requireViewById<View>(R.id.controls_scroll_view).visibility = View.GONE
|
||||
@@ -698,6 +709,8 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
println("hidden: $hidden")
|
||||
println("selectedItem: $selectedItem")
|
||||
println("lastSelections: $lastSelections")
|
||||
println("setting: ${controlsSettingsRepository
|
||||
.allowActionOnTrivialControlsInLockscreen.value}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,26 @@
|
||||
|
||||
package com.android.systemui.controls.ui
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.UserHandle
|
||||
import android.service.controls.ControlsProviderService
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.controls.ControlsMetricsLogger
|
||||
import com.android.systemui.controls.ControlsServiceInfo
|
||||
import com.android.systemui.controls.CustomIconCache
|
||||
import com.android.systemui.controls.FakeControlsSettingsRepository
|
||||
import com.android.systemui.controls.controller.ControlsController
|
||||
import com.android.systemui.controls.controller.StructureInfo
|
||||
import com.android.systemui.controls.management.ControlsListingController
|
||||
@@ -38,19 +49,26 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.FakeSharedPreferences
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.android.wm.shell.TaskView
|
||||
import com.android.wm.shell.TaskViewFactory
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import dagger.Lazy
|
||||
import java.util.Optional
|
||||
import java.util.function.Consumer
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.anyString
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.clearInvocations
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.MockitoAnnotations
|
||||
@@ -70,9 +88,9 @@ class ControlsUiControllerImplTest : SysuiTestCase() {
|
||||
@Mock lateinit var userFileManager: UserFileManager
|
||||
@Mock lateinit var userTracker: UserTracker
|
||||
@Mock lateinit var taskViewFactory: TaskViewFactory
|
||||
@Mock lateinit var activityContext: Context
|
||||
@Mock lateinit var dumpManager: DumpManager
|
||||
val sharedPreferences = FakeSharedPreferences()
|
||||
lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
|
||||
|
||||
var uiExecutor = FakeExecutor(FakeSystemClock())
|
||||
var bgExecutor = FakeExecutor(FakeSystemClock())
|
||||
@@ -83,6 +101,17 @@ class ControlsUiControllerImplTest : SysuiTestCase() {
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
controlsSettingsRepository = FakeControlsSettingsRepository()
|
||||
|
||||
// This way, it won't be cloned every time `LayoutInflater.fromContext` is called, but we
|
||||
// need to clone it once so we don't modify the original one.
|
||||
mContext.addMockSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE,
|
||||
mContext.baseContext
|
||||
.getSystemService(LayoutInflater::class.java)!!
|
||||
.cloneInContext(mContext)
|
||||
)
|
||||
|
||||
parent = FrameLayout(mContext)
|
||||
|
||||
underTest =
|
||||
@@ -100,6 +129,7 @@ class ControlsUiControllerImplTest : SysuiTestCase() {
|
||||
userFileManager,
|
||||
userTracker,
|
||||
Optional.of(taskViewFactory),
|
||||
controlsSettingsRepository,
|
||||
dumpManager
|
||||
)
|
||||
`when`(
|
||||
@@ -113,11 +143,12 @@ class ControlsUiControllerImplTest : SysuiTestCase() {
|
||||
`when`(userFileManager.getSharedPreferences(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(sharedPreferences)
|
||||
`when`(userTracker.userId).thenReturn(0)
|
||||
`when`(userTracker.userHandle).thenReturn(UserHandle.of(0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetPreferredStructure() {
|
||||
val structureInfo = mock(StructureInfo::class.java)
|
||||
val structureInfo = mock<StructureInfo>()
|
||||
underTest.getPreferredSelectedItem(listOf(structureInfo))
|
||||
verify(userFileManager)
|
||||
.getSharedPreferences(
|
||||
@@ -189,14 +220,195 @@ class ControlsUiControllerImplTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testPanelDoesNotRefreshControls() {
|
||||
val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
|
||||
setUpPanel(panel)
|
||||
|
||||
underTest.show(parent, {}, context)
|
||||
verify(controlsController, never()).refreshStatus(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPanelCallsTaskViewFactoryCreate() {
|
||||
mockLayoutInflater()
|
||||
val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
|
||||
val serviceInfo = setUpPanel(panel)
|
||||
|
||||
underTest.show(parent, {}, context)
|
||||
|
||||
val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
|
||||
|
||||
verify(controlsListingController).addCallback(capture(captor))
|
||||
|
||||
captor.value.onServicesUpdated(listOf(serviceInfo))
|
||||
FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
|
||||
|
||||
verify(taskViewFactory).create(eq(context), eq(uiExecutor), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPanelControllerStartActivityWithCorrectArguments() {
|
||||
mockLayoutInflater()
|
||||
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
|
||||
|
||||
val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
|
||||
val serviceInfo = setUpPanel(panel)
|
||||
|
||||
underTest.show(parent, {}, context)
|
||||
|
||||
val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
|
||||
|
||||
verify(controlsListingController).addCallback(capture(captor))
|
||||
|
||||
captor.value.onServicesUpdated(listOf(serviceInfo))
|
||||
FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
|
||||
|
||||
val pendingIntent = verifyPanelCreatedAndStartTaskView()
|
||||
|
||||
with(pendingIntent) {
|
||||
assertThat(isActivity).isTrue()
|
||||
assertThat(intent.component).isEqualTo(serviceInfo.panelActivity)
|
||||
assertThat(
|
||||
intent.getBooleanExtra(
|
||||
ControlsProviderService.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
|
||||
false
|
||||
)
|
||||
)
|
||||
.isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPendingIntentExtrasAreModified() {
|
||||
mockLayoutInflater()
|
||||
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
|
||||
|
||||
val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
|
||||
val serviceInfo = setUpPanel(panel)
|
||||
|
||||
underTest.show(parent, {}, context)
|
||||
|
||||
val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
|
||||
|
||||
verify(controlsListingController).addCallback(capture(captor))
|
||||
|
||||
captor.value.onServicesUpdated(listOf(serviceInfo))
|
||||
FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
|
||||
|
||||
val pendingIntent = verifyPanelCreatedAndStartTaskView()
|
||||
assertThat(
|
||||
pendingIntent.intent.getBooleanExtra(
|
||||
ControlsProviderService.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
|
||||
false
|
||||
)
|
||||
)
|
||||
.isTrue()
|
||||
|
||||
underTest.hide()
|
||||
|
||||
clearInvocations(controlsListingController, taskViewFactory)
|
||||
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(false)
|
||||
underTest.show(parent, {}, context)
|
||||
|
||||
verify(controlsListingController).addCallback(capture(captor))
|
||||
captor.value.onServicesUpdated(listOf(serviceInfo))
|
||||
FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
|
||||
|
||||
val newPendingIntent = verifyPanelCreatedAndStartTaskView()
|
||||
assertThat(
|
||||
newPendingIntent.intent.getBooleanExtra(
|
||||
ControlsProviderService.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
|
||||
false
|
||||
)
|
||||
)
|
||||
.isFalse()
|
||||
}
|
||||
|
||||
private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo {
|
||||
val activity = ComponentName("pkg", "activity")
|
||||
sharedPreferences
|
||||
.edit()
|
||||
.putString("controls_component", panel.componentName.flattenToString())
|
||||
.putString("controls_structure", panel.appName.toString())
|
||||
.putBoolean("controls_is_panel", true)
|
||||
.commit()
|
||||
return ControlsServiceInfo(panel.componentName, panel.appName, activity)
|
||||
}
|
||||
|
||||
underTest.show(parent, {}, activityContext)
|
||||
verify(controlsController, never()).refreshStatus(any(), any())
|
||||
private fun verifyPanelCreatedAndStartTaskView(): PendingIntent {
|
||||
val taskViewConsumerCaptor = argumentCaptor<Consumer<TaskView>>()
|
||||
verify(taskViewFactory).create(eq(context), eq(uiExecutor), capture(taskViewConsumerCaptor))
|
||||
|
||||
val taskView: TaskView = mock {
|
||||
`when`(this.post(any())).thenAnswer {
|
||||
uiExecutor.execute(it.arguments[0] as Runnable)
|
||||
true
|
||||
}
|
||||
}
|
||||
// calls PanelTaskViewController#launchTaskView
|
||||
taskViewConsumerCaptor.value.accept(taskView)
|
||||
val listenerCaptor = argumentCaptor<TaskView.Listener>()
|
||||
verify(taskView).setListener(any(), capture(listenerCaptor))
|
||||
listenerCaptor.value.onInitialized()
|
||||
FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
|
||||
|
||||
val pendingIntentCaptor = argumentCaptor<PendingIntent>()
|
||||
verify(taskView).startActivity(capture(pendingIntentCaptor), any(), any(), any())
|
||||
return pendingIntentCaptor.value
|
||||
}
|
||||
|
||||
private fun ControlsServiceInfo(
|
||||
componentName: ComponentName,
|
||||
label: CharSequence,
|
||||
panelComponentName: ComponentName? = null
|
||||
): ControlsServiceInfo {
|
||||
val serviceInfo =
|
||||
ServiceInfo().apply {
|
||||
applicationInfo = ApplicationInfo()
|
||||
packageName = componentName.packageName
|
||||
name = componentName.className
|
||||
}
|
||||
return spy(ControlsServiceInfo(mContext, serviceInfo)).apply {
|
||||
`when`(loadLabel()).thenReturn(label)
|
||||
`when`(loadIcon()).thenReturn(mock())
|
||||
`when`(panelActivity).thenReturn(panelComponentName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mockLayoutInflater() {
|
||||
LayoutInflater.from(context)
|
||||
.setPrivateFactory(
|
||||
object : LayoutInflater.Factory2 {
|
||||
override fun onCreateView(
|
||||
view: View?,
|
||||
name: String,
|
||||
context: Context,
|
||||
attrs: AttributeSet
|
||||
): View? {
|
||||
return onCreateView(name, context, attrs)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
name: String,
|
||||
context: Context,
|
||||
attrs: AttributeSet
|
||||
): View? {
|
||||
if (FrameLayout::class.java.simpleName.equals(name)) {
|
||||
val mock: FrameLayout = mock {
|
||||
`when`(this.context).thenReturn(context)
|
||||
`when`(this.id).thenReturn(R.id.controls_panel)
|
||||
`when`(this.requireViewById<View>(any())).thenCallRealMethod()
|
||||
`when`(this.findViewById<View>(R.id.controls_panel))
|
||||
.thenReturn(this)
|
||||
`when`(this.post(any())).thenAnswer {
|
||||
uiExecutor.execute(it.arguments[0] as Runnable)
|
||||
true
|
||||
}
|
||||
}
|
||||
return mock
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user