Merge "Add broadcast receiver to listen for safety center flag changes" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3795120659
@@ -79,5 +79,6 @@
|
|||||||
<permission name="android.permission.LOG_COMPAT_CHANGE" />
|
<permission name="android.permission.LOG_COMPAT_CHANGE" />
|
||||||
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
|
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
|
||||||
<permission name="android.permission.READ_DEVICE_CONFIG" />
|
<permission name="android.permission.READ_DEVICE_CONFIG" />
|
||||||
|
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
|
||||||
</privapp-permissions>
|
</privapp-permissions>
|
||||||
</permissions>
|
</permissions>
|
||||||
|
|||||||
@@ -319,6 +319,9 @@
|
|||||||
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
|
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
|
||||||
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
|
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
|
||||||
|
|
||||||
|
<!-- To read safety center status -->
|
||||||
|
<uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
|
||||||
|
|
||||||
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
|
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
|
||||||
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
|
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
|
||||||
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
|
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import android.os.ServiceManager;
|
|||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.permission.PermissionManager;
|
import android.permission.PermissionManager;
|
||||||
|
import android.safetycenter.SafetyCenterManager;
|
||||||
import android.service.dreams.DreamService;
|
import android.service.dreams.DreamService;
|
||||||
import android.service.dreams.IDreamManager;
|
import android.service.dreams.IDreamManager;
|
||||||
import android.telecom.TelecomManager;
|
import android.telecom.TelecomManager;
|
||||||
@@ -476,4 +477,11 @@ public class FrameworkServicesModule {
|
|||||||
static SmartspaceManager provideSmartspaceManager(Context context) {
|
static SmartspaceManager provideSmartspaceManager(Context context) {
|
||||||
return context.getSystemService(SmartspaceManager.class);
|
return context.getSystemService(SmartspaceManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static SafetyCenterManager provideSafetyCenterManager(Context context) {
|
||||||
|
return context.getSystemService(SafetyCenterManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
package com.android.systemui.qs
|
package com.android.systemui.qs
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.permission.PermissionGroupUsage
|
import android.permission.PermissionGroupUsage
|
||||||
import android.permission.PermissionManager
|
import android.permission.PermissionManager
|
||||||
import android.provider.DeviceConfig
|
import android.safetycenter.SafetyCenterManager
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import com.android.internal.R
|
import com.android.internal.R
|
||||||
import com.android.internal.logging.UiEventLogger
|
import com.android.internal.logging.UiEventLogger
|
||||||
import com.android.systemui.animation.ActivityLaunchAnimator
|
import com.android.systemui.animation.ActivityLaunchAnimator
|
||||||
import com.android.systemui.appops.AppOpsController
|
import com.android.systemui.appops.AppOpsController
|
||||||
|
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.privacy.OngoingPrivacyChip
|
import com.android.systemui.privacy.OngoingPrivacyChip
|
||||||
import com.android.systemui.privacy.PrivacyChipEvent
|
import com.android.systemui.privacy.PrivacyChipEvent
|
||||||
@@ -18,7 +22,6 @@ import com.android.systemui.privacy.PrivacyItem
|
|||||||
import com.android.systemui.privacy.PrivacyItemController
|
import com.android.systemui.privacy.PrivacyItemController
|
||||||
import com.android.systemui.privacy.logging.PrivacyLogger
|
import com.android.systemui.privacy.logging.PrivacyLogger
|
||||||
import com.android.systemui.statusbar.phone.StatusIconContainer
|
import com.android.systemui.statusbar.phone.StatusIconContainer
|
||||||
import com.android.systemui.util.DeviceConfigProxy
|
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import com.android.systemui.dagger.qualifiers.Background
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
@@ -50,15 +53,11 @@ class HeaderPrivacyIconsController @Inject constructor(
|
|||||||
@Main private val uiExecutor: Executor,
|
@Main private val uiExecutor: Executor,
|
||||||
private val activityStarter: ActivityStarter,
|
private val activityStarter: ActivityStarter,
|
||||||
private val appOpsController: AppOpsController,
|
private val appOpsController: AppOpsController,
|
||||||
private val deviceConfigProxy: DeviceConfigProxy
|
private val broadcastDispatcher: BroadcastDispatcher,
|
||||||
|
private val safetyCenterManager: SafetyCenterManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
var chipVisibilityListener: ChipVisibilityListener? = null
|
var chipVisibilityListener: ChipVisibilityListener? = null
|
||||||
|
|
||||||
private var listening = false
|
private var listening = false
|
||||||
private var micCameraIndicatorsEnabled = false
|
private var micCameraIndicatorsEnabled = false
|
||||||
private var locationIndicatorsEnabled = false
|
private var locationIndicatorsEnabled = false
|
||||||
@@ -68,20 +67,40 @@ class HeaderPrivacyIconsController @Inject constructor(
|
|||||||
private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
|
private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
|
||||||
private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
|
private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
|
||||||
|
|
||||||
private val devicePropertiesChangedListener =
|
private val safetyCenterReceiver = object : BroadcastReceiver() {
|
||||||
object : DeviceConfig.OnPropertiesChangedListener {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
|
safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
|
||||||
safetyCenterEnabled = properties.getBoolean(SAFETY_CENTER_ENABLED, false)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val attachStateChangeListener = object : View.OnAttachStateChangeListener {
|
||||||
|
override fun onViewAttachedToWindow(v: View) {
|
||||||
|
broadcastDispatcher.registerReceiver(
|
||||||
|
safetyCenterReceiver,
|
||||||
|
IntentFilter(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
|
||||||
|
executor = backgroundExecutor
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onViewDetachedFromWindow(v: View) {
|
||||||
|
broadcastDispatcher.unregisterReceiver(safetyCenterReceiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
safetyCenterEnabled = deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
|
backgroundExecutor.execute {
|
||||||
SAFETY_CENTER_ENABLED, false)
|
safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
|
||||||
deviceConfigProxy.addOnPropertiesChangedListener(
|
}
|
||||||
DeviceConfig.NAMESPACE_PRIVACY,
|
|
||||||
uiExecutor,
|
if (privacyChip.isAttachedToWindow()) {
|
||||||
devicePropertiesChangedListener)
|
broadcastDispatcher.registerReceiver(
|
||||||
|
safetyCenterReceiver,
|
||||||
|
IntentFilter(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
|
||||||
|
executor = backgroundExecutor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
privacyChip.addOnAttachStateChangeListener(attachStateChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val picCallback: PrivacyItemController.Callback =
|
private val picCallback: PrivacyItemController.Callback =
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package com.android.systemui.qs
|
package com.android.systemui.qs
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.permission.PermissionManager
|
import android.permission.PermissionManager
|
||||||
import android.provider.DeviceConfig
|
import android.safetycenter.SafetyCenterManager
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.internal.logging.UiEventLogger
|
import com.android.internal.logging.UiEventLogger
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.appops.AppOpsController
|
import com.android.systemui.appops.AppOpsController
|
||||||
|
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.privacy.OngoingPrivacyChip
|
import com.android.systemui.privacy.OngoingPrivacyChip
|
||||||
import com.android.systemui.privacy.PrivacyDialogController
|
import com.android.systemui.privacy.PrivacyDialogController
|
||||||
@@ -16,30 +20,30 @@ import com.android.systemui.privacy.PrivacyItemController
|
|||||||
import com.android.systemui.privacy.logging.PrivacyLogger
|
import com.android.systemui.privacy.logging.PrivacyLogger
|
||||||
import com.android.systemui.statusbar.phone.StatusIconContainer
|
import com.android.systemui.statusbar.phone.StatusIconContainer
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
import com.android.systemui.util.concurrency.FakeExecutor
|
||||||
import com.android.systemui.util.DeviceConfigProxy
|
|
||||||
import com.android.systemui.util.DeviceConfigProxyFake
|
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
import com.android.systemui.util.mockito.argumentCaptor
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
import com.android.systemui.util.mockito.capture
|
import com.android.systemui.util.mockito.capture
|
||||||
|
import com.android.systemui.util.mockito.nullable
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.times
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
import java.util.concurrent.Executor
|
|
||||||
import org.mockito.Mockito.`when` as whenever
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
|
||||||
|
private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
|
||||||
|
private fun <T> any(): T = Mockito.any<T>()
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
|
class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var privacyItemController: PrivacyItemController
|
private lateinit var privacyItemController: PrivacyItemController
|
||||||
@Mock
|
@Mock
|
||||||
@@ -55,14 +59,16 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
|
|||||||
@Mock
|
@Mock
|
||||||
private lateinit var permissionManager: PermissionManager
|
private lateinit var permissionManager: PermissionManager
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var backgroundExecutor: Executor
|
|
||||||
@Mock
|
|
||||||
private lateinit var activityStarter: ActivityStarter
|
private lateinit var activityStarter: ActivityStarter
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var appOpsController: AppOpsController
|
private lateinit var appOpsController: AppOpsController
|
||||||
|
@Mock
|
||||||
|
private lateinit var broadcastDispatcher: BroadcastDispatcher
|
||||||
|
@Mock
|
||||||
|
private lateinit var safetyCenterManager: SafetyCenterManager
|
||||||
|
|
||||||
private val uiExecutor = FakeExecutor(FakeSystemClock())
|
private val uiExecutor = FakeExecutor(FakeSystemClock())
|
||||||
private lateinit var deviceConfigProxy: DeviceConfigProxy
|
private val backgroundExecutor = FakeExecutor(FakeSystemClock())
|
||||||
private lateinit var cameraSlotName: String
|
private lateinit var cameraSlotName: String
|
||||||
private lateinit var microphoneSlotName: String
|
private lateinit var microphoneSlotName: String
|
||||||
private lateinit var locationSlotName: String
|
private lateinit var locationSlotName: String
|
||||||
@@ -73,11 +79,11 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
whenever(privacyChip.context).thenReturn(context)
|
whenever(privacyChip.context).thenReturn(context)
|
||||||
whenever(privacyChip.resources).thenReturn(context.resources)
|
whenever(privacyChip.resources).thenReturn(context.resources)
|
||||||
|
whenever(privacyChip.isAttachedToWindow).thenReturn(true)
|
||||||
|
|
||||||
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
|
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
|
||||||
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
|
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
|
||||||
locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
|
locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
|
||||||
deviceConfigProxy = DeviceConfigProxyFake()
|
|
||||||
|
|
||||||
controller = HeaderPrivacyIconsController(
|
controller = HeaderPrivacyIconsController(
|
||||||
privacyItemController,
|
privacyItemController,
|
||||||
@@ -91,8 +97,11 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
|
|||||||
uiExecutor,
|
uiExecutor,
|
||||||
activityStarter,
|
activityStarter,
|
||||||
appOpsController,
|
appOpsController,
|
||||||
deviceConfigProxy
|
broadcastDispatcher,
|
||||||
|
safetyCenterManager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
backgroundExecutor.runAllReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -141,19 +150,25 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPrivacyChipClicked() {
|
fun testPrivacyChipClicked() {
|
||||||
changeProperty(SAFETY_CENTER_ENABLED, false)
|
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
|
||||||
controller.onParentVisible()
|
controller.onParentVisible()
|
||||||
|
|
||||||
val captor = argumentCaptor<View.OnClickListener>()
|
val captor = argumentCaptor<View.OnClickListener>()
|
||||||
verify(privacyChip).setOnClickListener(capture(captor))
|
verify(privacyChip).setOnClickListener(capture(captor))
|
||||||
captor.value.onClick(privacyChip)
|
captor.value.onClick(privacyChip)
|
||||||
|
|
||||||
verify(privacyDialogController).showDialog(any(Context::class.java))
|
verify(privacyDialogController).showDialog(any(Context::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSafetyCenterFlag() {
|
fun testSafetyCenterFlag() {
|
||||||
changeProperty(SAFETY_CENTER_ENABLED, true)
|
val receiverCaptor = argumentCaptor<BroadcastReceiver>()
|
||||||
|
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
|
||||||
|
verify(broadcastDispatcher).registerReceiver(capture(receiverCaptor),
|
||||||
|
any(), any(), nullable(), anyInt())
|
||||||
|
receiverCaptor.value.onReceive(
|
||||||
|
context,
|
||||||
|
Intent(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED)
|
||||||
|
)
|
||||||
|
backgroundExecutor.runAllReady()
|
||||||
controller.onParentVisible()
|
controller.onParentVisible()
|
||||||
val captor = argumentCaptor<View.OnClickListener>()
|
val captor = argumentCaptor<View.OnClickListener>()
|
||||||
verify(privacyChip).setOnClickListener(capture(captor))
|
verify(privacyChip).setOnClickListener(capture(captor))
|
||||||
@@ -161,18 +176,29 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
|
|||||||
verify(privacyDialogController, never()).showDialog(any(Context::class.java))
|
verify(privacyDialogController, never()).showDialog(any(Context::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBroadcastReceiverUnregisteredWhenChipDetached() {
|
||||||
|
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
|
||||||
|
controller.attachStateChangeListener.onViewDetachedFromWindow(privacyChip)
|
||||||
|
backgroundExecutor.runAllReady()
|
||||||
|
val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>()
|
||||||
|
verify(broadcastDispatcher).unregisterReceiver(capture(broadcastReceiverCaptor))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBroadcastReceiverRegisteredWhenChipAttached() {
|
||||||
|
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
|
||||||
|
controller.attachStateChangeListener.onViewAttachedToWindow(privacyChip)
|
||||||
|
backgroundExecutor.runAllReady()
|
||||||
|
val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>()
|
||||||
|
val intentFilterCaptor = argumentCaptor<IntentFilter>()
|
||||||
|
// Broadcast receiver is registered on init and when privacy chip is attached
|
||||||
|
verify(broadcastDispatcher, times(2)).registerReceiver(capture(broadcastReceiverCaptor),
|
||||||
|
capture(intentFilterCaptor), any(), nullable(), anyInt())
|
||||||
|
}
|
||||||
|
|
||||||
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
|
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
|
||||||
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
|
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
|
||||||
whenever(privacyItemController.locationAvailable).thenReturn(location)
|
whenever(privacyItemController.locationAvailable).thenReturn(location)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeProperty(name: String, value: Boolean?) {
|
|
||||||
deviceConfigProxy.setProperty(
|
|
||||||
DeviceConfig.NAMESPACE_PRIVACY,
|
|
||||||
name,
|
|
||||||
value?.toString(),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
uiExecutor.runAllReady()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user