Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37951206 authored by Ganesh Olekar's avatar Ganesh Olekar Committed by Android (Google) Code Review
Browse files

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

parents 93b65eaa ff11a6e2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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>
+3 −0
Original line number Diff line number Diff line
@@ -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" />
+8 −0
Original line number Diff line number Diff line
@@ -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);
    }

}
+38 −19
Original line number Diff line number Diff line
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 =
+53 −27
Original line number Diff line number Diff line
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()
    }
}
 No newline at end of file