Loading packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyConfigTest.kt 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.privacy import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.util.DeviceConfigProxy import com.android.systemui.util.DeviceConfigProxyFake import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class PrivacyConfigTest : SysuiTestCase() { private lateinit var privacyConfig: PrivacyConfig @Mock private lateinit var callback: PrivacyConfig.Callback @Mock private lateinit var dumpManager: DumpManager private lateinit var executor: FakeExecutor private lateinit var deviceConfigProxy: DeviceConfigProxy fun createPrivacyConfig(): PrivacyConfig { return PrivacyConfig(executor, deviceConfigProxy, dumpManager) } @Before fun setup() { MockitoAnnotations.initMocks(this) executor = FakeExecutor(FakeSystemClock()) deviceConfigProxy = DeviceConfigProxyFake() privacyConfig = createPrivacyConfig() privacyConfig.addCallback(callback) executor.runAllReady() } @Test fun getPrivacyColor_locationOnly_returnsLocationOnlyColor() { assertEquals( R.color.privacy_chip_location_only_background, PrivacyConfig.Companion.getPrivacyColor(locationOnly = true), ) } @Test fun getPrivacyColor_multiplePrivacyItems_returnsDefaultPrivacyColor() { assertEquals( R.color.privacy_chip_background, PrivacyConfig.Companion.getPrivacyColor(locationOnly = false), ) } @Test fun privacyItemsAreLocationOnly_locationOnly_returnsTrue() { assertTrue( PrivacyConfig.Companion.privacyItemsAreLocationOnly( listOf(PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("app", 1))) ) ) } @Test fun privacyItemsAreLocationOnly_multiplePrivacyItems_returnsFalse() { assertFalse( PrivacyConfig.Companion.privacyItemsAreLocationOnly( listOf( PrivacyItem(PrivacyType.TYPE_CAMERA, PrivacyApplication("app", 1)), PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("app", 1)), ) ) ) } } packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt +93 −6 Original line number Diff line number Diff line Loading @@ -18,20 +18,27 @@ package com.android.systemui.statusbar.events import android.graphics.Point import android.graphics.Rect import android.location.flags.Flags import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.UsesFlags import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableLooper.RunWithLooper import android.view.Display import android.view.DisplayAdjustments import android.view.View import android.widget.FrameLayout import android.widget.FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY import androidx.test.ext.junit.runners.AndroidJUnit4 import android.widget.ImageView import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_SHADE_WINDOW_GOES_AROUND import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.privacy.PrivacyApplication import com.android.systemui.privacy.PrivacyItem import com.android.systemui.privacy.PrivacyType import com.android.systemui.res.R import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository import com.android.systemui.shade.domain.interactor.shadeDisplaysInteractor Loading Loading @@ -61,11 +68,14 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mockito import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @RunWithLooper class PrivacyDotViewControllerTest : SysuiTestCase() { @UsesFlags(Flags::class) class PrivacyDotViewControllerTest(flags: FlagsParameterization) : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val mockDisplay = createMockDisplay() Loading Loading @@ -105,6 +115,18 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { shadeDisplaysInteractor = { shadeDisplaysInteractor }, ) companion object { @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(Flags.FLAG_LOCATION_INDICATORS_ENABLED) } } init { mSetFlagsRule.setFlagsParameterization(flags) } @Test fun topMargin_topLeftView_basedOnSeascapeArea() { createAndInitializeController() Loading Loading @@ -383,7 +405,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = false // This informs the controller of an active privacy event. callback.onSystemStatusAnimationTransitionToPersistentDot(null) callback.onSystemStatusAnimationTransitionToPersistentDot(null, null) assertThat(controller.currentViewState.shouldShowDot()).isEqualTo(true) } Loading @@ -396,16 +418,81 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = true // This informs the controller of an active privacy event. callback.onSystemStatusAnimationTransitionToPersistentDot(null) callback.onSystemStatusAnimationTransitionToPersistentDot(null, null) assertThat(controller.currentViewState.shouldShowDot()).isEqualTo(false) } @Test @DisableFlags(Flags.FLAG_LOCATION_INDICATORS_ENABLED) fun animationCallback_locationIndicatorsDisabled_doesNotDetermineLocationOnlyEvents() = kosmos.runTest { val captor = ArgumentCaptor.forClass(SystemStatusAnimationCallback::class.java) val controller: PrivacyDotViewController = createAndInitializeController() Mockito.verify(mockAnimationScheduler).addCallback(captor.capture()) val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = false // This informs the controller of an active privacy event. // Even with just the location event, the location-only flag is not set. callback.onSystemStatusAnimationTransitionToPersistentDot( null, listOf( PrivacyItem( privacyType = PrivacyType.TYPE_LOCATION, application = PrivacyApplication(packageName = "com.android", uid = 1), ) ), ) assertThat(controller.currentViewState.systemPrivacyEventLocationOnlyIsActive) .isEqualTo(false) } @Test @EnableFlags(Flags.FLAG_LOCATION_INDICATORS_ENABLED) fun animationCallback_locationIndicatorsEnabled_determinesLocationOnlyEvents() = kosmos.runTest { val captor = ArgumentCaptor.forClass(SystemStatusAnimationCallback::class.java) val controller: PrivacyDotViewController = createAndInitializeController() Mockito.verify(mockAnimationScheduler).addCallback(captor.capture()) val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = false // This informs the controller of an active privacy event. // Multiple privacy items are active, so the location-only flag is not set. callback.onSystemStatusAnimationTransitionToPersistentDot( null, listOf( PrivacyItem( privacyType = PrivacyType.TYPE_CAMERA, application = PrivacyApplication(packageName = "com.android", uid = 1), ), PrivacyItem( privacyType = PrivacyType.TYPE_LOCATION, application = PrivacyApplication(packageName = "com.android", uid = 2), ), ), ) assertThat(controller.currentViewState.systemPrivacyEventLocationOnlyIsActive) .isEqualTo(false) // Only location event is active, so the location-only flag is set. callback.onSystemStatusAnimationTransitionToPersistentDot( null, listOf( PrivacyItem( privacyType = PrivacyType.TYPE_LOCATION, application = PrivacyApplication(packageName = "com.android", uid = 1), ) ), ) assertThat(controller.currentViewState.systemPrivacyEventLocationOnlyIsActive) .isEqualTo(true) } private fun setRotation(rotation: Int) { whenever(mockDisplay.rotation).thenReturn(rotation) } private fun initDotView(): View { val privacyDot = View(context).also { it.id = R.id.privacy_dot } val privacyDot = ImageView(context).also { it.id = R.id.privacy_dot } return FrameLayout(context).also { it.layoutParams = FrameLayout.LayoutParams(/* width= */ 0, /* height= */ 0) it.addView(privacyDot) Loading packages/SystemUI/res/values/colors.xml +1 −0 Original line number Diff line number Diff line Loading @@ -249,6 +249,7 @@ <color name="screenrecord_icon_color">#D93025</color><!-- red 600 --> <color name="privacy_chip_background">#3ddc84</color> <color name="privacy_chip_location_only_background">#3dbaf4</color> <!-- Accessibility floating menu --> <color name="accessibility_floating_menu_background">#CCFFFFFF</color> <!-- 80% --> Loading packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +30 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.privacy import android.content.Context import android.content.pm.ActivityInfo import android.content.res.Configuration import android.graphics.drawable.GradientDrawable import android.location.flags.Flags.locationIndicatorsEnabled import android.util.AttributeSet import android.view.Gravity.CENTER_VERTICAL import android.view.Gravity.END Loading @@ -27,6 +29,7 @@ import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout import androidx.annotation.VisibleForTesting import com.android.settingslib.Utils import com.android.systemui.Flags import com.android.systemui.res.R Loading @@ -46,8 +49,9 @@ constructor( private var iconMargin = 0 private var iconSize = 0 private var iconColor = 0 private var chipDrawable: GradientDrawable? = null private val iconsContainer: LinearLayout @VisibleForTesting val iconsContainer: LinearLayout val launchableContentView get() = iconsContainer Loading @@ -55,6 +59,17 @@ constructor( set(value) { field = value updateView(PrivacyChipBuilder(context, field)) if (locationIndicatorsEnabled()) { updateResources() } } private val locationOnly: Boolean private get() = if (locationIndicatorsEnabled()) { PrivacyConfig.Companion.privacyItemsAreLocationOnly(privacyList) } else { false } init { Loading Loading @@ -149,6 +164,19 @@ constructor( context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) iconsContainer.layoutParams.height = height iconsContainer.setPaddingRelative(padding, 0, padding, 0) if (locationIndicatorsEnabled()) { if (chipDrawable == null) { chipDrawable = context.getDrawable(R.drawable.statusbar_privacy_chip_bg)?.mutate() as? GradientDrawable iconsContainer.background = chipDrawable } chipDrawable?.let { drawable -> val color = context.getColor(PrivacyConfig.Companion.getPrivacyColor(locationOnly)) drawable.setColor(color) } } else { iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg) } } } packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt +14 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.util.DeviceConfigProxy import com.android.systemui.util.asIndenting import com.android.systemui.util.concurrency.DelayableExecutor Loading @@ -43,13 +44,25 @@ constructor( ) : Dumpable { @VisibleForTesting internal companion object { companion object { const val TAG = "PrivacyConfig" private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED private const val MEDIA_PROJECTION = SystemUiDeviceConfigFlags.PROPERTY_MEDIA_PROJECTION_INDICATORS_ENABLED private const val DEFAULT_MIC_CAMERA = true private const val DEFAULT_MEDIA_PROJECTION = true fun getPrivacyColor(locationOnly: Boolean): Int { if (locationOnly) { return R.color.privacy_chip_location_only_background } return R.color.privacy_chip_background } fun privacyItemsAreLocationOnly(privacyItems: List<PrivacyItem>): Boolean { return privacyItems.isNotEmpty() && privacyItems.all { it.privacyType == PrivacyType.TYPE_LOCATION } } } private val callbacks = mutableListOf<WeakReference<Callback>>() Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyConfigTest.kt 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.privacy import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.util.DeviceConfigProxy import com.android.systemui.util.DeviceConfigProxyFake import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class PrivacyConfigTest : SysuiTestCase() { private lateinit var privacyConfig: PrivacyConfig @Mock private lateinit var callback: PrivacyConfig.Callback @Mock private lateinit var dumpManager: DumpManager private lateinit var executor: FakeExecutor private lateinit var deviceConfigProxy: DeviceConfigProxy fun createPrivacyConfig(): PrivacyConfig { return PrivacyConfig(executor, deviceConfigProxy, dumpManager) } @Before fun setup() { MockitoAnnotations.initMocks(this) executor = FakeExecutor(FakeSystemClock()) deviceConfigProxy = DeviceConfigProxyFake() privacyConfig = createPrivacyConfig() privacyConfig.addCallback(callback) executor.runAllReady() } @Test fun getPrivacyColor_locationOnly_returnsLocationOnlyColor() { assertEquals( R.color.privacy_chip_location_only_background, PrivacyConfig.Companion.getPrivacyColor(locationOnly = true), ) } @Test fun getPrivacyColor_multiplePrivacyItems_returnsDefaultPrivacyColor() { assertEquals( R.color.privacy_chip_background, PrivacyConfig.Companion.getPrivacyColor(locationOnly = false), ) } @Test fun privacyItemsAreLocationOnly_locationOnly_returnsTrue() { assertTrue( PrivacyConfig.Companion.privacyItemsAreLocationOnly( listOf(PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("app", 1))) ) ) } @Test fun privacyItemsAreLocationOnly_multiplePrivacyItems_returnsFalse() { assertFalse( PrivacyConfig.Companion.privacyItemsAreLocationOnly( listOf( PrivacyItem(PrivacyType.TYPE_CAMERA, PrivacyApplication("app", 1)), PrivacyItem(PrivacyType.TYPE_LOCATION, PrivacyApplication("app", 1)), ) ) ) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt +93 −6 Original line number Diff line number Diff line Loading @@ -18,20 +18,27 @@ package com.android.systemui.statusbar.events import android.graphics.Point import android.graphics.Rect import android.location.flags.Flags import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.UsesFlags import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableLooper.RunWithLooper import android.view.Display import android.view.DisplayAdjustments import android.view.View import android.widget.FrameLayout import android.widget.FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY import androidx.test.ext.junit.runners.AndroidJUnit4 import android.widget.ImageView import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_SHADE_WINDOW_GOES_AROUND import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.privacy.PrivacyApplication import com.android.systemui.privacy.PrivacyItem import com.android.systemui.privacy.PrivacyType import com.android.systemui.res.R import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository import com.android.systemui.shade.domain.interactor.shadeDisplaysInteractor Loading Loading @@ -61,11 +68,14 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mockito import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @RunWithLooper class PrivacyDotViewControllerTest : SysuiTestCase() { @UsesFlags(Flags::class) class PrivacyDotViewControllerTest(flags: FlagsParameterization) : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val mockDisplay = createMockDisplay() Loading Loading @@ -105,6 +115,18 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { shadeDisplaysInteractor = { shadeDisplaysInteractor }, ) companion object { @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(Flags.FLAG_LOCATION_INDICATORS_ENABLED) } } init { mSetFlagsRule.setFlagsParameterization(flags) } @Test fun topMargin_topLeftView_basedOnSeascapeArea() { createAndInitializeController() Loading Loading @@ -383,7 +405,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = false // This informs the controller of an active privacy event. callback.onSystemStatusAnimationTransitionToPersistentDot(null) callback.onSystemStatusAnimationTransitionToPersistentDot(null, null) assertThat(controller.currentViewState.shouldShowDot()).isEqualTo(true) } Loading @@ -396,16 +418,81 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = true // This informs the controller of an active privacy event. callback.onSystemStatusAnimationTransitionToPersistentDot(null) callback.onSystemStatusAnimationTransitionToPersistentDot(null, null) assertThat(controller.currentViewState.shouldShowDot()).isEqualTo(false) } @Test @DisableFlags(Flags.FLAG_LOCATION_INDICATORS_ENABLED) fun animationCallback_locationIndicatorsDisabled_doesNotDetermineLocationOnlyEvents() = kosmos.runTest { val captor = ArgumentCaptor.forClass(SystemStatusAnimationCallback::class.java) val controller: PrivacyDotViewController = createAndInitializeController() Mockito.verify(mockAnimationScheduler).addCallback(captor.capture()) val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = false // This informs the controller of an active privacy event. // Even with just the location event, the location-only flag is not set. callback.onSystemStatusAnimationTransitionToPersistentDot( null, listOf( PrivacyItem( privacyType = PrivacyType.TYPE_LOCATION, application = PrivacyApplication(packageName = "com.android", uid = 1), ) ), ) assertThat(controller.currentViewState.systemPrivacyEventLocationOnlyIsActive) .isEqualTo(false) } @Test @EnableFlags(Flags.FLAG_LOCATION_INDICATORS_ENABLED) fun animationCallback_locationIndicatorsEnabled_determinesLocationOnlyEvents() = kosmos.runTest { val captor = ArgumentCaptor.forClass(SystemStatusAnimationCallback::class.java) val controller: PrivacyDotViewController = createAndInitializeController() Mockito.verify(mockAnimationScheduler).addCallback(captor.capture()) val callback: SystemStatusAnimationCallback = captor.value fakeAvControlsChipInteractor.isShowingAvChip.value = false // This informs the controller of an active privacy event. // Multiple privacy items are active, so the location-only flag is not set. callback.onSystemStatusAnimationTransitionToPersistentDot( null, listOf( PrivacyItem( privacyType = PrivacyType.TYPE_CAMERA, application = PrivacyApplication(packageName = "com.android", uid = 1), ), PrivacyItem( privacyType = PrivacyType.TYPE_LOCATION, application = PrivacyApplication(packageName = "com.android", uid = 2), ), ), ) assertThat(controller.currentViewState.systemPrivacyEventLocationOnlyIsActive) .isEqualTo(false) // Only location event is active, so the location-only flag is set. callback.onSystemStatusAnimationTransitionToPersistentDot( null, listOf( PrivacyItem( privacyType = PrivacyType.TYPE_LOCATION, application = PrivacyApplication(packageName = "com.android", uid = 1), ) ), ) assertThat(controller.currentViewState.systemPrivacyEventLocationOnlyIsActive) .isEqualTo(true) } private fun setRotation(rotation: Int) { whenever(mockDisplay.rotation).thenReturn(rotation) } private fun initDotView(): View { val privacyDot = View(context).also { it.id = R.id.privacy_dot } val privacyDot = ImageView(context).also { it.id = R.id.privacy_dot } return FrameLayout(context).also { it.layoutParams = FrameLayout.LayoutParams(/* width= */ 0, /* height= */ 0) it.addView(privacyDot) Loading
packages/SystemUI/res/values/colors.xml +1 −0 Original line number Diff line number Diff line Loading @@ -249,6 +249,7 @@ <color name="screenrecord_icon_color">#D93025</color><!-- red 600 --> <color name="privacy_chip_background">#3ddc84</color> <color name="privacy_chip_location_only_background">#3dbaf4</color> <!-- Accessibility floating menu --> <color name="accessibility_floating_menu_background">#CCFFFFFF</color> <!-- 80% --> Loading
packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +30 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.privacy import android.content.Context import android.content.pm.ActivityInfo import android.content.res.Configuration import android.graphics.drawable.GradientDrawable import android.location.flags.Flags.locationIndicatorsEnabled import android.util.AttributeSet import android.view.Gravity.CENTER_VERTICAL import android.view.Gravity.END Loading @@ -27,6 +29,7 @@ import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout import androidx.annotation.VisibleForTesting import com.android.settingslib.Utils import com.android.systemui.Flags import com.android.systemui.res.R Loading @@ -46,8 +49,9 @@ constructor( private var iconMargin = 0 private var iconSize = 0 private var iconColor = 0 private var chipDrawable: GradientDrawable? = null private val iconsContainer: LinearLayout @VisibleForTesting val iconsContainer: LinearLayout val launchableContentView get() = iconsContainer Loading @@ -55,6 +59,17 @@ constructor( set(value) { field = value updateView(PrivacyChipBuilder(context, field)) if (locationIndicatorsEnabled()) { updateResources() } } private val locationOnly: Boolean private get() = if (locationIndicatorsEnabled()) { PrivacyConfig.Companion.privacyItemsAreLocationOnly(privacyList) } else { false } init { Loading Loading @@ -149,6 +164,19 @@ constructor( context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) iconsContainer.layoutParams.height = height iconsContainer.setPaddingRelative(padding, 0, padding, 0) if (locationIndicatorsEnabled()) { if (chipDrawable == null) { chipDrawable = context.getDrawable(R.drawable.statusbar_privacy_chip_bg)?.mutate() as? GradientDrawable iconsContainer.background = chipDrawable } chipDrawable?.let { drawable -> val color = context.getColor(PrivacyConfig.Companion.getPrivacyColor(locationOnly)) drawable.setColor(color) } } else { iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg) } } }
packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt +14 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.util.DeviceConfigProxy import com.android.systemui.util.asIndenting import com.android.systemui.util.concurrency.DelayableExecutor Loading @@ -43,13 +44,25 @@ constructor( ) : Dumpable { @VisibleForTesting internal companion object { companion object { const val TAG = "PrivacyConfig" private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED private const val MEDIA_PROJECTION = SystemUiDeviceConfigFlags.PROPERTY_MEDIA_PROJECTION_INDICATORS_ENABLED private const val DEFAULT_MIC_CAMERA = true private const val DEFAULT_MEDIA_PROJECTION = true fun getPrivacyColor(locationOnly: Boolean): Int { if (locationOnly) { return R.color.privacy_chip_location_only_background } return R.color.privacy_chip_background } fun privacyItemsAreLocationOnly(privacyItems: List<PrivacyItem>): Boolean { return privacyItems.isNotEmpty() && privacyItems.all { it.privacyType == PrivacyType.TYPE_LOCATION } } } private val callbacks = mutableListOf<WeakReference<Callback>>() Loading