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

Commit 22913bb0 authored by Lucy Miyuki Narita's avatar Lucy Miyuki Narita Committed by Android (Google) Code Review
Browse files

Merge "[QSSecurityFooter] Change visuals depending on supervision pin presence." into main

parents c0b34b93 d0a8326f
Loading
Loading
Loading
Loading
+115 −7
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.supervision.data.model.SupervisionModel;
import com.android.systemui.supervision.data.repository.FakeSupervisionRepository;

import org.junit.Before;
import org.junit.Test;
@@ -142,8 +144,13 @@ public class QSSecurityFooterTest extends SysuiTestCase {

    @Nullable
    private SecurityButtonConfig getButtonConfig() {
        return getButtonConfig(null);
    }

    @Nullable
    private SecurityButtonConfig getButtonConfig(@Nullable SupervisionModel supervisionModel) {
        SecurityModel securityModel = SecurityModel.create(mSecurityController);
        return mFooterUtils.getButtonConfig(securityModel);
        return mFooterUtils.getButtonConfig(securityModel, supervisionModel);
    }

    private void assertIsDefaultIcon(Icon icon) {
@@ -674,7 +681,8 @@ public class QSSecurityFooterTest extends SysuiTestCase {
        // We use the default icon when there is no admin icon.
        when(mSecurityController.getIcon(any())).thenReturn(null);
        SecurityButtonConfig buttonConfig = getButtonConfig();
        assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
        assertEquals(
                mContext.getString(R.string.quick_settings_disclosure_parental_controls),
                buttonConfig.getText());
        assertIsDefaultIcon(buttonConfig.getIcon());

@@ -683,7 +691,8 @@ public class QSSecurityFooterTest extends SysuiTestCase {

        buttonConfig = getButtonConfig();
        assertNotNull(buttonConfig);
        assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
        assertEquals(
                mContext.getString(R.string.quick_settings_disclosure_parental_controls),
                buttonConfig.getText());
        assertIsIconDrawable(buttonConfig.getIcon(), testDrawable);

@@ -704,7 +713,8 @@ public class QSSecurityFooterTest extends SysuiTestCase {
        // We use the default icon when there is no admin icon.
        when(mSecurityController.getIcon()).thenReturn(null);
        SecurityButtonConfig buttonConfig = getButtonConfig();
        assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
        assertEquals(
                mContext.getString(R.string.quick_settings_disclosure_parental_controls),
                buttonConfig.getText());
        assertIsDefaultIcon(buttonConfig.getIcon());

@@ -713,7 +723,8 @@ public class QSSecurityFooterTest extends SysuiTestCase {

        buttonConfig = getButtonConfig();
        assertNotNull(buttonConfig);
        assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
        assertEquals(
                mContext.getString(R.string.quick_settings_disclosure_parental_controls),
                buttonConfig.getText());
        assertIsIconDrawable(buttonConfig.getIcon(), testDrawable);

@@ -725,7 +736,65 @@ public class QSSecurityFooterTest extends SysuiTestCase {
    }

    @Test
    @DisableFlags(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS)
    @EnableFlags({
        android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS,
        android.app.supervision.flags.Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE
    })
    public void testParentalControls_newSupervisionApisGetInfoFromSupervisionModel() {
        // Make sure the security footer is visible, so that the images are updated.
        when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true);
        SupervisionModel supervisionModel =
                new SupervisionModel(
                        /* isSupervisionEnabled= */ true,
                        /* label= */ null,
                        /* icon= */ null,
                        /* footerText= */ mContext.getString(
                                R.string.quick_settings_disclosure_pin_protection),
                        /* disclaimerText= */ null);

        // We use the default icon when there is no admin icon.
        SecurityButtonConfig buttonConfig = getButtonConfig(supervisionModel);
        assertEquals(
                mContext.getString(R.string.quick_settings_disclosure_pin_protection),
                buttonConfig.getText());
        assertIsDefaultIcon(buttonConfig.getIcon());

        Drawable testDrawable = new VectorDrawable();
        supervisionModel =
                new SupervisionModel(
                        /* isSupervisionEnabled= */ true,
                        /* label= */ null,
                        /* icon= */ testDrawable,
                        /* footerText= */ mContext.getString(
                                R.string.quick_settings_disclosure_pin_protection),
                        /* disclaimerText= */ null);

        buttonConfig = getButtonConfig(supervisionModel);
        assertNotNull(buttonConfig);
        assertEquals(
                mContext.getString(R.string.quick_settings_disclosure_pin_protection),
                buttonConfig.getText());
        assertIsIconDrawable(buttonConfig.getIcon(), testDrawable);

        // Ensure the primary icon is back to default after parental controls are gone
        supervisionModel =
                new SupervisionModel(
                        /* isSupervisionEnabled= */ false,
                        /* label= */ null,
                        /* icon= */ testDrawable,
                        /* footerText= */ mContext.getString(
                                R.string.quick_settings_disclosure_pin_protection),
                        /* disclaimerText= */ null);
        buttonConfig = getButtonConfig(supervisionModel);
        assertNotNull(buttonConfig);
        assertIsDefaultIcon(buttonConfig.getIcon());
    }

    @Test
    @DisableFlags({
        android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS,
        android.app.supervision.flags.Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE
    })
    public void testParentalControlsDialog() {
        when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
        when(mSecurityController.getLabel(any())).thenReturn(PARENTAL_CONTROLS_LABEL);
@@ -733,17 +802,56 @@ public class QSSecurityFooterTest extends SysuiTestCase {
        View view = mFooterUtils.createDialogView(getContext());
        TextView textView = (TextView) view.findViewById(R.id.parental_controls_title);
        assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText().toString());
        TextView contentView = view.findViewById(R.id.parental_controls_warning);
        assertEquals(
                mContext.getString(R.string.monitoring_description_parental_controls),
                contentView.getText().toString());
    }

    @Test
    @EnableFlags(android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS)
    public void testParentalControlsDialog_newSupervisionApis() {
    @DisableFlags(android.app.supervision.flags.Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE)
    public void testParentalControlsDialog_newSupervisionApisDefaultDisclaimerText() {
        FakeSupervisionRepository supervisionRepository = new FakeSupervisionRepository();
        supervisionRepository.setIsSupervisionEnabled(true);
        supervisionRepository.setLabel(PARENTAL_CONTROLS_LABEL);
        when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
        when(mSecurityController.getLabel()).thenReturn(PARENTAL_CONTROLS_LABEL);
        when(mSecurityController.getSupervisionModel())
                .thenReturn(supervisionRepository.getSupervisionModel());

        View view = mFooterUtils.createDialogView(getContext());
        TextView textView = (TextView) view.findViewById(R.id.parental_controls_title);
        assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText().toString());
        TextView contentView = view.findViewById(R.id.parental_controls_warning);
        assertEquals(
                mContext.getString(R.string.monitoring_description_parental_controls),
                contentView.getText().toString());
    }

    @Test
    @EnableFlags({
        android.app.supervision.flags.Flags.FLAG_DEPRECATE_DPM_SUPERVISION_APIS,
        android.app.supervision.flags.Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE
    })
    public void testParentalControlsDialog_newSupervisionApisCustomDisclaimerText() {
        FakeSupervisionRepository supervisionRepository = new FakeSupervisionRepository();
        supervisionRepository.setIsSupervisionEnabled(true);
        supervisionRepository.setLabel(PARENTAL_CONTROLS_LABEL);
        supervisionRepository.setDisclaimerText(
                mContext.getString(R.string.monitoring_description_pin_protection));
        when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
        when(mSecurityController.getLabel()).thenReturn(PARENTAL_CONTROLS_LABEL);
        when(mSecurityController.getSupervisionModel())
                .thenReturn(supervisionRepository.getSupervisionModel());

        View view = mFooterUtils.createDialogView(getContext());
        TextView textView = view.findViewById(R.id.parental_controls_title);
        assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText().toString());
        TextView contentView = view.findViewById(R.id.parental_controls_warning);
        assertEquals(
                mContext.getString(R.string.monitoring_description_pin_protection),
                contentView.getText().toString());
    }

    @Test
+120 −10
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.systemui.qs.footer.domain.interactor

import android.content.Context
import android.content.Intent
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -28,20 +31,29 @@ import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.QSSecurityFooterUtils
import com.android.systemui.qs.footer.FooterActionsTestUtils
import com.android.systemui.security.data.model.SecurityModel
import com.android.systemui.security.data.repository.SecurityRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.supervision.data.model.SupervisionModel
import com.android.systemui.supervision.data.repository.FakeSupervisionRepository
import com.android.systemui.truth.correspondence.FakeUiEvent
import com.android.systemui.truth.correspondence.LogMaker
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
@@ -49,11 +61,14 @@ import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock

@SmallTest
@RunWith(AndroidJUnit4::class)
@ExperimentalCoroutinesApi
@TestableLooper.RunWithLooper
class FooterActionsInteractorTest : SysuiTestCase() {
    @get:Rule val setFlagsRule = SetFlagsRule()
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private lateinit var utils: FooterActionsTestUtils
@@ -65,15 +80,15 @@ class FooterActionsInteractorTest : SysuiTestCase() {

    @Test
    fun showDeviceMonitoringDialog() {
        val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
        val qsSecurityFooterUtils: QSSecurityFooterUtils = mock()
        val underTest = utils.footerActionsInteractor(qsSecurityFooterUtils = qsSecurityFooterUtils)

        val quickSettingsContext = mock<Context>()
        val quickSettingsContext: Context = mock()

        underTest.showDeviceMonitoringDialog(quickSettingsContext, null)
        verify(qsSecurityFooterUtils).showDeviceMonitoringDialog(quickSettingsContext, null)

        val expandable = mock<Expandable>()
        val expandable: Expandable = mock()
        underTest.showDeviceMonitoringDialog(quickSettingsContext, expandable)
        verify(qsSecurityFooterUtils).showDeviceMonitoringDialog(quickSettingsContext, expandable)
    }
@@ -83,8 +98,8 @@ class FooterActionsInteractorTest : SysuiTestCase() {
        val uiEventLogger = UiEventLoggerFake()
        val underTest = utils.footerActionsInteractor(uiEventLogger = uiEventLogger)

        val globalActionsDialogLite = mock<GlobalActionsDialogLite>()
        val expandable = mock<Expandable>()
        val globalActionsDialogLite: GlobalActionsDialogLite = mock()
        val expandable: Expandable = mock()
        underTest.showPowerMenuDialog(globalActionsDialogLite, expandable)

        // Event is logged.
@@ -105,8 +120,8 @@ class FooterActionsInteractorTest : SysuiTestCase() {

    @Test
    fun showSettings_userSetUp() {
        val activityStarter = mock<ActivityStarter>()
        val deviceProvisionedController = mock<DeviceProvisionedController>()
        val activityStarter: ActivityStarter = mock()
        val deviceProvisionedController: DeviceProvisionedController = mock()
        val metricsLogger = FakeMetricsLogger()

        // User is set up.
@@ -139,8 +154,8 @@ class FooterActionsInteractorTest : SysuiTestCase() {

    @Test
    fun showSettings_userNotSetUp() {
        val activityStarter = mock<ActivityStarter>()
        val deviceProvisionedController = mock<DeviceProvisionedController>()
        val activityStarter: ActivityStarter = mock()
        val deviceProvisionedController: DeviceProvisionedController = mock()

        // User is not set up.
        whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
@@ -156,4 +171,99 @@ class FooterActionsInteractorTest : SysuiTestCase() {
        // We only unlock the device.
        verify(activityStarter).postQSRunnableDismissingKeyguard(any())
    }

    @Test
    @EnableFlags(android.app.supervision.flags.Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE)
    fun securityButtonConfig_flagEnabled_usesSupervisionModel() =
        testScope.runTest {
            val qsSecurityFooterUtils: QSSecurityFooterUtils = mock()
            val securityRepository: SecurityRepository = mock()
            val securityModel =
                SecurityModel(
                    isDeviceManaged = false,
                    hasWorkProfile = false,
                    isWorkProfileOn = false,
                    isProfileOwnerOfOrganizationOwnedDevice = false,
                    deviceOwnerOrganizationName = null,
                    workProfileOrganizationName = null,
                    isNetworkLoggingEnabled = false,
                    isVpnBranded = false,
                    primaryVpnName = null,
                    workProfileVpnName = null,
                    hasCACertInCurrentUser = false,
                    hasCACertInWorkProfile = false,
                    isParentalControlsEnabled = false,
                    deviceAdminIcon = null,
                )
            val securityModelFlow = MutableStateFlow(securityModel)
            whenever(securityRepository.security).thenReturn(securityModelFlow)
            val supervisionRepository = FakeSupervisionRepository()
            supervisionRepository.updateState(
                SupervisionModel(
                    isSupervisionEnabled = true,
                    label = "Test app",
                    footerText = "This device is managed",
                    disclaimerText = "Some settings are managed by Test app",
                    icon = null,
                )
            )
            val underTest =
                utils.footerActionsInteractor(
                    qsSecurityFooterUtils = qsSecurityFooterUtils,
                    supervisionRepository = supervisionRepository,
                )

            collectLastValue(underTest.securityButtonConfig)
            runCurrent()

            verify(qsSecurityFooterUtils)
                .getButtonConfig(eq(securityModel), eq(supervisionRepository.getSupervisionModel()))
        }

    @Test
    @DisableFlags(android.app.supervision.flags.Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE)
    fun securityButtonConfig_flagDisabled_doesNotUseSupervisionModel() =
        testScope.runTest {
            val qsSecurityFooterUtils: QSSecurityFooterUtils = mock()
            val securityRepository: SecurityRepository = mock()
            val securityModel =
                SecurityModel(
                    isDeviceManaged = false,
                    hasWorkProfile = false,
                    isWorkProfileOn = false,
                    isProfileOwnerOfOrganizationOwnedDevice = false,
                    deviceOwnerOrganizationName = null,
                    workProfileOrganizationName = null,
                    isNetworkLoggingEnabled = false,
                    isVpnBranded = false,
                    primaryVpnName = null,
                    workProfileVpnName = null,
                    hasCACertInCurrentUser = false,
                    hasCACertInWorkProfile = false,
                    isParentalControlsEnabled = false,
                    deviceAdminIcon = null,
                )
            val securityModelFlow = MutableStateFlow(securityModel)
            whenever(securityRepository.security).thenReturn(securityModelFlow)
            val supervisionRepository = FakeSupervisionRepository()
            supervisionRepository.updateState(
                SupervisionModel(
                    isSupervisionEnabled = true,
                    label = "Test app",
                    footerText = "This device is managed",
                    disclaimerText = "Some settings are managed by Test app",
                    icon = null,
                )
            )
            val underTest =
                utils.footerActionsInteractor(
                    qsSecurityFooterUtils = qsSecurityFooterUtils,
                    supervisionRepository = supervisionRepository,
                )

            collectLastValue(underTest.securityButtonConfig)
            runCurrent()

            verify(qsSecurityFooterUtils).getButtonConfig(eq(securityModel), eq(null))
        }
}
+4 −4
Original line number Diff line number Diff line
@@ -53,7 +53,6 @@ import com.android.systemui.statusbar.policy.FakeSecurityController
import com.android.systemui.statusbar.policy.FakeUserInfoController
import com.android.systemui.statusbar.policy.FakeUserInfoController.FakeInfo
import com.android.systemui.statusbar.policy.MockUserSwitcherControllerWrapper
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeGlobalSettings
@@ -70,6 +69,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.`when` as whenever
import org.mockito.kotlin.any

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -211,7 +211,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
        // Mock QSSecurityFooter to map a SecurityModel into a SecurityButtonConfig using the
        // logic in securityToConfig.
        var securityToConfig: (SecurityModel) -> SecurityButtonConfig? = { null }
        whenever(qsSecurityFooterUtils.getButtonConfig(any())).thenAnswer {
        whenever(qsSecurityFooterUtils.getButtonConfig(any(), any())).thenAnswer {
            securityToConfig(it.arguments.first() as SecurityModel)
        }

@@ -269,7 +269,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
        // Mock QSSecurityFooter to map a SecurityModel into a SecurityButtonConfig using the
        // logic in securityToConfig.
        var securityToConfig: (SecurityModel) -> SecurityButtonConfig? = { null }
        whenever(qsSecurityFooterUtils.getButtonConfig(any())).thenAnswer {
        whenever(qsSecurityFooterUtils.getButtonConfig(any(), any())).thenAnswer {
            securityToConfig(it.arguments.first() as SecurityModel)
        }

@@ -484,7 +484,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
        // Mock QSSecurityFooter to map a SecurityModel into a SecurityButtonConfig using the
        // logic in securityToConfig.
        val securityToConfig: (SecurityModel) -> SecurityButtonConfig? = { null }
        whenever(qsSecurityFooterUtils.getButtonConfig(any())).thenAnswer {
        whenever(qsSecurityFooterUtils.getButtonConfig(any(), any())).thenAnswer {
            securityToConfig(it.arguments.first() as SecurityModel)
        }
        val fgsManagerController =
+21 −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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
    android:height="24dp" android:viewportWidth="960" android:viewportHeight="960"
    android:tint="?attr/colorControlNormal">
    <path android:fillColor="@android:color/white"
        android:pathData="M480,80Q530,80 565,115Q600,150 600,200Q600,250 565,285Q530,320 480,320Q430,320 395,285Q360,250 360,200Q360,150 395,115Q430,80 480,80ZM480,360Q527,360 573,371Q619,382 656,402Q694,421 717,447Q740,473 740,504L740,736Q740,753 732,769.5Q724,786 710,800Q696,814 677.5,826Q659,838 636,848L636,758Q636,720 583.5,696Q531,672 480,672Q430,672 383.5,692.5Q337,713 326,746Q364,761 404,767Q444,773 486,774Q494,774 502.5,774Q511,774 520,774L520,878Q513,880 505.5,880Q498,880 490,880Q454,880 407.5,872Q361,864 319,847Q277,830 248.5,802.5Q220,775 220,736L220,504Q220,473 243,447Q266,421 303,402Q341,382 387,371Q433,360 480,360ZM480,600Q513,600 536.5,576.5Q560,553 560,520Q560,487 536.5,463.5Q513,440 480,440Q447,440 423.5,463.5Q400,487 400,520Q400,553 423.5,576.5Q447,600 480,600Z" />
</vector>
+9 −0
Original line number Diff line number Diff line
@@ -1659,6 +1659,9 @@
    <!-- Disclosure at the bottom of Quick Settings that indicates that parental controls are enabled. [CHAR LIMIT=100] -->
    <string name="quick_settings_disclosure_parental_controls">This device is managed by your parent</string>

    <!-- Disclosure at the bottom of Quick Settings that indicates that some of the settings changes are protected by a PIN. [CHAR LIMIT=100] -->
    <string name="quick_settings_disclosure_pin_protection">Some device settings are PIN protected.</string>

    <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the organization can monitor network traffic on that device. [CHAR LIMIT=100] -->
    <string name="quick_settings_disclosure_management_monitoring">Your organization owns this device and may monitor network traffic</string>

@@ -1783,6 +1786,9 @@
    <!-- Dialog that a user can access via Quick Settings. [CHAR LIMIT=NONE]-->
    <string name="monitoring_description_parental_controls">This device is managed by your parent. Your parent can see and manage information such as the apps you use, your location, and your screen time.</string>

    <!-- Dialog that a user can access via Quick Settings. [CHAR LIMIT=NONE]-->
    <string name="monitoring_description_pin_protection">Some controls on your device, like daily limits \u0026 downtime, can only be changed with a PIN.</string>

    <!-- Name for a generic legacy VPN connection [CHAR LIMIT=20] -->
    <string name="legacy_vpn_name">VPN</string>

@@ -2032,6 +2038,9 @@
    <!-- Name of the supervision status bar icon. -->
    <string name="status_bar_supervision">Parental controls</string>

    <!-- Name of the supervision status bar icon when pin is set. -->
    <string name="status_bar_pin_supervision">PIN protection for some device controls</string>

    <!-- Description for adding  a quick settings tile -->

    <!-- Name of a quick settings tile controlled by broadcast -->
Loading