Merge "Migrate About phone" into main
diff --git a/Android.bp b/Android.bp
index 087030b..e81ef9e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -79,6 +79,7 @@
"BiometricsSharedLib",
"SystemUIUnfoldLib",
"WifiTrackerLib",
+ "android.hardware.biometrics.flags-aconfig-java",
"android.hardware.dumpstate-V1-java",
"android.hardware.dumpstate-V1.0-java",
"android.hardware.dumpstate-V1.1-java",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cdd9535..6f89a26 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3365,6 +3365,21 @@
android:value="@string/menu_key_battery"/>
</activity>
+ <activity
+ android:name="Settings$PowerUsageAdvancedActivity"
+ android:label="@string/advanced_battery_title"
+ android:exported="true"
+ android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize">
+ <intent-filter android:priority="1">
+ <action android:name="com.android.settings.battery.action.POWER_USAGE_ADVANCED" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.fuelgauge.batteryusage.PowerUsageAdvanced" />
+ <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+ android:value="@string/menu_key_battery"/>
+ </activity>
+
<provider
android:name=".fuelgauge.batteryusage.BatteryUsageContentProvider"
android:enabled="true"
diff --git a/aconfig/catalyst/network_and_internet.aconfig b/aconfig/catalyst/network_and_internet.aconfig
index e8943e6..aa26ce4 100644
--- a/aconfig/catalyst/network_and_internet.aconfig
+++ b/aconfig/catalyst/network_and_internet.aconfig
@@ -23,6 +23,13 @@
}
flag {
+ name: "catalyst_tether_settings"
+ namespace: "android_settings"
+ description: "Flag for Hotspot & tethering"
+ bug: "323791114"
+}
+
+flag {
name: "catalyst_adaptive_connectivity"
namespace: "android_settings"
description: "Flag for Adaptive connectivity"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a58702a..e986ce0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1234,6 +1234,16 @@
<string name="security_settings_fingerprint_bad_calibration_title">Can\u2019t use fingerprint sensor</string>
<!-- Text shown during fingerprint enrollment to indicate bad sensor calibration. [CHAR LIMIT=100] -->
<string name="security_settings_fingerprint_bad_calibration">Visit a repair provider.</string>
+
+ <!-- Key for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_screen_off_unlock_udfps_key" translatable="false">security_settings_screen_off_unlock_udfps</string>
+ <!-- Title for Key for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_screen_off_unlock_udfps_title">Screen-off Fingerprint Unlock</string>
+ <!-- Description for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_screen_off_unlock_udfps_description">Use Fingerprint Unlock even when the screen is off</string>
+ <!-- Description for screen off udfps unlock feature. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_screen_off_unlock_udfps_keywords">Screen-off, Unlock</string>
+
<!-- Title for the section that has additional security settings. [CHAR LIMIT=60] -->
<string name="security_advanced_settings">More security settings</string>
<!-- String for the "More security settings" summary when a work profile is on the device. [CHAR_LIMIT=NONE] -->
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index 4e52cf4..c7e2967 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -28,6 +28,7 @@
<com.android.settingslib.RestrictedPreference
android:key="@string/preference_key_brightness_level"
android:title="@string/brightness"
+ android:persistent="false"
settings:keywords="@string/keywords_display_brightness_level"
settings:useAdminDisabledSummary="true"
settings:userRestriction="no_config_brightness"/>
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index 292f182..1437db6 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -56,7 +56,7 @@
settings:controller="com.android.settings.network.MobileNetworkSummaryController" />
<com.android.settingslib.RestrictedSwitchPreference
- android:key="airplane_mode"
+ android:key="airplane_mode_on"
android:title="@string/airplane_mode"
android:icon="@drawable/ic_airplanemode_active"
android:order="-5"
diff --git a/res/xml/security_settings_fingerprint.xml b/res/xml/security_settings_fingerprint.xml
index 9c8b0a3..32f0924 100644
--- a/res/xml/security_settings_fingerprint.xml
+++ b/res/xml/security_settings_fingerprint.xml
@@ -40,7 +40,16 @@
android:title="@string/security_settings_require_screen_on_to_auth_title"
android:summary="@string/security_settings_require_screen_on_to_auth_description"
settings:keywords="@string/security_settings_require_screen_on_to_auth_keywords"
- settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController" />
+ settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController"
+ settings:isPreferenceVisible="false"/>
+
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="@string/security_settings_screen_off_unlock_udfps_key"
+ android:title="@string/security_settings_screen_off_unlock_udfps_title"
+ android:summary="@string/security_settings_screen_off_unlock_udfps_description"
+ settings:keywords="@string/security_settings_screen_off_unlock_udfps_keywords"
+ settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsScreenOffUnlockUdfpsPreferenceController"
+ settings:isPreferenceVisible="false"/>
</PreferenceCategory>
<PreferenceCategory
diff --git a/src/com/android/settings/MainClearConfirm.java b/src/com/android/settings/MainClearConfirm.java
index c9887e8..5aee5de 100644
--- a/src/com/android/settings/MainClearConfirm.java
+++ b/src/com/android/settings/MainClearConfirm.java
@@ -17,6 +17,8 @@
package com.android.settings;
+import static android.content.Context.MODE_PRIVATE;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.ProgressDialog;
@@ -25,6 +27,7 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -33,6 +36,7 @@
import android.os.UserManager;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -42,6 +46,7 @@
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
+import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.google.android.setupcompat.template.FooterBarMixin;
@@ -90,7 +95,7 @@
} else {
pdbManager = null;
}
-
+ setSimDialogProgressState();
if (shouldWipePersistentDataBlock(pdbManager)) {
new AsyncTask<Void, Void, Void>() {
@@ -128,6 +133,17 @@
} else {
doMainClear();
}
+
+ }
+
+ private void setSimDialogProgressState() {
+ if (getActivity() != null) {
+ final SharedPreferences prefs = getActivity().getSharedPreferences(
+ SubscriptionActionDialogActivity.SIM_ACTION_DIALOG_PREFS, MODE_PRIVATE);
+ prefs.edit().putInt(SubscriptionActionDialogActivity.KEY_PROGRESS_STATE,
+ SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING).apply();
+ Log.d(TAG, "SIM dialog setProgressState: 1");
+ }
}
private ProgressDialog getProgressDialog() {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index b04f3af..e3925fe 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -484,6 +484,7 @@
public static class NetworkDashboardActivity extends SettingsActivity {}
public static class ConnectedDeviceDashboardActivity extends SettingsActivity {}
public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
+ public static class PowerUsageAdvancedActivity extends SettingsActivity { /* empty */ }
public static class StorageDashboardActivity extends SettingsActivity {}
public static class AccountDashboardActivity extends SettingsActivity {}
public static class SystemDashboardActivity extends SettingsActivity {}
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 66397c0..609b96a 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -16,6 +16,8 @@
package com.android.settings;
+import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+
import android.app.Activity;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
@@ -45,6 +47,7 @@
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.flags.Flags;
import com.android.settings.support.actionbar.HelpResourceProvider;
import com.android.settings.widget.HighlightablePreferenceGroupAdapter;
import com.android.settings.widget.LoadingViewController;
@@ -367,9 +370,13 @@
@Override
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
final Bundle arguments = getArguments();
- mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen,
- arguments == null
- ? null : arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY),
+ String key = arguments == null ? null : arguments.getString(EXTRA_FRAGMENT_ARG_KEY);
+ if (Flags.catalyst() && key == null) {
+ Activity activity = getActivity();
+ Intent intent = activity != null ? activity.getIntent() : null;
+ key = intent != null ? intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY) : null;
+ }
+ mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen, key,
mPreferenceHighlighted);
return mAdapter;
}
diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
index 851d763..62c5910 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
@@ -16,8 +16,6 @@
package com.android.settings.applications;
-import static android.webkit.Flags.updateServiceV2;
-
import android.Manifest;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -173,11 +171,9 @@
}
// Keep WebView default package enabled.
- if (updateServiceV2()) {
- String packageName = mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName();
- if (packageName != null) {
- keepEnabledPackages.add(packageName);
- }
+ String packageName = mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName();
+ if (packageName != null) {
+ keepEnabledPackages.add(packageName);
}
keepEnabledPackages.addAll(getEnabledPackageAllowlist());
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index adb6700..c7f7ad7 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -20,6 +20,7 @@
import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE;
import static android.app.admin.DevicePolicyResources.UNDEFINED;
+import static android.hardware.biometrics.Flags.screenOffUnlockUdfps;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import static com.android.settings.Utils.isPrivateProfile;
@@ -207,6 +208,17 @@
context,
KEY_REQUIRE_SCREEN_ON_TO_AUTH
));
+ } else if (screenOffUnlockUdfps()) {
+ controllers.add(
+ new FingerprintUnlockCategoryController(
+ context,
+ KEY_FINGERPRINT_UNLOCK_CATEGORY
+ ));
+ controllers.add(
+ new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(
+ context,
+ KEY_SCREEN_OFF_FINGERPRINT_UNLOCK
+ ));
}
controllers.add(new FingerprintsEnrolledCategoryPreferenceController(context,
KEY_FINGERPRINTS_ENROLLED_CATEGORY));
@@ -233,6 +245,9 @@
@VisibleForTesting
static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH =
"security_settings_require_screen_on_to_auth";
+ @VisibleForTesting
+ static final String KEY_SCREEN_OFF_FINGERPRINT_UNLOCK =
+ "security_settings_screen_off_unlock_udfps";
private static final String KEY_FINGERPRINTS_ENROLLED_CATEGORY =
"security_settings_fingerprints_enrolled";
private static final String KEY_FINGERPRINT_UNLOCK_CATEGORY =
@@ -263,8 +278,11 @@
mFingerprintUnlockCategoryPreferenceController;
private FingerprintSettingsRequireScreenOnToAuthPreferenceController
mRequireScreenOnToAuthPreferenceController;
+ private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController
+ mScreenOffUnlockUdfpsPreferenceController;
private Preference mAddFingerprintPreference;
private RestrictedSwitchPreference mRequireScreenOnToAuthPreference;
+ private RestrictedSwitchPreference mScreenOffUnlockUdfpsPreference;
private PreferenceCategory mFingerprintsEnrolledCategory;
private PreferenceCategory mFingerprintUnlockCategory;
private PreferenceCategory mFingerprintUnlockFooter;
@@ -621,7 +639,7 @@
// This needs to be after setting ids, otherwise
// |mRequireScreenOnToAuthPreferenceController.isChecked| is always checking the primary
// user instead of the user with |mUserId|.
- if (isSfps()) {
+ if (isSfps() || screenOffUnlockUdfps()) {
scrollToPreference(fpPrefKey);
addFingerprintUnlockCategory();
}
@@ -671,33 +689,46 @@
private void addFingerprintUnlockCategory() {
mFingerprintUnlockCategory = findPreference(KEY_FINGERPRINT_UNLOCK_CATEGORY);
- setupFingerprintUnlockCategoryPreferences();
- final Preference restToUnlockPreference = FeatureFactory.getFeatureFactory()
- .getFingerprintFeatureProvider()
- .getSfpsRestToUnlockFeature(getContext())
- .getRestToUnlockPreference(getContext());
- if (restToUnlockPreference != null) {
- // Use custom featured preference if any.
- mRequireScreenOnToAuthPreference.setTitle(restToUnlockPreference.getTitle());
- mRequireScreenOnToAuthPreference.setSummary(restToUnlockPreference.getSummary());
- mRequireScreenOnToAuthPreference.setChecked(
- ((TwoStatePreference) restToUnlockPreference).isChecked());
- mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
- restToUnlockPreference.getOnPreferenceChangeListener());
+ if (isSfps()) {
+ // For both SFPS "screen on to auth" and "rest to unlock"
+ final Preference restToUnlockPreference = FeatureFactory.getFeatureFactory()
+ .getFingerprintFeatureProvider()
+ .getSfpsRestToUnlockFeature(getContext())
+ .getRestToUnlockPreference(getContext());
+ if (restToUnlockPreference != null) {
+ // Use custom featured preference if any.
+ mRequireScreenOnToAuthPreference.setTitle(restToUnlockPreference.getTitle());
+ mRequireScreenOnToAuthPreference.setSummary(
+ restToUnlockPreference.getSummary());
+ mRequireScreenOnToAuthPreference.setChecked(
+ ((TwoStatePreference) restToUnlockPreference).isChecked());
+ mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
+ restToUnlockPreference.getOnPreferenceChangeListener());
+ }
+ setupFingerprintUnlockCategoryPreferencesForScreenOnToAuth();
+ } else if (screenOffUnlockUdfps()) {
+ setupFingerprintUnlockCategoryPreferencesForScreenOffUnlock();
}
updateFingerprintUnlockCategoryVisibility();
}
private void updateFingerprintUnlockCategoryVisibility() {
- final boolean mFingerprintUnlockCategoryAvailable =
+ final boolean fingerprintUnlockCategoryAvailable =
mFingerprintUnlockCategoryPreferenceController.isAvailable();
- if (mFingerprintUnlockCategory.isVisible() != mFingerprintUnlockCategoryAvailable) {
- mFingerprintUnlockCategory.setVisible(
- mFingerprintUnlockCategoryAvailable);
+ if (mFingerprintUnlockCategory.isVisible() != fingerprintUnlockCategoryAvailable) {
+ mFingerprintUnlockCategory.setVisible(fingerprintUnlockCategoryAvailable);
+ }
+ if (mRequireScreenOnToAuthPreferenceController != null) {
+ mRequireScreenOnToAuthPreference.setVisible(
+ mRequireScreenOnToAuthPreferenceController.isAvailable());
+ }
+ if (mScreenOffUnlockUdfpsPreferenceController != null) {
+ mScreenOffUnlockUdfpsPreference.setVisible(
+ mScreenOffUnlockUdfpsPreferenceController.isAvailable());
}
}
- private void setupFingerprintUnlockCategoryPreferences() {
+ private void setupFingerprintUnlockCategoryPreferencesForScreenOnToAuth() {
mRequireScreenOnToAuthPreference = findPreference(KEY_REQUIRE_SCREEN_ON_TO_AUTH);
mRequireScreenOnToAuthPreference.setChecked(
mRequireScreenOnToAuthPreferenceController.isChecked());
@@ -709,9 +740,21 @@
});
}
+ private void setupFingerprintUnlockCategoryPreferencesForScreenOffUnlock() {
+ mScreenOffUnlockUdfpsPreference = findPreference(KEY_SCREEN_OFF_FINGERPRINT_UNLOCK);
+ mScreenOffUnlockUdfpsPreference.setChecked(
+ mScreenOffUnlockUdfpsPreferenceController.isChecked());
+ mScreenOffUnlockUdfpsPreference.setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ final boolean isChecked = ((TwoStatePreference) preference).isChecked();
+ mScreenOffUnlockUdfpsPreferenceController.setChecked(!isChecked);
+ return true;
+ });
+ }
+
private void updatePreferencesAfterFingerprintRemoved() {
updateAddPreference();
- if (isSfps()) {
+ if (isSfps() || screenOffUnlockUdfps()) {
updateFingerprintUnlockCategoryVisibility();
}
updatePreferences();
@@ -955,6 +998,18 @@
}
}
+ } else if (screenOffUnlockUdfps()) {
+ for (AbstractPreferenceController controller : controllers) {
+ if (controller.getPreferenceKey() == KEY_FINGERPRINT_UNLOCK_CATEGORY) {
+ mFingerprintUnlockCategoryPreferenceController =
+ (FingerprintUnlockCategoryController) controller;
+ } else if (controller.getPreferenceKey() == KEY_SCREEN_OFF_FINGERPRINT_UNLOCK) {
+ mScreenOffUnlockUdfpsPreferenceController =
+ (FingerprintSettingsScreenOffUnlockUdfpsPreferenceController)
+ controller;
+ }
+
+ }
}
return controllers;
}
@@ -1070,7 +1125,8 @@
} else if (requestCode == BIOMETRIC_AUTH_REQUEST) {
mBiometricsAuthenticationRequested = false;
if (resultCode != RESULT_OK) {
- if (resultCode == ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT) {
+ if (resultCode
+ == ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT) {
IdentityCheckBiometricErrorDialog
.showBiometricErrorDialogAndFinishActivityOnDismiss(getActivity(),
Utils.BiometricStatus.LOCKOUT);
@@ -1408,7 +1464,7 @@
getContext().getSystemService(DevicePolicyManager.class);
String messageId =
isProfileChallengeUser ? WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE
- : UNDEFINED;
+ : UNDEFINED;
int defaultMessageId = isProfileChallengeUser
? R.string.fingerprint_last_delete_message_profile_challenge
: R.string.fingerprint_last_delete_message;
@@ -1417,7 +1473,7 @@
.setTitle(title)
.setMessage(devicePolicyManager.getResources().getString(
messageId,
- () -> message + "\n\n" + getContext().getString(defaultMessageId)))
+ () -> message + "\n\n" + getContext().getString(defaultMessageId)))
.setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_delete,
new DialogInterface.OnClickListener() {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java
new file mode 100644
index 0000000..5c32d90
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import static android.hardware.biometrics.Flags.screenOffUnlockUdfps;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.Utils;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * Preference controller that controls whether show screen off UDFPS unlock toggle for users to
+ * turn this feature ON or OFF
+ */
+@SearchIndexable
+public class FingerprintSettingsScreenOffUnlockUdfpsPreferenceController
+ extends FingerprintSettingsPreferenceController {
+ private static final String TAG =
+ "FingerprintSettingsScreenOffUnlockUdfpsPreferenceController";
+
+ @VisibleForTesting
+ protected FingerprintManager mFingerprintManager;
+
+ public FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(
+ @NonNull Context context, @NonNull String prefKey) {
+ super(context, prefKey);
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ }
+
+ @Override
+ public boolean isChecked() {
+ if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+ return false;
+ } else if (getRestrictingAdmin() != null) {
+ return false;
+ }
+ final boolean defEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_screen_off_udfps_enabled);
+ final int value = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
+ defEnabled ? 1 : 0 /* config_screen_off_udfps_enabled */,
+ getUserHandle());
+ return value == 1;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
+ isChecked ? 1 : 0,
+ getUserHandle());
+ return true;
+ }
+
+ @Override
+ public void updateState(@NonNull Preference preference) {
+ super.updateState(preference);
+ if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
+ preference.setEnabled(false);
+ } else if (!mFingerprintManager.hasEnrolledTemplates(getUserId())) {
+ preference.setEnabled(false);
+ } else if (getRestrictingAdmin() != null) {
+ preference.setEnabled(false);
+ } else {
+ preference.setEnabled(true);
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ @Override
+ public int getAvailabilityStatus() {
+ if (mFingerprintManager != null
+ && mFingerprintManager.isHardwareDetected()
+ && screenOffUnlockUdfps()
+ && !mFingerprintManager.isPowerbuttonFps()) {
+ return mFingerprintManager.hasEnrolledTemplates(getUserId())
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ } else {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ }
+
+ private int getUserHandle() {
+ return UserHandle.of(getUserId()).getIdentifier();
+ }
+
+ /**
+ * This feature is not directly searchable.
+ */
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {};
+
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
index 674a0df..c949d3d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
@@ -16,6 +16,8 @@
package com.android.settings.biometrics.fingerprint;
+import static android.hardware.biometrics.Flags.screenOffUnlockUdfps;
+
import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
@@ -42,7 +44,7 @@
public int getAvailabilityStatus() {
if (mFingerprintManager != null
&& mFingerprintManager.isHardwareDetected()
- && mFingerprintManager.isPowerbuttonFps()) {
+ && (mFingerprintManager.isPowerbuttonFps() || screenOffUnlockUdfps())) {
return mFingerprintManager.hasEnrolledTemplates(getUserId())
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
} else {
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 117364f..1b92436 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -117,6 +117,7 @@
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings;
import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
+import com.android.settings.fuelgauge.batteryusage.PowerUsageAdvanced;
import com.android.settings.fuelgauge.batteryusage.PowerUsageSummary;
import com.android.settings.gestures.ButtonNavigationSettingsFragment;
import com.android.settings.gestures.DoubleTapPowerSettings;
@@ -276,6 +277,7 @@
DevelopmentSettingsDashboardFragment.class.getName(),
WifiDisplaySettings.class.getName(),
PowerUsageSummary.class.getName(),
+ PowerUsageAdvanced.class.getName(),
AccountSyncSettings.class.getName(),
FaceSettings.class.getName(),
FingerprintSettings.FingerprintSettingsFragment.class.getName(),
@@ -415,6 +417,7 @@
Settings.SoundSettingsActivity.class.getName(),
Settings.StorageDashboardActivity.class.getName(),
Settings.PowerUsageSummaryActivity.class.getName(),
+ Settings.PowerUsageAdvancedActivity.class.getName(),
Settings.AccountDashboardActivity.class.getName(),
Settings.PrivacySettingsActivity.class.getName(),
Settings.SecurityDashboardActivity.class.getName(),
diff --git a/src/com/android/settings/display/BrightnessLevelPreferenceController.java b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
index 96043db..9c4af66 100644
--- a/src/com/android/settings/display/BrightnessLevelPreferenceController.java
+++ b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
@@ -57,6 +57,7 @@
/**
* The top-level preference controller that updates the adaptive brightness level.
*/
+// LINT.IfChange
public class BrightnessLevelPreferenceController extends BasePreferenceController implements
PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
private static final Uri BRIGHTNESS_ADJ_URI;
@@ -187,3 +188,4 @@
return (value - min) / (max - min);
}
}
+// LINT.ThenChange(BrightnessLevelRestrictedPreference.kt)
diff --git a/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt b/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt
new file mode 100644
index 0000000..a412b8c
--- /dev/null
+++ b/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.display
+
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.ACTION_SHOW_BRIGHTNESS_DIALOG
+import android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH
+import android.hardware.display.BrightnessInfo
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Process
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.Settings.System
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.core.SettingsBaseActivity
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.RestrictedPreference
+import com.android.settingslib.datastore.HandlerExecutor
+import com.android.settingslib.datastore.KeyedObserver
+import com.android.settingslib.datastore.SettingsSystemStore
+import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX
+import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN
+import com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat
+import com.android.settingslib.metadata.PreferenceLifecycleContext
+import com.android.settingslib.metadata.PreferenceLifecycleProvider
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceRestrictionProvider
+import com.android.settingslib.metadata.PreferenceSummaryProvider
+import com.android.settingslib.preference.PreferenceBinding
+import com.android.settingslib.transition.SettingsTransitionHelper
+import java.text.NumberFormat
+
+// LINT.IfChange
+class BrightnessLevelRestrictedPreference :
+ PreferenceMetadata,
+ PreferenceBinding,
+ PreferenceRestrictionProvider,
+ PreferenceSummaryProvider,
+ PreferenceLifecycleProvider,
+ Preference.OnPreferenceClickListener {
+
+ private var brightnessObserver: KeyedObserver<String>? = null
+ private var displayListener: DisplayListener? = null
+
+ override val key: String
+ get() = "brightness"
+
+ override val title: Int
+ get() = R.string.brightness
+
+ override val keywords: Int
+ get() = R.string.keywords_display_brightness_level
+
+ override fun getSummary(context: Context) =
+ NumberFormat.getPercentInstance().format(getCurrentBrightness(context))
+
+ override fun isEnabled(context: Context) =
+ !UserManager.get(context)
+ .hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, Process.myUserHandle())
+
+ override fun isRestricted(context: Context) =
+ RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ UserManager.DISALLOW_CONFIG_BRIGHTNESS,
+ UserHandle.myUserId(),
+ ) != null
+
+ override fun createWidget(context: Context) = RestrictedPreference(context)
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ if (preference is RestrictedPreference) preference.useAdminDisabledSummary(true)
+ preference.onPreferenceClickListener = this
+ }
+
+ override fun onStart(context: PreferenceLifecycleContext) {
+ val observer =
+ object : KeyedObserver<String> {
+ override fun onKeyChanged(key: String, reason: Int) {
+ context.notifyPreferenceChange(this@BrightnessLevelRestrictedPreference)
+ }
+ }
+ brightnessObserver = observer
+ SettingsSystemStore.get(context)
+ .addObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, observer, HandlerExecutor.main)
+
+ val listener =
+ object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {}
+
+ override fun onDisplayRemoved(displayId: Int) {}
+
+ override fun onDisplayChanged(displayId: Int) {
+ context.notifyPreferenceChange(this@BrightnessLevelRestrictedPreference)
+ }
+ }
+ displayListener = listener
+ context
+ .getSystemService(DisplayManager::class.java)
+ .registerDisplayListener(
+ listener,
+ HandlerExecutor.main,
+ DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS,
+ )
+ }
+
+ override fun onStop(context: PreferenceLifecycleContext) {
+ brightnessObserver?.let {
+ SettingsSystemStore.get(context).removeObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, it)
+ brightnessObserver = null
+ }
+
+ displayListener?.let {
+ context.getSystemService(DisplayManager::class.java).unregisterDisplayListener(it)
+ displayListener = null
+ }
+ }
+
+ override fun onPreferenceClick(preference: Preference): Boolean {
+ val context = preference.context
+ val intent =
+ Intent(ACTION_SHOW_BRIGHTNESS_DIALOG)
+ .setPackage(Utils.SYSTEMUI_PACKAGE_NAME)
+ .putExtra(
+ SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
+ SettingsTransitionHelper.TransitionType.TRANSITION_NONE,
+ )
+ .putExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, true)
+ val options =
+ ActivityOptions.makeCustomAnimation(
+ context,
+ android.R.anim.fade_in,
+ android.R.anim.fade_out,
+ )
+ context.startActivityForResult(preference.key, intent, 0, options.toBundle())
+ return true
+ }
+
+ private fun getCurrentBrightness(context: Context): Double {
+ val info: BrightnessInfo? = context.display.brightnessInfo
+ val value =
+ info?.run {
+ convertLinearToGammaFloat(brightness, brightnessMinimum, brightnessMaximum)
+ }
+ return getPercentage(value?.toDouble() ?: 0.0)
+ }
+
+ private fun getPercentage(value: Double): Double =
+ when {
+ value > GAMMA_SPACE_MAX -> 1.0
+ value < GAMMA_SPACE_MIN -> 0.0
+ else -> (value - GAMMA_SPACE_MIN) / (GAMMA_SPACE_MAX - GAMMA_SPACE_MIN)
+ }
+}
+// LINT.ThenChange(BrightnessLevelPreferenceController.java)
diff --git a/src/com/android/settings/display/DisplayScreen.kt b/src/com/android/settings/display/DisplayScreen.kt
index bd21e8e..5248367 100644
--- a/src/com/android/settings/display/DisplayScreen.kt
+++ b/src/com/android/settings/display/DisplayScreen.kt
@@ -18,10 +18,13 @@
import android.content.Context
import com.android.settings.DisplaySettings
import com.android.settings.R
+import com.android.settings.Settings.DisplaySettingsActivity
import com.android.settings.display.darkmode.DarkModeScreen
import com.android.settings.flags.Flags
+import com.android.settings.utils.makeLaunchIntent
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceIconProvider
+import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
@@ -48,10 +51,14 @@
override fun fragmentClass() = DisplaySettings::class.java
override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {
+ +BrightnessLevelRestrictedPreference()
+DarkModeScreen.KEY
+PeakRefreshRateSwitchPreference()
}
+ override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
+ makeLaunchIntent(context, DisplaySettingsActivity::class.java, metadata?.key)
+
override fun isAvailable(context: Context) =
context.resources.getBoolean(R.bool.config_show_top_level_display)
diff --git a/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt b/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
index 793b0e2..5302fce 100644
--- a/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
+++ b/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
@@ -18,53 +18,51 @@
import android.content.Context
import android.hardware.display.DisplayManager
import android.provider.DeviceConfig
-import android.util.Log
+import android.provider.Settings.System.PEAK_REFRESH_RATE
import com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE
import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateAmongAllDisplays
import com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay
import com.android.server.display.feature.flags.Flags
import com.android.settings.R
import com.android.settingslib.datastore.HandlerExecutor
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.datastore.KeyedObservableDelegate
+import com.android.settingslib.datastore.SettingsStore
import com.android.settingslib.datastore.SettingsSystemStore
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.SwitchPreference
-import com.android.settingslib.preference.SwitchPreferenceBinding
import kotlin.math.roundToInt
// LINT.IfChange
class PeakRefreshRateSwitchPreference :
- SwitchPreference("peak_refresh_rate", R.string.peak_refresh_rate_title),
- SwitchPreferenceBinding,
+ SwitchPreference(PEAK_REFRESH_RATE, R.string.peak_refresh_rate_title),
PreferenceAvailabilityProvider,
PreferenceSummaryProvider,
PreferenceLifecycleProvider {
private var propertiesChangedListener: DeviceConfig.OnPropertiesChangedListener? = null
- override fun storage(context: Context) = SettingsSystemStore.get(context)
+ override fun storage(context: Context): KeyValueStore =
+ PeakRefreshRateStore(context, SettingsSystemStore.get(context))
override fun isAvailable(context: Context) =
context.resources.getBoolean(R.bool.config_show_smooth_display) &&
- (getPeakRefreshRate(context) > DEFAULT_REFRESH_RATE)
+ context.peakRefreshRate > DEFAULT_REFRESH_RATE
- override fun getSummary(context: Context) =
- context.getString(
- R.string.peak_refresh_rate_summary,
- getPeakRefreshRate(context).roundToInt(),
- )
+ override fun getSummary(context: Context): CharSequence? =
+ context.getString(R.string.peak_refresh_rate_summary, context.peakRefreshRate.roundToInt())
override fun onStart(context: PreferenceLifecycleContext) {
val listener =
- object : DeviceConfig.OnPropertiesChangedListener {
+ DeviceConfig.OnPropertiesChangedListener {
// Got notified if any property has been changed in NAMESPACE_DISPLAY_MANAGER. The
// KEY_PEAK_REFRESH_RATE_DEFAULT value could be added, changed, removed or
// unchanged.
// Just force a UI update for any case.
- override fun onPropertiesChanged(properties: DeviceConfig.Properties) =
- context.notifyPreferenceChange(this@PeakRefreshRateSwitchPreference)
+ context.notifyPreferenceChange(this)
}
propertiesChangedListener = listener
@@ -83,38 +81,74 @@
}
}
- private fun getPeakRefreshRate(context: Context): Float =
- Math.round(
- when {
- Flags.backUpSmoothDisplayAndForcePeakRefreshRate() ->
- findHighestRefreshRateAmongAllDisplays(context)
- else -> findHighestRefreshRateForDefaultDisplay(context)
- }
- )
- .toFloat()
+ @Suppress("UNCHECKED_CAST")
+ private class PeakRefreshRateStore(
+ private val context: Context,
+ private val settingsStore: SettingsStore,
+ ) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
- private fun getDefaultPeakRefreshRate(context: Context): Float {
- var defaultPeakRefreshRate =
- DeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT,
- INVALIDATE_REFRESH_RATE,
- )
+ override fun contains(key: String) = settingsStore.contains(key)
- if (defaultPeakRefreshRate == INVALIDATE_REFRESH_RATE) {
- defaultPeakRefreshRate =
- context.resources
- .getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate)
- .toFloat()
+ override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>): T? {
+ if (key != PEAK_REFRESH_RATE) return super.getDefaultValue(key, valueType)
+ return context.defaultPeakRefreshRate.refreshRateAsBoolean(context) as T
}
- Log.d(TAG, "DeviceConfig getDefaultPeakRefreshRate : $defaultPeakRefreshRate")
- return defaultPeakRefreshRate
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
+ if (key != PEAK_REFRESH_RATE) return null
+ val refreshRate =
+ settingsStore.getFloat(PEAK_REFRESH_RATE) ?: context.defaultPeakRefreshRate
+ return refreshRate.refreshRateAsBoolean(context) as T
+ }
+
+ private fun Float.refreshRateAsBoolean(context: Context) =
+ this.isInfinite() || roundToInt() == context.peakRefreshRate.roundToInt()
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
+ when {
+ key != PEAK_REFRESH_RATE -> {}
+ value == null -> settingsStore.setFloat(PEAK_REFRESH_RATE, null)
+ else -> {
+ val peakRefreshRate =
+ if (value as Boolean) context.refreshRateIfON() else DEFAULT_REFRESH_RATE
+ settingsStore.setFloat(PEAK_REFRESH_RATE, peakRefreshRate)
+ }
+ }
+
+ private fun Context.refreshRateIfON() =
+ when {
+ Flags.backUpSmoothDisplayAndForcePeakRefreshRate() -> Float.POSITIVE_INFINITY
+ else -> peakRefreshRate
+ }
}
companion object {
- private const val TAG: String = "PeakRefreshRateSwitchPreference"
private const val INVALIDATE_REFRESH_RATE: Float = -1f
+
+ private val Context.peakRefreshRate: Float
+ get() =
+ Math.round(
+ when {
+ Flags.backUpSmoothDisplayAndForcePeakRefreshRate() ->
+ findHighestRefreshRateAmongAllDisplays(this)
+ else -> findHighestRefreshRateForDefaultDisplay(this)
+ }
+ )
+ .toFloat()
+
+ private val Context.defaultPeakRefreshRate: Float
+ get() {
+ val defaultPeakRefreshRate =
+ DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT,
+ INVALIDATE_REFRESH_RATE,
+ )
+ if (defaultPeakRefreshRate != INVALIDATE_REFRESH_RATE) return defaultPeakRefreshRate
+ return resources
+ .getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate)
+ .toFloat()
+ }
}
}
// LINT.ThenChange(PeakRefreshRatePreferenceController.java)
diff --git a/src/com/android/settings/network/AirplaneModePreference.kt b/src/com/android/settings/network/AirplaneModePreference.kt
new file mode 100644
index 0000000..5b7903c
--- /dev/null
+++ b/src/com/android/settings/network/AirplaneModePreference.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.provider.Settings.Global.AIRPLANE_MODE_ON
+import androidx.annotation.DrawableRes
+import com.android.settings.R
+import com.android.settingslib.datastore.SettingsGlobalStore
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.SwitchPreference
+
+// LINT.IfChange
+class AirplaneModePreference :
+ SwitchPreference(AIRPLANE_MODE_ON, R.string.airplane_mode),
+ PreferenceAvailabilityProvider {
+
+ override val icon: Int
+ @DrawableRes get() = R.drawable.ic_airplanemode_active
+
+ override fun storage(context: Context) = SettingsGlobalStore.get(context)
+
+ override fun isAvailable(context: Context) =
+ (context.resources.getBoolean(R.bool.config_show_toggle_airplane)
+ && !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+}
+// LINT.ThenChange(AirplaneModePreferenceController.java)
diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java
index d4bd4a3..430feb1 100644
--- a/src/com/android/settings/network/AirplaneModePreferenceController.java
+++ b/src/com/android/settings/network/AirplaneModePreferenceController.java
@@ -52,6 +52,7 @@
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
+// LINT.IfChange
public class AirplaneModePreferenceController extends TogglePreferenceController
implements LifecycleObserver, OnStart, OnResume, OnStop, OnDestroy,
AirplaneModeEnabler.OnAirplaneModeChangedListener {
@@ -217,3 +218,4 @@
}
}
}
+// LINT.ThenChange(AirplaneModePreference.kt)
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index be07809..660fd14 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -26,7 +26,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.provider.SettingsSlicesContract;
import android.security.Credentials;
import android.security.LegacyVpnProfileStore;
import android.util.Log;
@@ -39,7 +38,6 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.settings.R;
-import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.vpn2.VpnInfoPreference;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -50,7 +48,6 @@
import com.android.settingslib.utils.ThreadUtils;
import java.util.Arrays;
-import java.util.List;
import java.util.function.Function;
public class VpnPreferenceController extends AbstractPreferenceController
@@ -87,7 +84,7 @@
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
// Manually set dependencies for Wifi when not toggleable.
if (toggleable == null || !toggleable.contains(Settings.Global.RADIO_WIFI)) {
- preference.setDependency(SettingsSlicesContract.KEY_AIRPLANE_MODE);
+ preference.setDependency(Settings.Global.AIRPLANE_MODE_ON);
}
return preference;
}
diff --git a/src/com/android/settings/network/tether/TetherScreen.kt b/src/com/android/settings/network/tether/TetherScreen.kt
new file mode 100644
index 0000000..20dc4b7
--- /dev/null
+++ b/src/com/android/settings/network/tether/TetherScreen.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.tether
+
+import android.content.Context
+import android.net.TetheringManager
+import com.android.settings.R
+import com.android.settings.flags.Flags
+import com.android.settings.network.TetherPreferenceController
+import com.android.settingslib.TetherUtil
+import com.android.settingslib.Utils
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.ProvidePreferenceScreen
+import com.android.settingslib.metadata.preferenceHierarchy
+import com.android.settingslib.preference.PreferenceScreenCreator
+
+@ProvidePreferenceScreen
+class TetherScreen : PreferenceScreenCreator, PreferenceAvailabilityProvider {
+
+ override val key: String
+ get() = KEY
+
+ override val icon: Int
+ get() = R.drawable.ic_wifi_tethering
+
+ override val keywords: Int
+ get() = R.string.keywords_hotspot_tethering
+
+ override fun getPreferenceTitle(context: Context): CharSequence? =
+ if (TetherPreferenceController.isTetherConfigDisallowed(context)) {
+ context.getText(R.string.tether_settings_title_all)
+ } else {
+ val tetheringManager = context.getSystemService(TetheringManager::class.java)!!
+ context.getText(Utils.getTetheringLabel(tetheringManager))
+ }
+
+ override fun isAvailable(context: Context) = TetherUtil.isTetherAvailable(context)
+
+ override fun isFlagEnabled(context: Context) = Flags.catalystTetherSettings()
+
+ override fun hasCompleteHierarchy() = false
+
+ override fun fragmentClass() = TetherSettings::class.java
+
+ override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
+
+ companion object {
+ const val KEY = "tether_settings"
+ }
+}
diff --git a/src/com/android/settings/network/tether/TetherSettings.java b/src/com/android/settings/network/tether/TetherSettings.java
index 7458514..1db1802 100644
--- a/src/com/android/settings/network/tether/TetherSettings.java
+++ b/src/com/android/settings/network/tether/TetherSettings.java
@@ -48,6 +48,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
@@ -731,4 +732,9 @@
}
updateBluetoothAndEthernetState();
}
+
+ @Override
+ public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
+ return TetherScreen.KEY;
+ }
}
diff --git a/src/com/android/settings/notification/MediaVolumePreference.kt b/src/com/android/settings/notification/MediaVolumePreference.kt
new file mode 100644
index 0000000..13fd029
--- /dev/null
+++ b/src/com/android/settings/notification/MediaVolumePreference.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.content.Context
+import android.media.AudioManager.STREAM_MUSIC
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.datastore.NoOpKeyedObservable
+import com.android.settingslib.metadata.*
+import com.android.settingslib.preference.PreferenceBinding
+
+// LINT.IfChange
+open class MediaVolumePreference :
+ PreferenceMetadata,
+ PreferenceBinding,
+ PersistentPreference<Int>,
+ RangeValue,
+ PreferenceAvailabilityProvider,
+ PreferenceIconProvider,
+ PreferenceRestrictionProvider {
+ override val key: String
+ get() = KEY
+
+ override val title: Int
+ get() = R.string.media_volume_option_title
+
+ override fun getIcon(context: Context) =
+ when {
+ VolumeHelper.isMuted(context, STREAM_MUSIC) -> R.drawable.ic_media_stream_off
+ else -> R.drawable.ic_media_stream
+ }
+
+ override fun isAvailable(context: Context) =
+ context.resources.getBoolean(R.bool.config_show_media_volume)
+
+ override fun isRestricted(context: Context) =
+ RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ context,
+ UserManager.DISALLOW_ADJUST_VOLUME,
+ UserHandle.myUserId(),
+ ) ||
+ RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ UserManager.DISALLOW_ADJUST_VOLUME,
+ UserHandle.myUserId(),
+ ) != null
+
+ override fun storage(context: Context): KeyValueStore {
+ val helper = createAudioHelper(context)
+ return object : NoOpKeyedObservable<String>(), KeyValueStore {
+ override fun contains(key: String) = key == KEY
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+ helper.getStreamVolume(STREAM_MUSIC) as T
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ helper.setStreamVolume(STREAM_MUSIC, value as Int)
+ }
+ }
+ }
+
+ override fun getMinValue(context: Context) =
+ createAudioHelper(context).getMinVolume(STREAM_MUSIC)
+
+ override fun getMaxValue(context: Context) =
+ createAudioHelper(context).getMaxVolume(STREAM_MUSIC)
+
+ override fun createWidget(context: Context) = VolumeSeekBarPreference(context)
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ (preference as VolumeSeekBarPreference).apply {
+ setStream(STREAM_MUSIC)
+ setMuteIcon(R.drawable.ic_media_stream_off)
+ setListener { updateContentDescription(this) }
+ }
+ }
+
+ open fun createAudioHelper(context: Context) = AudioHelper(context)
+
+ fun updateContentDescription(preference: VolumeSeekBarPreference) {
+ when {
+ preference.isMuted() ->
+ preference.updateContentDescription(
+ preference.context.getString(
+ R.string.volume_content_description_silent_mode,
+ preference.title,
+ )
+ )
+ else -> preference.updateContentDescription(preference.title)
+ }
+ }
+
+ companion object {
+ const val KEY = "media_volume"
+ }
+}
+// LINT.ThenChange(MediaVolumePreferenceController.java)
diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java
index e70cf95..431806a 100644
--- a/src/com/android/settings/notification/MediaVolumePreferenceController.java
+++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java
@@ -42,6 +42,7 @@
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.media.MediaOutputConstants;
+// LINT.IfChange
public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceController {
private static final String TAG = "MediaVolumePreCtrl";
private static final String KEY_MEDIA_VOLUME = "media_volume";
@@ -204,3 +205,4 @@
return MediaOutputIndicatorWorker.class;
}
}
+// LINT.ThenChange(MediaVolumePreference.kt)
diff --git a/src/com/android/settings/notification/SeparateRingVolumePreference.kt b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
new file mode 100644
index 0000000..2f696b1
--- /dev/null
+++ b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.app.INotificationManager
+import android.app.NotificationManager
+import android.content.Context
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.media.AudioManager.STREAM_RING
+import android.os.ServiceManager
+import android.os.UserHandle
+import android.os.UserManager.DISALLOW_ADJUST_VOLUME
+import android.os.Vibrator
+import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS
+import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.datastore.KeyValueStore
+import com.android.settingslib.datastore.NoOpKeyedObservable
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.PreferenceIconProvider
+import com.android.settingslib.metadata.PreferenceMetadata
+import com.android.settingslib.metadata.PreferenceRestrictionProvider
+import com.android.settingslib.metadata.RangeValue
+import com.android.settingslib.preference.PreferenceBinding
+
+// LINT.IfChange
+open class SeparateRingVolumePreference :
+ PreferenceMetadata,
+ PreferenceBinding,
+ PersistentPreference<Int>,
+ RangeValue,
+ PreferenceAvailabilityProvider,
+ PreferenceIconProvider,
+ PreferenceRestrictionProvider {
+ override val key: String
+ get() = KEY
+
+ override val title: Int
+ get() = R.string.separate_ring_volume_option_title
+
+ override fun getIcon(context: Context) =
+ when {
+ VolumeHelper.isMuted(context, STREAM_RING) -> getMuteIcon(context)
+ else -> R.drawable.ic_ring_volume
+ }
+
+ override fun isAvailable(context: Context) = !createAudioHelper(context).isSingleVolume()
+
+ override fun isEnabled(context: Context) =
+ !RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ context,
+ DISALLOW_ADJUST_VOLUME,
+ UserHandle.myUserId(),
+ )
+
+ override fun isRestricted(context: Context) =
+ RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ DISALLOW_ADJUST_VOLUME,
+ UserHandle.myUserId(),
+ ) != null
+
+ override fun storage(context: Context): KeyValueStore {
+ val helper = createAudioHelper(context)
+ return object : NoOpKeyedObservable<String>(), KeyValueStore {
+ override fun contains(key: String) = key == KEY
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+ helper.getStreamVolume(STREAM_RING) as T
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ helper.setStreamVolume(STREAM_RING, value as Int)
+ }
+ }
+ }
+
+ override fun getMinValue(context: Context) =
+ createAudioHelper(context).getMinVolume(STREAM_RING)
+
+ override fun getMaxValue(context: Context) =
+ createAudioHelper(context).getMaxVolume(STREAM_RING)
+
+ override fun createWidget(context: Context) = VolumeSeekBarPreference(context)
+
+ override fun bind(preference: Preference, metadata: PreferenceMetadata) {
+ super.bind(preference, metadata)
+ (preference as VolumeSeekBarPreference).apply {
+ setStream(STREAM_RING)
+ setMuteIcon(getMuteIcon(preference.context))
+ setListener { updateContentDescription(this) }
+ setSuppressionText(getSuppressionText(preference.context))
+ }
+ }
+
+ open fun createAudioHelper(context: Context) = AudioHelper(context)
+
+ fun updateContentDescription(preference: VolumeSeekBarPreference) {
+ val context = preference.context
+ val ringerMode = getEffectiveRingerMode(context)
+ when (ringerMode) {
+ RINGER_MODE_VIBRATE ->
+ preference.updateContentDescription(
+ context.getString(R.string.ringer_content_description_vibrate_mode)
+ )
+ RINGER_MODE_SILENT ->
+ preference.updateContentDescription(
+ context.getString(R.string.ringer_content_description_silent_mode)
+ )
+ else -> preference.updateContentDescription(preference.title)
+ }
+ }
+
+ fun getMuteIcon(context: Context): Int {
+ val ringerMode = getEffectiveRingerMode(context)
+ return when (ringerMode) {
+ RINGER_MODE_NORMAL -> R.drawable.ic_ring_volume
+ RINGER_MODE_VIBRATE -> R.drawable.ic_volume_ringer_vibrate
+ else -> R.drawable.ic_ring_volume_off
+ }
+ }
+
+ fun getEffectiveRingerMode(context: Context): Int {
+ val hasVibrator = context.getSystemService(Vibrator::class.java)?.hasVibrator() ?: false
+ val ringerMode = createAudioHelper(context).ringerModeInternal
+ return when {
+ !hasVibrator && ringerMode == RINGER_MODE_VIBRATE -> RINGER_MODE_SILENT
+ else -> ringerMode
+ }
+ }
+
+ fun getSuppressionText(context: Context): String? {
+ val suppressor = NotificationManager.from(context).getEffectsSuppressor()
+ val notificationManager =
+ INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE)
+ )
+ val hints = notificationManager.getHintsFromListenerNoToken()
+ return when {
+ hintsMatch(hints) -> SuppressorHelper.getSuppressionText(context, suppressor)
+ else -> null
+ }
+ }
+
+ private fun hintsMatch(hints: Int) =
+ (hints and HINT_HOST_DISABLE_CALL_EFFECTS) != 0 ||
+ (hints and HINT_HOST_DISABLE_EFFECTS) != 0
+
+ companion object {
+ const val KEY = "separate_ring_volume"
+ }
+}
+// LINT.ThenChange(SeparateRingVolumePreferenceController.java)
diff --git a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
index 91926e3..feb976f 100644
--- a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
+++ b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
@@ -35,6 +35,7 @@
/**
* This slider is used to represent ring volume when ring is separated from notification
*/
+// LINT.IfChange
public class SeparateRingVolumePreferenceController extends
RingerModeAffectedVolumePreferenceController {
@@ -149,3 +150,4 @@
}
}
+// LINT.ThenChange(SeparateRingVolumePreference.kt)
diff --git a/src/com/android/settings/notification/SoundScreen.kt b/src/com/android/settings/notification/SoundScreen.kt
index e36576d..e074301 100644
--- a/src/com/android/settings/notification/SoundScreen.kt
+++ b/src/com/android/settings/notification/SoundScreen.kt
@@ -49,7 +49,9 @@
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(this) {
+ +MediaVolumePreference() order -180
+CallVolumePreference() order -170
+ +SeparateRingVolumePreference() order -155
+DialPadTonePreference() order -50
}
diff --git a/src/com/android/settings/notification/VolumeHelper.kt b/src/com/android/settings/notification/VolumeHelper.kt
new file mode 100644
index 0000000..73e490e
--- /dev/null
+++ b/src/com/android/settings/notification/VolumeHelper.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.app.NotificationManager
+import android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
+import android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+import android.content.Context
+import android.media.AudioManager
+import android.media.AudioManager.*
+import android.provider.Settings.Global.ZEN_MODE_ALARMS
+import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+import android.service.notification.ZenModeConfig
+
+class VolumeHelper {
+ companion object {
+ fun isMuted(context: Context, streamType: Int): Boolean {
+ val audioManager = context.getSystemService(AudioManager::class.java)
+ return audioManager.isStreamMute(streamType) && !isZenMuted(context, streamType)
+ }
+
+ fun isZenMuted(context: Context, streamType: Int): Boolean {
+ val notificationManager = context.getSystemService(NotificationManager::class.java)
+ val zenMode = notificationManager.getZenMode()
+ val notificationPolicy = notificationManager.getConsolidatedNotificationPolicy()
+ val isAllowAlarms =
+ (notificationPolicy.priorityCategories and PRIORITY_CATEGORY_ALARMS) != 0
+ val isAllowMedia =
+ (notificationPolicy.priorityCategories and PRIORITY_CATEGORY_MEDIA) != 0
+ val isAllowRinger =
+ !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(notificationPolicy)
+ return isNotificationOrRingStream(streamType)
+ && zenMode == ZEN_MODE_ALARMS || zenMode == ZEN_MODE_NO_INTERRUPTIONS
+ || (zenMode == ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ && (!isAllowRinger && isNotificationOrRingStream(streamType)
+ || !isAllowMedia && isMediaStream(streamType)
+ || !isAllowAlarms && isAlarmStream(streamType)))
+ }
+
+ private fun isNotificationOrRingStream(streamType: Int) =
+ streamType == STREAM_RING || streamType == STREAM_NOTIFICATION
+
+ private fun isAlarmStream(streamType: Int) = streamType == STREAM_ALARM
+
+ private fun isMediaStream(streamType: Int) = streamType == STREAM_MUSIC
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index d656934..302cf1f 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -377,7 +377,14 @@
setBiometricPromptPropertiesForPrivateProfile(promptInfo);
showBiometricPrompt(promptInfo, effectiveUserId);
launchedBiometric = true;
+ } else if (Flags.privateSpaceBp()) {
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL);
+ setBiometricPromptPropertiesForPrivateProfile(promptInfo);
+ showBiometricPrompt(promptInfo, mUserId);
+ launchedBiometric = true;
} else {
+ // TODO(b/376328272): Remove custom private space behavior
+ mDetails = Utils.getConfirmCredentialStringForUser(this, mUserId, credentialType);
showConfirmCredentials();
launchedCDC = true;
}
diff --git a/src/com/android/settings/utils/IntentUtils.kt b/src/com/android/settings/utils/IntentUtils.kt
new file mode 100644
index 0000000..dbdfa23
--- /dev/null
+++ b/src/com/android/settings/utils/IntentUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.utils
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import com.android.settings.SettingsActivity
+
+/**
+ * Returns the [Intent] to start given settings activity and locate the preference.
+ *
+ * @param context context
+ * @param activityClass activity to start
+ * @param key preference key to locate
+ */
+fun makeLaunchIntent(context: Context, activityClass: Class<out Activity>, key: String?) =
+ Intent(context, activityClass).apply {
+ if (key != null) putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key)
+ }
diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
index 82ef58b..4eb30d2 100644
--- a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
+++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
@@ -30,6 +30,7 @@
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceGroupAdapter;
@@ -64,7 +65,7 @@
private final Context mContext;
private final int mNormalBackgroundRes;
- private final String mHighlightKey;
+ private final @Nullable String mHighlightKey;
private boolean mHighlightRequested;
private int mHighlightPosition = RecyclerView.NO_POSITION;
@@ -101,7 +102,8 @@
screen.setInitialExpandedChildrenCount(initialCount);
}
- public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup, String key,
+ public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup,
+ @Nullable String key,
boolean highlightRequested) {
super(preferenceGroup);
mHighlightKey = key;
diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
index a23ca69..3afca4d 100644
--- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
@@ -38,11 +38,8 @@
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.webkit.Flags;
import com.android.settings.testutils.ApplicationTestUtils;
import com.android.settings.webview.WebViewUpdateServiceWrapper;
@@ -373,7 +370,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_UPDATE_SERVICE_V2)
public void getKeepEnabledPackages_shouldContainWebViewPackage() {
final String testWebViewPackageName = "com.android.webview";
when(mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName())
@@ -383,16 +379,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_UPDATE_SERVICE_V2)
- public void getKeepEnabledPackages_shouldNotContainWebViewPackageIfFlagDisabled() {
- final String testWebViewPackageName = "com.android.webview";
- when(mWebViewUpdateServiceWrapper.getDefaultWebViewPackageName())
- .thenReturn(testWebViewPackageName);
- final Set<String> allowlist = mProvider.getKeepEnabledPackages();
- assertThat(allowlist).doesNotContain(testWebViewPackageName);
- }
-
- @Test
@Config(shadows = {ShadowSmsApplication.class, ShadowDefaultDialerManager.class})
public void getKeepEnabledPackages_shouldContainPackageInstaller() {
final String testDialer = "com.android.test.defaultdialer";
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java
new file mode 100644
index 0000000..7660e88
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUtils.class})
+public class FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private RestrictedSwitchPreference mPreference;
+
+ private Context mContext;
+ private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(eq(Context.FINGERPRINT_SERVICE))).thenReturn(
+ mFingerprintManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+ mController = spy(new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(mContext,
+ "test_key"));
+ ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUtils.reset();
+ }
+
+ @Test
+ public void onPreferenceChange_settingIsUpdated() {
+ boolean state = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, 1) != 0;
+
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mController.onPreferenceChange(mPreference, !state)).isTrue();
+ boolean newState = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, 1) != 0;
+ assertThat(newState).isEqualTo(!state);
+ }
+
+ @Test
+ @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+ public void isAvailable_isEnabled_whenUdfpsHardwareDetected_AndHasEnrolledFingerprints() {
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ false /* isPowerbuttonFps false implies udfps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isEqualTo(true);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+ public void isUnavailable_isDisabled_whenUdfpsHardwareDetected_AndNoEnrolledFingerprints() {
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ false /* isPowerbuttonFps false implies udfps */,
+ false /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+ public void isUnavailable_whenHardwareNotDetected() {
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+ false /* isHardwareDetected */,
+ false /* isPowerbuttonFps false implies udfps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isEqualTo(false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+ public void isUnavailable_onNonUdfpsDevice() {
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ true /* isPowerbuttonFps false implies udfps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ private void configure_hardwareDetected_isUdfps_hasEnrolledTemplates(
+ boolean isHardwareDetected, boolean isPowerbuttonFps, boolean hasEnrolledTemplates) {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(isHardwareDetected);
+ when(mFingerprintManager.isPowerbuttonFps()).thenReturn(isPowerbuttonFps);
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(hasEnrolledTemplates);
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
index 7b6a70e..59af934 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java
@@ -30,12 +30,15 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -49,6 +52,8 @@
@Config(shadows = {ShadowUtils.class})
public class FingerprintSettingsUnlockCategoryControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
private FingerprintManager mFingerprintManager;
@Mock
@@ -59,6 +64,8 @@
private Context mContext;
private FingerprintSettingsRequireScreenOnToAuthPreferenceController mController;
+ private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController mScreenOffUnlockController;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -69,7 +76,12 @@
mController = spy(new FingerprintSettingsRequireScreenOnToAuthPreferenceController(mContext,
"test_key"));
+ mScreenOffUnlockController = spy(
+ new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(mContext,
+ "screen_off_unlock_test_key"));
ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager);
+ ReflectionHelpers.setField(mScreenOffUnlockController, "mFingerprintManager",
+ mFingerprintManager);
}
@After
@@ -90,6 +102,20 @@
}
@Test
+ @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+ public void isAvailable_isEnabled_whenUdfpsHardwareDetected_AndHasEnrolledFingerprints() {
+ assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+ assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+ UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ false /* isPowerbuttonFps false implies udfps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(true);
+ assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
public void isUnavailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() {
assertThat(mController.isAvailable()).isEqualTo(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
@@ -102,7 +128,22 @@
}
@Test
- public void isUnavailable_whenHardwareNotDetected() {
+ @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+ public void isUnavailable_isDisabled_whenUdfpsHardwareDetected_AndNoEnrolledFingerprints() {
+ assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+ assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+ UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ true /* isHardwareDetected */,
+ false /* isPowerbuttonFps false implies udfps */,
+ false /* hasEnrolledTemplates */);
+ assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+ assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+ CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void isUnavailable_whenHardwareNotDetected_onSfpsDevice() {
assertThat(mController.isAvailable()).isFalse();
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
configure_hardwareDetected_isSfps_hasEnrolledTemplates(
@@ -114,6 +155,21 @@
}
@Test
+ @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS)
+ public void isUnavailable_whenHardwareNotDetected_onUdfpsDevice() {
+ assertThat(mScreenOffUnlockController.isAvailable()).isFalse();
+ assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+ UNSUPPORTED_ON_DEVICE);
+ configure_hardwareDetected_isSfps_hasEnrolledTemplates(
+ false /* isHardwareDetected */,
+ false /* isPowerbuttonFps false implies udfps */,
+ true /* hasEnrolledTemplates */);
+ assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false);
+ assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(
+ UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
public void isUnavailable_onNonSfpsDevice() {
assertThat(mController.isAvailable()).isFalse();
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
diff --git a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt
new file mode 100644
index 0000000..67bcc10
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.ContextWrapper
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.res.Resources
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class AirplaneModePreferenceTest {
+
+ private val mockPackageManager = mock<PackageManager>()
+ private val mockResources = mock<Resources>()
+
+ private val context =
+ object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+ override fun getPackageManager(): PackageManager = mockPackageManager
+
+ override fun getResources(): Resources = mockResources
+ }
+
+ private val airplaneModePreference = AirplaneModePreference()
+
+ @Test
+ fun isAvailable_hasConfigAndNoFeatureLeanback_shouldReturnTrue() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+ mockPackageManager.stub { on { hasSystemFeature(FEATURE_LEANBACK) } doReturn false }
+
+ assertThat(airplaneModePreference.isAvailable(context)).isTrue()
+ }
+
+ @Test
+ fun isAvailable_noConfig_shouldReturnFalse() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn false }
+ mockPackageManager.stub { on { hasSystemFeature(FEATURE_LEANBACK) } doReturn false }
+
+ assertThat(airplaneModePreference.isAvailable(context)).isFalse()
+ }
+
+ @Test
+ fun isAvailable_hasFeatureLeanback_shouldReturnFalse() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+ mockPackageManager.stub { on { hasSystemFeature(FEATURE_LEANBACK) } doReturn true }
+
+ assertThat(airplaneModePreference.isAvailable(context)).isFalse()
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt b/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt
new file mode 100644
index 0000000..0eeac43
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.tether
+
+import android.net.TetheringManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.flags.Flags
+import com.android.settings.testutils.shadow.ShadowConnectivityManager
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal
+import com.android.settingslib.Utils
+import com.android.settingslib.preference.CatalystScreenTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.Implementation
+import org.robolectric.annotation.Implements
+
+@RunWith(AndroidJUnit4::class)
+@Config(shadows = [ShadowConnectivityManager::class, ShadowRestrictedLockUtilsInternal::class,
+ ShadowTetheringManager::class])
+class TetherScreenTest : CatalystScreenTestCase() {
+ override val preferenceScreenCreator = TetherScreen()
+
+ override val flagName: String
+ get() = Flags.FLAG_CATALYST_TETHER_SETTINGS
+
+ @Before
+ fun setUp() {
+ ShadowConnectivityManager.getShadow().setTetheringSupported(true)
+ }
+
+ @Test
+ fun key() {
+ assertThat(preferenceScreenCreator.key).isEqualTo(TetherScreen.KEY)
+ }
+
+ @Test
+ fun getPreferenceTitle_tetherConfigDisallowed_shouldShowAll() {
+ ShadowRestrictedLockUtilsInternal.setRestricted(true)
+
+ assertThat(preferenceScreenCreator.getPreferenceTitle(appContext)).isEqualTo(
+ appContext.getString(R.string.tether_settings_title_all))
+ }
+
+ @Test
+ fun getPreferenceTitle_tetherConfigAllowed_shouldShowTetheringLabel() {
+ ShadowRestrictedLockUtilsInternal.setRestricted(false)
+ val tm = appContext.getSystemService(TetheringManager::class.java)
+
+ assertThat(preferenceScreenCreator.getPreferenceTitle(appContext)).isEqualTo(
+ appContext.getText(Utils.getTetheringLabel(tm)))
+ }
+
+ @Test
+ fun isAvailable_tetherIsAvailable_shouldReturnTrue() {
+ ShadowRestrictedLockUtilsInternal.setRestricted(false)
+
+ assertThat(preferenceScreenCreator.isAvailable(appContext)).isTrue()
+ }
+
+ @Test
+ fun isAvailable_tetherIsUnavailable_shouldReturnFalse() {
+ ShadowRestrictedLockUtilsInternal.setRestricted(true)
+
+ assertThat(preferenceScreenCreator.isAvailable(appContext)).isFalse()
+ }
+}
+
+@Implements(TetheringManager::class)
+class ShadowTetheringManager {
+ private val emptyArray = arrayOf<String>()
+
+ @Implementation
+ fun getTetheredIfaces() = emptyArray
+
+ @Implementation
+ fun getTetherableIfaces() = emptyArray
+
+ @Implementation
+ fun getTetherableWifiRegexs() = emptyArray
+
+ @Implementation
+ fun getTetherableUsbRegexs() = emptyArray
+
+ @Implementation
+ fun getTetherableBluetoothRegexs() = emptyArray
+}
diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java
index a25f472..2965acf 100644
--- a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java
@@ -58,6 +58,7 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+// LINT.IfChange
@RunWith(RobolectricTestRunner.class)
@Config(shadows = MediaVolumePreferenceControllerTest.ShadowSliceBackgroundWorker.class)
public class MediaVolumePreferenceControllerTest {
@@ -269,3 +270,4 @@
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
}
+// LINT.ThenChange(MediaVolumePreference.kt)
diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt
new file mode 100644
index 0000000..383acfb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.content.ContextWrapper
+import android.content.res.Resources
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+// LINT.IfChange
+@RunWith(AndroidJUnit4::class)
+class MediaVolumePreferenceTest {
+ private val mockResources = mock<Resources>()
+
+ private val mediaVolumePreference = MediaVolumePreference()
+ private val context =
+ object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+ override fun getResources(): Resources = mockResources
+ }
+
+ @Test
+ fun isAvailable_configTrue_shouldReturnTrue() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
+
+ assertThat(mediaVolumePreference.isAvailable(context)).isTrue()
+ }
+
+ @Test
+ fun isAvailable_configFalse_shouldReturnFalse() {
+ mockResources.stub { on { getBoolean(anyInt()) } doReturn false }
+
+ assertThat(mediaVolumePreference.isAvailable(context)).isFalse()
+ }
+}
+// LINT.ThenChange(MediaVolumePreferenceControllerTest.java)
diff --git a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java
index 2974af6..a243643 100644
--- a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceControllerTest.java
@@ -41,6 +41,7 @@
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
+// LINT.IfChange
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDeviceConfig.class})
public class SeparateRingVolumePreferenceControllerTest {
@@ -108,3 +109,4 @@
}
}
+// LINT.ThenChange(SeparateRingVolumePreferenceTest.kt)
diff --git a/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt
new file mode 100644
index 0000000..21ddd75
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/SeparateRingVolumePreferenceTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification
+
+import android.content.ContextWrapper
+import android.media.AudioManager.RINGER_MODE_NORMAL
+import android.media.AudioManager.RINGER_MODE_SILENT
+import android.media.AudioManager.RINGER_MODE_VIBRATE
+import android.os.Vibrator
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+// LINT.IfChange
+@RunWith(AndroidJUnit4::class)
+class SeparateRingVolumePreferenceTest {
+ private var audioHelper = mock<AudioHelper>()
+ private var vibrator: Vibrator? = null
+ private var ringVolumePreference = SeparateRingVolumePreference()
+
+ private val context =
+ object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+ override fun getSystemService(name: String): Any? =
+ when {
+ name == getSystemServiceName(Vibrator::class.java) -> vibrator
+ else -> super.getSystemService(name)
+ }
+ }
+
+ @Test
+ fun isAvailable_singleVolume_shouldReturnFalse() {
+ audioHelper = mock { on { isSingleVolume } doReturn true }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.isAvailable(context)).isFalse()
+ }
+
+ @Test
+ fun isAvailable_noSingleVolume_shouldReturnTrue() {
+ audioHelper = mock { on { isSingleVolume } doReturn false }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.isAvailable(context)).isTrue()
+ }
+
+ @Test
+ fun getEffectiveRingerMode_noVibratorAndVibrateMode_shouldReturnSilentMode() {
+ vibrator = mock { on { hasVibrator() } doReturn false }
+ audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.getEffectiveRingerMode(context))
+ .isEqualTo(RINGER_MODE_SILENT)
+ }
+
+ @Test
+ fun getEffectiveRingerMode_hasVibratorAndVibrateMode_shouldReturnVibrateMode() {
+ vibrator = mock { on { hasVibrator() } doReturn true }
+ audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.getEffectiveRingerMode(context))
+ .isEqualTo(RINGER_MODE_VIBRATE)
+ }
+
+ @Test
+ fun getEffectiveRingerMode_hasVibratorAndNormalMode_shouldReturnNormalMode() {
+ vibrator = mock { on { hasVibrator() } doReturn true }
+ audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_NORMAL }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.getEffectiveRingerMode(context))
+ .isEqualTo(RINGER_MODE_NORMAL)
+ }
+
+ @Test
+ fun getMuteIcon_normalMode_shouldReturnRingVolumeIcon() {
+ vibrator = mock { on { hasVibrator() } doReturn true }
+ audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_NORMAL }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.getMuteIcon(context)).isEqualTo(R.drawable.ic_ring_volume)
+ }
+
+ @Test
+ fun getMuteIcon_vibrateMode_shouldReturnVibrateIcon() {
+ vibrator = mock { on { hasVibrator() } doReturn true }
+ audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.getMuteIcon(context))
+ .isEqualTo(R.drawable.ic_volume_ringer_vibrate)
+ }
+
+ @Test
+ fun getMuteIcon_silentMode_shouldReturnSilentIcon() {
+ vibrator = mock { on { hasVibrator() } doReturn false }
+ audioHelper = mock { on { ringerModeInternal } doReturn RINGER_MODE_VIBRATE }
+ ringVolumePreference =
+ spy(ringVolumePreference).stub {
+ onGeneric { createAudioHelper(context) } doReturn audioHelper
+ }
+
+ assertThat(ringVolumePreference.getMuteIcon(context))
+ .isEqualTo(R.drawable.ic_ring_volume_off)
+ }
+}
+// LINT.ThenChange(SeparateRingVolumePreferenceControllerTest.java)
diff --git a/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
index ee239ba..7a6a99a 100644
--- a/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
@@ -34,7 +34,6 @@
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.SettingsSlicesContract;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
@@ -102,7 +101,7 @@
controller.displayPreference(mScreen);
- verify(mPreference).setDependency(SettingsSlicesContract.KEY_AIRPLANE_MODE);
+ verify(mPreference).setDependency(Settings.Global.AIRPLANE_MODE_ON);
}
@Test