Merge "Add a toggle for enabling/disabling sensitive lockscreen notifications for Private Space" into main
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fa55d88..ad35e26 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1372,6 +1372,12 @@
     <string name="private_space_choose_your_password_header">Set a password for your private space</string>
     <!-- Header for private space choose your pattern screen [CHAR LIMIT=40] -->
     <string name="private_space_choose_your_pattern_header">Set a pattern for your private space</string>
+    <!-- Header for private space apps and notifications section [CHAR LIMIT=40] -->
+    <string name="private_space_apps_and_notifications_header">Apps and notifications</string>
+    <!-- Title for private space sensitive notifications toggle [CHAR LIMIT=80] -->
+    <string name="private_space_notifications_title">Sensitive notifications on lock screen</string>
+    <!-- Summary description for private space sensitive notifications toggle [CHAR LIMIT=200] -->
+    <string name="private_space_sensitive_notifications_description">Show sensitive content when private space is unlocked</string>
 
     <!-- Text shown when "Add fingerprint" button is disabled -->
     <string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml
index 0ed9c93..e718ca8 100644
--- a/res/xml/private_space_settings.xml
+++ b/res/xml/private_space_settings.xml
@@ -60,6 +60,17 @@
     </PreferenceCategory>
 
     <PreferenceCategory
+        android:title="@string/private_space_apps_and_notifications_header">
+
+        <com.android.settingslib.RestrictedSwitchPreference
+            android:key="private_space_sensitive_notifications"
+            android:title="@string/private_space_notifications_title"
+            android:summary="@string/private_space_sensitive_notifications_description"
+            settings:controller="com.android.settings.privatespace.HidePrivateSpaceSensitiveNotificationsController" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
         android:title="@string/private_space_category_system">
 
         <Preference
diff --git a/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java b/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java
new file mode 100644
index 0000000..1a89d37
--- /dev/null
+++ b/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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.settings.privatespace;
+
+import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.core.TogglePreferenceController;
+
+import java.util.Objects;
+
+/**
+ * A controller object for sensitive notifications in Private Space settings page.
+ */
+public class HidePrivateSpaceSensitiveNotificationsController extends TogglePreferenceController {
+    private final PrivateSpaceMaintainer mPrivateSpaceMaintainer;
+    private final UserHandle mPrivateProfileId;
+    public static final int ENABLED = 1;
+    public static final int DISABLED = 0;
+    private static final int DEVICE_SENSITIVE_NOTIFICATIONS_DEFAULT = ENABLED;
+    private static final int DEVICE_LOCK_SCREEN_NOTIFICATIONS_DEFAULT = ENABLED;
+    private static final int PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DEFAULT = DISABLED;
+
+    public HidePrivateSpaceSensitiveNotificationsController(@NonNull Context context,
+            @NonNull String preferenceKey) {
+        super(context, preferenceKey);
+        mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(context);
+        mPrivateProfileId = Objects.requireNonNull(
+                mPrivateSpaceMaintainer.getPrivateProfileHandle());
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (!android.os.Flags.allowPrivateProfile()
+                || !android.multiuser.Flags.enablePsSensitiveNotificationsToggle()
+                || !mPrivateSpaceMaintainer.doesPrivateSpaceExist()) {
+            return UNSUPPORTED_ON_DEVICE;
+        }
+        if (!getLockscreenNotificationsEnabled(mContext)
+                || !getLockscreenSensitiveNotificationsEnabledOnDevice(mContext)) {
+            return DISABLED_DEPENDENT_SETTING;
+        }
+        return AVAILABLE;
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DEFAULT, mPrivateProfileId.getIdentifier())
+                != DISABLED;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                isChecked ? ENABLED : DISABLED, mPrivateProfileId.getIdentifier());
+        return true;
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return 0;
+    }
+
+    /**
+     * If notifications are disabled on the device, the toggle for private space sensitive
+     * notifications should be unavailable.
+     */
+    private static boolean getLockscreenNotificationsEnabled(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                DEVICE_LOCK_SCREEN_NOTIFICATIONS_DEFAULT) != DISABLED;
+    }
+
+    /**
+     * If sensitive notifications are hidden on the device, they should be hidden for private space
+     * also.
+     */
+    private static boolean getLockscreenSensitiveNotificationsEnabledOnDevice(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                DEVICE_SENSITIVE_NOTIFICATIONS_DEFAULT) != DISABLED;
+    }
+}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index a283147..2d38ae2 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -45,6 +45,7 @@
 import java.util.List;
 
 // TODO(b/293569406): Update the javadoc when we have the setup flow in place to create PS
