diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 2d1db716b700..ebf5832147f7 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -79,5 +79,6 @@
+
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index dfe683dbee9a..eb7fea3eb89a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -319,6 +319,9 @@
+
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index fc85a85d0acb..6d727b4cf966 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -61,6 +61,7 @@ import android.os.ServiceManager;
import android.os.UserManager;
import android.os.Vibrator;
import android.permission.PermissionManager;
+import android.safetycenter.SafetyCenterManager;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.telecom.TelecomManager;
@@ -476,4 +477,11 @@ public class FrameworkServicesModule {
static SmartspaceManager provideSmartspaceManager(Context context) {
return context.getSystemService(SmartspaceManager.class);
}
+
+ @Provides
+ @Singleton
+ static SafetyCenterManager provideSafetyCenterManager(Context context) {
+ return context.getSystemService(SafetyCenterManager.class);
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 95b4b725e4c6..dc79f40ffef6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -1,15 +1,19 @@
package com.android.systemui.qs
+import android.content.BroadcastReceiver
+import android.content.Context
import android.content.Intent
+import android.content.IntentFilter
import android.permission.PermissionGroupUsage
import android.permission.PermissionManager
-import android.provider.DeviceConfig
+import android.safetycenter.SafetyCenterManager
import android.view.View
import androidx.annotation.WorkerThread
import com.android.internal.R
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
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.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
-import com.android.systemui.util.DeviceConfigProxy
import java.util.concurrent.Executor
import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background
@@ -50,15 +53,11 @@ class HeaderPrivacyIconsController @Inject constructor(
@Main private val uiExecutor: Executor,
private val activityStarter: ActivityStarter,
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
-
private var listening = false
private var micCameraIndicatorsEnabled = 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 locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
- private val devicePropertiesChangedListener =
- object : DeviceConfig.OnPropertiesChangedListener {
- override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
- safetyCenterEnabled = properties.getBoolean(SAFETY_CENTER_ENABLED, false)
- }
+ private val safetyCenterReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
+ }
+ }
+
+ 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 {
- safetyCenterEnabled = deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- SAFETY_CENTER_ENABLED, false)
- deviceConfigProxy.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_PRIVACY,
- uiExecutor,
- devicePropertiesChangedListener)
+ backgroundExecutor.execute {
+ safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
+ }
+
+ if (privacyChip.isAttachedToWindow()) {
+ broadcastDispatcher.registerReceiver(
+ safetyCenterReceiver,
+ IntentFilter(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
+ executor = backgroundExecutor
+ )
+ }
+
+ privacyChip.addOnAttachStateChangeListener(attachStateChangeListener)
}
private val picCallback: PrivacyItemController.Callback =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index 30a5308d9334..a67483b10c4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -1,14 +1,18 @@
package com.android.systemui.qs
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.permission.PermissionManager
-import android.provider.DeviceConfig
+import android.safetycenter.SafetyCenterManager
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
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.statusbar.phone.StatusIconContainer
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.argumentCaptor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever
+private fun eq(value: T): T = Mockito.eq(value) ?: value
+private fun any(): T = Mockito.any()
+
@SmallTest
@RunWith(AndroidTestingRunner::class)
class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
- companion object {
- const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
- }
-
@Mock
private lateinit var privacyItemController: PrivacyItemController
@Mock
@@ -55,14 +59,16 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
@Mock
private lateinit var permissionManager: PermissionManager
@Mock
- private lateinit var backgroundExecutor: Executor
- @Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var appOpsController: AppOpsController
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var safetyCenterManager: SafetyCenterManager
private val uiExecutor = FakeExecutor(FakeSystemClock())
- private lateinit var deviceConfigProxy: DeviceConfigProxy
+ private val backgroundExecutor = FakeExecutor(FakeSystemClock())
private lateinit var cameraSlotName: String
private lateinit var microphoneSlotName: String
private lateinit var locationSlotName: String
@@ -73,11 +79,11 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
whenever(privacyChip.context).thenReturn(context)
whenever(privacyChip.resources).thenReturn(context.resources)
+ whenever(privacyChip.isAttachedToWindow).thenReturn(true)
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
- deviceConfigProxy = DeviceConfigProxyFake()
controller = HeaderPrivacyIconsController(
privacyItemController,
@@ -91,8 +97,11 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
uiExecutor,
activityStarter,
appOpsController,
- deviceConfigProxy
+ broadcastDispatcher,
+ safetyCenterManager
)
+
+ backgroundExecutor.runAllReady()
}
@Test
@@ -141,19 +150,25 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
@Test
fun testPrivacyChipClicked() {
- changeProperty(SAFETY_CENTER_ENABLED, false)
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
controller.onParentVisible()
-
val captor = argumentCaptor()
verify(privacyChip).setOnClickListener(capture(captor))
captor.value.onClick(privacyChip)
-
verify(privacyDialogController).showDialog(any(Context::class.java))
}
@Test
fun testSafetyCenterFlag() {
- changeProperty(SAFETY_CENTER_ENABLED, true)
+ val receiverCaptor = argumentCaptor()
+ 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()
val captor = argumentCaptor()
verify(privacyChip).setOnClickListener(capture(captor))
@@ -161,18 +176,29 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
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()
+ verify(broadcastDispatcher).unregisterReceiver(capture(broadcastReceiverCaptor))
+ }
+
+ @Test
+ fun testBroadcastReceiverRegisteredWhenChipAttached() {
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
+ controller.attachStateChangeListener.onViewAttachedToWindow(privacyChip)
+ backgroundExecutor.runAllReady()
+ val broadcastReceiverCaptor = argumentCaptor()
+ val intentFilterCaptor = argumentCaptor()
+ // 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) {
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
whenever(privacyItemController.locationAvailable).thenReturn(location)
}
-
- private fun changeProperty(name: String, value: Boolean?) {
- deviceConfigProxy.setProperty(
- DeviceConfig.NAMESPACE_PRIVACY,
- name,
- value?.toString(),
- false
- )
- uiExecutor.runAllReady()
- }
}
\ No newline at end of file