diff --git a/res/values/strings.xml b/res/values/strings.xml index 012ddc3498b..2ae7ed5d96a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1372,6 +1372,12 @@ Set a password for your private space Set a pattern for your private space + + Apps and notifications + + Sensitive notifications on lock screen + + Show sensitive content when private space is unlocked You can add up to %d fingerprints diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml index 0ed9c93fb84..e718ca81a6b 100644 --- a/res/xml/private_space_settings.xml +++ b/res/xml/private_space_settings.xml @@ -59,6 +59,17 @@ + + + + + + diff --git a/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java b/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java new file mode 100644 index 00000000000..1a89d37b8b6 --- /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 a2831478a8b..2d38ae2007f 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java @@ -45,6 +45,7 @@ import com.android.internal.annotations.GuardedBy; 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 class PrivateSpaceMaintainer { 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 @@ public class PrivateSpaceMaintainer { 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 @@ public class PrivateSpaceMaintainer { return true; } - /** Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation. + /** + * Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation. * *

This method should be used ONLY by the delete-PS controller in the PS Settings page. */ @@ -212,6 +214,7 @@ public class PrivateSpaceMaintainer { // 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 @@ public class PrivateSpaceMaintainer { 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 00000000000..1430dfd1736 --- /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 4bb5d43b59d..8510b11c9c6 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; @@ -139,6 +140,24 @@ public class PrivateSpaceMaintainerTest { .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL); } + /** + * 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 @@ public class PrivateSpaceMaintainerTest { 0, privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier()); } + + private int getPsSensitiveNotificationsValue(PrivateSpaceMaintainer privateSpaceMaintainer) { + return Settings.Secure.getIntForUser(mContentResolver, + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + HidePrivateSpaceSensitiveNotificationsController.ENABLED, + privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier()); + } }