+
 /** A class to help with the creation / deletion of Private Space */
 public class PrivateSpaceMaintainer {
     private static final String TAG = "PrivateSpaceMaintainer";
@@ -65,9 +66,9 @@
     public static final int PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL = PRIVATE_SPACE_AUTO_LOCK_NEVER;
 
     public enum ErrorDeletingPrivateSpace {
-            DELETE_PS_ERROR_NONE,
-            DELETE_PS_ERROR_NO_PRIVATE_SPACE,
-            DELETE_PS_ERROR_INTERNAL
+        DELETE_PS_ERROR_NONE,
+        DELETE_PS_ERROR_NO_PRIVATE_SPACE,
+        DELETE_PS_ERROR_INTERNAL
     }
 
     /**
@@ -90,7 +91,7 @@
         if (mUserHandle == null) {
             try {
                 mUserHandle = mUserManager.createProfile(
-                                userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
+                        userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
             } catch (Exception e) {
                 Log.e(TAG, "Error creating private space", e);
                 return false;
@@ -117,7 +118,8 @@
         return true;
     }
 
-    /** Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation.
+    /**
+     * Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation.
      *
      * <p> This method should be used ONLY by the delete-PS controller in the PS Settings page.
      */
@@ -212,6 +214,7 @@
 
 
     // TODO(b/307281644): Remove this method once new auth change is merged
+
     /**
      * Returns true if private space exists and a separate private profile lock is set
      * otherwise false when the private space does not exit or exists but does not have a
@@ -290,9 +293,20 @@
         return false;
     }
 
+    @GuardedBy("this")
     private void resetPrivateSpaceSettings() {
         setHidePrivateSpaceEntryPointSetting(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
         setPrivateSpaceAutoLockSetting(PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL);
+        setPrivateSpaceSensitiveNotificationsDefaultValue();
+    }
+
+    /** Sets private space sensitive notifications hidden on lockscreen by default */
+    @GuardedBy("this")
+    private void setPrivateSpaceSensitiveNotificationsDefaultValue() {
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                HidePrivateSpaceSensitiveNotificationsController.DISABLED,
+                mUserHandle.getIdentifier());
     }
 
     /**
diff --git a/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java b/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java
new file mode 100644
index 0000000..1430dfd
--- /dev/null
+++ b/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 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.settings.privatespace;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Tests for HidePrivateSpaceSensitiveNotificationsController.
+ * Run as {@code atest SettingsUnitTests:HidePrivateSpaceSensitiveNotificationsControllerTest}
+ */
+@RunWith(AndroidJUnit4.class)
+public class HidePrivateSpaceSensitiveNotificationsControllerTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private Context mContext;
+    private HidePrivateSpaceSensitiveNotificationsController
+            mHidePrivateSpaceSensitiveNotificationsController;
+    @Mock
+    private ContentResolver mContentResolver;
+    private int mOriginalDeviceSensitiveNotifValue;
+    private int mOriginalDeviceNotifValue;
+    private int mOriginalPsSensitiveNotifValue;
+    private int mPrivateProfileId;
+
+    @Before
+    public void setUp() {
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mContentResolver = mContext.getContentResolver();
+        assumeTrue(PrivateSpaceMaintainer.getInstance(mContext).doesPrivateSpaceExist());
+
+        mSetFlagsRule.enableFlags(
+                android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mPrivateProfileId = PrivateSpaceMaintainer.getInstance(
+                mContext).getPrivateProfileHandle().getIdentifier();
+
+        mOriginalDeviceSensitiveNotifValue = Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+        mOriginalDeviceNotifValue = Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mOriginalPsSensitiveNotifValue = Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mPrivateProfileId);
+
+        final String preferenceKey = "private_space_sensitive_notifications";
+        mHidePrivateSpaceSensitiveNotificationsController =
+                new HidePrivateSpaceSensitiveNotificationsController(mContext, preferenceKey);
+    }
+
+    @After
+    public void tearDown() {
+        Settings.Secure.putInt(mContentResolver,
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                mOriginalDeviceSensitiveNotifValue
+        );
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, mOriginalDeviceNotifValue);
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                mOriginalPsSensitiveNotifValue, mPrivateProfileId);
+    }
+
+    /**
+     * Tests that the controller is unavailable if lockscreen sensitive notifications are disabled
+     * on the device.
+     */
+    @Test
+    public void getAvailabilityStatus_lockScreenPrivateNotificationsOff() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+                .isEqualTo(DISABLED_DEPENDENT_SETTING);
+    }
+
+    /**
+     * Tests that the controller is unavailable if lockscreen notifications are disabled on the
+     * device.
+     */
+    @Test
+    public void getAvailabilityStatus_lockScreenNotificationsOff() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+                .isEqualTo(DISABLED_DEPENDENT_SETTING);
+    }
+
+    /**
+     * Tests that the controller is available if lockscreen notifications and lockscreen private
+     * notifications are enabled on the device.
+     */
+    @Test
+    public void getAvailabilityStatus_returnAvailable() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+                .isEqualTo(AVAILABLE);
+    }
+
+
+    /**
+     * Tests that toggle is not available if the flag for this feature and MVP flag are disabled.
+     */
+    @Test
+    public void getAvailabilityStatus_flagDisabled() {
+        mSetFlagsRule.disableFlags(
+                android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
+        mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus())
+                .isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void testSetChecked() {
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.setChecked(true)).isTrue();
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.isChecked()).isEqualTo(true);
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.setChecked(false)).isTrue();
+        assertThat(mHidePrivateSpaceSensitiveNotificationsController.isChecked()).isEqualTo(false);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
index 4bb5d43..8510b11 100644
--- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.privatespace;
 
+import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
 import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK;
 
 import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
@@ -140,6 +141,24 @@
     }
 
     /**
+     * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} sets the PS sensitive
+     * notifications to hidden by default.
+     */
+    @Test
+    public void createPrivateSpace_psDoesNotExist_setsDefaultPsSensitiveNotificationsValue() {
+        mSetFlagsRule.enableFlags(
+                Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE);
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.deletePrivateSpace();
+        privateSpaceMaintainer.createPrivateSpace();
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+        assertThat(getPsSensitiveNotificationsValue(privateSpaceMaintainer))
+                .isEqualTo(HidePrivateSpaceSensitiveNotificationsController.DISABLED);
+    }
+
+    /**
      * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exist does not reset
      * hide PS Settings.
      */
@@ -287,4 +306,11 @@
                 0,
                 privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier());
     }
+
+    private int getPsSensitiveNotificationsValue(PrivateSpaceMaintainer privateSpaceMaintainer) {
+        return Settings.Secure.getIntForUser(mContentResolver,
+                LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                HidePrivateSpaceSensitiveNotificationsController.ENABLED,
+                privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier());
+    }
 }