Merge "Add broadcast receiver to listen for safety center flag changes" into tm-dev

This commit is contained in:
Ganesh Olekar 2022-03-28 18:32:53 +00:00 committed by Android (Google) Code Review
commit 3795120659
5 changed files with 103 additions and 46 deletions

View File

@ -79,5 +79,6 @@
<permission name="android.permission.LOG_COMPAT_CHANGE" />
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<permission name="android.permission.READ_DEVICE_CONFIG" />
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
</privapp-permissions>
</permissions>

View File

@ -319,6 +319,9 @@
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_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.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />

View File

@ -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);
}
}

View File

@ -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 =

View File

@ -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 <T> eq(value: T): T = Mockito.eq(value) ?: value
private fun <T> any(): T = Mockito.any<T>()
@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<View.OnClickListener>()
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<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()
val captor = argumentCaptor<View.OnClickListener>()
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<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) {
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()
}
}