Merge "Fix incorrect colored bar when task bar is hidden" into tm-dev
diff --git a/res/drawable/accessibility_auto_added_qs_tooltips_illustration.xml b/res/drawable/accessibility_auto_added_qs_tooltips_illustration.xml
new file mode 100644
index 0000000..2583b6f
--- /dev/null
+++ b/res/drawable/accessibility_auto_added_qs_tooltips_illustration.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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="178dp"
+ android:height="150dp"
+ android:viewportWidth="178"
+ android:viewportHeight="150">
+ <path
+ android:pathData="M161.612,147.208L16.619,147.208A13,13 0,0 1,3.619 134.208L3.619,-442.725A13,13 0,0 1,16.619 -455.725L161.612,-455.725A13,13 0,0 1,174.612 -442.725L174.612,134.208A13,13 0,0 1,161.612 147.208z"
+ android:strokeWidth="6"
+ android:fillColor="#ffffff"
+ android:strokeColor="#EDEDED"/>
+ <path
+ android:pathData="M28.47,103.945L48.47,103.945A12,12 0,0 1,60.47 115.945L60.47,115.945A12,12 0,0 1,48.47 127.945L28.47,127.945A12,12 0,0 1,16.47 115.945L16.47,115.945A12,12 0,0 1,28.47 103.945z"
+ android:fillColor="#797272"/>
+ <path
+ android:pathData="M38.5,115.5m-15.5,0a15.5,15.5 0,1 1,31 0a15.5,15.5 0,1 1,-31 0"
+ android:fillColor="#BCEDDF"
+ android:fillAlpha="0.5"/>
+ <path
+ android:pathData="M45.279,108.52L46.48,109.72C47.182,110.414 47.182,111.543 46.48,112.237L34.717,124H31V120.283L42.763,108.52C43.457,107.827 44.586,107.827 45.279,108.52ZM32.778,122.222L34.032,122.275L42.763,113.535L41.51,112.281L32.778,121.013V122.222Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M79,104L99,104A12,12 0,0 1,111 116L111,116A12,12 0,0 1,99 128L79,128A12,12 0,0 1,67 116L67,116A12,12 0,0 1,79 104z"
+ android:fillColor="#E0DCDC"/>
+ <path
+ android:pathData="M79,104L99,104A12,12 0,0 1,111 116L111,116A12,12 0,0 1,99 128L79,128A12,12 0,0 1,67 116L67,116A12,12 0,0 1,79 104z"
+ android:fillColor="#E7E7E7"/>
+ <path
+ android:pathData="M129,104L149,104A12,12 0,0 1,161 116L161,116A12,12 0,0 1,149 128L129,128A12,12 0,0 1,117 116L117,116A12,12 0,0 1,129 104z"
+ android:fillColor="#E0DCDC"/>
+ <path
+ android:pathData="M129,104L149,104A12,12 0,0 1,161 116L161,116A12,12 0,0 1,149 128L129,128A12,12 0,0 1,117 116L117,116A12,12 0,0 1,129 104z"
+ android:fillColor="#E7E7E7"/>
+ <path
+ android:pathData="M22,14L76.49,14A6,6 0,0 1,82.49 20L82.49,38.782A6,6 0,0 1,76.49 44.782L22,44.782A6,6 0,0 1,16 38.782L16,20A6,6 0,0 1,22 14z"
+ android:fillColor="#EDEDED"/>
+ <path
+ android:pathData="M22,56L76.49,56A6,6 0,0 1,82.49 62L82.49,80.782A6,6 0,0 1,76.49 86.782L22,86.782A6,6 0,0 1,16 80.782L16,62A6,6 0,0 1,22 56z"
+ android:fillColor="#EDEDED"/>
+ <path
+ android:pathData="M101,14L155.49,14A6,6 0,0 1,161.49 20L161.49,38.782A6,6 0,0 1,155.49 44.782L101,44.782A6,6 0,0 1,95 38.782L95,20A6,6 0,0 1,101 14z"
+ android:fillColor="#EDEDED"/>
+ <path
+ android:pathData="M101,56L155.49,56A6,6 0,0 1,161.49 62L161.49,80.782A6,6 0,0 1,155.49 86.782L101,86.782A6,6 0,0 1,95 80.782L95,62A6,6 0,0 1,101 56z"
+ android:fillColor="#EDEDED"/>
+</vector>
diff --git a/res/values/config.xml b/res/values/config.xml
index 50eddb5..bf78fd7 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -570,7 +570,19 @@
<item>2</item>
<item>3</item>
</integer-array>
-
+
+ <!-- The settings/preference description for each settable device state defined in the array
+ "config_perDeviceStateRotationLockDefaults".
+ The item in position "i" describes the auto-rotation setting for the device state also in
+ position "i" in the array "config_perDeviceStateRotationLockDefaults". -->
+ <string-array name="config_settableAutoRotationDeviceStatesDescriptions">
+ <!-- Example:
+ <item>Auto-rotate when folded</item>
+ <item>@null</item> No description for state in position 1
+ <item>Auto-rotate when unfolded</item>
+ -->
+ </string-array>
+
<!-- Whether to aggregate for network selection list-->
<bool name="config_network_selection_list_aggregation_enabled">false</bool>
@@ -596,4 +608,5 @@
<!-- Whether the dream setup activity should be enabled as part of setupwizard -->
<bool name="dream_setup_supported">false</bool>
+
</resources>
diff --git a/res/xml/accessibility_system_controls.xml b/res/xml/accessibility_system_controls.xml
index 71e1143..37c4d67 100644
--- a/res/xml/accessibility_system_controls.xml
+++ b/res/xml/accessibility_system_controls.xml
@@ -42,9 +42,22 @@
android:title="@string/accessibility_power_button_ends_call_prerefence_title"
settings:controller="com.android.settings.accessibility.PowerButtonEndsCallPreferenceController"/>
+ <!-- Standard auto-rotation preference that will be shown when device state based auto-rotation
+ settings are NOT available. -->
<SwitchPreference
android:key="toggle_lock_screen_rotation_preference"
android:persistent="false"
android:title="@string/accelerometer_title"
settings:controller="com.android.settings.accessibility.LockScreenRotationPreferenceController"/>
+
+ <!-- Auto-rotation preference that will be shown when device state based auto-rotation settings
+ are available. -->
+ <Preference
+ android:key="device_state_auto_rotate_accessibility"
+ android:persistent="false"
+ android:title="@string/accelerometer_title"
+ android:fragment="com.android.settings.display.DeviceStateAutoRotateDetailsFragment"
+ settings:keywords="@string/keywords_auto_rotate"
+ settings:controller="com.android.settings.display.DeviceStateAutoRotateOverviewController"/>
+
</PreferenceScreen>
diff --git a/res/xml/device_state_auto_rotate_settings.xml b/res/xml/device_state_auto_rotate_settings.xml
new file mode 100644
index 0000000..2ddb4c7
--- /dev/null
+++ b/res/xml/device_state_auto_rotate_settings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/accelerometer_title" >
+
+ <!-- Device state based auto-rotation preferences will be added programmatically here. -->
+
+</PreferenceScreen>
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index 0724eea..ba52a30 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -96,12 +96,27 @@
<PreferenceCategory
android:title="@string/category_name_display_controls">
+ <!--
+ Standard auto-rotation preference that will be shown when device state based
+ auto-rotation settings are NOT available.
+ -->
<SwitchPreference
android:key="auto_rotate"
android:title="@string/accelerometer_title"
settings:keywords="@string/keywords_auto_rotate"
settings:controller="com.android.settings.display.AutoRotatePreferenceController"/>
+ <!--
+ Auto-rotation preference that will be shown when device state based auto-rotation
+ settings are available.
+ -->
+ <Preference
+ android:key="device_state_auto_rotate"
+ android:title="@string/accelerometer_title"
+ android:fragment="com.android.settings.display.DeviceStateAutoRotateDetailsFragment"
+ settings:keywords="@string/keywords_auto_rotate"
+ settings:controller="com.android.settings.display.DeviceStateAutoRotateOverviewController"/>
+
<SwitchPreference
android:key="display_white_balance"
android:title="@string/display_white_balance_title"
diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
index 5798723..59618d6 100644
--- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
@@ -509,7 +509,9 @@
? R.string.accessibility_service_qs_tooltips_content
: R.string.accessibility_service_auto_added_qs_tooltips_content;
final String title = getString(titleResId, tileName);
- final int imageResId = R.drawable.accessibility_qs_tooltips_illustration;
+ final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
+ ? R.drawable.accessibility_qs_tooltips_illustration
+ : R.drawable.accessibility_auto_added_qs_tooltips_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
mTooltipWindow.setup(title, imageResId);
mTooltipWindow.showAtTopCenter(getView());
diff --git a/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java b/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java
index 5ed4b5c..a1c292a 100644
--- a/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java
+++ b/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java
@@ -25,6 +25,7 @@
import com.android.internal.view.RotationPolicy.RotationPolicyListener;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.display.DeviceStateAutoRotationHelper;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -59,7 +60,9 @@
@Override
public int getAvailabilityStatus() {
- return RotationPolicy.isRotationSupported(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ return RotationPolicy.isRotationSupported(mContext)
+ && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabledForA11y(mContext)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 5992ceb..69b8f75 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -79,6 +79,7 @@
public abstract class ToggleFeaturePreferenceFragment extends SettingsPreferenceFragment
implements ShortcutPreference.OnClickCallback, OnMainSwitchChangeListener {
+ protected TopIntroPreference mTopIntroPreference;
protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
protected ShortcutPreference mShortcutPreference;
protected Preference mSettingsPreference;
@@ -483,10 +484,10 @@
if (TextUtils.isEmpty(mTopIntroTitle)) {
return;
}
- final TopIntroPreference topIntroPreference = new TopIntroPreference(getPrefContext());
- topIntroPreference.setKey(KEY_TOP_INTRO_PREFERENCE);
- topIntroPreference.setTitle(mTopIntroTitle);
- getPreferenceScreen().addPreference(topIntroPreference);
+ mTopIntroPreference = new TopIntroPreference(getPrefContext());
+ mTopIntroPreference.setKey(KEY_TOP_INTRO_PREFERENCE);
+ mTopIntroPreference.setTitle(mTopIntroTitle);
+ getPreferenceScreen().addPreference(mTopIntroPreference);
}
private void initToggleServiceSwitchPreference() {
@@ -879,7 +880,9 @@
? R.string.accessibility_service_qs_tooltips_content
: R.string.accessibility_service_auto_added_qs_tooltips_content;
final String title = getString(titleResId, tileName);
- final int imageResId = R.drawable.accessibility_qs_tooltips_illustration;
+ final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
+ ? R.drawable.accessibility_qs_tooltips_illustration
+ : R.drawable.accessibility_auto_added_qs_tooltips_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
mTooltipWindow.setup(title, imageResId);
mTooltipWindow.showAtTopCenter(getView());
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
index d92fd51..cb5ca75 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
@@ -40,7 +40,7 @@
final String title = getContext().getString(
R.string.accessibility_screen_magnification_title);
final String description = getContext().getString(
- R.string.accessibility_preference_magnification_summary);
+ R.string.accessibility_screen_magnification_intro_text);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
description, icon);
@@ -51,6 +51,8 @@
* Hide the magnification preference settings in the SuW's vision settings.
*/
private void hidePreferenceSettingComponents() {
+ // Intro
+ mTopIntroPreference.setVisible(false);
// Setting of magnification type
mSettingsPreference.setVisible(false);
// Setting of following typing
diff --git a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
index 7adddf9..f9a1113 100644
--- a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
@@ -46,6 +46,9 @@
description, icon);
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
+ if (mTopIntroPreference != null) {
+ mTopIntroPreference.setVisible(false);
+ }
}
@Override
diff --git a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
index b5c1123..a460419 100644
--- a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
@@ -46,6 +46,9 @@
description, icon);
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
+ if (mTopIntroPreference != null) {
+ mTopIntroPreference.setVisible(false);
+ }
}
@Override
diff --git a/src/com/android/settings/biometrics/BiometricNavigationUtils.java b/src/com/android/settings/biometrics/BiometricNavigationUtils.java
new file mode 100644
index 0000000..e4f2b7f
--- /dev/null
+++ b/src/com/android/settings/biometrics/BiometricNavigationUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.Utils;
+import com.android.settings.core.SettingsBaseActivity;
+import com.android.settingslib.transition.SettingsTransitionHelper;
+
+/**
+ * Utilities for navigation shared between Security Settings and Safety Center.
+ */
+public class BiometricNavigationUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+
+ /**
+ * Tries to launch the Settings screen if Quiet Mode is not enabled
+ * for managed profile, otherwise shows a dialog to disable the Quiet Mode.
+ *
+ * @param className The class name of Settings screen to launch.
+ * @param extras Extras to put into the launching {@link Intent}.
+ * @return true if the Settings screen is launching.
+ */
+ public boolean launchBiometricSettings(Context context, String className, Bundle extras) {
+ final UserManager userManager = UserManager.get(context);
+ if (Utils.startQuietModeDialogIfNecessary(context, userManager, mUserId)) {
+ return false;
+ }
+
+ final Intent intent = new Intent();
+ intent.setClassName(SETTINGS_PACKAGE_NAME, className);
+ if (!extras.isEmpty()) {
+ intent.putExtras(extras);
+ }
+ intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
+ SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
+ context.startActivity(intent);
+ return true;
+ }
+}
diff --git a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
index 801dd03..617529f 100644
--- a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
@@ -16,11 +16,7 @@
package com.android.settings.biometrics;
-import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
-import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
-
import android.content.Context;
-import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -30,9 +26,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.transition.SettingsTransitionHelper;
public abstract class BiometricStatusPreferenceController extends BasePreferenceController {
@@ -42,36 +36,23 @@
private final int mUserId = UserHandle.myUserId();
protected final int mProfileChallengeUserId;
+ private final BiometricNavigationUtils mBiometricNavigationUtils;
+
/**
* @return true if the manager is not null and the hardware is detected.
*/
protected abstract boolean isDeviceSupported();
/**
- * @return true if the user has enrolled biometrics of the subclassed type.
+ * @return the summary text.
*/
- protected abstract boolean hasEnrolledBiometrics();
-
- /**
- * @return the summary text if biometrics are enrolled.
- */
- protected abstract String getSummaryTextEnrolled();
-
- /**
- * @return the summary text if no biometrics are enrolled.
- */
- protected abstract String getSummaryTextNoneEnrolled();
+ protected abstract String getSummaryText();
/**
* @return the class name for the settings page.
*/
protected abstract String getSettingsClassName();
- /**
- * @return the class name for entry to enrollment.
- */
- protected abstract String getEnrollClassName();
-
public BiometricStatusPreferenceController(Context context, String key) {
super(context, key);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -79,6 +60,7 @@
.getSecurityFeatureProvider()
.getLockPatternUtils(context);
mProfileChallengeUserId = Utils.getManagedProfileId(mUm, mUserId);
+ mBiometricNavigationUtils = new BiometricNavigationUtils();
}
@Override
@@ -103,8 +85,7 @@
} else {
preference.setVisible(true);
}
- preference.setSummary(hasEnrolledBiometrics() ? getSummaryTextEnrolled()
- : getSummaryTextNoneEnrolled());
+ preference.setSummary(getSummaryText());
}
@Override
@@ -113,26 +94,8 @@
return super.handlePreferenceTreeClick(preference);
}
- final Context context = preference.getContext();
- final UserManager userManager = UserManager.get(context);
- final int userId = getUserId();
- if (Utils.startQuietModeDialogIfNecessary(context, userManager, userId)) {
- return false;
- }
-
- final Intent intent = new Intent();
- final String clazz = hasEnrolledBiometrics() ? getSettingsClassName()
- : getEnrollClassName();
- intent.setClassName(SETTINGS_PACKAGE_NAME, clazz);
- if (!preference.getExtras().isEmpty()) {
- intent.putExtras(preference.getExtras());
- }
- intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
- intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
- SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
- context.startActivity(intent);
- return true;
+ return mBiometricNavigationUtils.launchBiometricSettings(
+ preference.getContext(), getSettingsClassName(), preference.getExtras());
}
protected int getUserId() {
diff --git a/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java
index 3ac14a1..800139c 100644
--- a/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java
@@ -29,7 +29,7 @@
public class BiometricFaceStatusPreferenceController extends FaceStatusPreferenceController {
public BiometricFaceStatusPreferenceController(Context context, String key) {
- super(context, key);
+ super(context, key, null /* lifecycle */);
}
public BiometricFaceStatusPreferenceController(
diff --git a/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java
index 782d889..be19cb5 100644
--- a/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java
@@ -30,7 +30,7 @@
FingerprintStatusPreferenceController {
public BiometricFingerprintStatusPreferenceController(Context context, String key) {
- super(context, key);
+ super(context, key, null /* lifecycle */);
}
public BiometricFingerprintStatusPreferenceController(
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java
index ddc69e1..b8706a5 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java
@@ -20,8 +20,6 @@
import androidx.lifecycle.Lifecycle;
-import com.android.settings.Settings;
-
/**
* Preference controller for biometrics settings page of work profile, controlling the ability to
* unlock the phone with face and fingerprint.
@@ -62,11 +60,6 @@
@Override
protected String getSettingsClassName() {
- return Settings.CombinedBiometricProfileSettingsActivity.class.getName();
- }
-
- @Override
- protected String getEnrollClassName() {
- return Settings.CombinedBiometricProfileSettingsActivity.class.getName();
+ return mCombinedBiometricStatusUtils.getProfileSettingsClassName();
}
}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
index 32fb3a0..27e3ae7 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
@@ -16,9 +16,6 @@
package com.android.settings.biometrics.combination;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.face.FaceManager;
-import android.hardware.fingerprint.FingerprintManager;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
@@ -28,11 +25,7 @@
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settings.Settings;
-import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
-import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
@@ -44,12 +37,9 @@
BiometricStatusPreferenceController implements LifecycleObserver {
private static final String KEY_BIOMETRIC_SETTINGS = "biometric_settings";
- @Nullable
- FingerprintManager mFingerprintManager;
- @Nullable
- FaceManager mFaceManager;
@VisibleForTesting
RestrictedPreference mPreference;
+ protected final CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
public CombinedBiometricStatusPreferenceController(Context context) {
this(context, KEY_BIOMETRIC_SETTINGS, null /* lifecycle */);
@@ -66,8 +56,7 @@
public CombinedBiometricStatusPreferenceController(
Context context, String key, Lifecycle lifecycle) {
super(context, key);
- mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
- mFaceManager = Utils.getFaceManagerOrNull(context);
+ mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(context);
if (lifecycle != null) {
lifecycle.addObserver(this);
@@ -87,12 +76,7 @@
@Override
protected boolean isDeviceSupported() {
- return Utils.hasFingerprintHardware(mContext) && Utils.hasFaceHardware(mContext);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics() {
- return false;
+ return mCombinedBiometricStatusUtils.isAvailable();
}
@Override
@@ -102,80 +86,35 @@
}
private void updateStateInternal() {
- // This controller currently is shown if fingerprint&face exist on the device. If this
- // changes in the future, the modalities passed into the below will need to be updated.
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ mCombinedBiometricStatusUtils.getDisablingAdmin();
- final RestrictedLockUtils.EnforcedAdmin faceAdmin = ParentalControlsUtils
- .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FACE);
- final RestrictedLockUtils.EnforcedAdmin fpAdmin = ParentalControlsUtils
- .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
-
- // If the admins are non-null, they are actually always the same. Just the helper class
- // we create above always return the admin, instead of a boolean.
- final boolean faceConsentRequired = faceAdmin != null;
- final boolean fpConsentRequired = fpAdmin != null;
- final RestrictedLockUtils.EnforcedAdmin admin = faceAdmin != null ? faceAdmin : fpAdmin;
-
- updateStateInternal(admin, faceConsentRequired, fpConsentRequired);
+ updateStateInternal(admin);
}
+ /**
+ * Disables the preference and shows the consent flow only if consent is required for all
+ * modalities.
+ *
+ * <p>Otherwise, users will not be able to enter and modify settings for modalities which have
+ * already been consented. In any case, the controllers for the modalities which have not yet
+ * been consented will be disabled in the combined page anyway - users can go through the
+ * consent+enrollment flow from there.
+ */
@VisibleForTesting
- void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin,
- boolean faceConsentRequired, boolean fpConsentRequired) {
- // Disable the preference (and show the consent flow) only if consent is required for all
- // modalities. Otherwise, users will not be able to enter and modify settings for modalities
- // which have already been consented. In any case, the controllers for the modalities which
- // have not yet been consented will be disabled in the combined page anyway - users can
- // go through the consent+enrollment flow from there.
- final boolean disablePreference = faceConsentRequired && fpConsentRequired;
- if (!disablePreference) {
- enforcedAdmin = null;
- }
-
+ void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
if (mPreference != null) {
mPreference.setDisabledByAdmin(enforcedAdmin);
}
}
@Override
- protected String getSummaryTextEnrolled() {
- // Note that this is currently never called (see the super class)
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_none_enrolled);
- }
-
- @Override
- protected String getSummaryTextNoneEnrolled() {
- final int numFingerprintsEnrolled = mFingerprintManager != null ?
- mFingerprintManager.getEnrolledFingerprints(getUserId()).size() : 0;
- final boolean faceEnrolled = mFaceManager != null
- && mFaceManager.hasEnrolledTemplates(getUserId());
-
- if (faceEnrolled && numFingerprintsEnrolled > 1) {
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_both_fp_multiple);
- } else if (faceEnrolled && numFingerprintsEnrolled == 1) {
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_both_fp_single);
- } else if (faceEnrolled) {
- return mContext.getString(R.string.security_settings_face_preference_summary);
- } else if (numFingerprintsEnrolled > 0) {
- return mContext.getResources().getQuantityString(
- R.plurals.security_settings_fingerprint_preference_summary,
- numFingerprintsEnrolled, numFingerprintsEnrolled);
- } else {
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_none_enrolled);
- }
+ protected String getSummaryText() {
+ return mCombinedBiometricStatusUtils.getSummary();
}
@Override
protected String getSettingsClassName() {
- return Settings.CombinedBiometricSettingsActivity.class.getName();
- }
-
- @Override
- protected String getEnrollClassName() {
- return Settings.CombinedBiometricSettingsActivity.class.getName();
+ return mCombinedBiometricStatusUtils.getSettingsClassName();
}
}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
new file mode 100644
index 0000000..a7554c8
--- /dev/null
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 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.biometrics.combination;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Utilities for combined biometric details shared between Security Settings and Safety Center.
+ */
+public class CombinedBiometricStatusUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+ private final Context mContext;
+ @Nullable
+ FingerprintManager mFingerprintManager;
+ @Nullable
+ FaceManager mFaceManager;
+
+ public CombinedBiometricStatusUtils(Context context) {
+ mContext = context;
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ mFaceManager = Utils.getFaceManagerOrNull(context);
+ }
+
+ /**
+ * Returns whether the combined biometric settings entity should be shown.
+ */
+ public boolean isAvailable() {
+ return Utils.hasFingerprintHardware(mContext) && Utils.hasFaceHardware(mContext);
+ }
+
+ /**
+ * Returns the {@link EnforcedAdmin} in case parental consent is required to change both
+ * face and fingerprint settings.
+ *
+ * @return null if either face or fingerprint settings do not require a parental consent.
+ */
+ public EnforcedAdmin getDisablingAdmin() {
+ // This controller currently is shown if fingerprint&face exist on the device. If this
+ // changes in the future, the modalities passed into the below will need to be updated.
+
+ final EnforcedAdmin faceAdmin = ParentalControlsUtils
+ .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FACE);
+ final EnforcedAdmin fpAdmin = ParentalControlsUtils
+ .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
+
+ final boolean faceConsentRequired = faceAdmin != null;
+ final boolean fpConsentRequired = fpAdmin != null;
+
+ // Result is only required if all modalities require consent.
+ // If the admins are non-null, they are actually always the same.
+ return faceConsentRequired && fpConsentRequired ? faceAdmin : null;
+ }
+
+ /**
+ * Returns the summary of combined biometric settings entity.
+ */
+ public String getSummary() {
+ final int numFingerprintsEnrolled = mFingerprintManager != null
+ ? mFingerprintManager.getEnrolledFingerprints(mUserId).size() : 0;
+ final boolean faceEnrolled = mFaceManager != null
+ && mFaceManager.hasEnrolledTemplates(mUserId);
+
+ if (faceEnrolled && numFingerprintsEnrolled > 1) {
+ return mContext.getString(
+ R.string.security_settings_biometric_preference_summary_both_fp_multiple);
+ } else if (faceEnrolled && numFingerprintsEnrolled == 1) {
+ return mContext.getString(
+ R.string.security_settings_biometric_preference_summary_both_fp_single);
+ } else if (faceEnrolled) {
+ return mContext.getString(R.string.security_settings_face_preference_summary);
+ } else if (numFingerprintsEnrolled > 0) {
+ return mContext.getResources().getQuantityString(
+ R.plurals.security_settings_fingerprint_preference_summary,
+ numFingerprintsEnrolled, numFingerprintsEnrolled);
+ } else {
+ return mContext.getString(
+ R.string.security_settings_biometric_preference_summary_none_enrolled);
+ }
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to combined biometric settings.
+ */
+ public String getSettingsClassName() {
+ return Settings.CombinedBiometricSettingsActivity.class.getName();
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to combined biometric settings
+ * for work profile.
+ */
+ public String getProfileSettingsClassName() {
+ return Settings.CombinedBiometricProfileSettingsActivity.class.getName();
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
index 319166e..cd0bc15 100644
--- a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.biometrics.face;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.FaceManager;
import androidx.annotation.Nullable;
@@ -28,11 +27,8 @@
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settings.Settings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
-import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
@@ -44,6 +40,7 @@
protected final FaceManager mFaceManager;
@VisibleForTesting
RestrictedPreference mPreference;
+ private final FaceStatusUtils mFaceStatusUtils;
public FaceStatusPreferenceController(Context context) {
this(context, KEY_FACE_SETTINGS, null /* lifecycle */);
@@ -60,6 +57,7 @@
public FaceStatusPreferenceController(Context context, String key, Lifecycle lifecycle) {
super(context, key);
mFaceManager = Utils.getFaceManagerOrNull(context);
+ mFaceStatusUtils = new FaceStatusUtils(context, mFaceManager);
if (lifecycle != null) {
lifecycle.addObserver(this);
@@ -79,12 +77,7 @@
@Override
protected boolean isDeviceSupported() {
- return !Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFaceHardware(mContext);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics() {
- return mFaceManager.hasEnrolledTemplates(getUserId());
+ return mFaceStatusUtils.isAvailable();
}
@Override
@@ -94,8 +87,7 @@
}
private void updateStateInternal() {
- updateStateInternal(ParentalControlsUtils.parentConsentRequired(
- mContext, BiometricAuthenticator.TYPE_FACE));
+ updateStateInternal(mFaceStatusUtils.getDisablingAdmin());
}
@VisibleForTesting
@@ -106,25 +98,12 @@
}
@Override
- protected String getSummaryTextEnrolled() {
- return mContext.getResources()
- .getString(R.string.security_settings_face_preference_summary);
- }
-
- @Override
- protected String getSummaryTextNoneEnrolled() {
- return mContext.getResources()
- .getString(R.string.security_settings_face_preference_summary_none);
+ protected String getSummaryText() {
+ return mFaceStatusUtils.getSummary();
}
@Override
protected String getSettingsClassName() {
- return Settings.FaceSettingsActivity.class.getName();
+ return mFaceStatusUtils.getSettingsClassName();
}
-
- @Override
- protected String getEnrollClassName() {
- return FaceEnrollIntroduction.class.getName();
- }
-
}
diff --git a/src/com/android/settings/biometrics/face/FaceStatusUtils.java b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
new file mode 100644
index 0000000..dd32708
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.FaceManager;
+import android.os.UserHandle;
+
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Utilities for face details shared between Security Settings and Safety Center.
+ */
+public class FaceStatusUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+ private final Context mContext;
+ private final FaceManager mFaceManager;
+
+ public FaceStatusUtils(Context context, FaceManager faceManager) {
+ mContext = context;
+ mFaceManager = faceManager;
+ }
+
+ /**
+ * Returns whether the face settings entity should be shown.
+ */
+ public boolean isAvailable() {
+ return !Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFaceHardware(mContext);
+ }
+
+ /**
+ * Returns the {@link EnforcedAdmin} if parental consent is required to change face settings.
+ *
+ * @return null if face settings does not require a parental consent.
+ */
+ public EnforcedAdmin getDisablingAdmin() {
+ return ParentalControlsUtils.parentConsentRequired(
+ mContext, BiometricAuthenticator.TYPE_FACE);
+ }
+
+ /**
+ * Returns the summary of face settings entity.
+ */
+ public String getSummary() {
+ return mContext.getResources().getString(hasEnrolled()
+ ? R.string.security_settings_face_preference_summary
+ : R.string.security_settings_face_preference_summary_none);
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to face settings.
+ */
+ public String getSettingsClassName() {
+ return hasEnrolled() ? Settings.FaceSettingsActivity.class.getName()
+ : FaceEnrollIntroduction.class.getName();
+ }
+
+ private boolean hasEnrolled() {
+ return mFaceManager.hasEnrolledTemplates(mUserId);
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
index 76c809c..646af4d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.biometrics.fingerprint;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.fingerprint.FingerprintManager;
import androidx.annotation.Nullable;
@@ -28,10 +27,8 @@
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
-import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
@@ -43,6 +40,7 @@
protected final FingerprintManager mFingerprintManager;
@VisibleForTesting
RestrictedPreference mPreference;
+ private final FingerprintStatusUtils mFingerprintStatusUtils;
public FingerprintStatusPreferenceController(Context context) {
this(context, KEY_FINGERPRINT_SETTINGS);
@@ -59,6 +57,8 @@
public FingerprintStatusPreferenceController(Context context, String key, Lifecycle lifecycle) {
super(context, key);
mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ mFingerprintStatusUtils =
+ new FingerprintStatusUtils(context, mFingerprintManager);
if (lifecycle != null) {
lifecycle.addObserver(this);
@@ -78,13 +78,7 @@
@Override
protected boolean isDeviceSupported() {
- return !Utils.isMultipleBiometricsSupported(mContext)
- && Utils.hasFingerprintHardware(mContext);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics() {
- return mFingerprintManager.hasEnrolledFingerprints(getUserId());
+ return mFingerprintStatusUtils.isAvailable();
}
@Override
@@ -94,8 +88,17 @@
}
private void updateStateInternal() {
- updateStateInternal(ParentalControlsUtils.parentConsentRequired(
- mContext, BiometricAuthenticator.TYPE_FINGERPRINT));
+ updateStateInternal(mFingerprintStatusUtils.getDisablingAdmin());
+ }
+
+ @Override
+ protected String getSummaryText() {
+ return mFingerprintStatusUtils.getSummary();
+ }
+
+ @Override
+ protected String getSettingsClassName() {
+ return mFingerprintStatusUtils.getSettingsClassName();
}
@VisibleForTesting
@@ -104,28 +107,4 @@
mPreference.setDisabledByAdmin(enforcedAdmin);
}
}
-
- @Override
- protected String getSummaryTextEnrolled() {
- final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(getUserId()).size();
- return mContext.getResources().getQuantityString(
- R.plurals.security_settings_fingerprint_preference_summary,
- numEnrolled, numEnrolled);
- }
-
- @Override
- protected String getSummaryTextNoneEnrolled() {
- return mContext.getString(R.string.security_settings_fingerprint_preference_summary_none);
- }
-
- @Override
- protected String getSettingsClassName() {
- return FingerprintSettings.class.getName();
- }
-
- @Override
- protected String getEnrollClassName() {
- return FingerprintEnrollIntroduction.class.getName();
- }
-
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
new file mode 100644
index 0000000..36edd2e
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Utilities for fingerprint details shared between Security Settings and Safety Center.
+ */
+public class FingerprintStatusUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+ private final Context mContext;
+ private final FingerprintManager mFingerprintManager;
+
+ public FingerprintStatusUtils(Context context, FingerprintManager fingerprintManager) {
+ mContext = context;
+ mFingerprintManager = fingerprintManager;
+ }
+
+ /**
+ * Returns whether the fingerprint settings entity should be shown.
+ */
+ public boolean isAvailable() {
+ return !Utils.isMultipleBiometricsSupported(mContext)
+ && Utils.hasFingerprintHardware(mContext);
+ }
+
+ /**
+ * Returns the {@link EnforcedAdmin} if parental consent is required to change face settings.
+ *
+ * @return null if face settings does not require a parental consent.
+ */
+ public EnforcedAdmin getDisablingAdmin() {
+ return ParentalControlsUtils.parentConsentRequired(
+ mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
+ }
+
+ /**
+ * Returns the summary of fingerprint settings entity.
+ */
+ public String getSummary() {
+ if (hasEnrolled()) {
+ final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size();
+ return mContext.getResources().getQuantityString(
+ R.plurals.security_settings_fingerprint_preference_summary,
+ numEnrolled, numEnrolled);
+ } else {
+ return mContext.getString(
+ R.string.security_settings_fingerprint_preference_summary_none);
+ }
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to fingerprint settings.
+ */
+ public String getSettingsClassName() {
+ return hasEnrolled() ? FingerprintSettings.class.getName()
+ : FingerprintEnrollIntroduction.class.getName();
+ }
+
+ private boolean hasEnrolled() {
+ return mFingerprintManager.hasEnrolledFingerprints(mUserId);
+ }
+}
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
index b60f1b6..1c12c6a 100644
--- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
@@ -99,6 +99,10 @@
@VisibleForTesting
boolean mIsRegisterCallback = false;
@VisibleForTesting
+ boolean mIsLeftDeviceEstimateReady;
+ @VisibleForTesting
+ boolean mIsRightDeviceEstimateReady;
+ @VisibleForTesting
final BluetoothAdapter.OnMetadataChangedListener mMetadataListener =
new BluetoothAdapter.OnMetadataChangedListener() {
@Override
@@ -226,6 +230,8 @@
BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING,
R.string.bluetooth_right_name,
RIGHT_DEVICE_ID);
+
+ showBothDevicesBatteryPredictionIfNecessary();
}
}
}
@@ -365,8 +371,13 @@
+ ", ESTIMATE_READY : " + estimateReady
+ ", BATTERY_ESTIMATE : " + batteryEstimate);
}
- showBatteryPredictionIfNecessary(estimateReady, batteryEstimate,
- linearLayout);
+
+ showBatteryPredictionIfNecessary(estimateReady, batteryEstimate, linearLayout);
+ if (batteryId == LEFT_DEVICE_ID) {
+ mIsLeftDeviceEstimateReady = estimateReady == 1;
+ } else if (batteryId == RIGHT_DEVICE_ID) {
+ mIsRightDeviceEstimateReady = estimateReady == 1;
+ }
}
} finally {
cursor.close();
@@ -380,7 +391,6 @@
ThreadUtils.postOnMainThread(() -> {
final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
if (estimateReady == 1) {
- textView.setVisibility(View.VISIBLE);
textView.setText(
StringUtil.formatElapsedTime(
mContext,
@@ -393,6 +403,24 @@
});
}
+ @VisibleForTesting
+ void showBothDevicesBatteryPredictionIfNecessary() {
+ TextView leftDeviceTextView =
+ mLayoutPreference.findViewById(R.id.layout_left)
+ .findViewById(R.id.bt_battery_prediction);
+ TextView rightDeviceTextView =
+ mLayoutPreference.findViewById(R.id.layout_right)
+ .findViewById(R.id.bt_battery_prediction);
+
+ boolean isBothDevicesEstimateReady =
+ mIsLeftDeviceEstimateReady && mIsRightDeviceEstimateReady;
+ int visibility = isBothDevicesEstimateReady ? View.VISIBLE : View.GONE;
+ ThreadUtils.postOnMainThread(() -> {
+ leftDeviceTextView.setVisibility(visibility);
+ rightDeviceTextView.setVisibility(visibility);
+ });
+ }
+
private void showBatteryIcon(LinearLayout linearLayout, int level, int lowBatteryLevel,
boolean charging) {
final boolean enableLowBattery = level <= lowBatteryLevel && !charging;
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index cfdfdaa..8b1d633 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -53,6 +53,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -280,6 +281,11 @@
return null;
}
+ /** Returns all controllers of type T. */
+ protected <T extends AbstractPreferenceController> List<T> useAll(Class<T> clazz) {
+ return (List<T>) mPreferenceControllers.getOrDefault(clazz, Collections.emptyList());
+ }
+
protected void addPreferenceController(AbstractPreferenceController controller) {
if (mPreferenceControllers.get(controller.getClass()) == null) {
mPreferenceControllers.put(controller.getClass(), new ArrayList<>());
diff --git a/src/com/android/settings/development/tare/TareFactorController.java b/src/com/android/settings/development/tare/TareFactorController.java
index 50531a0..7e9f314 100644
--- a/src/com/android/settings/development/tare/TareFactorController.java
+++ b/src/com/android/settings/development/tare/TareFactorController.java
@@ -83,9 +83,9 @@
new TareFactorData(mResources.getString(R.string.tare_max_satiated_balance),
EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE,
POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MAX_CIRCULATION,
+ mAlarmManagerMap.put(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
new TareFactorData(mResources.getString(R.string.tare_max_circulation),
- EconomyManager.DEFAULT_AM_MAX_CIRCULATION,
+ EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT,
POLICY_ALARM_MANAGER));
mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
new TareFactorData(mResources.getString(R.string.tare_top_activity),
@@ -265,9 +265,9 @@
new TareFactorData(mResources.getString(R.string.tare_max_satiated_balance),
EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE,
POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MAX_CIRCULATION,
+ mJobSchedulerMap.put(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
new TareFactorData(mResources.getString(R.string.tare_max_circulation),
- EconomyManager.DEFAULT_JS_MAX_CIRCULATION,
+ EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT,
POLICY_JOB_SCHEDULER));
mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
new TareFactorData(mResources.getString(R.string.tare_top_activity),
@@ -565,7 +565,6 @@
}
}
-
/**
* Iterates through the factor policy map for keys and current values to
* rebuild a current string that is then assigned to be the new global settings string.
@@ -573,50 +572,18 @@
* @param factorPolicy policy being updated
*/
private void rebuildPolicyConstants(int factorPolicy) {
- StringBuilder newConstantsStringBuilder = new StringBuilder();
-
switch (factorPolicy) {
case POLICY_ALARM_MANAGER:
- int sizeAM = mAlarmManagerMap.size();
-
- for (int i = 0; i < sizeAM; i++) {
- if (i > 0) {
- newConstantsStringBuilder.append(",");
- }
-
- String key = mAlarmManagerMap.keyAt(i);
- newConstantsStringBuilder.append(key + "=" + mAlarmManagerMap.get(key)
- .currentValue);
- }
-
- String newAMConstantsString = newConstantsStringBuilder.toString();
-
- Settings.Global.putString(mContentResolver, Settings.Global
- .TARE_ALARM_MANAGER_CONSTANTS,
- newAMConstantsString);
+ writeConstantsToSettings(mAlarmManagerMap,
+ Settings.Global.TARE_ALARM_MANAGER_CONSTANTS);
mAlarmManagerConstants = Settings.Global
.getString(mContentResolver, Settings.Global
.TARE_ALARM_MANAGER_CONSTANTS);
break;
case POLICY_JOB_SCHEDULER:
- int sizeJS = mJobSchedulerMap.size();
-
- for (int i = 0; i < sizeJS; i++) {
- if (i > 0) {
- newConstantsStringBuilder.append(",");
- }
-
- String key = mJobSchedulerMap.keyAt(i);
- newConstantsStringBuilder.append(key + "=" + mJobSchedulerMap.get(key)
- .currentValue);
- }
-
- String newJSConstantsString = newConstantsStringBuilder.toString();
-
- Settings.Global.putString(mContentResolver, Settings.Global
- .TARE_JOB_SCHEDULER_CONSTANTS,
- newJSConstantsString);
+ writeConstantsToSettings(mJobSchedulerMap,
+ Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS);
mJobSchedulerConstants = Settings.Global
.getString(mContentResolver, Settings.Global
@@ -625,6 +592,29 @@
}
}
+ private void writeConstantsToSettings(ArrayMap<String, TareFactorData> factorMap,
+ String settingsKey) {
+ final StringBuilder constantsStringBuilder = new StringBuilder();
+
+ for (int i = 0, size = factorMap.size(); i < size; ++i) {
+ final TareFactorData factor = factorMap.valueAt(i);
+ if (factor.currentValue == factor.defaultValue) {
+ continue;
+ }
+
+ if (constantsStringBuilder.length() > 0) {
+ constantsStringBuilder.append(",");
+ }
+
+ constantsStringBuilder
+ .append(factorMap.keyAt(i))
+ .append("=")
+ .append(factor.currentValue);
+ }
+
+ Settings.Global.putString(mContentResolver, settingsKey, constantsStringBuilder.toString());
+ }
+
/**
* Creates a dialog with the values linked to the key.
*
diff --git a/src/com/android/settings/display/AutoRotatePreferenceController.java b/src/com/android/settings/display/AutoRotatePreferenceController.java
index 5dc2286..90423fb 100644
--- a/src/com/android/settings/display/AutoRotatePreferenceController.java
+++ b/src/com/android/settings/display/AutoRotatePreferenceController.java
@@ -74,6 +74,7 @@
@Override
public int getAvailabilityStatus() {
return RotationPolicy.isRotationLockToggleVisible(mContext)
+ && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java b/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java
new file mode 100644
index 0000000..fb6d9f4
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.search.Indexable;
+import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import java.util.List;
+
+/** Fragment that shows all the available device state based auto-rotation preferences. */
+@SearchIndexable
+public class DeviceStateAutoRotateDetailsFragment extends DashboardFragment {
+
+ private static final String TAG = "DeviceStateAutoRotateDetailsFragment";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DISPLAY_AUTO_ROTATE_SETTINGS;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.device_state_auto_rotate_settings;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ DeviceStateAutoRotationHelper.initControllers(
+ getLifecycle(),
+ useAll(DeviceStateAutoRotateSettingController.class)
+ );
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return DeviceStateAutoRotationHelper.createPreferenceControllers(context);
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.device_state_auto_rotate_settings) {
+
+ @Override
+ public List<SearchIndexableRaw> getRawDataToIndex(Context context,
+ boolean enabled) {
+ return DeviceStateAutoRotationHelper.getRawDataToIndex(context, enabled);
+ }
+ };
+}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotateOverviewController.java b/src/com/android/settings/display/DeviceStateAutoRotateOverviewController.java
new file mode 100644
index 0000000..5e49bf3
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotateOverviewController.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * The top-level preference controller for device state based auto-rotation settings.
+ *
+ * It doesn't do anything on its own besides showing/hiding. The toggling of the settings will
+ * always be done in the details screen when device state based auto-rotation is enabled.
+ */
+public class DeviceStateAutoRotateOverviewController extends BasePreferenceController {
+
+ /** Preference key for when it is used in "accessibility_system_controls.xml". */
+ private static final String ACCESSIBILITY_PREF_KEY = "device_state_auto_rotate_accessibility";
+
+ public DeviceStateAutoRotateOverviewController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return isAvailableInternal() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ private boolean isAvailableInternal() {
+ return isA11yPage()
+ ? DeviceStateAutoRotationHelper.isDeviceStateRotationEnabledForA11y(mContext)
+ : DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext);
+ }
+
+ private boolean isA11yPage() {
+ return TextUtils.equals(getPreferenceKey(), ACCESSIBILITY_PREF_KEY);
+ }
+}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java b/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java
new file mode 100644
index 0000000..c8f6280
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import java.util.List;
+
+/** Controller for device state based auto rotation preferences. */
+public class DeviceStateAutoRotateSettingController extends TogglePreferenceController implements
+ LifecycleObserver {
+
+ private SwitchPreference mPreference;
+
+ private final DeviceStateRotationLockSettingsManager mAutoRotateSettingsManager;
+ private final int mOrder;
+ private final DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
+ mDeviceStateRotationLockSettingsListener = () -> updateState(mPreference);
+ private final int mDeviceState;
+ private final String mDeviceStateDescription;
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+
+ public DeviceStateAutoRotateSettingController(Context context, int deviceState,
+ String deviceStateDescription, int order) {
+ super(context, getPreferenceKeyForDeviceState(deviceState));
+ mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+ mDeviceState = deviceState;
+ mDeviceStateDescription = deviceStateDescription;
+ mAutoRotateSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(context);
+ mOrder = order;
+ }
+
+ void init(Lifecycle lifecycle) {
+ lifecycle.addObserver(this);
+ }
+
+ @OnLifecycleEvent(ON_START)
+ void onStart() {
+ mAutoRotateSettingsManager.registerListener(mDeviceStateRotationLockSettingsListener);
+ }
+
+ @OnLifecycleEvent(ON_STOP)
+ void onStop() {
+ mAutoRotateSettingsManager.unregisterListener(mDeviceStateRotationLockSettingsListener);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreference = new SwitchPreference(mContext);
+ mPreference.setTitle(mDeviceStateDescription);
+ mPreference.setKey(getPreferenceKey());
+ mPreference.setOrder(mOrder);
+ screen.addPreference(mPreference);
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return getPreferenceKeyForDeviceState(mDeviceState);
+ }
+
+ private static String getPreferenceKeyForDeviceState(int deviceState) {
+ return "auto_rotate_device_state_" + deviceState;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return !mAutoRotateSettingsManager.isRotationLocked(mDeviceState);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ boolean isRotationLocked = !isChecked;
+ mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATION_LOCK,
+ isRotationLocked);
+ mAutoRotateSettingsManager.updateSetting(mDeviceState, isRotationLocked);
+ return true;
+ }
+
+ @Override
+ public void updateRawDataToIndex(List<SearchIndexableRaw> rawData) {
+ SearchIndexableRaw indexable = new SearchIndexableRaw(mContext);
+ indexable.key = getPreferenceKey();
+ indexable.title = mDeviceStateDescription;
+ // Maybe pass screen title as param?
+ indexable.screenTitle = mContext.getString(R.string.accelerometer_title);
+ rawData.add(indexable);
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_display;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return true; // Maybe set to false if in accessibility settings screen
+ }
+
+ @Override
+ public boolean isPublicSlice() {
+ return true;
+ }
+}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotationHelper.java b/src/com/android/settings/display/DeviceStateAutoRotationHelper.java
new file mode 100644
index 0000000..223ef1a
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotationHelper.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.lifecycle.Lifecycle;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class with utility methods related to device state auto-rotation that can be used in
+ * auto-rotation settings fragments and controllers.
+ */
+public class DeviceStateAutoRotationHelper {
+
+ private static final String TAG = "DeviceStateAutoRotHelpr";
+
+ static void initControllers(Lifecycle lifecycle,
+ List<DeviceStateAutoRotateSettingController> controllers) {
+ for (DeviceStateAutoRotateSettingController controller : controllers) {
+ controller.init(lifecycle);
+ }
+ }
+
+ static ImmutableList<AbstractPreferenceController> createPreferenceControllers(
+ Context context) {
+ List<SettableDeviceState> settableDeviceStates = DeviceStateRotationLockSettingsManager
+ .getInstance(context).getSettableDeviceStates();
+ int numDeviceStates = settableDeviceStates.size();
+ if (numDeviceStates == 0) {
+ return ImmutableList.of();
+ }
+ String[] deviceStateSettingDescriptions = context.getResources().getStringArray(
+ R.array.config_settableAutoRotationDeviceStatesDescriptions);
+ if (numDeviceStates != deviceStateSettingDescriptions.length) {
+ Log.wtf(TAG,
+ "Mismatch between number of device states and device states descriptions.");
+ return ImmutableList.of();
+ }
+
+ ImmutableList.Builder<AbstractPreferenceController> controllers =
+ ImmutableList.builderWithExpectedSize(numDeviceStates);
+ for (int i = 0; i < numDeviceStates; i++) {
+ SettableDeviceState settableDeviceState = settableDeviceStates.get(i);
+ if (!settableDeviceState.isSettable()) {
+ continue;
+ }
+ // Preferences with a lower order will be showed first. Here we go below 0 to make sure
+ // we are shown before statically declared preferences in XML.
+ int order = -numDeviceStates + i;
+ controllers.add(new DeviceStateAutoRotateSettingController(
+ context,
+ settableDeviceState.getDeviceState(),
+ deviceStateSettingDescriptions[i],
+ order
+ ));
+ }
+ return controllers.build();
+ }
+
+ static List<SearchIndexableRaw> getRawDataToIndex(
+ Context context, boolean enabled) {
+ // Check what the "enabled" param is for
+ List<AbstractPreferenceController> controllers = createPreferenceControllers(context);
+ List<SearchIndexableRaw> rawData = new ArrayList<>();
+ for (AbstractPreferenceController controller : controllers) {
+ ((BasePreferenceController) controller).updateRawDataToIndex(rawData);
+ }
+ return rawData;
+ }
+
+ /** Returns whether the device state based auto-rotation settings are enabled. */
+ public static boolean isDeviceStateRotationEnabled(Context context) {
+ return RotationPolicy.isRotationLockToggleVisible(context)
+ && DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(context);
+ }
+
+ /**
+ * Returns whether the device state based auto-rotation settings are enabled for the
+ * accessibility settings page.
+ */
+ public static boolean isDeviceStateRotationEnabledForA11y(Context context) {
+ return RotationPolicy.isRotationSupported(context)
+ && DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(context);
+ }
+}
diff --git a/src/com/android/settings/display/SmartAutoRotateController.java b/src/com/android/settings/display/SmartAutoRotateController.java
index 76a222a..d29a64e 100644
--- a/src/com/android/settings/display/SmartAutoRotateController.java
+++ b/src/com/android/settings/display/SmartAutoRotateController.java
@@ -47,6 +47,7 @@
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
/**
* SmartAutoRotateController controls whether auto rotation is enabled
@@ -54,6 +55,8 @@
public class SmartAutoRotateController extends TogglePreferenceController implements
Preference.OnPreferenceChangeListener, LifecycleObserver {
+ protected Preference mPreference;
+
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final SensorPrivacyManager mPrivacyManager;
private final PowerManager mPowerManager;
@@ -63,7 +66,9 @@
updateState(mPreference);
}
};
- protected Preference mPreference;
+ private final DeviceStateRotationLockSettingsManager mDeviceStateAutoRotateSettingsManager;
+ private final DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
+ mDeviceStateRotationLockSettingsListener = () -> updateState(mPreference);
private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
public SmartAutoRotateController(Context context, String preferenceKey) {
@@ -73,6 +78,8 @@
mPrivacyManager
.addSensorPrivacyListener(CAMERA, (sensor, enabled) -> updateState(mPreference));
mPowerManager = context.getSystemService(PowerManager.class);
+ mDeviceStateAutoRotateSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(
+ context);
}
public void init(Lifecycle lifecycle) {
@@ -89,6 +96,9 @@
}
protected boolean isRotationLocked() {
+ if (DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)) {
+ return mDeviceStateAutoRotateSettingsManager.isRotationLockedForAllStates();
+ }
return RotationPolicy.isRotationLocked(mContext);
}
@@ -127,6 +137,8 @@
};
}
RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener);
+ mDeviceStateAutoRotateSettingsManager.registerListener(
+ mDeviceStateRotationLockSettingsListener);
}
@OnLifecycleEvent(ON_STOP)
@@ -136,6 +148,8 @@
RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
mRotationPolicyListener = null;
}
+ mDeviceStateAutoRotateSettingsManager.unregisterListener(
+ mDeviceStateRotationLockSettingsListener);
}
@Override
diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceController.java b/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
index bd8ee84..d02e336 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
@@ -77,6 +77,7 @@
@Override
public int getAvailabilityStatus() {
return RotationPolicy.isRotationLockToggleVisible(mContext)
+ && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
index 4737336..9fda03c 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
@@ -34,10 +34,14 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.search.SearchIndexableRaw;
import com.android.settingslib.widget.FooterPreference;
+import java.util.List;
+
/**
* Preference fragment used for auto rotation
*/
@@ -60,6 +64,15 @@
public void onAttach(Context context) {
super.onAttach(context);
use(SmartAutoRotateController.class).init(getLifecycle());
+ DeviceStateAutoRotationHelper.initControllers(
+ getLifecycle(),
+ useAll(DeviceStateAutoRotateSettingController.class)
+ );
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return DeviceStateAutoRotationHelper.createPreferenceControllers(context);
}
@Override
@@ -79,7 +92,9 @@
@VisibleForTesting
void createHeader(SettingsActivity activity) {
- if (isRotationResolverServiceAvailable(activity)) {
+ boolean deviceStateRotationEnabled =
+ DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(activity);
+ if (isRotationResolverServiceAvailable(activity) && !deviceStateRotationEnabled) {
final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
switchBar.setTitle(
getContext().getString(R.string.auto_rotate_settings_primary_switch_title));
@@ -127,5 +142,12 @@
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.auto_rotate_settings);
+ new BaseSearchIndexProvider(R.xml.auto_rotate_settings) {
+
+ @Override
+ public List<SearchIndexableRaw> getRawDataToIndex(
+ Context context, boolean enabled) {
+ return DeviceStateAutoRotationHelper.getRawDataToIndex(context, enabled);
+ }
+ };
}
diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
index 71e65bf..436cde8 100644
--- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
@@ -112,7 +112,8 @@
if (intent != null && mBatteryListener != null) {
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
final String batteryLevel = Utils.getBatteryPercentage(intent);
- final String batteryStatus = Utils.getBatteryStatus(mContext, intent);
+ final String batteryStatus =
+ Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false);
final int batteryHealth = intent.getIntExtra(
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
if (!Utils.isBatteryPresent(intent)) {
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index 4e90710..98f19fe 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -37,7 +37,6 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.UsageView;
import com.android.settingslib.R;
-import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.fuelgauge.EstimateKt;
import com.android.settingslib.utils.PowerUtil;
@@ -244,6 +243,8 @@
@NonNull BatteryUsageStats batteryUsageStats, Estimate estimate,
long elapsedRealtimeUs, boolean shortString) {
final long startTime = System.currentTimeMillis();
+ final boolean isCompactStatus = context.getResources().getBoolean(
+ com.android.settings.R.bool.config_use_compact_battery_status);
BatteryInfo info = new BatteryInfo();
info.mBatteryUsageStats = batteryUsageStats;
info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
@@ -254,21 +255,21 @@
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN)
== BatteryManager.BATTERY_HEALTH_OVERHEAT;
- info.statusLabel = getBatteryStatus(context, batteryBroadcast);
+ info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus);
info.batteryStatus = batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info);
} else {
updateBatteryInfoCharging(context, batteryBroadcast, batteryUsageStats,
- info);
+ info, isCompactStatus);
}
BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime);
return info;
}
private static void updateBatteryInfoCharging(Context context, Intent batteryBroadcast,
- BatteryUsageStats stats, BatteryInfo info) {
+ BatteryUsageStats stats, BatteryInfo info, boolean compactStatus) {
final Resources resources = context.getResources();
final long chargeTimeMs = stats.getChargeTimeRemainingMs();
final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
@@ -292,7 +293,8 @@
R.string.power_remaining_charging_duration_only, timeString);
info.chargeLabel = context.getString(resId, info.batteryPercentString, timeString);
} else {
- final String chargeStatusLabel = getBatteryStatus(context, batteryBroadcast);
+ final String chargeStatusLabel =
+ Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
info.remainingLabel = null;
info.chargeLabel = info.batteryLevel == 100 ? info.batteryPercentString :
resources.getString(R.string.power_charging, info.batteryPercentString,
@@ -326,35 +328,6 @@
}
}
- private static String getBatteryStatus(Context context, Intent batteryChangedIntent) {
- final Resources res = context.getResources();
- final boolean isShortStatus =
- res.getBoolean(com.android.settings.R.bool.config_use_compact_battery_status);
-
- if (!isShortStatus) {
- return Utils.getBatteryStatus(context, batteryChangedIntent);
- }
-
- final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
- BatteryManager.BATTERY_STATUS_UNKNOWN);
- final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent);
- String statusString = res.getString(R.string.battery_info_status_unknown);
-
- if (batteryStatus.isCharged()) {
- statusString = res.getString(R.string.battery_info_status_full);
- } else {
- if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- statusString = res.getString(R.string.battery_info_status_charging);
- } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
- statusString = res.getString(R.string.battery_info_status_discharging);
- } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
- statusString = res.getString(R.string.battery_info_status_not_charging);
- }
- }
-
- return statusString;
- }
-
public interface BatteryDataParser {
void onParsingStarted(long startTime, long endTime);
diff --git a/src/com/android/settings/safetycenter/LockScreenSafetySource.java b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
index 66001f2..c9c1cd4 100644
--- a/src/com/android/settings/safetycenter/LockScreenSafetySource.java
+++ b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
@@ -17,51 +17,76 @@
package com.android.settings.safetycenter;
import android.app.PendingIntent;
-import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
+import android.os.UserHandle;
import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceStatus;
+import android.safetycenter.SafetySourceStatus.IconAction;
-import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.password.ChooseLockGeneric;
-import com.android.settingslib.transition.SettingsTransitionHelper;
+import com.android.settings.R;
+import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
/** Lock Screen Safety Source for Safety Center. */
public final class LockScreenSafetySource {
public static final String SAFETY_SOURCE_ID = "LockScreenSafetySource";
- private LockScreenSafetySource() {}
+ private LockScreenSafetySource() {
+ }
/** Sends lock screen safety data to Safety Center. */
- public static void sendSafetyData(Context context) {
+ public static void sendSafetyData(Context context,
+ ScreenLockPreferenceDetailsUtils screenLockPreferenceDetailsUtils) {
if (!SafetyCenterStatusHolder.get().isEnabled(context)) {
return;
}
- // TODO(b/215515298): Replace placeholder SafetySourceData with real data.
- // TODO(b/217409995): Replace SECURITY_ALTERNATIVE with Safety Center metrics category.
- Intent clickIntent = new SubSettingLauncher(context)
- .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
- .setSourceMetricsCategory(SettingsEnums.SECURITY_ALTERNATIVE)
- .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
- .toIntent();
- PendingIntent pendingIntent = PendingIntent
+ if (!screenLockPreferenceDetailsUtils.isAvailable()) {
+ return;
+ }
+
+ final int userId = UserHandle.myUserId();
+ final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtilsInternal
+ .checkIfPasswordQualityIsSet(context, userId);
+ final PendingIntent pendingIntent = createPendingIntent(context,
+ screenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent());
+ final IconAction gearMenuIconAction = createGearMenuIconAction(context,
+ screenLockPreferenceDetailsUtils);
+
+ final SafetySourceStatus status = new SafetySourceStatus.Builder(
+ context.getString(R.string.unlock_set_unlock_launch_picker_title_profile),
+ screenLockPreferenceDetailsUtils.getSummary(UserHandle.myUserId()),
+ screenLockPreferenceDetailsUtils.isLockPatternSecure()
+ ? SafetySourceStatus.STATUS_LEVEL_OK
+ : SafetySourceStatus.STATUS_LEVEL_RECOMMENDATION,
+ pendingIntent)
+ .setEnabled(
+ !screenLockPreferenceDetailsUtils.isPasswordQualityManaged(userId, admin))
+ .setIconAction(gearMenuIconAction).build();
+ final SafetySourceData safetySourceData = new SafetySourceData.Builder(
+ SAFETY_SOURCE_ID).setStatus(status).build();
+
+ SafetyCenterManagerWrapper.get().sendSafetyCenterUpdate(context, safetySourceData);
+ }
+
+ private static IconAction createGearMenuIconAction(Context context,
+ ScreenLockPreferenceDetailsUtils screenLockPreferenceDetailsUtils) {
+ return screenLockPreferenceDetailsUtils.shouldShowGearMenu() ? new IconAction(
+ IconAction.ICON_TYPE_GEAR,
+ createPendingIntent(context,
+ screenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent()))
+ : null;
+ }
+
+ private static PendingIntent createPendingIntent(Context context, Intent intent) {
+ return PendingIntent
.getActivity(
context,
0 /* requestCode */,
- clickIntent,
+ intent,
PendingIntent.FLAG_IMMUTABLE);
- SafetySourceData safetySourceData =
- new SafetySourceData.Builder(SAFETY_SOURCE_ID).setStatus(
- new SafetySourceStatus.Builder(
- "Lock Screen",
- "Lock screen settings",
- SafetySourceStatus.STATUS_LEVEL_OK,
- pendingIntent).build()
- ).build();
-
- SafetyCenterManagerWrapper.get().sendSafetyCenterUpdate(context, safetySourceData);
}
}
diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
index bdc52ad..558a8b9 100644
--- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
+++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
@@ -20,10 +20,13 @@
import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS;
+import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
+
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -54,7 +57,8 @@
private static void refreshSafetySources(Context context, List<String> sourceIds) {
if (sourceIds.contains(LockScreenSafetySource.SAFETY_SOURCE_ID)) {
- LockScreenSafetySource.sendSafetyData(context);
+ LockScreenSafetySource.sendSafetyData(context,
+ new ScreenLockPreferenceDetailsUtils(context, SettingsEnums.SAFETY_CENTER));
}
if (sourceIds.contains(BiometricsSafetySource.SAFETY_SOURCE_ID)) {
@@ -63,7 +67,8 @@
}
private static void refreshAllSafetySources(Context context) {
- LockScreenSafetySource.sendSafetyData(context);
+ LockScreenSafetySource.sendSafetyData(context,
+ new ScreenLockPreferenceDetailsUtils(context, SettingsEnums.SAFETY_CENTER));
BiometricsSafetySource.sendSafetyData(context);
}
}
diff --git a/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java b/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java
index abcacf0..a191acd 100644
--- a/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java
+++ b/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java
@@ -18,12 +18,14 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import androidx.annotation.StringRes;
+import com.android.internal.app.UnlaunchableAppActivity;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
@@ -82,20 +84,34 @@
}
/**
+ * Returns whether the lock pattern is secure.
+ */
+ public boolean isLockPatternSecure() {
+ return mLockPatternUtils.isSecure(mUserId);
+ }
+
+ /**
* Returns whether the Gear Menu should be shown.
*/
public boolean shouldShowGearMenu() {
- return mLockPatternUtils.isSecure(mUserId);
+ return isLockPatternSecure();
}
/**
* Launches the {@link ScreenLockSettings}.
*/
public void openScreenLockSettings() {
- new SubSettingLauncher(mContext)
+ mContext.startActivity(getLaunchScreenLockSettingsIntent());
+ }
+
+ /**
+ * Returns {@link Intent} to launch the {@link ScreenLockSettings}.
+ */
+ public Intent getLaunchScreenLockSettingsIntent() {
+ return new SubSettingLauncher(mContext)
.setDestination(ScreenLockSettings.class.getName())
.setSourceMetricsCategory(mSourceMetricsCategory)
- .launch();
+ .toIntent();
}
/**
@@ -105,6 +121,29 @@
* @return true if the {@link ChooseLockGenericFragment} is launching.
*/
public boolean openChooseLockGenericFragment() {
+ final Intent quietModeDialogIntent = getQuietModeDialogIntent();
+ if (quietModeDialogIntent != null) {
+ mContext.startActivity(quietModeDialogIntent);
+ return false;
+ }
+ mContext.startActivity(getChooseLockGenericFragmentIntent());
+ return true;
+ }
+
+ /**
+ * Returns {@link Intent} to launch an appropriate Settings screen.
+ *
+ * <p>If Quiet Mode is enabled for managed profile, returns {@link Intent} to launch a dialog
+ * to disable the Quiet Mode, otherwise returns {@link Intent} to launch
+ * {@link ChooseLockGenericFragment}.
+ */
+ public Intent getLaunchChooseLockGenericFragmentIntent() {
+ final Intent quietModeDialogIntent = getQuietModeDialogIntent();
+ return quietModeDialogIntent != null ? quietModeDialogIntent
+ : getChooseLockGenericFragmentIntent();
+ }
+
+ private Intent getQuietModeDialogIntent() {
// TODO(b/35930129): Remove once existing password can be passed into vold directly.
// Currently we need this logic to ensure that the QUIET_MODE is off for any work
// profile with unified challenge on FBE-enabled devices. Otherwise, vold would not be
@@ -112,17 +151,20 @@
if (mProfileChallengeUserId != UserHandle.USER_NULL
&& !mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileChallengeUserId)
&& StorageManager.isFileEncryptedNativeOnly()) {
- if (Utils.startQuietModeDialogIfNecessary(mContext, mUm, mProfileChallengeUserId)) {
- return false;
+ if (mUm.isQuietModeEnabled(UserHandle.of(mProfileChallengeUserId))) {
+ return UnlaunchableAppActivity.createInQuietModeDialogIntent(
+ mProfileChallengeUserId);
}
}
+ return null;
+ }
- new SubSettingLauncher(mContext)
+ private Intent getChooseLockGenericFragmentIntent() {
+ return new SubSettingLauncher(mContext)
.setDestination(ChooseLockGenericFragment.class.getName())
.setSourceMetricsCategory(mSourceMetricsCategory)
.setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
- .launch();
- return true;
+ .toIntent();
}
@StringRes
diff --git a/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java
index ef8f569..f908b8a 100644
--- a/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java
@@ -26,6 +26,7 @@
import com.android.internal.view.RotationPolicy;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
import com.android.settings.testutils.shadow.ShadowRotationPolicy;
import org.junit.Before;
@@ -50,7 +51,10 @@
}
@Test
- @Config(shadows = {ShadowRotationPolicy.class})
+ @Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+ })
public void getAvailabilityStatus_supportedRotation_shouldReturnAvailable() {
ShadowRotationPolicy.setRotationSupported(true /* supported */);
@@ -59,8 +63,23 @@
}
@Test
- @Config(shadows = {ShadowRotationPolicy.class})
- public void getAvailabilityStatus_unsupportedRotation_shouldReturnUnsupportedOnDevice() {
+ @Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+ })
+ public void getAvailabilityStatus_deviceStateRotationEnabled_returnsUnsupported() {
+ ShadowRotationPolicy.setRotationSupported(true /* supported */);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ @Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+ }) public void getAvailabilityStatus_unsupportedRotation_shouldReturnUnsupportedOnDevice() {
ShadowRotationPolicy.setRotationSupported(false /* supported */);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
diff --git a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java
index 00cf052..c9bb687 100644
--- a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java
@@ -18,10 +18,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,7 +39,6 @@
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -96,7 +92,6 @@
mContext, TEST_PREF_KEY, mLifecycle);
}
- @Ignore
@Test
public void updateState_parentalConsentRequired_preferenceDisabled() {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
@@ -106,16 +101,10 @@
RestrictedLockUtils.EnforcedAdmin admin = mock(RestrictedLockUtils.EnforcedAdmin.class);
mController.mPreference = restrictedPreference;
- mController.updateStateInternal(admin, true, true);
+ mController.updateStateInternal(admin);
verify(restrictedPreference).setDisabledByAdmin(eq(admin));
- mController.updateStateInternal(admin, true, false);
- verify(restrictedPreference).setDisabledByAdmin(eq(null));
-
- mController.updateStateInternal(admin, false, true);
- verify(restrictedPreference).setDisabledByAdmin(eq(null));
-
- mController.updateStateInternal(admin, false, false);
+ mController.updateStateInternal(null);
verify(restrictedPreference).setDisabledByAdmin(eq(null));
}
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
index 6087ef2..51cad70 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
@@ -387,34 +387,52 @@
}
@Test
- public void showBatteryPredictionIfNecessary_estimateReadyIsAvailable_showView() {
- mController.showBatteryPredictionIfNecessary(1, 14218009,
- mLayoutPreference.findViewById(R.id.layout_left));
- mController.showBatteryPredictionIfNecessary(1, 14218009,
- mLayoutPreference.findViewById(R.id.layout_middle));
- mController.showBatteryPredictionIfNecessary(1, 14218009,
- mLayoutPreference.findViewById(R.id.layout_right));
+ public void estimateReadyIsBothAvailable_showsView() {
+ mController.mIsLeftDeviceEstimateReady = true;
+ mController.mIsRightDeviceEstimateReady = true;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
View.VISIBLE);
- assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_middle),
- View.VISIBLE);
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
View.VISIBLE);
}
@Test
- public void showBatteryPredictionIfNecessary_estimateReadyIsNotAvailable_notShowView() {
- mController.showBatteryPredictionIfNecessary(0, 14218009,
- mLayoutPreference.findViewById(R.id.layout_left));
- mController.showBatteryPredictionIfNecessary(0, 14218009,
- mLayoutPreference.findViewById(R.id.layout_middle));
- mController.showBatteryPredictionIfNecessary(0, 14218009,
- mLayoutPreference.findViewById(R.id.layout_right));
+ public void leftDeviceEstimateIsReadyRightDeviceIsNotReady_notShowView() {
+ mController.mIsLeftDeviceEstimateReady = true;
+ mController.mIsRightDeviceEstimateReady = false;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
View.GONE);
- assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_middle),
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
+ View.GONE);
+ }
+
+ @Test
+ public void leftDeviceEstimateIsNotReadyRightDeviceIsReady_notShowView() {
+ mController.mIsLeftDeviceEstimateReady = false;
+ mController.mIsRightDeviceEstimateReady = true;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
+
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
+ View.GONE);
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
+ View.GONE);
+ }
+
+ @Test
+ public void bothDevicesEstimateIsNotReady_notShowView() {
+ mController.mIsLeftDeviceEstimateReady = false;
+ mController.mIsRightDeviceEstimateReady = false;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
+
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
View.GONE);
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
View.GONE);
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index fd1c8ff..aa5f980 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -144,6 +144,21 @@
}
@Test
+ public void useAll_returnsAllControllersOfType() {
+ final TestPreferenceController controller1 = new TestPreferenceController(mContext);
+ final TestPreferenceController controller2 = new TestPreferenceController(mContext);
+ final SubTestPreferenceController controller3 = new SubTestPreferenceController(mContext);
+ mTestFragment.addPreferenceController(controller1);
+ mTestFragment.addPreferenceController(controller2);
+ mTestFragment.addPreferenceController(controller3);
+
+ final List<TestPreferenceController> retrievedControllers = mTestFragment.useAll(
+ TestPreferenceController.class);
+
+ assertThat(retrievedControllers).containsExactly(controller1, controller2);
+ }
+
+ @Test
public void displayTilesAsPreference_shouldAddTilesWithIntent() {
when(mFakeFeatureFactory.dashboardFeatureProvider
.getTilesForCategory(nullable(String.class)))
@@ -360,6 +375,13 @@
}
}
+ public static class SubTestPreferenceController extends TestPreferenceController {
+
+ private SubTestPreferenceController(Context context) {
+ super(context);
+ }
+ }
+
private static class TestFragment extends DashboardFragment {
private final PreferenceManager mPreferenceManager;
diff --git a/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
index 1d175de..54e6b99 100644
--- a/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
@@ -30,6 +30,7 @@
import androidx.preference.SwitchPreference;
+import com.android.internal.R;
import com.android.internal.view.RotationPolicy;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -64,6 +65,7 @@
mPreference = new SwitchPreference(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ disableDeviceStateRotation();
mController = new AutoRotatePreferenceController(mContext, "auto_rotate");
}
@@ -112,6 +114,26 @@
}
@Test
+ public void getAvailabilityStatus_deviceRotationDisabled_returnsAvailable() {
+ enableAutoRotationPreference();
+ disableDeviceStateRotation();
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceRotationEnabled_returnsUnsupported() {
+ enableAutoRotationPreference();
+ enableDeviceStateRotation();
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
public void testIsCheck() {
assertThat(mController.isChecked()).isFalse();
@@ -180,4 +202,15 @@
Settings.System.putIntForUser(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT);
}
+
+ private void enableDeviceStateRotation() {
+ when(mContext.getResources().getStringArray(
+ R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+ new String[]{"0:0", "1:1", "2:2"});
+ }
+
+ private void disableDeviceStateRotation() {
+ when(mContext.getResources().getStringArray(
+ R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(new String[]{});
+ }
}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
new file mode 100644
index 0000000..b773657
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class DeviceStateAutoRotateDetailsFragmentTest {
+
+ private final DeviceStateAutoRotateDetailsFragment mFragment =
+ spy(new DeviceStateAutoRotateDetailsFragment());
+ private final Context mContext = spy(RuntimeEnvironment.application);
+ private final Resources mResources = spy(mContext.getResources());
+
+ @Before
+ public void setUp() throws Exception {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mFragment.getContext()).thenReturn(mContext);
+ when(mFragment.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void getMetricsCategory_returnsAutoRotateSettings() {
+ assertThat(mFragment.getMetricsCategory()).isEqualTo(
+ SettingsEnums.DISPLAY_AUTO_ROTATE_SETTINGS);
+ }
+
+ @Test
+ public void getPreferenceScreenResId_returnsDeviceStateAutoRotationSettings() {
+ assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(
+ R.xml.device_state_auto_rotate_settings);
+ }
+
+ @Test
+ public void createPreferenceControllers_settableDeviceStates_returnsDeviceStateControllers() {
+ enableDeviceStateSettableRotationStates(new String[]{"0:1", "1:1"},
+ new String[]{"Folded", "Unfolded"});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).hasSize(2);
+ assertThat(preferenceControllers.get(0)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ assertThat(preferenceControllers.get(1)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ }
+
+ @Test
+ public void createPreferenceControllers_noSettableDeviceStates_returnsEmptyList() {
+ enableDeviceStateSettableRotationStates(new String[]{}, new String[]{});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).isEmpty();
+ }
+
+ private void enableDeviceStateSettableRotationStates(String[] settableStates,
+ String[] settableStatesDescriptions) {
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+ settableStates);
+ when(mResources.getStringArray(
+ R.array.config_settableAutoRotationDeviceStatesDescriptions)).thenReturn(
+ settableStatesDescriptions);
+ DeviceStateRotationLockSettingsManager.resetInstance();
+ DeviceStateRotationLockSettingsManager.getInstance(mContext)
+ .resetStateForTesting(mResources);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java
new file mode 100644
index 0000000..a5416e7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowRotationPolicy.class, ShadowDeviceStateRotationLockSettingsManager.class})
+public class DeviceStateAutoRotateOverviewControllerTest {
+
+ private final DeviceStateAutoRotateOverviewController mController =
+ new DeviceStateAutoRotateOverviewController(
+ RuntimeEnvironment.application, "device_state_auto_rotate");
+
+ @Test
+ public void getAvailabilityStatus_rotationAndDeviceStateRotationEnabled_returnsAvailable() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_rotationNotSupported_returnsUnsupportedOnDevice() {
+ ShadowRotationPolicy.setRotationSupported(false);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceStateRotationNotSupported_returnsUnsupportedOnDevice() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java
new file mode 100644
index 0000000..28a071a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+})
+public class DeviceStateAutoRotateSettingControllerTest {
+
+ private static final int DEFAULT_DEVICE_STATE = 1;
+ private static final String DEFAULT_DEVICE_STATE_DESCRIPTION = "Device state description";
+ private static final int DEFAULT_ORDER = -10;
+
+ private final Context mContext = RuntimeEnvironment.application;
+ private final DeviceStateAutoRotateSettingController mController =
+ new DeviceStateAutoRotateSettingController(mContext, DEFAULT_DEVICE_STATE,
+ DEFAULT_DEVICE_STATE_DESCRIPTION, DEFAULT_ORDER);
+ private final DeviceStateRotationLockSettingsManager mAutoRotateSettingsManager =
+ DeviceStateRotationLockSettingsManager.getInstance(mContext);
+
+ @Test
+ public void displayPreference_addsPreferenceToPreferenceScreen() {
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+
+ mController.displayPreference(screen);
+
+ assertThat(screen.getPreferenceCount()).isEqualTo(1);
+ Preference preference = screen.getPreference(0);
+ assertThat(preference.getTitle().toString()).isEqualTo(DEFAULT_DEVICE_STATE_DESCRIPTION);
+ assertThat(preference.getOrder()).isEqualTo(DEFAULT_ORDER);
+ assertThat(preference.getKey()).isEqualTo(mController.getPreferenceKey());
+ }
+
+ @Test
+ public void getAvailabilityStatus_rotationAndDeviceStateRotationEnabled_returnsAvailable() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceStateRotationDisabled_returnsUnsupported() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_rotationDisabled_returnsUnsupported() {
+ ShadowRotationPolicy.setRotationSupported(false);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getPreferenceKey_returnsKeyBasedOnDeviceState() {
+ String key = mController.getPreferenceKey();
+
+ String expectedKey = "auto_rotate_device_state_" + DEFAULT_DEVICE_STATE;
+ assertThat(key).isEqualTo(expectedKey);
+ }
+
+ @Test
+ public void isChecked_settingForStateIsUnlocked_returnsTrue() {
+ mAutoRotateSettingsManager.updateSetting(DEFAULT_DEVICE_STATE, /* rotationLocked= */ false);
+
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void isChecked_settingForStateIsLocked_returnsFalse() {
+ mAutoRotateSettingsManager.updateSetting(DEFAULT_DEVICE_STATE, /* rotationLocked= */ true);
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void setChecked_true_deviceStateSettingIsUnlocked() {
+ mController.setChecked(true);
+
+ boolean rotationLocked = mAutoRotateSettingsManager.isRotationLocked(DEFAULT_DEVICE_STATE);
+
+ assertThat(rotationLocked).isFalse();
+ }
+
+ @Test
+ public void setChecked_false_deviceStateSettingIsLocked() {
+ mController.setChecked(false);
+
+ boolean rotationLocked = mAutoRotateSettingsManager.isRotationLocked(DEFAULT_DEVICE_STATE);
+
+ assertThat(rotationLocked).isTrue();
+ }
+
+ @Test
+ public void updateRawDataToIndex_addsItemToList() {
+ List<SearchIndexableRaw> rawData = new ArrayList<>();
+
+ mController.updateRawDataToIndex(rawData);
+
+ assertThat(rawData).hasSize(1);
+ SearchIndexableRaw item = rawData.get(0);
+ assertThat(item.key).isEqualTo(mController.getPreferenceKey());
+ assertThat(item.title).isEqualTo(DEFAULT_DEVICE_STATE_DESCRIPTION);
+ assertThat(item.screenTitle).isEqualTo(mContext.getString(R.string.accelerometer_title));
+ }
+
+ @Test
+ public void getSliceHighlightMenuRes_returnsMenuKeyDisplay() {
+ int sliceHighlightMenuRes = mController.getSliceHighlightMenuRes();
+
+ assertThat(sliceHighlightMenuRes).isEqualTo(R.string.menu_key_display);
+ }
+
+ @Test
+ public void isSliceable_returnsTrue() {
+ assertThat(mController.isSliceable()).isTrue();
+ }
+
+ @Test
+ public void isPublicSlice_returnsTrue() {
+ assertThat(mController.isPublicSlice()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
index 778721a..4fec38b 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
@@ -39,7 +39,10 @@
import androidx.preference.Preference;
import com.android.settings.testutils.ResolveInfoBuilder;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import org.junit.Before;
import org.junit.Test;
@@ -63,6 +66,8 @@
@Mock
private Preference mPreference;
private ContentResolver mContentResolver;
+ private final DeviceStateRotationLockSettingsManager mDeviceStateAutoRotateSettingsManager =
+ DeviceStateRotationLockSettingsManager.getInstance(RuntimeEnvironment.application);
@Before
public void setUp() {
@@ -122,6 +127,34 @@
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
+ @Test
+ @Config(shadows = {
+ ShadowDeviceStateRotationLockSettingsManager.class,
+ ShadowRotationPolicy.class
+ })
+ public void getAvailabilityStatus_deviceStateRotationLocked_returnDisableDependentSetting() {
+ enableDeviceStateRotation();
+ lockDeviceStateRotation();
+
+ int availabilityStatus = mController.getAvailabilityStatus();
+
+ assertThat(availabilityStatus).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ @Config(shadows = {
+ ShadowDeviceStateRotationLockSettingsManager.class,
+ ShadowRotationPolicy.class
+ })
+ public void getAvailabilityStatus_deviceStateRotationUnlocked_returnAvailable() {
+ enableDeviceStateRotation();
+ unlockDeviceStateRotation();
+
+ int availabilityStatus = mController.getAvailabilityStatus();
+
+ assertThat(availabilityStatus).isEqualTo(AVAILABLE);
+ }
+
private void enableAutoRotation() {
Settings.System.putIntForUser(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION, 1, UserHandle.USER_CURRENT);
@@ -131,4 +164,23 @@
Settings.System.putIntForUser(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT);
}
+
+ private void enableDeviceStateRotation() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+ }
+
+ private void lockDeviceStateRotation() {
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */0, /* rotationLocked= */ true);
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */1, /* rotationLocked= */ true);
+ }
+
+ private void unlockDeviceStateRotation() {
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */0, /* rotationLocked= */ false);
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */1, /* rotationLocked= */ true);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java
index 068de34..39fdb04 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java
@@ -40,6 +40,7 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.ResolveInfoBuilder;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
import org.junit.Before;
@@ -53,7 +54,10 @@
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowSensorPrivacyManager.class)
+@Config(shadows = {
+ ShadowSensorPrivacyManager.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+})
public class SmartAutoRotatePreferenceControllerTest {
private static final String PACKAGE_NAME = "package_name";
@@ -95,6 +99,7 @@
new SmartAutoRotatePreferenceController(mContext, "smart_auto_rotate"));
when(mController.isCameraLocked()).thenReturn(false);
when(mController.isPowerSaveMode()).thenReturn(false);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
}
@Test
@@ -199,6 +204,16 @@
.UNSUPPORTED_ON_DEVICE);
}
+
+ @Test
+ public void getAvailabilityStatus_deviceStateRotationEnabled_returnsUnsupported() {
+ enableAutoRotationPreference();
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
@Test
public void isSliceableCorrectKey_returnsTrue() {
final AutoRotatePreferenceController controller =
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
index 877d2c1..942fed6 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
@@ -18,6 +18,8 @@
import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_ID;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -33,6 +35,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.view.View;
import androidx.preference.Preference;
@@ -40,7 +43,11 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.ResolveInfoBuilder;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import org.junit.Before;
import org.junit.Test;
@@ -49,8 +56,15 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowDeviceStateRotationLockSettingsManager.class,
+ ShadowRotationPolicy.class
+})
public class SmartAutoRotatePreferenceFragmentTest {
private static final String PACKAGE_NAME = "package_name";
@@ -70,19 +84,24 @@
@Mock
private Preference mRotateSwitchPreference;
+ private Resources mResources;
+ private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- final Context context = spy(RuntimeEnvironment.application);
+ mContext = spy(RuntimeEnvironment.application);
ContentResolver mContentResolver = RuntimeEnvironment.application.getContentResolver();
- when(context.getPackageManager()).thenReturn(mPackageManager);
- when(context.getContentResolver()).thenReturn(mContentResolver);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
doReturn(PACKAGE_NAME).when(mPackageManager).getRotationResolverPackageName();
doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission(
Manifest.permission.CAMERA, PACKAGE_NAME);
+ mResources = spy(mContext.getResources());
+ when(mContext.getResources()).thenReturn(mResources);
+
final ResolveInfo resolveInfo = new ResolveInfoBuilder(PACKAGE_NAME).build();
resolveInfo.serviceInfo = new ServiceInfo();
when(mPackageManager.resolveService(any(), anyInt())).thenReturn(resolveInfo);
@@ -90,15 +109,16 @@
mFragment = spy(new SmartAutoRotatePreferenceFragment());
when(mActivity.getPackageManager()).thenReturn(mPackageManager);
when(mFragment.getActivity()).thenReturn(mActivity);
- when(mFragment.getContext()).thenReturn(context);
+ when(mFragment.getContext()).thenReturn(mContext);
doReturn(mView).when(mFragment).getView();
when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID)).thenReturn(
mRotateSwitchPreference);
- mSwitchBar = spy(new SettingsMainSwitchBar(context));
+ mSwitchBar = spy(new SettingsMainSwitchBar(mContext));
when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
doReturn(mSwitchBar).when(mView).findViewById(R.id.switch_bar);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
}
@@ -111,6 +131,17 @@
}
@Test
+ public void createHeader_deviceStateRotationSupported_switchBarIsDisabled() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ mFragment.createHeader(mActivity);
+
+ verify(mSwitchBar, never()).show();
+ verify(mRotateSwitchPreference, never()).setVisible(false);
+ }
+
+ @Test
public void createHeader_faceDetectionUnSupported_switchBarIsDisabled() {
doReturn(null).when(mPackageManager).getRotationResolverPackageName();
@@ -120,4 +151,41 @@
verify(mRotateSwitchPreference, never()).setVisible(false);
}
+ @Test
+ public void createPreferenceControllers_noSettableDeviceStates_returnsEmptyList() {
+ enableDeviceStateSettableRotationStates(new String[]{}, new String[]{});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).isEmpty();
+ }
+
+ @Test
+ public void createPreferenceControllers_settableDeviceStates_returnsDeviceStateControllers() {
+ enableDeviceStateSettableRotationStates(new String[]{"0:1", "1:1"},
+ new String[]{"Folded", "Unfolded"});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).hasSize(2);
+ assertThat(preferenceControllers.get(0)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ assertThat(preferenceControllers.get(1)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ }
+
+ private void enableDeviceStateSettableRotationStates(String[] settableStates,
+ String[] settableStatesDescriptions) {
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+ settableStates);
+ when(mResources.getStringArray(
+ R.array.config_settableAutoRotationDeviceStatesDescriptions)).thenReturn(
+ settableStatesDescriptions);
+ DeviceStateRotationLockSettingsManager.resetInstance();
+ DeviceStateRotationLockSettingsManager.getInstance(mContext)
+ .resetStateForTesting(mResources);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
index 5f08698..d446930 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
@@ -85,8 +85,8 @@
assertThat(mBatteryBroadcastReceiver.mBatteryLevel)
.isEqualTo(Utils.getBatteryPercentage(mChargingIntent));
- assertThat(mBatteryBroadcastReceiver.mBatteryStatus)
- .isEqualTo(Utils.getBatteryStatus(mContext, mChargingIntent));
+ assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false));
verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL);
}
@@ -134,7 +134,7 @@
public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() {
final String batteryLevel = Utils.getBatteryPercentage(mChargingIntent);
final String batteryStatus =
- Utils.getBatteryStatus(mContext, mChargingIntent);
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false);
mBatteryBroadcastReceiver.mBatteryLevel = batteryLevel;
mBatteryBroadcastReceiver.mBatteryStatus = batteryStatus;
@@ -159,8 +159,8 @@
assertThat(mBatteryBroadcastReceiver.mBatteryLevel)
.isEqualTo(Utils.getBatteryPercentage(mChargingIntent));
- assertThat(mBatteryBroadcastReceiver.mBatteryStatus)
- .isEqualTo(Utils.getBatteryStatus(mContext, mChargingIntent));
+ assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false));
assertThat(mBatteryBroadcastReceiver.mBatteryHealth)
.isEqualTo(BatteryManager.BATTERY_HEALTH_UNKNOWN);
// 2 times because register will force update the battery
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java
new file mode 100644
index 0000000..72df3cc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.testutils.shadow;
+
+import android.content.Context;
+
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(DeviceStateRotationLockSettingsManager.class)
+public class ShadowDeviceStateRotationLockSettingsManager {
+
+ private static boolean sDeviceStateRotationLockEnabled;
+
+ @Implementation
+ public static boolean isDeviceStateRotationLockEnabled(Context context) {
+ return sDeviceStateRotationLockEnabled;
+ }
+
+ public static void setDeviceStateRotationLockEnabled(boolean enabled) {
+ sDeviceStateRotationLockEnabled = enabled;
+ }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java
new file mode 100644
index 0000000..3e6ac09
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class BiometricNavigationUtilsTest {
+
+ private static final String SETTINGS_CLASS_NAME = "SettingsClassName";
+ private static final String EXTRA_KEY = "EXTRA_KEY";
+
+ @Mock
+ private UserManager mUserManager;
+ private Context mContext;
+ private BiometricNavigationUtils mBiometricNavigationUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+ doNothing().when(mContext).startActivity(any());
+ mBiometricNavigationUtils = new BiometricNavigationUtils();
+ }
+
+ @Test
+ public void openBiometricSettings_quietMode_launchesQuiteModeDialog() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
+
+ mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
+ Bundle.EMPTY);
+
+ assertQuietModeDialogLaunchRequested();
+ }
+
+ @Test
+ public void openBiometricSettings_quietMode_returnsFalse() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
+
+ assertThat(mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isFalse();
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_emptyExtras_launchesFragmentWithoutExtras() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY);
+
+ assertSettingsPageLaunchRequested(false /* shouldContainExtras */);
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_emptyExtras_returnsTrue() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ assertThat(mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isTrue();
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_withExtras_launchesFragmentWithExtras() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ final Bundle extras = createNotEmptyExtras();
+ mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME, extras);
+
+ assertSettingsPageLaunchRequested(true /* shouldContainExtras */);
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_withExtras_returnsTrue() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ assertThat(mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras())).isTrue();
+ }
+
+ private Bundle createNotEmptyExtras() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_KEY, 0);
+ return bundle;
+ }
+
+ private void assertQuietModeDialogLaunchRequested() {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivity(intentCaptor.capture());
+
+ Intent intent = intentCaptor.getValue();
+ assertThat(intent.getComponent().getPackageName())
+ .isEqualTo("android");
+ assertThat(intent.getComponent().getClassName())
+ .isEqualTo("com.android.internal.app.UnlaunchableAppActivity");
+ }
+
+ private void assertSettingsPageLaunchRequested(boolean shouldContainExtras) {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivity(intentCaptor.capture());
+
+ Intent intent = intentCaptor.getValue();
+ assertThat(intent.getComponent().getPackageName())
+ .isEqualTo("com.android.settings");
+ assertThat(intent.getComponent().getClassName())
+ .isEqualTo(SETTINGS_CLASS_NAME);
+ assertThat(intent.getExtras().containsKey(EXTRA_KEY)).isEqualTo(shouldContainExtras);
+ }
+
+}
diff --git a/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
new file mode 100644
index 0000000..55b3fae
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2022 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.biometrics.combination;
+
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.Settings;
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class CombinedBiometricStatusUtilsTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("package", "class");
+ private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
+
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
+
+ private Context mApplicationContext;
+ private CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mApplicationContext = spy(ApplicationProvider.getApplicationContext());
+ when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
+ .thenReturn(COMPONENT_NAME);
+ when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
+ .thenReturn(mFingerprintManager);
+ when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .thenReturn(mDevicePolicyManager);
+ when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+ mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(mApplicationContext);
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_whenFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_whenFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_whenFingerprint_whenFace_returnsTrue() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintDisabled_whenFaceDisabled_returnsEnforcedAdmin() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(KEYGUARD_DISABLE_FACE | KEYGUARD_DISABLE_FINGERPRINT);
+
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ mCombinedBiometricStatusUtils.getDisablingAdmin();
+
+ assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
+ COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintDisabled_whenFaceEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
+
+ assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintEnabled_whenFaceDisabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(KEYGUARD_DISABLE_FACE);
+
+ assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintEnabled_whenFaceEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
+
+ assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getSummary_whenFaceEnrolled_whenMultipleFingerprints_returnsBothFpMultiple() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(2));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_biometric_preference_summary_both_fp_multiple"));
+ }
+
+ @Test
+ public void getSummary_whenFaceEnrolled_whenSingleFingerprint_returnsBothFpSingle() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(1));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_biometric_preference_summary_both_fp_single"));
+ }
+
+ @Test
+ public void getSummary_whenFaceEnrolled_whenNoFingerprints_returnsFace() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(0));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_face_preference_summary"));
+ }
+
+ @Test
+ public void getSummary_whenNoFaceEnrolled_whenMultipleFingerprints_returnsFingerprints() {
+ final int enrolledFingerprintsCount = 2;
+ final int stringResId = ResourcesUtils.getResourcesId(
+ ApplicationProvider.getApplicationContext(), "plurals",
+ "security_settings_fingerprint_preference_summary");
+ final String summary = mApplicationContext.getResources().getQuantityString(
+ stringResId, enrolledFingerprintsCount /* quantity */,
+ enrolledFingerprintsCount /* formatArgs */);
+
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(enrolledFingerprintsCount));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
+ }
+
+ @Test
+ public void getSummary_whenNoFaceEnrolled_whenSingleFingerprints_returnsFingerprints() {
+ final int enrolledFingerprintsCount = 1;
+ final int stringResId = ResourcesUtils.getResourcesId(
+ ApplicationProvider.getApplicationContext(), "plurals",
+ "security_settings_fingerprint_preference_summary");
+ final String summary = mApplicationContext.getResources().getQuantityString(
+ stringResId, enrolledFingerprintsCount /* quantity */,
+ enrolledFingerprintsCount /* formatArgs */);
+
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(enrolledFingerprintsCount));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
+ }
+
+ @Test
+ public void getSummary_whenNoFaceEnrolled_whenNoFingerprints_returnsNoneEnrolled() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(0));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_biometric_preference_summary_none_enrolled"));
+ }
+
+ @Test
+ public void getSettingsClassName_returnsCombinedBiometricSettings() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.getSettingsClassName())
+ .isEqualTo(Settings.CombinedBiometricSettingsActivity.class.getName());
+ }
+
+ @Test
+ public void getProfileSettingsClassName_returnsCombinedBiometricProfileSettings() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.getProfileSettingsClassName())
+ .isEqualTo(Settings.CombinedBiometricProfileSettingsActivity.class.getName());
+ }
+
+ private List<Fingerprint> createFingerprintList(int size) {
+ final List<Fingerprint> fingerprintList = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
+ }
+ return fingerprintList;
+ }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java
new file mode 100644
index 0000000..f670fad
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.biometrics.face;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.Settings;
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class FaceStatusUtilsTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("package", "class");
+ private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
+
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
+
+ private Context mApplicationContext;
+ private FaceStatusUtils mFaceStatusUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mApplicationContext = spy(ApplicationProvider.getApplicationContext());
+ when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
+ .thenReturn(COMPONENT_NAME);
+ when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
+ .thenReturn(mFingerprintManager);
+ when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .thenReturn(mDevicePolicyManager);
+ when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+ mFaceStatusUtils = new FaceStatusUtils(mApplicationContext, mFaceManager);
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withFace_returnsTrue() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFaceDisabled_returnsEnforcedAdmin() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
+
+ final RestrictedLockUtils.EnforcedAdmin admin = mFaceStatusUtils.getDisablingAdmin();
+
+ assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
+ COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
+ }
+
+ @Test
+ public void getDisabledAdmin_withFaceEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
+
+ assertThat(mFaceStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getSummary_whenNotEnrolled_returnsSummaryNone() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_face_preference_summary_none"));
+ }
+
+ @Test
+ public void getSummary_whenEnrolled_returnsSummary() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_face_preference_summary"));
+ }
+
+ @Test
+ public void getSettingsClassName_whenNotEnrolled_returnsFaceEnrollInduction() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.getSettingsClassName())
+ .isEqualTo(FaceEnrollIntroduction.class.getName());
+ }
+
+ @Test
+ public void getSettingsClassName_whenEnrolled_returnsFaceSettings() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.getSettingsClassName())
+ .isEqualTo(Settings.FaceSettingsActivity.class.getName());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
new file mode 100644
index 0000000..b4abb5d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 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.biometrics.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class FingerprintStatusUtilsTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("package", "class");
+ private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
+
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
+
+ private Context mApplicationContext;
+ private FingerprintStatusUtils mFingerprintStatusUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mApplicationContext = spy(ApplicationProvider.getApplicationContext());
+ when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
+ .thenReturn(COMPONENT_NAME);
+ when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
+ .thenReturn(mFingerprintManager);
+ when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .thenReturn(mDevicePolicyManager);
+ when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+ mFingerprintStatusUtils =
+ new FingerprintStatusUtils(mApplicationContext, mFingerprintManager);
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withoutFace_returnsTrue() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintDisabled_returnsEnforcedAdmin() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ mFingerprintStatusUtils.getDisablingAdmin();
+
+ assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
+ COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
+ }
+
+ @Test
+ public void getDisabledAdmin_withFingerprintEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
+
+ assertThat(mFingerprintStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getSummary_whenNotEnrolled_returnsSummaryNone() {
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_fingerprint_preference_summary_none"));
+ }
+
+ @Test
+ public void getSummary_whenEnrolled_returnsSummary() {
+ final int enrolledFingerprintsCount = 2;
+ final int stringResId = ResourcesUtils.getResourcesId(
+ ApplicationProvider.getApplicationContext(), "plurals",
+ "security_settings_fingerprint_preference_summary");
+ final String summary = mApplicationContext.getResources().getQuantityString(
+ stringResId, enrolledFingerprintsCount /* quantity */,
+ enrolledFingerprintsCount /* formatArgs */);
+
+ when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
+ createFingerprintList(enrolledFingerprintsCount));
+
+ assertThat(mFingerprintStatusUtils.getSummary()).isEqualTo(summary);
+ }
+
+ @Test
+ public void getSettingsClassName_whenNotEnrolled_returnsFingerprintEnrollInduction() {
+ when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.getSettingsClassName())
+ .isEqualTo(FingerprintEnrollIntroduction.class.getName());
+ }
+
+ @Test
+ public void getSettingsClassName_whenEnrolled_returnsFingerprintSettings() {
+ when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+
+ assertThat(mFingerprintStatusUtils.getSettingsClassName())
+ .isEqualTo(FingerprintSettings.class.getName());
+ }
+
+ private List<Fingerprint> createFingerprintList(int size) {
+ final List<Fingerprint> fingerprintList = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
+ }
+ return fingerprintList;
+ }
+}
diff --git a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
index 052f981..4a58b4c 100644
--- a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
@@ -19,19 +19,22 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceStatus;
+import android.safetycenter.SafetySourceStatus.IconAction;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.android.settings.SettingsActivity;
-import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
+import com.android.settings.testutils.ResourcesUtils;
import org.junit.After;
import org.junit.Before;
@@ -44,6 +47,10 @@
@RunWith(AndroidJUnit4.class)
public class LockScreenSafetySourceTest {
+ private static final String SUMMARY = "summary";
+ private static final String FAKE_ACTION_CHOOSE_LOCK_GENERIC_FRAGMENT = "choose_lock_generic";
+ private static final String FAKE_ACTION_SCREEN_LOCK_SETTINGS = "screen_lock_settings";
+
private Context mApplicationContext;
@Mock
@@ -52,6 +59,9 @@
@Mock
private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ @Mock
+ private ScreenLockPreferenceDetailsUtils mScreenLockPreferenceDetailsUtils;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -70,30 +80,160 @@
public void sendSafetyData_whenSafetyCenterIsDisabled_sendsNoData() {
when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(false);
- LockScreenSafetySource.sendSafetyData(mApplicationContext);
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
}
@Test
- public void sendSafetyData_whenSafetyCenterIsEnabled_sendsPlaceholderData() {
+ public void sendSafetyData_whenScreenLockIsDisabled_sendsNoData() {
when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ when(mScreenLockPreferenceDetailsUtils.isAvailable()).thenReturn(false);
- LockScreenSafetySource.sendSafetyData(mApplicationContext);
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
+ }
+
+ @Test
+ public void sendSafetyData_whenScreenLockIsEnabled_sendsData() {
+ whenScreenLockIsEnabled();
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
SafetySourceData safetySourceData = captor.getValue();
SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
assertThat(safetySourceData.getId()).isEqualTo(LockScreenSafetySource.SAFETY_SOURCE_ID);
- assertThat(safetySourceStatus.getTitle().toString()).isEqualTo("Lock Screen");
+ assertThat(safetySourceStatus.getTitle().toString())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "unlock_set_unlock_launch_picker_title_profile"));
assertThat(safetySourceStatus.getSummary().toString())
- .isEqualTo("Lock screen settings");
+ .isEqualTo(SUMMARY);
+ assertThat(safetySourceStatus.getPendingIntent().getIntent()).isNotNull();
+ assertThat(safetySourceStatus.getPendingIntent().getIntent().getAction())
+ .isEqualTo(FAKE_ACTION_CHOOSE_LOCK_GENERIC_FRAGMENT);
+ }
+
+ @Test
+ public void sendSafetyData_whenLockPatternIsSecure_sendsStatusLevelOk() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).thenReturn(true);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
assertThat(safetySourceStatus.getStatusLevel())
.isEqualTo(SafetySourceStatus.STATUS_LEVEL_OK);
- assertThat(safetySourceStatus.getPendingIntent()).isNotNull();
- assertThat(safetySourceStatus.getPendingIntent().getIntent().getStringExtra(
- SettingsActivity.EXTRA_SHOW_FRAGMENT))
- .isEqualTo(ChooseLockGeneric.ChooseLockGenericFragment.class.getName());
+ }
+
+ @Test
+ public void sendSafetyData_whenLockPatternIsNotSecure_sendsStatusLevelRecommendation() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).thenReturn(false);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.getStatusLevel())
+ .isEqualTo(SafetySourceStatus.STATUS_LEVEL_RECOMMENDATION);
+ }
+
+ @Test
+ public void sendSafetyData_whenPasswordQualityIsManaged_sendsDisabled() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
+ .thenReturn(true);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void sendSafetyData_whenPasswordQualityIsNotManaged_sendsEnabled() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
+ .thenReturn(false);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void sendSafetyData_whenShouldShowGearMenu_sendsGearMenuActionIcon() {
+ whenScreenLockIsEnabled();
+ final Intent launchScreenLockSettings = new Intent(FAKE_ACTION_SCREEN_LOCK_SETTINGS);
+ when(mScreenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent())
+ .thenReturn(launchScreenLockSettings);
+ when(mScreenLockPreferenceDetailsUtils.shouldShowGearMenu()).thenReturn(true);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ final ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(
+ SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ final IconAction iconAction = captor.getValue().getStatus().getIconAction();
+
+ assertThat(iconAction.getIconType()).isEqualTo(IconAction.ICON_TYPE_GEAR);
+ assertThat(iconAction.getPendingIntent().getIntent().getAction())
+ .isEqualTo(FAKE_ACTION_SCREEN_LOCK_SETTINGS);
+ }
+
+ @Test
+ public void sendSafetyData_whenShouldNotShowGearMenu_sendsNoGearMenuActionIcon() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.shouldShowGearMenu()).thenReturn(false);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.getIconAction()).isNull();
+ }
+
+ private void whenScreenLockIsEnabled() {
+ when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ when(mScreenLockPreferenceDetailsUtils.isAvailable()).thenReturn(true);
+ when(mScreenLockPreferenceDetailsUtils.getSummary(anyInt())).thenReturn(SUMMARY);
+
+ Intent launchChooseLockGenericFragment = new Intent(
+ FAKE_ACTION_CHOOSE_LOCK_GENERIC_FRAGMENT);
+ when(mScreenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent())
+ .thenReturn(launchChooseLockGenericFragment);
}
}
diff --git a/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java b/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java
index 3d13b48..f471128 100644
--- a/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java
+++ b/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java
@@ -85,7 +85,7 @@
.thenReturn(mDevicePolicyManager);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
doNothing().when(mContext).startActivity(any());
- when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[] {});
+ when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
@@ -254,6 +254,20 @@
}
@Test
+ public void isLockPatternSecure_patternIsSecure_shouldReturnTrue() {
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+
+ assertThat(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).isTrue();
+ }
+
+ @Test
+ public void isLockPatternSecure_patternIsNotSecure_shouldReturnFalse() {
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false);
+
+ assertThat(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).isFalse();
+ }
+
+ @Test
public void shouldShowGearMenu_patternIsSecure_shouldReturnTrue() {
when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
@@ -275,6 +289,13 @@
}
@Test
+ public void getLaunchScreenLockSettingsIntent_returnsIntent() {
+ final Intent intent = mScreenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent();
+
+ assertFragmentLaunchIntent(intent, ScreenLockSettings.class.getName());
+ }
+
+ @Test
public void openChooseLockGenericFragment_noQuietMode_shouldSendIntent_shouldReturnTrue() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
@@ -282,6 +303,17 @@
assertFragmentLaunchRequested(ChooseLockGeneric.ChooseLockGenericFragment.class.getName());
}
+ @Test
+ public void getLaunchChooseLockGenericFragmentIntent_noQuietMode_returnsIntent() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ final Intent intent =
+ mScreenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent();
+
+ assertFragmentLaunchIntent(intent,
+ ChooseLockGeneric.ChooseLockGenericFragment.class.getName());
+ }
+
private void whenConfigShowUnlockSetOrChangeIsEnabled(boolean enabled) {
final int resId = ResourcesUtils.getResourcesId(
ApplicationProvider.getApplicationContext(), "bool",
@@ -301,6 +333,10 @@
verify(mContext).startActivity(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
+ assertFragmentLaunchIntent(intent, fragmentClassName);
+ }
+
+ private void assertFragmentLaunchIntent(Intent intent, String fragmentClassName) {
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(fragmentClassName);
assertThat(intent.getIntExtra(