createPreferenceControllers(
Context context) {
diff --git a/src/com/android/settings/IntervalSeekBar.java b/src/com/android/settings/IntervalSeekBar.java
new file mode 100644
index 00000000000..0997c592ccd
--- /dev/null
+++ b/src/com/android/settings/IntervalSeekBar.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Pure Nexus 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;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.SeekBar;
+
+/**
+ * Custom SeekBar that allows setting both a minimum and maximum value.
+ * This also handles floating point values (to 2 decimal places) through
+ * integer conversions.
+ */
+public class IntervalSeekBar extends SeekBar {
+ private float mMin;
+ private float mMax;
+ private float mDefault;
+ private float mMultiplier;
+
+ public IntervalSeekBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray seekBarType = context.obtainStyledAttributes(attrs,
+ R.styleable.IntervalSeekBar, 0, 0);
+
+ mMax = seekBarType.getFloat(R.styleable.IntervalSeekBar_maxI, 1.5f);
+ mMin = seekBarType.getFloat(R.styleable.IntervalSeekBar_minI, 0.0f);
+ mDefault = seekBarType.getFloat(R.styleable.IntervalSeekBar_defaultValuePure, 1.0f);
+
+ int digits = seekBarType.getInt(R.styleable.IntervalSeekBar_digits, 0);
+ mMultiplier = (float) Math.pow(10, digits);
+
+ if (mMin > mMax) {
+ float temp = mMax;
+ mMax = mMin;
+ mMin = temp;
+ }
+
+ setMax(convertFloatToProgress(mMax));
+ setProgressFloat(mDefault);
+
+ seekBarType.recycle();
+ }
+
+ /*
+ * Converts from SeekBar units (which the SeekBar uses), to scale units
+ * (which are saved).
+ * This operation is the inverse of setFontScaling.
+ */
+ public float getProgressFloat() {
+ return (getProgress() / mMultiplier) + mMin;
+ }
+
+ /*
+ * Converts from scale units (which are saved), to SeekBar units
+ * (which the SeekBar uses). This also sets the SeekBar progress.
+ * This operation is the inverse of getProgressFloat.
+ */
+ public void setProgressFloat(float progress) {
+ setProgress(convertFloatToProgress(progress));
+ }
+
+ private int convertFloatToProgress(float value) {
+ return Math.round((value - mMin) * mMultiplier);
+ }
+
+ public float getMinimum() {
+ return mMin;
+ }
+
+ public float getMaximum() {
+ return mMax;
+ }
+
+ public float getDefault() {
+ return mDefault;
+ }
+
+ public void setMaximum(float max) {
+ mMax = max;
+ setMax(convertFloatToProgress(mMax));
+ }
+
+ public void setMinimum(float min) {
+ mMin = min;
+ }
+}
diff --git a/src/com/android/settings/RingtonePreference.java b/src/com/android/settings/RingtonePreference.java
index 6b9dad7ffbf..0647cd52ae8 100644
--- a/src/com/android/settings/RingtonePreference.java
+++ b/src/com/android/settings/RingtonePreference.java
@@ -40,6 +40,7 @@
*
* If the user chooses the "Default" item, the saved string will be one of
* {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_RINGTONE2_URI},
* {@link System#DEFAULT_NOTIFICATION_URI}, or
* {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent"
* item, the saved string will be an empty string.
@@ -55,6 +56,9 @@ public class RingtonePreference extends Preference {
private static final String TAG = "RingtonePreference";
+ // Default is slot0
+ private int mSlotId = 0;
+
private int mRingtoneType;
private boolean mShowDefault;
private boolean mShowSilent;
@@ -74,8 +78,12 @@ public RingtonePreference(Context context, AttributeSet attrs) {
true);
mShowSilent = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showSilent,
true);
- setIntent(new Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
- .setPackage(context.getString(R.string.config_sound_picker_package_name)));
+ String packageName = context.getString(R.string.config_sound_picker_package_name);
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ if (com.android.internal.util.crdroid.Utils.isPackageInstalled(context, packageName)) {
+ intent.setPackage(packageName);
+ }
+ setIntent(intent);
setUserId(UserHandle.myUserId());
a.recycle();
}
@@ -89,6 +97,25 @@ public int getUserId() {
return mUserId;
}
+ /**
+ * Sets the slot id that this preference belongs to.
+ *
+ * @param slotId The slot id that this preference belongs to.
+ */
+ public void setSlotId(int slotId) {
+ mSlotId = slotId;
+ }
+
+ /**
+ * Returns the slot id that this preference belongs to.
+ *
+ * @return The slot id that this preference belongs to.
+ * @see #setSlotId(int)
+ */
+ public int getSlotId() {
+ return mSlotId;
+ }
+
/**
* Returns the sound type(s) that are shown in the picker.
*
@@ -167,7 +194,7 @@ public void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, mShowDefault);
if (mShowDefault) {
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
- RingtoneManager.getDefaultUri(getRingtoneType()));
+ RingtoneManager.getDefaultUriBySlot(getRingtoneType(), getSlotId()));
}
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, mShowSilent);
diff --git a/src/com/android/settings/SeekBarDialogPreference.java b/src/com/android/settings/SeekBarDialogPreference.java
index d0c8134176b..2428035f269 100644
--- a/src/com/android/settings/SeekBarDialogPreference.java
+++ b/src/com/android/settings/SeekBarDialogPreference.java
@@ -18,12 +18,15 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
+import android.widget.TextView;
import com.android.settingslib.CustomDialogPreferenceCompat;
+import com.android.settings.R;
/**
* Based on frameworks/base/core/java/android/preference/SeekBarDialogPreference.java
@@ -31,6 +34,7 @@
*/
public class SeekBarDialogPreference extends CustomDialogPreferenceCompat {
private final Drawable mMyIcon;
+ private TextView mTextView;
public SeekBarDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -64,6 +68,19 @@ protected void onBindDialogView(View view) {
} else {
iconView.setVisibility(View.GONE);
}
+
+ mTextView = view.findViewById(R.id.text);
+ }
+
+ public void setText(String text) {
+ if (mTextView != null) {
+ if (TextUtils.isEmpty(text)) {
+ mTextView.setVisibility(View.GONE);
+ } else {
+ mTextView.setVisibility(View.VISIBLE);
+ }
+ mTextView.setText(text);
+ }
}
protected static SeekBar getSeekBar(View dialogView) {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 7678338976a..bcf504eebec 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -108,6 +108,7 @@ public static class DisplaySettingsActivity extends SettingsActivity { /* empty
public static class NightDisplaySettingsActivity extends SettingsActivity { /* empty */ }
public static class NightDisplaySuggestionActivity extends NightDisplaySettingsActivity { /* empty */ }
public static class SmartAutoRotateSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class RefreshRateSettingsActivity extends SettingsActivity { /* empty */ }
public static class MyDeviceInfoActivity extends SettingsActivity { /* empty */ }
public static class ModuleLicensesActivity extends SettingsActivity { /* empty */ }
public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }
@@ -515,4 +516,12 @@ public static class ResetMobileNetworkSettingsActivity extends SettingsActivity
public static class HearingDevicesActivity extends SettingsActivity { /* empty */ }
public static class HearingDevicesPairingActivity extends SettingsActivity { /* empty */ }
+
+ public static class crDroidSettingsLayoutActivity extends SettingsActivity {}
+
+ public static class DevRunningServicesActivity extends SettingsActivity { /* empty */ }
+
+ public static class SmartPixelsActivity extends SettingsActivity {}
+
+ public static class PrivateDnsSettingsActivity extends SettingsActivity { /* empty */ }
}
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 66397c0e66f..50cf9407129 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -54,6 +54,8 @@
import com.android.settingslib.search.Indexable;
import com.android.settingslib.widget.LayoutPreference;
+import com.crdroid.settings.preferences.CustomDialogPref;
+
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -559,6 +561,9 @@ public void onDisplayPreferenceDialog(Preference preference) {
} else if (preference instanceof CustomDialogPreferenceCompat) {
f = CustomDialogPreferenceCompat.CustomPreferenceDialogFragment
.newInstance(preference.getKey());
+ } else if (preference instanceof CustomDialogPref) {
+ f = CustomDialogPref.CustomPreferenceDialogFragment
+ .newInstance(preference.getKey());
} else if (preference instanceof CustomEditTextPreferenceCompat) {
f = CustomEditTextPreferenceCompat.CustomPreferenceDialogFragment
.newInstance(preference.getKey());
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index a9144ead6b7..6666eb94a84 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1329,8 +1329,7 @@ public static Bitmap convertCornerRadiusBitmap(@NonNull Context context,
*/
@ColorInt
public static int getHomepageIconColor(Context context) {
- return getColorAttrDefaultColor(
- context, com.android.internal.R.attr.materialColorOnSurface);
+ return getColorAttrDefaultColor(context, android.R.attr.colorAccent);
}
/**
diff --git a/src/com/android/settings/accounts/AccountFeatureProviderImpl.java b/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
index 90b581ba80e..af02ba5122a 100644
--- a/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
+++ b/src/com/android/settings/accounts/AccountFeatureProviderImpl.java
@@ -1,16 +1,20 @@
package com.android.settings.accounts;
import android.accounts.Account;
+import android.accounts.AccountManager;
import android.content.Context;
+import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
+
public class AccountFeatureProviderImpl implements AccountFeatureProvider {
@Override
public String getAccountType() {
- return null;
+ return "com.google";
}
@Override
public Account[] getAccounts(Context context) {
- return new Account[0];
+ return AccountManager.get(context).getAccountsByType("com.google");
}
}
diff --git a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java
index 3b254e9b844..71c71346adb 100644
--- a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java
+++ b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java
@@ -20,6 +20,7 @@
import android.accounts.Account;
import android.accounts.AuthenticatorDescription;
import android.content.ClipData;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -186,9 +187,9 @@ public boolean onPreferenceClick(Preference preference) {
prefIntent, mUserHandle);
} else {
Log.e(TAG,
- "Refusing to launch authenticator intent because"
- + "it exploits Settings permissions: "
- + prefIntent);
+ "Refusing to launch authenticator intent because "
+ + "it exploits Settings permissions: "
+ + prefIntent);
}
return true;
}
@@ -242,13 +243,19 @@ void filterBlockedFragments(@Nullable PreferenceGroup prefs,
}
/**
- * Determines if the supplied Intent is safe. A safe intent is one that is
- * will launch a exported=true activity or owned by the same uid as the
+ * Determines if the supplied Intent is safe. A safe intent is one that
+ * will launch an exported=true activity or owned by the same uid as the
* authenticator supplying the intent.
*/
- private boolean isSafeIntent(PackageManager pm, Intent intent, String acccountType) {
+ @VisibleForTesting
+ boolean isSafeIntent(PackageManager pm, Intent intent, String accountType) {
+ if (TextUtils.equals(intent.getScheme(), ContentResolver.SCHEME_CONTENT)) {
+ Log.e(TAG, "Intent with a content scheme is unsafe.");
+ return false;
+ }
+
AuthenticatorDescription authDesc =
- mAuthenticatorHelper.getAccountTypeDescription(acccountType);
+ mAuthenticatorHelper.getAccountTypeDescription(accountType);
ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mUserHandle.getIdentifier());
if (resolveInfo == null) {
return false;
diff --git a/src/com/android/settings/applications/AppDashboardFragment.java b/src/com/android/settings/applications/AppDashboardFragment.java
index 41160d82316..e4eae5e4c2b 100644
--- a/src/com/android/settings/applications/AppDashboardFragment.java
+++ b/src/com/android/settings/applications/AppDashboardFragment.java
@@ -25,8 +25,10 @@
import com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.security.applock.AppLockSettingsPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
@@ -40,9 +42,11 @@ public class AppDashboardFragment extends DashboardFragment {
private static final String TAG = "AppDashboardFragment";
private static final String ADVANCED_CATEGORY_KEY = "advanced_category";
private static final String ASPECT_RATIO_PREF_KEY = "aspect_ratio_apps";
+ private static final String APP_LOCK_PREF_KEY = "app_lock";
private AppsPreferenceController mAppsPreferenceController;
- private static List buildPreferenceControllers(Context context) {
+ private static List buildPreferenceControllers(Context context,
+ DashboardFragment host, Lifecycle lifecycle) {
final List controllers = new ArrayList<>();
controllers.add(new AppsPreferenceController(context));
@@ -52,6 +56,8 @@ private static List buildPreferenceControllers(Con
new AdvancedAppsPreferenceCategoryController(context, ADVANCED_CATEGORY_KEY);
advancedCategoryController.setChildren(List.of(aspectRatioAppsPreferenceController));
controllers.add(advancedCategoryController);
+ controllers.add(new AppLockSettingsPreferenceController(
+ context, APP_LOCK_PREF_KEY, host, lifecycle));
return controllers;
}
@@ -95,7 +101,7 @@ PreferenceCategoryController getAdvancedAppsPreferenceCategoryController() {
@Override
protected List createPreferenceControllers(Context context) {
- return buildPreferenceControllers(context);
+ return buildPreferenceControllers(context, this /* host */, getSettingsLifecycle());
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
@@ -111,7 +117,7 @@ public List getXmlResourcesToIndex(
@Override
public List createPreferenceControllers(
Context context) {
- return buildPreferenceControllers(context);
+ return buildPreferenceControllers(context, null /* host */, null);
}
};
}
diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java
index 1d774826c2d..363bff816cc 100644
--- a/src/com/android/settings/applications/AppInfoBase.java
+++ b/src/com/android/settings/applications/AppInfoBase.java
@@ -213,7 +213,7 @@ protected void showDialogInner(int id, int moveErrorCode) {
@Override
public void onRunningStateChanged(boolean running) {
- // No op.
+ refreshUi();
}
@Override
diff --git a/src/com/android/settings/applications/AppStateClonedAppsBridge.java b/src/com/android/settings/applications/AppStateClonedAppsBridge.java
index de0251dfffb..238b277c854 100644
--- a/src/com/android/settings/applications/AppStateClonedAppsBridge.java
+++ b/src/com/android/settings/applications/AppStateClonedAppsBridge.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.util.Log;
+import com.android.settings.custom.utils.AppUtils;
import com.android.settings.Utils;
import com.android.settingslib.applications.ApplicationsState;
@@ -42,13 +43,13 @@ public class AppStateClonedAppsBridge extends AppStateBaseBridge{
private final List mAllowedApps;
private List mCloneProfileApps = new ArrayList<>();
private int mCloneUserId;
+ private AppUtils appUtils = new AppUtils();
public AppStateClonedAppsBridge(Context context, ApplicationsState appState,
Callback callback) {
super(appState, callback);
mContext = context;
- mAllowedApps = Arrays.asList(mContext.getResources()
- .getStringArray(com.android.internal.R.array.cloneable_apps));
+ mAllowedApps = appUtils.getCloneableAppListStr(mContext);
}
@Override
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 0f52053b336..ce7efbbdf8a 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -53,7 +53,6 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.deviceinfo.StorageWizardMoveConfirm;
-import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
@@ -207,10 +206,14 @@ void handleClearCacheClick() {
@VisibleForTesting
void handleClearDataClick() {
+ handleClearDataClick(false);
+ }
+
+ private void handleClearDataClick(boolean ignoreAppActivity) {
if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
getActivity(), mAppsControlDisallowedAdmin);
- } else if (mAppEntry.info.manageSpaceActivityName != null) {
+ } else if (!ignoreAppActivity && mAppEntry.info.manageSpaceActivityName != null) {
if (!Utils.isMonkeyRunning()) {
Intent intent = new Intent(Intent.ACTION_DEFAULT);
intent.setClassName(mAppEntry.info.packageName,
@@ -296,15 +299,7 @@ private void initDataButtons() {
(mAppEntry.info.flags & (FLAG_SYSTEM | FLAG_ALLOW_CLEAR_USER_DATA)) == FLAG_SYSTEM;
final boolean appRestrictsClearingData = isNonClearableSystemApp || appHasActiveAdmins;
- final Intent intent = new Intent(Intent.ACTION_DEFAULT);
- if (appHasSpaceManagementUI) {
- intent.setClassName(mAppEntry.info.packageName, mAppEntry.info.manageSpaceActivityName);
- }
- final boolean isManageSpaceActivityAvailable =
- getPackageManager().resolveActivity(intent, 0) != null;
-
- if ((!appHasSpaceManagementUI && appRestrictsClearingData)
- || !isManageSpaceActivityAvailable) {
+ if (appRestrictsClearingData) {
mButtonsPref
.setButton1Text(R.string.clear_user_data_text)
.setButton1Icon(R.drawable.ic_settings_delete)
@@ -313,11 +308,25 @@ private void initDataButtons() {
} else {
mButtonsPref.setButton1Text(R.string.clear_user_data_text);
mButtonsPref.setButton1Icon(R.drawable.ic_settings_delete)
- .setButton1OnClickListener(v -> handleClearDataClick());
+ .setButton1OnClickListener(v -> handleClearDataClick(true));
+ }
+
+ if (appHasSpaceManagementUI) {
+ final Intent intent = new Intent(Intent.ACTION_DEFAULT);
+ intent.setClassName(mAppEntry.info.packageName, mAppEntry.info.manageSpaceActivityName);
+ final boolean isManageSpaceActivityAvailable = getPackageManager().resolveActivity(intent, 0) != null;
+
+ if (isManageSpaceActivityAvailable) {
+ ActionButtonsPreference bp = mButtonsPref;
+ bp.setButton3Text(R.string.automatic_storage_manager_settings);
+ bp.setButton3Icon(R.drawable.ic_settings_open);
+ bp.setButton3OnClickListener(v -> handleClearDataClick(false));
+ }
}
if (mAppsControlDisallowedBySystem || AppUtils.isMainlineModule(mPm, mPackageName)) {
mButtonsPref.setButton1Enabled(false);
+ mButtonsPref.setButton3Enabled(false);
}
}
@@ -362,8 +371,6 @@ private void initiateClearUserData() {
mButtonsPref.setButton1Enabled(false);
// Invoke uninstall or clear user data based on sysPackage
String packageName = mAppEntry.info.packageName;
- DynamicDenylistManager.getInstance(getContext())
- .resetDenylistIfNeeded(packageName, /* force= */ false);
Log.i(TAG, "Clearing user data for package : " + packageName);
if (mClearDataObserver == null) {
mClearDataObserver = new ClearUserDataObserver();
@@ -574,7 +581,7 @@ void updateUiWithSize(AppStorageStats result) {
mButtonsPref.setButton1Enabled(false);
} else {
mButtonsPref.setButton1Enabled(true)
- .setButton1OnClickListener(v -> handleClearDataClick());
+ .setButton1OnClickListener(v -> handleClearDataClick(true));
}
if (cacheSize <= 0 || mCacheCleared) {
mButtonsPref.setButton2Enabled(false);
diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
index 851d763ea94..4e1ec775bf6 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
@@ -27,10 +27,16 @@
import android.content.pm.ComponentInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
import android.location.LocationManager;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemConfigManager;
import android.os.UserManager;
@@ -48,10 +54,16 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.Set;
public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvider {
private static final String TAG = "AppFeatureProviderImpl";
+ private static final boolean DEBUG = false;
+
+ private static final String WELLBEING_APP_PACKAGE = "com.google.android.apps.wellbeing.api";
+ private static final String GET_APP_USAGE_MILLIS = "get_app_usage_millis";
+ private static final String TOTAL_TIME_MILLIS = "total_time_millis";
protected final Context mContext;
private final PackageManager mPm;
@@ -368,4 +380,85 @@ public boolean isLongBackgroundTaskPermissionToggleSupported() {
// converted to a special app-op permission, this should be updated.
return false;
}
+
+ public CharSequence getTimeSpentInApp(String packageName) {
+ try {
+ if (!isPrivilegedApp(WELLBEING_APP_PACKAGE)) {
+ if (DEBUG) {
+ Log.d("ApplicationFeatureProviderImpl", "Not a privileged app.");
+ }
+ return "";
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putString("packageName", packageName);
+
+ Bundle providerCall = mContext.getContentResolver().call(WELLBEING_APP_PACKAGE,
+ GET_APP_USAGE_MILLIS, null, bundle);
+ if (providerCall != null) {
+ if (providerCall.getBoolean("success")) {
+ Bundle dataBundle = providerCall.getBundle("data");
+ if (dataBundle == null) {
+ if (DEBUG) {
+ Log.d("ApplicationFeatureProviderImpl", "data bundle is null.");
+ }
+ return "";
+ }
+ String readableDuration = getReadableDuration(dataBundle.getLong(TOTAL_TIME_MILLIS),
+ R.string.duration_less_than_one_minute);
+ return mContext.getString(R.string.screen_time_summary_usage_today, readableDuration);
+ }
+ }
+ if (DEBUG) {
+ Log.d("ApplicationFeatureProviderImpl", "Provider call unsuccessful");
+ }
+ return "";
+ } catch (Exception e) {
+ Log.w("ApplicationFeatureProviderImpl",
+ "Error getting time spent for app " + packageName, e);
+ return "";
+ }
+ }
+
+ private String getReadableDuration(Long totalTime, int defaultString) {
+ long hours, minutes;
+
+ if (totalTime >= 3_600_000) {
+ hours = totalTime / 3_600_000;
+ totalTime -= 3_600_000 * hours;
+ } else {
+ hours = 0;
+ }
+
+ if (totalTime >= 60_000) {
+ minutes = totalTime / 60_000;
+ totalTime -= 60_000 * minutes;
+ } else {
+ minutes = 0;
+ }
+
+ Locale locale = Locale.getDefault();
+ MeasureFormat measureFormat = MeasureFormat.getInstance(locale, FormatWidth.NARROW);
+
+ if (hours > 0 && minutes > 0) {
+ return measureFormat.formatMeasures(new Measure(hours, MeasureUnit.HOUR),
+ new Measure(minutes, MeasureUnit.MINUTE));
+ } else if (hours > 0) {
+ return measureFormat.formatMeasures(new Measure(hours, MeasureUnit.HOUR));
+ } else if (minutes > 0) {
+ return measureFormat.formatMeasures(new Measure(minutes, MeasureUnit.MINUTE));
+ } else if (totalTime <= 0) {
+ return measureFormat.formatMeasures(new Measure(0, MeasureUnit.MINUTE));
+ }
+
+ return mContext.getResources().getString(defaultString);
+ }
+
+ private boolean isPrivilegedApp(String packageName) {
+ ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(packageName, 0);
+ if (providerInfo != null) {
+ return providerInfo.applicationInfo.isPrivilegedApp();
+ }
+ return false;
+ }
}
diff --git a/src/com/android/settings/applications/ClonedAppsPreferenceController.java b/src/com/android/settings/applications/ClonedAppsPreferenceController.java
index d134ceccd88..38f6dcc80f0 100644
--- a/src/com/android/settings/applications/ClonedAppsPreferenceController.java
+++ b/src/com/android/settings/applications/ClonedAppsPreferenceController.java
@@ -30,6 +30,7 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import com.android.settings.custom.utils.AppUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
@@ -45,6 +46,7 @@ public class ClonedAppsPreferenceController extends BasePreferenceController
implements LifecycleObserver {
private Preference mPreference;
private Context mContext;
+ private AppUtils appUtils = new AppUtils();
public ClonedAppsPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -81,9 +83,7 @@ private void updatePreferenceSummary() {
@Override
protected Integer[] doInBackground(Void... unused) {
// Get list of allowlisted cloneable apps.
- List cloneableApps = Arrays.asList(
- mContext.getResources().getStringArray(
- com.android.internal.R.array.cloneable_apps));
+ List cloneableApps = appUtils.getCloneableAppListStr(mContext);
List primaryUserApps = mContext.getPackageManager()
.getInstalledPackagesAsUser(/* flags*/ 0, UserHandle.myUserId()).stream()
.map(x -> x.packageName).toList();
diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java
index 4d1c166f904..b5f8f115611 100644
--- a/src/com/android/settings/applications/RunningServices.java
+++ b/src/com/android/settings/applications/RunningServices.java
@@ -26,11 +26,9 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.development.DeveloperOptionAwareMixin;
import com.android.settings.widget.LoadingViewController;
-public class RunningServices extends SettingsPreferenceFragment implements
- DeveloperOptionAwareMixin {
+public class RunningServices extends SettingsPreferenceFragment {
private static final int SHOW_RUNNING_SERVICES = 1;
private static final int SHOW_BACKGROUND_PROCESSES = 2;
@@ -112,6 +110,12 @@ private void updateOptionsMenu() {
boolean showingBackground = mRunningProcessesView.mAdapter.getShowBackground();
mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground);
mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground);
+
+ if (!showingBackground) {
+ getActivity().setTitle(com.android.settingslib.R.string.runningservices_settings_title);
+ } else {
+ getActivity().setTitle(R.string.background_processes_settings_title);
+ }
}
@Override
diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
index 1d80099f5c3..fb856f710e9 100644
--- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
@@ -127,6 +127,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
private boolean mFinishing = false;
private boolean mAppsControlDisallowedBySystem;
private boolean mAccessedFromAutoRevoke;
+ private boolean mLaunchedFromAppInfo;
public AppButtonsPreferenceController(SettingsActivity activity,
InstrumentedPreferenceFragment fragment,
@@ -164,10 +165,14 @@ public AppButtonsPreferenceController(SettingsActivity activity,
} else {
mFinishing = true;
}
+ mLaunchedFromAppInfo = activity.getIntent().getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0) == com.android.settings.spa.app.appinfo.AppInfoSettingsProvider.METRICS_CATEGORY;
}
@Override
public int getAvailabilityStatus() {
+ if (mLaunchedFromAppInfo) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
// TODO(b/37313605): Re-enable once this controller supports instant apps
return mFinishing || isInstantApp() || isSystemModule() ? DISABLED_FOR_USER : AVAILABLE;
}
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index f07241a2fe4..cfe0bb67ed5 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -120,6 +120,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
private Optional mSimulateHiddenForTests = Optional.empty();
private boolean mIsWorkProfile = false;
private boolean mSimulateConnectedForTests = false;
+ private boolean mIsPrivateSpace;
public CredentialManagerPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -207,6 +208,7 @@ public void init(
fragment.getSettingsLifecycle().addObserver(this);
mFragmentManager = fragmentManager;
mIsWorkProfile = isWorkProfile;
+ mIsPrivateSpace = fragment instanceof com.android.settings.accounts.AccountPrivateDashboardFragment;
setDelegate(delegate);
verifyReceivedIntent(launchIntent);
@@ -892,6 +894,15 @@ protected int getUser() {
return workProfile.getIdentifier();
}
}
+
+ if (mIsPrivateSpace) {
+ UserHandle user = com.android.settings.privatespace.PrivateSpaceMaintainer
+ .getInstance(mContext).getPrivateProfileHandle();
+ if (user != null) {
+ return user.getIdentifier();
+ }
+ }
+
return UserHandle.myUserId();
}
diff --git a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
index b2b7512d560..6da3e529065 100644
--- a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
+++ b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
@@ -39,7 +39,6 @@
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryOptimizeUtils;
-import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import java.util.Arrays;
import java.util.List;
@@ -156,8 +155,6 @@ public void resetApps() {
}
mAom.resetAllModes();
BatteryOptimizeUtils.resetAppOptimizationMode(mContext, mIPm, mAom);
- DynamicDenylistManager.getInstance(mContext)
- .resetDenylistIfNeeded(/* packageName= */ null, /* force= */ true);
final int[] restrictedUids = mNpm.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
final int currentUserId = ActivityManager.getCurrentUser();
for (int uid : restrictedUids) {
diff --git a/src/com/android/settings/backup/BackupSettingsFragment.java b/src/com/android/settings/backup/BackupSettingsFragment.java
index 7df19f54660..7fcbd63b7ce 100644
--- a/src/com/android/settings/backup/BackupSettingsFragment.java
+++ b/src/com/android/settings/backup/BackupSettingsFragment.java
@@ -42,6 +42,13 @@ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ // update information when we navigate back from TransportActivity
+ displayResourceTilesToScreen(getPreferenceScreen());
+ }
+
/**
* Get the tag string for logging.
*/
diff --git a/src/com/android/settings/backup/BackupSettingsPreferenceController.java b/src/com/android/settings/backup/BackupSettingsPreferenceController.java
index 4e0e3b4e748..3208ae4fe76 100644
--- a/src/com/android/settings/backup/BackupSettingsPreferenceController.java
+++ b/src/com/android/settings/backup/BackupSettingsPreferenceController.java
@@ -30,24 +30,24 @@ public class BackupSettingsPreferenceController extends AbstractPreferenceContro
implements PreferenceControllerMixin {
private static final String BACKUP_SETTINGS = "backup_settings";
private static final String MANUFACTURER_SETTINGS = "manufacturer_backup";
- private Intent mBackupSettingsIntent;
- private CharSequence mBackupSettingsTitle;
- private String mBackupSettingsSummary;
+ private final BackupSettingsHelper settingsHelper;
private Intent mManufacturerIntent;
private String mManufacturerLabel;
public BackupSettingsPreferenceController(Context context) {
super(context);
- BackupSettingsHelper settingsHelper = new BackupSettingsHelper(context);
- mBackupSettingsIntent = settingsHelper.getIntentForBackupSettings();
- mBackupSettingsTitle = settingsHelper.getLabelForBackupSettings();
- mBackupSettingsSummary = settingsHelper.getSummaryForBackupSettings();
+ settingsHelper = new BackupSettingsHelper(context);
mManufacturerIntent = settingsHelper.getIntentProvidedByManufacturer();
mManufacturerLabel = settingsHelper.getLabelProvidedByManufacturer();
}
@Override
public void displayPreference(PreferenceScreen screen) {
+ // we don't get these in the constructor, so we can get updates for them later
+ Intent mBackupSettingsIntent = settingsHelper.getIntentForBackupSettings();
+ CharSequence mBackupSettingsTitle = settingsHelper.getLabelForBackupSettings();
+ String mBackupSettingsSummary = settingsHelper.getSummaryForBackupSettings();
+
Preference backupSettings = screen.findPreference(BACKUP_SETTINGS);
Preference manufacturerSettings = screen.findPreference(MANUFACTURER_SETTINGS);
backupSettings.setIntent(mBackupSettingsIntent);
diff --git a/src/com/android/settings/backup/UserBackupSettingsActivity.java b/src/com/android/settings/backup/UserBackupSettingsActivity.java
index e6d313c61ca..7d24162cfed 100644
--- a/src/com/android/settings/backup/UserBackupSettingsActivity.java
+++ b/src/com/android/settings/backup/UserBackupSettingsActivity.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +29,7 @@
import com.android.settings.R;
import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.utils.InsetUtils;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
@@ -83,6 +85,9 @@ public void onCreate(Bundle savedInstanceState) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Manufacturer provided backup settings, showing the preference screen");
}
+
+ InsetUtils.applyWindowInsetsListener(findViewById(android.R.id.content));
+
// mFragmentManager can be set by {@link #setFragmentManager()} for testing
if (mFragmentManager == null) {
mFragmentManager = getSupportFragmentManager();
diff --git a/src/com/android/settings/backup/transport/Transport.java b/src/com/android/settings/backup/transport/Transport.java
new file mode 100644
index 00000000000..d2fd6e081f9
--- /dev/null
+++ b/src/com/android/settings/backup/transport/Transport.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Calyx Institute
+ *
+ * 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.backup.transport;
+
+class Transport {
+ final String name;
+ final CharSequence dataManagementLabel;
+ final CharSequence destinationString;
+
+ Transport(String name, CharSequence dataManagementLabel, CharSequence destinationString) {
+ this.name = name;
+ this.dataManagementLabel = dataManagementLabel;
+ this.destinationString = destinationString;
+ }
+}
diff --git a/src/com/android/settings/backup/transport/TransportActivity.java b/src/com/android/settings/backup/transport/TransportActivity.java
new file mode 100644
index 00000000000..408ed9d66a2
--- /dev/null
+++ b/src/com/android/settings/backup/transport/TransportActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Calyx Institute
+ * Copyright (C) 2024 The LineageOS 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.backup.transport;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.utils.InsetUtils;
+
+/**
+ * Activity to allow the user to choose the {@link android.app.backup.BackupTransport}.
+ *
+ * Set {@code config_backup_settings_intent} to {@code settings://com.android.settings.backup.transport} to activate.
+ * Don't forget to also set {@code config_backup_settings_label} or else it won't be shown.
+ */
+public class TransportActivity extends FragmentActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ InsetUtils.applyWindowInsetsListener(findViewById(android.R.id.content));
+
+ getSupportFragmentManager().beginTransaction()
+ .replace(android.R.id.content, new TransportFragment())
+ .commit();
+ }
+
+}
diff --git a/src/com/android/settings/backup/transport/TransportFragment.java b/src/com/android/settings/backup/transport/TransportFragment.java
new file mode 100644
index 00000000000..6c6a8ce9957
--- /dev/null
+++ b/src/com/android/settings/backup/transport/TransportFragment.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Calyx Institute
+ *
+ * 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.backup.transport;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import com.android.settings.R;
+import com.android.settings.backup.transport.TransportPreferenceController.OnTransportChangedListener;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TransportFragment extends DashboardFragment implements OnTransportChangedListener {
+
+ private static final String TAG = "TransportFragment";
+
+ /**
+ * Get the tag string for logging.
+ */
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ /**
+ * Get the res id for static preference xml for this fragment.
+ */
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.backup_transport_settings;
+ }
+
+ /**
+ * Get a list of {@link AbstractPreferenceController} for this fragment.
+ */
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ final List controllers = new ArrayList<>();
+ controllers.add(new TransportPreferenceController(context, this));
+ return controllers;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.BACKUP_SETTINGS;
+ }
+
+ @Override
+ public void onTransportChanged(String transportName) {
+ requireActivity().finish();
+ }
+
+}
diff --git a/src/com/android/settings/backup/transport/TransportHelper.java b/src/com/android/settings/backup/transport/TransportHelper.java
new file mode 100644
index 00000000000..1ab5a596054
--- /dev/null
+++ b/src/com/android/settings/backup/transport/TransportHelper.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Calyx Institute
+ *
+ * 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.backup.transport;
+
+import android.app.backup.IBackupManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Helper class for {@link TransportActivity} that interacts with {@link IBackupManager}.
+ */
+class TransportHelper {
+ private static final String TAG = "TransportHelper";
+
+ private final IBackupManager mBackupManager = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+
+ private Context mContext;
+
+ TransportHelper(Context context) {
+ mContext = context;
+ }
+
+ List getTransports() {
+ String[] backupTransports = getBackupTransports();
+ if (backupTransports == null) return Collections.emptyList();
+ ArrayList transports = new ArrayList<>(backupTransports.length);
+ String[] ignoredTransports = mContext.getResources().getStringArray(
+ R.array.config_ignored_backup_transports);
+ for (String name : getBackupTransports()) {
+ boolean ignored = false;
+ for (String ignoredTransport : ignoredTransports) {
+ if (name.equals(ignoredTransport)) ignored = true;
+ }
+ if (ignored) continue;
+ CharSequence label = getLabelFromBackupTransport(name);
+ if (label == null || label.length() == 0) label = name;
+ Transport transport = new Transport(name, label, getSummaryFromBackupTransport(name));
+ transports.add(transport);
+ }
+ return transports;
+ }
+
+ void selectTransport(String name) {
+ try {
+ mBackupManager.selectBackupTransport(name);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error selecting transport: " + name, e);
+ }
+ }
+
+ @Nullable
+ private String[] getBackupTransports() {
+ try {
+ String[] transports = mBackupManager.listAllTransports();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Received all backup transports: " + Arrays.toString(transports));
+ }
+ return transports;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting all backup transports", e);
+ }
+ return null;
+ }
+
+ private CharSequence getLabelFromBackupTransport(String transport) {
+ try {
+ CharSequence label = mBackupManager.getDataManagementLabelForUser(UserHandle.myUserId(), transport);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Received the backup settings label from " + transport + ": " + label);
+ }
+ return label;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting data management label for " + transport, e);
+ }
+ return null;
+ }
+
+ private String getSummaryFromBackupTransport(String transport) {
+ try {
+ String summary = mBackupManager.getDestinationString(transport);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Received the backup settings summary from " + transport + ": " + summary);
+ }
+ return summary;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting data management summary", e);
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/settings/backup/transport/TransportPreferenceController.java b/src/com/android/settings/backup/transport/TransportPreferenceController.java
new file mode 100644
index 00000000000..1dc5a511911
--- /dev/null
+++ b/src/com/android/settings/backup/transport/TransportPreferenceController.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Calyx Institute
+ *
+ * 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.backup.transport;
+
+import android.content.Context;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public class TransportPreferenceController extends AbstractPreferenceController {
+
+ interface OnTransportChangedListener {
+ void onTransportChanged(String transportName);
+ }
+
+ private final OnTransportChangedListener listener;
+ private final TransportHelper transportHelper;
+
+ public TransportPreferenceController(Context context, OnTransportChangedListener listener) {
+ super(context);
+ this.listener = listener;
+ transportHelper = new TransportHelper(context);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ for (Transport transport : transportHelper.getTransports()) {
+ screen.addPreference(getPreferenceForTransport(transport));
+ }
+ }
+
+ private Preference getPreferenceForTransport(Transport transport) {
+ Preference p = new Preference(mContext);
+ p.setTitle(transport.dataManagementLabel);
+ p.setSummary(transport.destinationString);
+ p.setIconSpaceReserved(false);
+ p.setOnPreferenceClickListener(preference -> {
+ transportHelper.selectTransport(transport.name);
+ listener.onTransportChanged(transport.name);
+ return true;
+ });
+ return p;
+ }
+
+ /**
+ * Returns true if preference is available (should be displayed)
+ */
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ /**
+ * Returns the key for this preference.
+ */
+ @Override
+ public String getPreferenceKey() {
+ return null;
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java b/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java
index 5be7c5331d3..88966782448 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java
@@ -28,6 +28,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import com.android.settingslib.Utils;
import com.android.settings.biometrics.BiometricEnrollSidecar;
/**
@@ -66,7 +67,7 @@ public FaceEnrollAnimationDrawable(Context context, ParticleCollection.Listener
mListener = listener;
mSquarePaint = new Paint();
- mSquarePaint.setColor(Color.WHITE);
+ mSquarePaint.setColor(Utils.getColorAttrDefaultColor(context, android.R.attr.colorBackground));
mSquarePaint.setAntiAlias(true);
mCircleCutoutPaint = new Paint();
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
index 32d2a11c6c8..d520cd64f43 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@@ -144,7 +144,7 @@ protected boolean shouldFinishWhenBackgrounded() {
@Override
protected void startEnrollmentInternal() {
- super.startEnrollment();
+ super.startEnrollmentInternal();
mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager()
.findFragmentByTag(TAG_FACE_PREVIEW);
if (mPreviewFragment == null) {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index b32ab4f7350..64cbdc92f86 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -102,6 +102,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
static final String ICON_TOUCH_DIALOG = "fps_icon_touch_dialog";
static final String KEY_STATE_CANCELED = "is_canceled";
static final String KEY_STATE_PREVIOUS_ROTATION = "previous_rotation";
+ static final String KEY_STATE_OVERLAY_SHOWN = "overlay_shown";
private static final int PROGRESS_BAR_MAX = 10000;
@@ -191,6 +192,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
private boolean mHaveShownSfpsLeftEdgeLottie;
private boolean mHaveShownSfpsRightEdgeLottie;
private boolean mShouldShowLottie;
+ private boolean mOverlayShown;
private Animator mHelpAnimation;
@@ -349,6 +351,20 @@ protected void onCreate(Bundle savedInstanceState) {
}
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // Ignore if it's not udfps.
+ if (!mCanAssumeUdfps) {
+ return;
+ }
+ View decorView = getWindow().getDecorView();
+ // Hide the navigation bar and make the layout stable.
+ // so that the content doesn't resize as the navigation bar hides and shows.
+ int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ decorView.setSystemUiVisibility(uiOptions);
+ }
+
private void setHelpAnimation() {
final RelativeLayout progressLottieLayout = findViewById(R.id.progress_lottie);
mHelpAnimation = mSfpsEnrollmentFeature.getHelpAnimator(progressLottieLayout);
@@ -375,12 +391,14 @@ protected boolean shouldStartAutomatically() {
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_STATE_CANCELED, mIsCanceled);
+ outState.putBoolean(KEY_STATE_OVERLAY_SHOWN, mOverlayShown);
outState.putInt(KEY_STATE_PREVIOUS_ROTATION, mPreviousRotation);
}
private void restoreSavedState(Bundle savedInstanceState) {
mRestoring = true;
mIsCanceled = savedInstanceState.getBoolean(KEY_STATE_CANCELED, false);
+ mOverlayShown = savedInstanceState.getBoolean(KEY_STATE_OVERLAY_SHOWN, false);
mPreviousRotation = savedInstanceState.getInt(KEY_STATE_PREVIOUS_ROTATION,
getDisplay().getRotation());
}
@@ -394,6 +412,9 @@ protected void onStart() {
if (mRestoring) {
startIconAnimation();
}
+ if (mOverlayShown) {
+ onUdfpsOverlayShown();
+ }
}
private void setupScreenFoldCallbackWhenNecessary() {
@@ -439,6 +460,7 @@ void onCancelEnrollment(@IdRes int errorMsgId) {
// showErrorDialog() will cause onWindowFocusChanged(false), set mIsCanceled to false
// before showErrorDialog() to prevent that another error dialog is triggered again.
mIsCanceled = true;
+ mOverlayShown = false;
FingerprintErrorDialog.showErrorDialog(this, errorMsgId,
this instanceof SetupFingerprintEnrollEnrolling);
cancelEnrollment();
@@ -452,6 +474,7 @@ void onCancelEnrollment(@IdRes int errorMsgId) {
@Override
protected void onStop() {
if (!isChangingConfigurations()) {
+ mOverlayShown = false;
if (!WizardManagerHelper.isAnySetupWizard(getIntent())
&& !BiometricUtils.isAnyMultiBiometricFlow(this)
&& !mFromSettingsSummary) {
@@ -894,6 +917,7 @@ public void onUdfpsPointerUp(int sensorId) {
public void onUdfpsOverlayShown() {
if (mCanAssumeUdfps) {
findViewById(R.id.udfps_animation_view).setVisibility(View.VISIBLE);
+ mOverlayShown = true;
}
}
@@ -1068,6 +1092,7 @@ public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationEnd(Animator animation) {
+ mOverlayShown = false;
stopIconAnimation();
if (mProgressBar.getProgress() >= PROGRESS_BAR_MAX) {
@@ -1204,10 +1229,19 @@ public static class IconTouchDialog extends InstrumentedDialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final boolean isFrontFacingFps = getResources().getBoolean(
+ R.bool.config_is_front_facing_fps);
+ final boolean isSideMountedFps = getResources().getBoolean(
+ R.bool.config_is_side_fps);
+ final String fpsLocation = getString(isSideMountedFps
+ ? R.string.fingerprint_enroll_touch_dialog_message_side : isFrontFacingFps
+ ? R.string.fingerprint_enroll_touch_dialog_message_front
+ : R.string.fingerprint_enroll_touch_dialog_message_rear);
+
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
R.style.Theme_AlertDialog);
builder.setTitle(R.string.security_settings_fingerprint_enroll_touch_dialog_title)
- .setMessage(R.string.security_settings_fingerprint_enroll_touch_dialog_message)
+ .setMessage(fpsLocation)
.setPositiveButton(R.string.security_settings_fingerprint_enroll_dialog_ok,
new DialogInterface.OnClickListener() {
@Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index aeb0dac97c4..533cfea2af6 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -122,8 +122,23 @@ protected void onCreate(Bundle savedInstanceState) {
mIsReverseDefaultRotation = getApplicationContext().getResources().getBoolean(
com.android.internal.R.bool.config_reverseDefaultRotation);
} else {
+ // Remaining usecases can be either front facing fps or dedicated
+ // side mounted fps (not embedded in the power button)
+ final boolean isFrontFacingFps = getResources().getBoolean(
+ R.bool.config_is_front_facing_fps);
+ final boolean isSideMountedFps = getResources().getBoolean(
+ R.bool.config_is_side_fps);
+ final String fpsLocation = getString(isSideMountedFps
+ ? R.string.fingerprint_enroll_find_sensor_message_side : isFrontFacingFps
+ ? R.string.fingerprint_enroll_find_sensor_message_front
+ : R.string.fingerprint_enroll_find_sensor_message_rear);
+
setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
- setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message);
+ setDescriptionText(fpsLocation);
+ if (isFrontFacingFps) {
+ findViewById(R.id.fingerprint_sensor_location_front_overlay)
+ .setVisibility(View.VISIBLE);
+ }
}
if (savedInstanceState != null) {
mNextClicked = savedInstanceState.getBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked);
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index f6626b213bf..1d781a57ad4 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
@@ -161,6 +162,10 @@ protected void onCreate(Bundle savedInstanceState) {
getNextButton().setEnabled(true);
}));
}
+
+ if (TextUtils.isEmpty(footerLink.getText())) {
+ findViewById(R.id.layout_footer_learn_more).setVisibility(View.GONE);
+ }
}
@Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintLocationAnimationView.java b/src/com/android/settings/biometrics/fingerprint/FingerprintLocationAnimationView.java
index 2916872e73f..08e86c3befa 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintLocationAnimationView.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintLocationAnimationView.java
@@ -27,6 +27,7 @@
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
@@ -54,6 +55,7 @@ public class FingerprintLocationAnimationView extends View implements
private float mPulseRadius;
private ValueAnimator mRadiusAnimator;
private ValueAnimator mAlphaAnimator;
+ private ImageView mOverlayImage;
public FingerprintLocationAnimationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -93,7 +95,11 @@ private float getCenterX() {
}
private float getCenterY() {
- return getHeight() * mFractionCenterY;
+ if (mOverlayImage == null) {
+ mOverlayImage = (ImageView) getRootView().findViewById(
+ R.id.fingerprint_sensor_location);
+ }
+ return mOverlayImage.getHeight() * mFractionCenterY;
}
@Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index adb6700de9b..272a8d29d4e 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -196,7 +196,17 @@ private static List createThePreferenceControllers
if (manager == null || !manager.isHardwareDetected()) {
return null;
}
- if (manager.isPowerbuttonFps()) {
+ List sensorProperties =
+ manager.getSensorPropertiesInternal();
+ boolean isUdfps = false;
+ for (FingerprintSensorPropertiesInternal prop : sensorProperties) {
+ if (prop.isAnyUdfpsType()) {
+ isUdfps = true;
+ break;
+ }
+ }
+ if (!isUdfps && context.getResources().getBoolean(
+ org.lineageos.platform.internal.R.bool.config_fingerprintWakeAndUnlock)) {
controllers.add(
new FingerprintUnlockCategoryController(
context,
@@ -268,6 +278,8 @@ private static class FooterColumn {
private PreferenceCategory mFingerprintsEnrolledCategory;
private PreferenceCategory mFingerprintUnlockCategory;
private PreferenceCategory mFingerprintUnlockFooter;
+ private boolean mFingerprintWakeAndUnlock;
+ private boolean mProximityCheckOnFingerprintUnlock;
private FingerprintManager mFingerprintManager;
private FingerprintUpdater mFingerprintUpdater;
@@ -445,6 +457,10 @@ public void onCreate(Bundle savedInstanceState) {
mFingerprintManager = Utils.getFingerprintManagerOrNull(activity);
mFingerprintUpdater = new FingerprintUpdater(activity, mFingerprintManager);
mSensorProperties = mFingerprintManager.getSensorPropertiesInternal();
+ mFingerprintWakeAndUnlock = getContext().getResources().getBoolean(
+ org.lineageos.platform.internal.R.bool.config_fingerprintWakeAndUnlock);
+ mProximityCheckOnFingerprintUnlock = getContext().getResources().getBoolean(
+ org.lineageos.platform.internal.R.bool.config_proximityCheckOnFpsUnlock);
mToken = getIntent().getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
@@ -516,10 +532,17 @@ public void onCreate(Bundle savedInstanceState) {
private void updateFooterColumns(@NonNull Activity activity) {
final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
activity, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId);
- final Intent helpIntent = HelpUtils.getHelpIntent(
- activity, getString(getHelpResource()), activity.getClass().getName());
- final View.OnClickListener learnMoreClickListener = (v) ->
- activity.startActivityForResult(helpIntent, 0);
+ final Intent helpIntent;
+ final View.OnClickListener learnMoreClickListener;
+ if (getHelpResource() != 0) {
+ helpIntent = HelpUtils.getHelpIntent(
+ activity, getString(getHelpResource()), activity.getClass().getName());
+ learnMoreClickListener = (v) ->
+ activity.startActivityForResult(helpIntent, 0);
+ } else {
+ helpIntent = null;
+ learnMoreClickListener = null;
+ }
mFooterColumns.clear();
if (admin != null) {
@@ -541,11 +564,13 @@ private void updateFooterColumns(@NonNull Activity activity) {
column2.mTitle = getText(
R.string.security_fingerprint_disclaimer_lockscreen_disabled_2
);
- if (isSfps()) {
- column2.mLearnMoreOverrideText = getText(
- R.string.security_settings_fingerprint_settings_footer_learn_more);
+ if (helpIntent != null) {
+ if (!isUdfps() && mFingerprintWakeAndUnlock) {
+ column2.mLearnMoreOverrideText = getText(
+ R.string.security_settings_fingerprint_settings_footer_learn_more);
+ }
+ column2.mLearnMoreClickListener = learnMoreClickListener;
}
- column2.mLearnMoreClickListener = learnMoreClickListener;
mFooterColumns.add(column2);
} else {
final FooterColumn column = new FooterColumn();
@@ -553,17 +578,23 @@ private void updateFooterColumns(@NonNull Activity activity) {
? R.string.private_space_fingerprint_enroll_introduction_message
: R.string.security_settings_fingerprint_enroll_introduction_v3_message,
DeviceHelper.getDeviceName(getActivity()));
- column.mLearnMoreClickListener = learnMoreClickListener;
- column.mLearnMoreOverrideText = getText(
- R.string.security_settings_fingerprint_settings_footer_learn_more);
+ if (helpIntent != null) {
+ column.mLearnMoreClickListener = learnMoreClickListener;
+ column.mLearnMoreOverrideText = getText(
+ R.string.security_settings_fingerprint_settings_footer_learn_more);
+ }
mFooterColumns.add(column);
}
}
private boolean isUdfps() {
- for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
- if (prop.isAnyUdfpsType()) {
- return true;
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
+ if (mFingerprintManager != null) {
+ mSensorProperties = mFingerprintManager.getSensorPropertiesInternal();
+ for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
+ if (prop.isAnyUdfpsType()) {
+ return true;
+ }
}
}
return false;
@@ -621,7 +652,7 @@ private void addFingerprintPreferences(PreferenceGroup root) {
// 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 (!isUdfps() && mFingerprintWakeAndUnlock) {
scrollToPreference(fpPrefKey);
addFingerprintUnlockCategory();
}
@@ -671,7 +702,9 @@ private void setupAddFingerprintPreference() {
private void addFingerprintUnlockCategory() {
mFingerprintUnlockCategory = findPreference(KEY_FINGERPRINT_UNLOCK_CATEGORY);
- setupFingerprintUnlockCategoryPreferences();
+ if (mRequireScreenOnToAuthPreferenceController != null) {
+ setupFingerprintUnlockCategoryPreferences();
+ }
final Preference restToUnlockPreference = FeatureFactory.getFeatureFactory()
.getFingerprintFeatureProvider()
.getSfpsRestToUnlockFeature(getContext())
@@ -685,7 +718,9 @@ private void addFingerprintUnlockCategory() {
mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
restToUnlockPreference.getOnPreferenceChangeListener());
}
- updateFingerprintUnlockCategoryVisibility();
+ if (mFingerprintUnlockCategoryPreferenceController != null) {
+ updateFingerprintUnlockCategoryVisibility();
+ }
}
private void updateFingerprintUnlockCategoryVisibility() {
@@ -707,11 +742,15 @@ private void setupFingerprintUnlockCategoryPreferences() {
mRequireScreenOnToAuthPreferenceController.setChecked(!isChecked);
return true;
});
+ if (mProximityCheckOnFingerprintUnlock) {
+ mRequireScreenOnToAuthPreference.setSummary(R.string.
+ security_settings_require_screen_on_to_auth_with_proximity_description);
+ }
}
private void updatePreferencesAfterFingerprintRemoved() {
updateAddPreference();
- if (isSfps()) {
+ if (!isUdfps() && mFingerprintWakeAndUnlock) {
updateFingerprintUnlockCategoryVisibility();
}
updatePreferences();
@@ -943,7 +982,8 @@ protected List createPreferenceControllers(Context
private List buildPreferenceControllers(Context context) {
final List controllers =
createThePreferenceControllers(context);
- if (isSfps()) {
+ if (!isUdfps() && context.getResources().getBoolean(
+ org.lineageos.platform.internal.R.bool.config_fingerprintWakeAndUnlock)) {
for (AbstractPreferenceController controller : controllers) {
if (controller.getPreferenceKey() == KEY_FINGERPRINT_UNLOCK_CATEGORY) {
mFingerprintUnlockCategoryPreferenceController =
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
index 58bc7e34202..f0b5c60ba7a 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsRequireScreenOnToAuthPreferenceController.java
@@ -61,7 +61,7 @@ public boolean isChecked() {
getUserHandle());
if (toReturn == -1) {
toReturn = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_performantAuthDefault) ? 1 : 0;
+ org.lineageos.platform.internal.R.bool.config_fingerprintWakeAndUnlock) ? 1 : 0;
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED, toReturn, getUserHandle());
}
@@ -96,8 +96,7 @@ public void updateState(Preference preference) {
@Override
public int getAvailabilityStatus() {
if (mFingerprintManager != null
- && mFingerprintManager.isHardwareDetected()
- && mFingerprintManager.isPowerbuttonFps()) {
+ && mFingerprintManager.isHardwareDetected()) {
return mFingerprintManager.hasEnrolledTemplates(getUserId())
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
} else {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
index 674a0dfa758..acca88fc0af 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java
@@ -41,8 +41,7 @@ public FingerprintUnlockCategoryController(Context context, String key) {
@Override
public int getAvailabilityStatus() {
if (mFingerprintManager != null
- && mFingerprintManager.isHardwareDetected()
- && mFingerprintManager.isPowerbuttonFps()) {
+ && mFingerprintManager.isHardwareDetected()) {
return mFingerprintManager.hasEnrolledTemplates(getUserId())
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
} else {
diff --git a/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java
new file mode 100644
index 00000000000..387390a9568
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothTimeoutPreferenceController.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020-2021 The Calyx Institute
+ *
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class BluetoothTimeoutPreferenceController extends BasePreferenceController implements
+ PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+ private static final String TAG = "BluetoothTimeoutPrefCtrl";
+
+ public static final int FALLBACK_BLUETOOTH_TIMEOUT_VALUE = 0;
+
+ private final String mBluetoothTimeoutKey;
+
+ protected BluetoothAdapter mBluetoothAdapter;
+
+ public BluetoothTimeoutPreferenceController(Context context, String key) {
+ super(context, key);
+ mBluetoothTimeoutKey = key;
+
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (mBluetoothAdapter == null) {
+ Log.e(TAG, "Bluetooth is not supported on this device");
+ return;
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mBluetoothAdapter != null) {
+ return UserManager.get(mContext).isAdminUser() ? AVAILABLE : DISABLED_FOR_USER;
+ }
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return mBluetoothTimeoutKey;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final ListPreference timeoutListPreference = (ListPreference) preference;
+ final long currentTimeout = Settings.Global.getLong(mContext.getContentResolver(),
+ Settings.Global.BLUETOOTH_OFF_TIMEOUT, FALLBACK_BLUETOOTH_TIMEOUT_VALUE);
+ timeoutListPreference.setValue(String.valueOf(currentTimeout));
+ updateTimeoutPreferenceDescription(timeoutListPreference,
+ Long.parseLong(timeoutListPreference.getValue()));
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ try {
+ long value = Long.parseLong((String) newValue);
+ Settings.Global.putLong(mContext.getContentResolver(),
+ Settings.Global.BLUETOOTH_OFF_TIMEOUT, value);
+ updateTimeoutPreferenceDescription((ListPreference) preference, value);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "could not persist bluetooth timeout setting", e);
+ }
+ return true;
+ }
+
+ public static CharSequence getTimeoutDescription(
+ long currentTimeout, CharSequence[] entries, CharSequence[] values) {
+ if (currentTimeout < 0 || entries == null || values == null
+ || values.length != entries.length) {
+ return null;
+ }
+
+ for (int i = 0; i < values.length; i++) {
+ long timeout = Long.parseLong(values[i].toString());
+ if (currentTimeout == timeout) {
+ return entries[i];
+ }
+ }
+ return null;
+ }
+
+ private void updateTimeoutPreferenceDescription(ListPreference preference,
+ long currentTimeout) {
+ final CharSequence[] entries = preference.getEntries();
+ final CharSequence[] values = preference.getEntryValues();
+ final CharSequence timeoutDescription = getTimeoutDescription(
+ currentTimeout, entries, values);
+ String summary = "";
+ if (timeoutDescription != null) {
+ if (currentTimeout != 0)
+ summary = mContext.getString(R.string.bluetooth_timeout_summary_auto,
+ timeoutDescription);
+ else
+ summary = mContext.getString(R.string.bluetooth_timeout_summary);
+ }
+ preference.setSummary(summary);
+ }
+}
diff --git a/src/com/android/settings/bluetooth/DevicePickerActivity.java b/src/com/android/settings/bluetooth/DevicePickerActivity.java
index a4f025b267a..c58ddcff376 100644
--- a/src/com/android/settings/bluetooth/DevicePickerActivity.java
+++ b/src/com/android/settings/bluetooth/DevicePickerActivity.java
@@ -20,15 +20,14 @@
import android.os.Bundle;
-import androidx.fragment.app.FragmentActivity;
-
import com.android.settings.R;
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
/**
* Activity for Bluetooth device picker dialog. The device picker logic
* is implemented in the {@link BluetoothPairingDetail} fragment.
*/
-public final class DevicePickerActivity extends FragmentActivity {
+public final class DevicePickerActivity extends CollapsingToolbarBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java b/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java
index ee0021ec951..f16dd378db5 100644
--- a/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java
+++ b/src/com/android/settings/connecteddevice/NfcAndPaymentFragmentController.java
@@ -16,21 +16,47 @@
package com.android.settings.connecteddevice;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.os.UserManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnStop;
/**
* Controller that used to show NFC and payment features
*/
-public class NfcAndPaymentFragmentController extends BasePreferenceController {
+public class NfcAndPaymentFragmentController extends BasePreferenceController
+ implements LifecycleObserver, OnResume, OnStop {
private final NfcAdapter mNfcAdapter;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
+ private final IntentFilter mIntentFilter;
+ private Preference mPreference;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mPreference == null) {
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(action)) {
+ refreshSummary(mPreference);
+ }
+ }
+ };
public NfcAndPaymentFragmentController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -38,6 +64,15 @@ public NfcAndPaymentFragmentController(Context context, String preferenceKey) {
mPackageManager = context.getPackageManager();
mUserManager = context.getSystemService(UserManager.class);
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
+
+ mIntentFilter = isNfcAvailable()
+ ? new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED) : null;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
}
@Override
@@ -61,4 +96,26 @@ public CharSequence getSummary() {
}
return null;
}
+
+ @Override
+ public void onStop() {
+ if (!isNfcAvailable()) {
+ return;
+ }
+
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public void onResume() {
+ if (!isNfcAvailable()) {
+ return;
+ }
+
+ mContext.registerReceiver(mReceiver, mIntentFilter);
+ }
+
+ private boolean isNfcAvailable() {
+ return mNfcAdapter != null;
+ }
}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbBackend.java b/src/com/android/settings/connecteddevice/usb/UsbBackend.java
index d194499c88c..6e643f42fea 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbBackend.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbBackend.java
@@ -165,6 +165,30 @@ public boolean areAllRolesSupported() {
&& mPortStatus.isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_HOST);
}
+ public boolean isSingleDataRoleSupported() {
+ return mPort != null && mPortStatus != null
+ && ((!mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+ && !mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_HOST))
+ || (!mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE)
+ && !mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_DEVICE)));
+ }
+
+ public boolean isSinglePowerRoleSupported() {
+ return mPort != null && mPortStatus != null
+ && ((!mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE)
+ && !mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST))
+ || (!mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_DEVICE)
+ && !mPortStatus
+ .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_HOST)));
+ }
+
public static String usbFunctionsToString(long functions) {
// TODO replace with UsbManager.usbFunctionsToString once supported by Roboelectric
return Long.toBinaryString(functions);
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java b/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java
index 5f3e4cddafe..b5a9e5a9317 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java
@@ -33,7 +33,6 @@
import com.android.settings.R;
import com.android.settings.Utils;
-import com.android.settings.development.DeveloperOptionAwareMixin;
import com.android.settings.widget.RadioButtonPickerFragment;
import com.android.settingslib.widget.CandidateInfo;
import com.android.settingslib.widget.FooterPreference;
@@ -46,8 +45,7 @@
/**
* Provides options for selecting the default USB mode.
*/
-public class UsbDefaultFragment extends RadioButtonPickerFragment implements
- DeveloperOptionAwareMixin {
+public class UsbDefaultFragment extends RadioButtonPickerFragment {
private static final String TAG = "UsbDefaultFragment";
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java
index 8782c796140..8c291abd205 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java
@@ -115,7 +115,8 @@ public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
@Override
public boolean isAvailable() {
- return !Utils.isMonkeyRunning();
+ return !Utils.isMonkeyRunning()
+ && !mUsbBackend.isSingleDataRoleSupported();
}
@Override
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
index f8cabbc08fb..31d5770d75f 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
@@ -106,7 +106,6 @@ protected List createPreferenceControllers(Context
private static List createControllerList(Context context,
UsbBackend usbBackend, UsbDetailsFragment fragment) {
List ret = new ArrayList<>();
- ret.add(new UsbDetailsHeaderController(context, fragment, usbBackend));
ret.add(new UsbDetailsDataRoleController(context, fragment, usbBackend));
ret.add(new UsbDetailsFunctionsController(context, fragment, usbBackend));
ret.add(new UsbDetailsPowerRoleController(context, fragment, usbBackend));
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java
deleted file mode 100644
index 39d7c751758..00000000000
--- a/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderController.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2018 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.connecteddevice.usb;
-
-import android.content.Context;
-
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settingslib.widget.LayoutPreference;
-
-/**
- * This class adds a header with device name.
- */
-public class UsbDetailsHeaderController extends UsbDetailsController {
- private static final String KEY_DEVICE_HEADER = "usb_device_header";
-
- private EntityHeaderController mHeaderController;
-
- public UsbDetailsHeaderController(Context context, UsbDetailsFragment fragment,
- UsbBackend backend) {
- super(context, fragment, backend);
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- final LayoutPreference headerPreference = screen.findPreference(KEY_DEVICE_HEADER);
- mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
- headerPreference.findViewById(R.id.entity_header));
- }
-
-
- @Override
- protected void refresh(boolean connected, long functions, int powerRole, int dataRole) {
- mHeaderController.setLabel(mContext.getString(R.string.usb_pref));
- mHeaderController.setIcon(mContext.getDrawable(R.drawable.ic_usb));
- mHeaderController.done(true /* rebindActions */);
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_DEVICE_HEADER;
- }
-}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java
index f00435a0cab..9a14f3cde86 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java
@@ -126,7 +126,8 @@ public boolean onPreferenceClick(Preference preference) {
@Override
public boolean isAvailable() {
- return !Utils.isMonkeyRunning();
+ return !Utils.isMonkeyRunning()
+ && !mUsbBackend.isSinglePowerRoleSupported();
}
@Override
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 734bddcbb2a..cbcf57ab730 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -46,6 +46,7 @@
import com.android.settings.applications.AppDashboardFragment;
import com.android.settings.applications.ProcessStatsSummary;
import com.android.settings.applications.ProcessStatsUi;
+import com.android.settings.applications.RunningServices;
import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.appcompat.UserAspectRatioDetails;
import com.android.settings.applications.appinfo.AlarmsAndRemindersDetails;
@@ -109,6 +110,7 @@
import com.android.settings.display.ColorContrastFragment;
import com.android.settings.display.NightDisplaySettings;
import com.android.settings.display.ScreenTimeoutSettings;
+import com.android.settings.display.RefreshRateSettings;
import com.android.settings.display.SmartAutoRotatePreferenceFragment;
import com.android.settings.display.darkmode.DarkModeSettingsFragment;
import com.android.settings.dream.DreamSettings;
@@ -145,6 +147,7 @@
import com.android.settings.network.MobileNetworkListFragment;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.network.NetworkProviderSettings;
+import com.android.settings.network.PrivateDnsSettings;
import com.android.settings.network.apn.ApnEditor;
import com.android.settings.network.apn.ApnSettings;
import com.android.settings.network.telephony.CellularSecuritySettingsFragment;
@@ -207,6 +210,9 @@
import com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2;
import com.android.settings.wifi.tether.WifiTetherSettings;
+import com.crdroid.settings.crDroidSettingsLayout;
+import com.crdroid.settings.fragments.ui.SmartPixels;
+
public class SettingsGateway {
/**
@@ -347,6 +353,7 @@ public class SettingsGateway {
MainClearConfirm.class.getName(),
ResetDashboardFragment.class.getName(),
NightDisplaySettings.class.getName(),
+ RefreshRateSettings.class.getName(),
ManageDomainUrls.class.getName(),
AutomaticStorageManagerSettings.class.getName(),
StorageDashboardFragment.class.getName(),
@@ -401,7 +408,11 @@ public class SettingsGateway {
AccessibilityHearingAidsFragment.class.getName(),
HearingDevicePairingFragment.class.getName(),
ZenModesListFragment.class.getName(),
- ZenModeFragment.class.getName()
+ ZenModeFragment.class.getName(),
+ crDroidSettingsLayout.class.getName(),
+ RunningServices.class.getName(),
+ SmartPixels.class.getName(),
+ PrivateDnsSettings.class.getName(),
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
@@ -448,5 +459,9 @@ public class SettingsGateway {
UserBackupSettingsActivity.class.getName(),
Settings.MemtagPageActivity.class.getName(),
Settings.NavigationModeSettingsActivity.class.getName(),
+ Settings.crDroidSettingsLayoutActivity.class.getName(),
+ Settings.DevRunningServicesActivity.class.getName(),
+ Settings.SmartPixelsActivity.class.getName(),
+ Settings.PrivateDnsSettingsActivity.class.getName(),
};
}
diff --git a/src/com/android/settings/custom/privacy/ClipboardAutoClearFragment.java b/src/com/android/settings/custom/privacy/ClipboardAutoClearFragment.java
new file mode 100644
index 00000000000..401d204d81a
--- /dev/null
+++ b/src/com/android/settings/custom/privacy/ClipboardAutoClearFragment.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 TheParasiteProject
+ *
+ * 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.custom.privacy;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.List;
+
+@SearchIndexable
+public class ClipboardAutoClearFragment extends SettingsPreferenceFragment {
+
+ public static final String TAG = "ClipboardAutoClearFragment";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.clipboard_auto_clear_settings);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.CRDROID_SETTINGS;
+ }
+
+ /**
+ * For search
+ */
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.clipboard_auto_clear_settings) {
+
+ @Override
+ public List getNonIndexableKeys(Context context) {
+ List keys = super.getNonIndexableKeys(context);
+
+ return keys;
+ }
+ };
+}
diff --git a/src/com/android/settings/custom/utils/AppUtils.kt b/src/com/android/settings/custom/utils/AppUtils.kt
new file mode 100644
index 00000000000..c6d49af0f8a
--- /dev/null
+++ b/src/com/android/settings/custom/utils/AppUtils.kt
@@ -0,0 +1,26 @@
+package com.android.settings.custom.utils
+
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.os.UserHandle
+
+class AppUtils {
+ fun getCloneableAppList(context: Context): List {
+ val packageManager = context.packageManager
+ val packageList: List =
+ packageManager.getInstalledPackagesAsUser(0, UserHandle.myUserId())
+ val cloneableApps = context.resources.getStringArray(com.android.internal.R.array.cloneable_apps)?.toList()
+ val filteredList = packageList.filter { packageInfo ->
+ val packageName = packageInfo.applicationInfo?.packageName
+ val isSystemApp = packageInfo.applicationInfo?.isSystemApp() == true
+ packageName != null && (cloneableApps?.contains(packageName) == true || !isSystemApp) &&
+ packageManager.getLaunchIntentForPackage(packageName) != null
+ }
+ return filteredList
+ }
+
+ fun getCloneableAppListStr(context: Context): List {
+ return getCloneableAppList(context).map { it.packageName }
+ }
+}
+
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index 791b6296389..5f69c9c9ce9 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -42,6 +42,7 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
@@ -92,6 +93,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
private static final String TAG = "DashboardFeatureImpl";
private static final String DASHBOARD_TILE_PREF_KEY_PREFIX = "dashboard_tile_pref_";
private static final String META_DATA_KEY_INTENT_ACTION = "com.android.settings.intent.action";
+ private static final String WELLBEING_PACKAGE = "com.google.android.apps.wellbeing";
protected final Context mContext;
@@ -455,7 +457,12 @@ private void setPreferenceIcon(Preference preference, Tile tile, boolean forceRo
return;
}
// Tint homepage icons
- if (TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) {
+ if (tile != null && TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) {
+ if (iconPackage != null && iconPackage.equals(WELLBEING_PACKAGE) && iconDrawable instanceof LayerDrawable
+ && ((LayerDrawable) iconDrawable).getDrawable(1) != null) {
+ iconDrawable = ((LayerDrawable) iconDrawable).getDrawable(1);
+ iconDrawable.mutate();
+ }
// Skip tinting and Adaptive Icon transformation for homepage account type raw icons
if (TextUtils.equals(tile.getGroupKey(), "top_level_account_category")
&& iconPackage == null) {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 6333f22b95b..539e2af180a 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -77,6 +77,29 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
private static final String TAG = "DashboardFragment";
private static final long TIMEOUT_MILLIS = 50L;
+ private static final List ACCOUNT_INJECTED_KEYS = Arrays.asList(
+ "dashboard_tile_pref_com.google.android.gms.backup.component.BackupOrRestoreSettingsActivity"
+ );
+
+ private static final List PERSONALIZATION_INJECTED_KEYS = Arrays.asList(
+ "crdroid_device_parts_settings"
+ );
+
+ private static final List SECURITY_PRIVACY_INJECTED_KEYS = Arrays.asList(
+ "top_level_wellbeing",
+ "top_level_google"
+ );
+
+ private static final ArrayMap KEY_ORDER = new ArrayMap<>();
+ static {
+ // Snuggle up just under crDroid Settings at "-60" above, and something else at -50 below
+ KEY_ORDER.put("crdroid_device_parts_settings", -55);
+ // We have "Passwords, passkeys & accounts with order "-10" above
+ KEY_ORDER.put("top_level_wellbeing", -5);
+ KEY_ORDER.put("top_level_google", 0);
+ // We have "Safety & emergency with order "10" below
+ }
+
@VisibleForTesting
final ArrayMap> mDashboardTilePrefKeys = new ArrayMap<>();
private final Map> mPreferenceControllers =
@@ -563,6 +586,10 @@ private void refreshDashboardTiles(final String tag) {
observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(
getActivity(), this, forceRoundedIcons, preference, tile, key,
mPlaceholderPreferenceController.getOrder());
+ // Order the prefs within their respective category
+ if (KEY_ORDER.containsKey(key)) {
+ preference.setOrder(KEY_ORDER.get(key));
+ }
} else {
// Don't have this key, add it.
final Preference pref = createPreference(tile);
@@ -581,13 +608,27 @@ private void refreshDashboardTiles(final String tag) {
screen.addPreference(pref);
}
} else {
+ Preference group = null;
if (tile.hasGroupKey()
&& mDashboardTilePrefKeys.containsKey(tile.getGroupKey())) {
- Preference group = screen.findPreference(tile.getGroupKey());
- if (group instanceof PreferenceCategory) {
- ((PreferenceCategory) group).addPreference(pref);
- }
+ group = screen.findPreference(tile.getGroupKey());
+ } else if (ACCOUNT_INJECTED_KEYS.contains(key)) {
+ group = screen.findPreference("top_level_account_category");
+ } else if (PERSONALIZATION_INJECTED_KEYS.contains(key)) {
+ group = screen.findPreference("top_level_personalize_category");
+ } else if (SECURITY_PRIVACY_INJECTED_KEYS.contains(key)) {
+ group = screen.findPreference("top_level_security_privacy_category");
+ } else {
+ group = screen.findPreference("top_level_category_undefined");
+ }
+ // Order the prefs within their respective category
+ if (KEY_ORDER.containsKey(key)) {
+ pref.setOrder(KEY_ORDER.get(key));
+ }
+ if (group instanceof PreferenceCategory) {
+ ((PreferenceCategory) group).addPreference(pref);
} else {
+ // Should never get here now
screen.addPreference(pref);
}
}
diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
index 849a80b3592..aad83754829 100644
--- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
+++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
@@ -50,6 +50,8 @@
import com.android.settings.system.SystemDashboardFragment;
import com.android.settingslib.drawer.CategoryKey;
+import com.crdroid.settings.crDroidSettingsLayout;
+
import java.util.Map;
/**
@@ -132,6 +134,8 @@ public class DashboardFragmentRegistry {
CategoryKey.CATEGORY_SPECIAL_APP_ACCESS);
PARENT_TO_CATEGORY_KEY_MAP.put(MoreSecurityPrivacyFragment.class.getName(),
CategoryKey.CATEGORY_MORE_SECURITY_PRIVACY_SETTINGS);
+ PARENT_TO_CATEGORY_KEY_MAP.put(crDroidSettingsLayout.class.getName(),
+ CategoryKey.CATEGORY_CRDROID);
CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index 8480c5cc830..2bdf14f635f 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -15,16 +15,22 @@
package com.android.settings.datausage;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_ALL;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_CELLULAR;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_VPN;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_WIFI;
import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUid;
import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUidList;
+import android.Manifest;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.os.UserHandle;
@@ -43,7 +49,6 @@
import com.android.settings.applications.AppInfoBase;
import com.android.settings.datausage.lib.AppDataUsageDetailsRepository;
import com.android.settings.datausage.lib.NetworkTemplates;
-import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.AppItem;
@@ -58,6 +63,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceChangeListener,
DataSaverBackend.Listener {
@@ -69,12 +75,20 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
static final String ARG_NETWORK_CYCLES = "network_cycles";
static final String ARG_SELECTED_CYCLE = "selected_cycle";
+ private static final String KEY_RESTRICT_ALL = "restrict_all";
private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
+ private static final String KEY_RESTRICT_CELLULAR = "restrict_cellular";
+ private static final String KEY_RESTRICT_VPN = "restrict_vpn";
+ private static final String KEY_RESTRICT_WIFI = "restrict_wifi";
private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver";
private PackageManager mPackageManager;
private final ArraySet mPackages = new ArraySet<>();
+ private RestrictedSwitchPreference mRestrictAll;
private RestrictedSwitchPreference mRestrictBackground;
+ private RestrictedSwitchPreference mRestrictCellular;
+ private RestrictedSwitchPreference mRestrictVpn;
+ private RestrictedSwitchPreference mRestrictWifi;
private Drawable mIcon;
@VisibleForTesting
@@ -92,10 +106,6 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
private long mSelectedCycle;
private boolean mIsLoading;
- public boolean isSimHardwareVisible(Context context) {
- return SubscriptionUtil.isSimHardwareVisible(context);
- }
-
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -138,12 +148,16 @@ public void onCreate(Bundle icicle) {
final UidDetailProvider uidDetailProvider = getUidDetailProvider();
if (mAppItem.key > 0) {
- if ((!isSimHardwareVisible(mContext)) || !UserHandle.isApp(mAppItem.key)) {
+ if (!UserHandle.isApp(mAppItem.key)) {
final UidDetail uidDetail = uidDetailProvider.getUidDetail(mAppItem.key, true);
mIcon = uidDetail.icon;
mLabel = uidDetail.label;
removePreference(KEY_UNRESTRICTED_DATA);
+ removePreference(KEY_RESTRICT_ALL);
removePreference(KEY_RESTRICT_BACKGROUND);
+ removePreference(KEY_RESTRICT_CELLULAR);
+ removePreference(KEY_RESTRICT_VPN);
+ removePreference(KEY_RESTRICT_WIFI);
} else {
if (mPackages.size() != 0) {
int userId = UserHandle.getUserId(mAppItem.key);
@@ -157,8 +171,16 @@ public void onCreate(Bundle icicle) {
}
use(AppDataUsageAppSettingsController.class).init(mPackages, userId);
}
+ mRestrictAll = findPreference(KEY_RESTRICT_ALL);
+ mRestrictAll.setOnPreferenceChangeListener(this);
mRestrictBackground = findPreference(KEY_RESTRICT_BACKGROUND);
mRestrictBackground.setOnPreferenceChangeListener(this);
+ mRestrictCellular = findPreference(KEY_RESTRICT_CELLULAR);
+ mRestrictCellular.setOnPreferenceChangeListener(this);
+ mRestrictVpn = findPreference(KEY_RESTRICT_VPN);
+ mRestrictVpn.setOnPreferenceChangeListener(this);
+ mRestrictWifi = findPreference(KEY_RESTRICT_WIFI);
+ mRestrictWifi.setOnPreferenceChangeListener(this);
mUnrestrictedData = findPreference(KEY_UNRESTRICTED_DATA);
mUnrestrictedData.setOnPreferenceChangeListener(this);
}
@@ -173,7 +195,11 @@ public void onCreate(Bundle icicle) {
mPackageName = context.getPackageName();
removePreference(KEY_UNRESTRICTED_DATA);
+ removePreference(KEY_RESTRICT_ALL);
removePreference(KEY_RESTRICT_BACKGROUND);
+ removePreference(KEY_RESTRICT_CELLULAR);
+ removePreference(KEY_RESTRICT_VPN);
+ removePreference(KEY_RESTRICT_WIFI);
}
addEntityHeader();
@@ -215,6 +241,22 @@ public boolean onPreferenceChange(@NonNull Preference preference, Object newValu
mDataSaverBackend.setIsDenylisted(mAppItem.key, mPackageName, !(Boolean) newValue);
updatePrefs();
return true;
+ } else if (preference == mRestrictAll) {
+ setAppRestrictAll(!(Boolean) newValue);
+ updatePrefs();
+ return true;
+ } else if (preference == mRestrictCellular) {
+ setAppRestrictCellular(!(Boolean) newValue);
+ updatePrefs();
+ return true;
+ } else if (preference == mRestrictVpn) {
+ setAppRestrictVpn(!(Boolean) newValue);
+ updatePrefs();
+ return true;
+ } else if (preference == mRestrictWifi) {
+ setAppRestrictWifi(!(Boolean) newValue);
+ updatePrefs();
+ return true;
} else if (preference == mUnrestrictedData) {
mDataSaverBackend.setIsAllowlisted(mAppItem.key, mPackageName, (Boolean) newValue);
return true;
@@ -234,7 +276,9 @@ protected String getLogTag() {
@VisibleForTesting
void updatePrefs() {
- updatePrefs(getAppRestrictBackground(), getUnrestrictData());
+ updatePrefs(getAppRestrictBackground(), getUnrestrictData(), getAppRestrictAll(),
+ getAppRestrictCellular(), getAppRestrictVpn(), getAppRestrictWifi(),
+ hasInternetPermission());
}
@VisibleForTesting
@@ -275,26 +319,44 @@ private void setBackPreferenceListAnimatorIfLoaded() {
}
}
- private void updatePrefs(boolean restrictBackground, boolean unrestrictData) {
- if (!isSimHardwareVisible(mContext)) {
- return;
- }
+ private void updatePrefs(boolean restrictBackground, boolean unrestrictData,
+ boolean restrictAll, boolean restrictCellular, boolean restrictVpn,
+ boolean restrictWifi, boolean hasInternetPermission) {
setBackPreferenceListAnimatorIfLoaded();
final EnforcedAdmin admin = RestrictedLockUtilsInternal
.checkIfMeteredDataUsageUserControlDisabled(mContext, mPackageName,
UserHandle.getUserId(mAppItem.key));
+ if (mRestrictAll != null) {
+ mRestrictAll.setEnabled(hasInternetPermission);
+ mRestrictAll.setChecked(!restrictAll);
+ }
if (mRestrictBackground != null) {
- mRestrictBackground.setChecked(!restrictBackground);
mRestrictBackground.setDisabledByAdmin(admin);
+ mRestrictBackground.setEnabled(hasInternetPermission &&
+ !mRestrictBackground.isDisabledByAdmin() && !restrictAll &&
+ !restrictCellular);
+ mRestrictBackground.setChecked(!restrictBackground && !restrictAll &&
+ !restrictCellular);
+ }
+ if (mRestrictCellular != null) {
+ mRestrictCellular.setEnabled(hasInternetPermission && !restrictAll);
+ mRestrictCellular.setChecked(!restrictAll && !restrictCellular);
+ }
+ if (mRestrictVpn != null) {
+ mRestrictVpn.setEnabled(hasInternetPermission && !restrictAll);
+ mRestrictVpn.setChecked(!restrictAll && !restrictVpn);
+ }
+ if (mRestrictWifi != null) {
+ mRestrictWifi.setEnabled(hasInternetPermission && !restrictAll);
+ mRestrictWifi.setChecked(!restrictAll && !restrictWifi);
}
if (mUnrestrictedData != null) {
- if (restrictBackground) {
- mUnrestrictedData.setVisible(false);
- } else {
- mUnrestrictedData.setVisible(true);
- mUnrestrictedData.setChecked(unrestrictData);
- mUnrestrictedData.setDisabledByAdmin(admin);
- }
+ mUnrestrictedData.setDisabledByAdmin(admin);
+ mUnrestrictedData.setEnabled(hasInternetPermission &&
+ !mUnrestrictedData.isDisabledByAdmin() && !restrictBackground && !restrictAll &&
+ !restrictCellular);
+ mUnrestrictedData.setChecked(unrestrictData && !restrictBackground && !restrictAll &&
+ !restrictCellular);
}
}
@@ -306,10 +368,23 @@ private void addUid(int uid) {
}
private boolean getAppRestrictBackground() {
- final int uid = mAppItem.key;
- final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
- return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0
- && DynamicDenylistManager.getInstance(mContext).isInManualDenylist(uid);
+ return getAppRestriction(POLICY_REJECT_METERED_BACKGROUND);
+ }
+
+ private boolean getAppRestrictCellular() {
+ return getAppRestriction(POLICY_REJECT_CELLULAR);
+ }
+
+ private boolean getAppRestrictVpn() {
+ return getAppRestriction(POLICY_REJECT_VPN);
+ }
+
+ private boolean getAppRestrictWifi() {
+ return getAppRestriction(POLICY_REJECT_WIFI);
+ }
+
+ private boolean getAppRestrictAll() {
+ return getAppRestriction(POLICY_REJECT_ALL);
}
private boolean getUnrestrictData() {
@@ -319,6 +394,42 @@ private boolean getUnrestrictData() {
return false;
}
+ private boolean getAppRestriction(int policy) {
+ final int uid = mAppItem.key;
+ final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
+ return (uidPolicy & policy) != 0;
+ }
+
+ private boolean hasInternetPermission() {
+ return mPackageManager.checkPermission(Manifest.permission.INTERNET, mPackageName)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void setAppRestrictAll(boolean restrict) {
+ setAppRestriction(POLICY_REJECT_ALL, restrict);
+ }
+
+ private void setAppRestrictCellular(boolean restrict) {
+ setAppRestriction(POLICY_REJECT_CELLULAR, restrict);
+ }
+
+ private void setAppRestrictVpn(boolean restrict) {
+ setAppRestriction(POLICY_REJECT_VPN, restrict);
+ }
+
+ private void setAppRestrictWifi(boolean restrict) {
+ setAppRestriction(POLICY_REJECT_WIFI, restrict);
+ }
+
+ private void setAppRestriction(int policy, boolean restrict) {
+ final int uid = mAppItem.key;
+ if (restrict) {
+ services.mPolicyManager.addUidPolicy(uid, policy);
+ } else {
+ services.mPolicyManager.removeUidPolicy(uid, policy);
+ }
+ }
+
@VisibleForTesting
void addEntityHeader() {
String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
@@ -361,14 +472,18 @@ public void onDataSaverChanged(boolean isDataSaving) {
@Override
public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {
if (mAppItem.uids.get(uid, false)) {
- updatePrefs(getAppRestrictBackground(), isAllowlisted);
+ updatePrefs(getAppRestrictBackground(), isAllowlisted, getAppRestrictAll(),
+ getAppRestrictCellular(), getAppRestrictVpn(), getAppRestrictWifi(),
+ hasInternetPermission());
}
}
@Override
public void onDenylistStatusChanged(int uid, boolean isDenylisted) {
if (mAppItem.uids.get(uid, false)) {
- updatePrefs(isDenylisted, getUnrestrictData());
+ updatePrefs(isDenylisted, getUnrestrictData(), getAppRestrictAll(),
+ getAppRestrictCellular(), getAppRestrictVpn(), getAppRestrictWifi(),
+ hasInternetPermission());
}
}
}
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index 6e994537aa8..4f731c71cde 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -23,7 +23,6 @@
import android.net.NetworkPolicyManager;
import android.util.SparseIntArray;
-import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;
@@ -40,7 +39,6 @@ public class DataSaverBackend {
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final NetworkPolicyManager mPolicyManager;
- private final DynamicDenylistManager mDynamicDenylistManager;
private final ArrayList mListeners = new ArrayList<>();
private SparseIntArray mUidPolicies = new SparseIntArray();
private boolean mAllowlistInitialized;
@@ -52,7 +50,6 @@ public DataSaverBackend(@NotNull Context context) {
mContext = context.getApplicationContext();
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
mPolicyManager = NetworkPolicyManager.from(mContext);
- mDynamicDenylistManager = DynamicDenylistManager.getInstance(mContext);
}
public void addListener(Listener listener) {
@@ -85,28 +82,27 @@ public void refreshAllowlist() {
}
public void setIsAllowlisted(int uid, String packageName, boolean allowlisted) {
- final int policy = allowlisted ? POLICY_ALLOW_METERED_BACKGROUND : POLICY_NONE;
- mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
- mUidPolicies.put(uid, policy);
+ setUidPolicyFlag(uid, POLICY_ALLOW_METERED_BACKGROUND, allowlisted);
if (allowlisted) {
+ mPolicyManager.addUidPolicy(uid, POLICY_ALLOW_METERED_BACKGROUND);
mMetricsFeatureProvider.action(
mContext, SettingsEnums.ACTION_DATA_SAVER_WHITELIST, packageName);
+ } else {
+ mPolicyManager.removeUidPolicy(uid, POLICY_ALLOW_METERED_BACKGROUND);
}
+ mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
}
public boolean isAllowlisted(int uid) {
loadAllowlist();
- return mUidPolicies.get(uid, POLICY_NONE) == POLICY_ALLOW_METERED_BACKGROUND;
+ return isUidPolicyFlagSet(uid, POLICY_ALLOW_METERED_BACKGROUND);
}
private void loadAllowlist() {
if (mAllowlistInitialized) {
return;
}
-
- for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)) {
- mUidPolicies.put(uid, POLICY_ALLOW_METERED_BACKGROUND);
- }
+ loadUidPolicies(POLICY_ALLOW_METERED_BACKGROUND);
mAllowlistInitialized = true;
}
@@ -115,28 +111,80 @@ public void refreshDenylist() {
}
public void setIsDenylisted(int uid, String packageName, boolean denylisted) {
- final int policy = denylisted ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE;
- mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
- mUidPolicies.put(uid, policy);
+ setUidPolicyFlag(uid, POLICY_REJECT_METERED_BACKGROUND, denylisted);
if (denylisted) {
+ mPolicyManager.addUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
mMetricsFeatureProvider.action(
mContext, SettingsEnums.ACTION_DATA_SAVER_BLACKLIST, packageName);
+ } else {
+ mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
+ }
+ mPolicyManager.removeUidPolicy(uid, POLICY_ALLOW_METERED_BACKGROUND);
+ }
+
+ private void loadUidPolicies(int policy) {
+ final int[] uidsWithPolicyArray = mPolicyManager.getUidsWithPolicy(policy);
+ final ArrayList uidsWithPolicy = new ArrayList<>(uidsWithPolicyArray.length);
+ for (final int uid : uidsWithPolicyArray) {
+ uidsWithPolicy.add(uid);
+ }
+ // Update existing cached UID policies.
+ for (int i = 0; i < mUidPolicies.size(); i++) {
+ final Integer cachedEntryUid = mUidPolicies.keyAt(i);
+ if (uidsWithPolicy.remove(cachedEntryUid)) {
+ // UID had the policy. It was removed so we don't have to process it twice.
+ setCachedUidPolicyFlagAt(i, policy, true);
+ } else {
+ // UID does not have the policy.
+ setCachedUidPolicyFlagAt(i, policy, false);
+ }
+ }
+ // Add policies for remaining UIDs, which did not have cached policies, so we're it.
+ for (final int uid : uidsWithPolicy) {
+ mUidPolicies.put(uid, policy);
}
}
+ private void setCachedUidPolicyFlag(int uid, int policy, boolean add) {
+ final int index = mUidPolicies.indexOfKey(uid);
+ if (index < 0) {
+ if (add) {
+ mUidPolicies.put(uid, policy);
+ }
+ return;
+ }
+ setCachedUidPolicyFlagAt(index, policy, add);
+ }
+
+ private void setCachedUidPolicyFlagAt(int index, int policy, boolean add) {
+ final int currentPolicy = mUidPolicies.valueAt(index);
+ final int newPolicy = add ? (currentPolicy | policy) : (currentPolicy & ~policy);
+ mUidPolicies.setValueAt(index, newPolicy);
+ }
+
+ private void setUidPolicyFlag(int uid, int policy, boolean add) {
+ if (add) {
+ mPolicyManager.addUidPolicy(uid, policy);
+ } else {
+ mPolicyManager.removeUidPolicy(uid, policy);
+ }
+ setCachedUidPolicyFlag(uid, policy, add);
+ }
+
+ private boolean isUidPolicyFlagSet(int uid, int policy) {
+ return (mUidPolicies.get(uid, POLICY_NONE) & policy) == policy;
+ }
+
public boolean isDenylisted(int uid) {
loadDenylist();
- return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND
- && mDynamicDenylistManager.isInManualDenylist(uid);
+ return isUidPolicyFlagSet(uid, POLICY_REJECT_METERED_BACKGROUND);
}
private void loadDenylist() {
if (mDenylistInitialized) {
return;
}
- for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
- mUidPolicies.put(uid, POLICY_REJECT_METERED_BACKGROUND);
- }
+ loadUidPolicies(POLICY_REJECT_METERED_BACKGROUND);
mDenylistInitialized = true;
}
@@ -162,6 +210,9 @@ private void handleUidPoliciesChanged(int uid, int newPolicy) {
loadAllowlist();
loadDenylist();
+ // We only care about allow/reject metered background policy here.
+ newPolicy &= POLICY_ALLOW_METERED_BACKGROUND | POLICY_REJECT_METERED_BACKGROUND;
+
final int oldPolicy = mUidPolicies.get(uid, POLICY_NONE);
if (newPolicy == POLICY_NONE) {
mUidPolicies.delete(uid);
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreference.java b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
index 6500501b210..8e0eaa38e38 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreference.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
@@ -56,7 +56,7 @@ public class DataUsageSummaryPreference extends Preference {
private static final long WARNING_AGE = TimeUnit.HOURS.toMillis(6L);
@VisibleForTesting
static final Typeface SANS_SERIF_MEDIUM =
- Typeface.create("sans-serif-medium", Typeface.NORMAL);
+ Typeface.create("@*android:string/config_bodyFontFamilyMedium", Typeface.NORMAL);
private boolean mChartEnabled = true;
private CharSequence mStartLabel;
@@ -263,7 +263,7 @@ private void updateCarrierInfo(TextView carrierInfo) {
if (updateAgeMillis <= WARNING_AGE) {
setCarrierInfoTextStyle(
- carrierInfo, android.R.attr.textColorSecondary, Typeface.SANS_SERIF);
+ carrierInfo, android.R.attr.textColorSecondary, Typeface.DEFAULT);
} else {
setCarrierInfoTextStyle(carrierInfo, android.R.attr.colorError, SANS_SERIF_MEDIUM);
}
diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
index adbedee7998..120d8ca7391 100644
--- a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
+++ b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
@@ -164,7 +164,9 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// To prevent a large space on tool bar.
- mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
+ if (mAppBarLayout != null) {
+ mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
+ }
// To prevent user can expand the collapsing tool bar view.
ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
return true;
@@ -173,7 +175,9 @@ public boolean onMenuItemActionExpand(MenuItem item) {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// We keep the collapsed status after user cancel the search function.
- mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
+ if (mAppBarLayout != null) {
+ mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
+ }
ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
return true;
}
@@ -196,6 +200,10 @@ public interface OnListItemClickListener 1) {
+ if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
super.onBackPressed();
} else {
setResult(RESULT_CANCELED);
@@ -105,106 +91,168 @@ private void handleBackPressed() {
}
}
- class MyApplicationInfo {
- ApplicationInfo info;
- CharSequence label;
- }
+ public static class AppPickerListFragment extends ListFragment {
+ private static final String EXTRA_PERMISSION_NAME = "extra_permission_name";
+ private static final String EXTRA_DEBUGGABLE_ONLY = "extra_debuggable_only";
+ private static final String EXTRA_NON_SYSTEM_ONLY = "extra_non_system_only";
+ private static final String EXTRA_INCLUDE_NOTHING = "extra_include_nothing";
- public class AppListAdapter extends ArrayAdapter {
- private final List mPackageInfoList = new ArrayList();
- private final LayoutInflater mInflater;
-
- public AppListAdapter(Context context) {
- super(context, 0);
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- List pkgs = context.getPackageManager().getInstalledApplications(0);
- for (int i=0; i {
+ private final List mPackageInfoList =
+ new ArrayList();
+ private final LayoutInflater mInflater;
+
+ public AppListAdapter(Context context) {
+ super(context, 0);
+ mInflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ List pkgs = context.getPackageManager().getInstalledApplications(
+ 0);
+ for (int i = 0; i < pkgs.size(); i++) {
+ ApplicationInfo ai = pkgs.get(i);
+ if (ai.uid == Process.SYSTEM_UID) {
continue;
}
- }
- // Filter out apps that are system apps if requested
- if (mNonSystemOnly && ai.isSystemApp()) {
- continue;
- }
-
- // Filter out apps that do not request the permission if required.
- if (mPermissionName != null) {
- boolean requestsPermission = false;
- try {
- PackageInfo pi = getPackageManager().getPackageInfo(ai.packageName,
- PackageManager.GET_PERMISSIONS);
- if (pi.requestedPermissions == null) {
+ // Filter out apps that are not debuggable if required.
+ if (mDebuggableOnly) {
+ // On a user build, we only allow debugging of apps that
+ // are marked as debuggable. Otherwise (for platform development)
+ // we allow all apps.
+ if ((ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0
+ && "user".equals(Build.TYPE)) {
continue;
}
- for (String requestedPermission : pi.requestedPermissions) {
- if (requestedPermission.equals(mPermissionName)) {
- requestsPermission = true;
- break;
+ }
+
+ // Filter out apps that are system apps if requested
+ if (mNonSystemOnly && ai.isSystemApp()) {
+ continue;
+ }
+
+ // Filter out apps that do not request the permission if required.
+ if (mPermissionName != null) {
+ boolean requestsPermission = false;
+ try {
+ PackageInfo pi = getContext().getPackageManager().getPackageInfo(
+ ai.packageName, PackageManager.GET_PERMISSIONS);
+ if (pi.requestedPermissions == null) {
+ continue;
}
- }
- if (!requestsPermission) {
+ for (String requestedPermission : pi.requestedPermissions) {
+ if (requestedPermission.equals(mPermissionName)) {
+ requestsPermission = true;
+ break;
+ }
+ }
+ if (!requestsPermission) {
+ continue;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
continue;
}
- } catch (PackageManager.NameNotFoundException e) {
- continue;
}
- }
- MyApplicationInfo info = new MyApplicationInfo();
- info.info = ai;
- info.label = info.info.loadLabel(getPackageManager()).toString();
- mPackageInfoList.add(info);
- }
- Collections.sort(mPackageInfoList, sDisplayNameComparator);
- if (mIncludeNothing) {
- MyApplicationInfo info = new MyApplicationInfo();
- info.label = context.getText(com.android.settingslib.R.string.no_application);
- mPackageInfoList.add(0, info);
+ MyApplicationInfo info = new MyApplicationInfo();
+ info.info = ai;
+ info.label = info.info.loadLabel(getContext().getPackageManager()).toString();
+ mPackageInfoList.add(info);
+ }
+ Collections.sort(mPackageInfoList, sDisplayNameComparator);
+ if (mIncludeNothing) {
+ MyApplicationInfo info = new MyApplicationInfo();
+ info.label = context.getText(com.android.settingslib.R.string.no_application);
+ mPackageInfoList.add(0, info);
+ }
+ addAll(mPackageInfoList);
}
- addAll(mPackageInfoList);
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // A ViewHolder keeps references to children views to avoid unnecessary calls
- // to findViewById() on each row.
- AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
- convertView = holder.rootView;
- MyApplicationInfo info = getItem(position);
- holder.appName.setText(info.label);
- if (info.info != null) {
- holder.appIcon.setImageDrawable(info.info.loadIcon(getPackageManager()));
- holder.summary.setText(info.info.packageName);
- } else {
- holder.appIcon.setImageDrawable(null);
- holder.summary.setText("");
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unnecessary calls
+ // to findViewById() on each row.
+ AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
+ convertView = holder.rootView;
+ MyApplicationInfo info = getItem(position);
+ holder.appName.setText(info.label);
+ if (info.info != null) {
+ holder.appIcon.setImageDrawable(
+ info.info.loadIcon(getContext().getPackageManager()));
+ holder.summary.setText(info.info.packageName);
+ } else {
+ holder.appIcon.setImageDrawable(null);
+ holder.summary.setText("");
+ }
+ holder.disabled.setVisibility(View.GONE);
+ holder.widget.setVisibility(View.GONE);
+ return convertView;
}
- holder.disabled.setVisibility(View.GONE);
- holder.widget.setVisibility(View.GONE);
- return convertView;
}
- }
- private final static Comparator sDisplayNameComparator
- = new Comparator() {
- public final int
- compare(MyApplicationInfo a, MyApplicationInfo b) {
- return collator.compare(a.label, b.label);
- }
+ private final static Comparator sDisplayNameComparator
+ = new Comparator() {
+ public final int
+ compare(MyApplicationInfo a, MyApplicationInfo b) {
+ return collator.compare(a.label, b.label);
+ }
- private final Collator collator = Collator.getInstance();
- };
+ private final Collator collator = Collator.getInstance();
+ };
+ }
}
diff --git a/src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java b/src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java
index 05a5dc90b4c..fbd661b653a 100644
--- a/src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java
+++ b/src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java
@@ -70,7 +70,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
@Override
public void updateState(Preference preference) {
final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, SETTING_VALUE_ON);
+ Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, SETTING_VALUE_OFF);
((TwoStatePreference) mPreference).setChecked(mode != SETTING_VALUE_OFF);
}
diff --git a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
index f460b9e33fe..7d4b7a6fdda 100644
--- a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
@@ -74,23 +74,10 @@ public void updateState(Preference preference) {
}
}
- @Override
- protected void onDeveloperOptionsSwitchDisabled() {
- super.onDeveloperOptionsSwitchDisabled();
- final boolean offloadSupported =
- SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false);
- if (offloadSupported) {
- ((TwoStatePreference) mPreference).setChecked(false);
- SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, "false");
- }
- }
-
public boolean isDefaultValue() {
- final boolean offloadSupported =
- SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false);
- final boolean offloadDisabled =
- SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
- return offloadSupported ? !offloadDisabled : true;
+ // Always return true here to avoid needing to reboot when disabling
+ // developer options, since we aren't turning this off when doing so anymore.
+ return true;
}
/**
diff --git a/src/com/android/settings/development/BugReportInPowerPreferenceController.java b/src/com/android/settings/development/BugReportInPowerPreferenceController.java
index c8c46c96a59..d37b5b96b4f 100644
--- a/src/com/android/settings/development/BugReportInPowerPreferenceController.java
+++ b/src/com/android/settings/development/BugReportInPowerPreferenceController.java
@@ -47,7 +47,7 @@ public BugReportInPowerPreferenceController(Context context) {
@Override
public boolean isAvailable() {
- return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
+ return false;
}
@Override
diff --git a/src/com/android/settings/development/BugReportPreferenceController.java b/src/com/android/settings/development/BugReportPreferenceController.java
index ac2ae0d9ca3..01c28e6e284 100644
--- a/src/com/android/settings/development/BugReportPreferenceController.java
+++ b/src/com/android/settings/development/BugReportPreferenceController.java
@@ -37,7 +37,7 @@ public BugReportPreferenceController(Context context) {
@Override
public boolean isAvailable() {
- return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
+ return false;
}
@Override
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 09b7503397e..bc528571427 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -33,6 +33,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
@@ -710,6 +711,7 @@ private static List buildPreferenceControllers(Con
controllers.add(new AdbPreferenceController(context, fragment));
controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
controllers.add(new WirelessDebuggingPreferenceController(context, lifecycle));
+ controllers.add(new AdbRootPreferenceController(context, fragment));
controllers.add(new AdbAuthorizationTimeoutPreferenceController(context));
controllers.add(new LocalTerminalPreferenceController(context));
controllers.add(new BugReportInPowerPreferenceController(context));
@@ -728,10 +730,12 @@ private static List buildPreferenceControllers(Con
controllers.add(new LogdSizePreferenceController(context));
controllers.add(new LogPersistPreferenceController(context, fragment, lifecycle));
controllers.add(new CameraLaserSensorPreferenceController(context));
- controllers.add(new WifiDisplayCertificationPreferenceController(context));
- controllers.add(new WifiVerboseLoggingPreferenceController(context));
- controllers.add(new WifiScanThrottlingPreferenceController(context));
- controllers.add(new WifiNonPersistentMacRandomizationPreferenceController(context));
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ controllers.add(new WifiDisplayCertificationPreferenceController(context));
+ controllers.add(new WifiVerboseLoggingPreferenceController(context));
+ controllers.add(new WifiScanThrottlingPreferenceController(context));
+ controllers.add(new WifiNonPersistentMacRandomizationPreferenceController(context));
+ }
controllers.add(new MobileDataAlwaysOnPreferenceController(context));
controllers.add(new TetheringHardwareAccelPreferenceController(context));
controllers.add(new BluetoothDeviceNoNamePreferenceController(context));
@@ -766,9 +770,7 @@ private static List buildPreferenceControllers(Con
controllers.add(new HardwareLayersUpdatesPreferenceController(context));
controllers.add(new DebugGpuOverdrawPreferenceController(context));
controllers.add(new DebugNonRectClipOperationsPreferenceController(context));
- controllers.add(new GameDefaultFrameRatePreferenceController(context));
controllers.add(new ForceDarkPreferenceController(context));
- controllers.add(new EnableBlursPreferenceController(context));
controllers.add(new ForceMSAAPreferenceController(context));
controllers.add(new HardwareOverlaysPreferenceController(context));
controllers.add(new SimulateColorSpacePreferenceController(context));
diff --git a/src/com/android/settings/development/OverlayCategoryPreferenceController.java b/src/com/android/settings/development/OverlayCategoryPreferenceController.java
index ce51b54cf8c..0c20c0329a0 100644
--- a/src/com/android/settings/development/OverlayCategoryPreferenceController.java
+++ b/src/com/android/settings/development/OverlayCategoryPreferenceController.java
@@ -54,7 +54,7 @@ public class OverlayCategoryPreferenceController extends DeveloperOptionsPrefere
static final String PACKAGE_DEVICE_DEFAULT = "package_device_default";
private static final String OVERLAY_TARGET_PACKAGE = "android";
private static final Comparator OVERLAY_INFO_COMPARATOR =
- Comparator.comparingInt(a -> a.priority);
+ Comparator.comparing(OverlayInfo::getPackageName);
private final IOverlayManager mOverlayManager;
private final boolean mAvailable;
private final String mCategory;
diff --git a/src/com/android/settings/development/TransitionAnimationScalePreferenceController.java b/src/com/android/settings/development/TransitionAnimationScalePreferenceController.java
index ac683641fea..ab8db232a60 100644
--- a/src/com/android/settings/development/TransitionAnimationScalePreferenceController.java
+++ b/src/com/android/settings/development/TransitionAnimationScalePreferenceController.java
@@ -22,22 +22,22 @@
import android.view.IWindowManager;
import androidx.annotation.VisibleForTesting;
-import androidx.preference.ListPreference;
import androidx.preference.Preference;
+import com.android.settings.AnimationScalePreference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
public class TransitionAnimationScalePreferenceController extends
DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
- PreferenceControllerMixin {
+ Preference.OnPreferenceClickListener, PreferenceControllerMixin {
private static final String TRANSITION_ANIMATION_SCALE_KEY = "transition_animation_scale";
@VisibleForTesting
static final int TRANSITION_ANIMATION_SCALE_SELECTOR = 1;
@VisibleForTesting
- static final float DEFAULT_VALUE = 1;
+ static final float DEFAULT_VALUE = 0.8f;
private final IWindowManager mWindowManager;
private final String[] mListValues;
@@ -90,19 +90,17 @@ private void updateAnimationScaleValue() {
try {
final float scale = mWindowManager.getAnimationScale(
TRANSITION_ANIMATION_SCALE_SELECTOR);
- int index = 0; // default
- for (int i = 0; i < mListValues.length; i++) {
- float val = Float.parseFloat(mListValues[i]);
- if (scale <= val) {
- index = i;
- break;
- }
- }
- final ListPreference listPreference = (ListPreference) mPreference;
- listPreference.setValue(mListValues[index]);
- listPreference.setSummary(mListSummaries[index]);
+ final AnimationScalePreference transitionPreference = (AnimationScalePreference) mPreference;
+ transitionPreference.setOnPreferenceClickListener(this);
+ transitionPreference.setScale(scale);
} catch (RemoteException e) {
// intentional no-op
}
}
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ ((AnimationScalePreference) preference).click();
+ return false;
+ }
}
diff --git a/src/com/android/settings/development/WindowAnimationScalePreferenceController.java b/src/com/android/settings/development/WindowAnimationScalePreferenceController.java
index d8575a0ddfd..cbc24501f0f 100644
--- a/src/com/android/settings/development/WindowAnimationScalePreferenceController.java
+++ b/src/com/android/settings/development/WindowAnimationScalePreferenceController.java
@@ -22,22 +22,22 @@
import android.view.IWindowManager;
import androidx.annotation.VisibleForTesting;
-import androidx.preference.ListPreference;
import androidx.preference.Preference;
+import com.android.settings.AnimationScalePreference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
public class WindowAnimationScalePreferenceController extends
DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
- PreferenceControllerMixin {
+ Preference.OnPreferenceClickListener, PreferenceControllerMixin {
private static final String WINDOW_ANIMATION_SCALE_KEY = "window_animation_scale";
@VisibleForTesting
static final int WINDOW_ANIMATION_SCALE_SELECTOR = 0;
@VisibleForTesting
- static final float DEFAULT_VALUE = 1;
+ static final float DEFAULT_VALUE = 0.8f;
private final IWindowManager mWindowManager;
private final String[] mListValues;
@@ -89,19 +89,18 @@ private void writeAnimationScaleOption(Object newValue) {
private void updateAnimationScaleValue() {
try {
final float scale = mWindowManager.getAnimationScale(WINDOW_ANIMATION_SCALE_SELECTOR);
- int index = 0; // default
- for (int i = 0; i < mListValues.length; i++) {
- float val = Float.parseFloat(mListValues[i]);
- if (scale <= val) {
- index = i;
- break;
- }
- }
- final ListPreference listPreference = (ListPreference) mPreference;
- listPreference.setValue(mListValues[index]);
- listPreference.setSummary(mListSummaries[index]);
+ final AnimationScalePreference windowPreference = (AnimationScalePreference) mPreference;
+ windowPreference.setOnPreferenceClickListener(this);
+ windowPreference.setScale(scale);
+
} catch (RemoteException e) {
// intentional no-op
}
}
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ ((AnimationScalePreference) preference).click();
+ return false;
+ }
}
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java b/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java
index f0b7961bb65..f5559c8365c 100644
--- a/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java
+++ b/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java
@@ -40,7 +40,7 @@ public FeatureFlagsPreferenceController(Context context, String key) {
@Override
public int getAvailabilityStatus() {
- return Build.IS_DEBUGGABLE ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ return AVAILABLE;
}
@Override
diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
index e8a4e8d2923..ac4b0dd3c0c 100644
--- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java
@@ -186,7 +186,7 @@ public boolean handlePreferenceTreeClick(Preference preference) {
}
mDevHitToast = Toast.makeText(mContext,
StringUtil.getIcuPluralsString(mContext, mDevHitCountdown,
- R.string.show_dev_countdown),
+ R.string.show_dev_countdown_cm),
Toast.LENGTH_SHORT);
mDevHitToast.show();
}
@@ -201,7 +201,7 @@ public boolean handlePreferenceTreeClick(Preference preference) {
if (mDevHitToast != null) {
mDevHitToast.cancel();
}
- mDevHitToast = Toast.makeText(mContext, R.string.show_dev_already,
+ mDevHitToast = Toast.makeText(mContext, R.string.show_dev_already_cm,
Toast.LENGTH_LONG);
mDevHitToast.show();
mMetricsFeatureProvider.action(
@@ -264,7 +264,7 @@ private void enableDevelopmentSettings() {
if (mDevHitToast != null) {
mDevHitToast.cancel();
}
- mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on,
+ mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on_cm,
Toast.LENGTH_LONG);
mDevHitToast.show();
diff --git a/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java
index e6d9dfdbb78..b7e1ca971c0 100644
--- a/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java
+++ b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java
@@ -22,6 +22,7 @@
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.text.SpannedString;
@@ -44,6 +45,8 @@ public class DeviceNamePreferenceController extends BasePreferenceController
LifecycleObserver,
OnSaveInstanceState,
OnCreate {
+
+ private static final String KEY_MARKET_NAME_PROP = "ro.product.marketname";
private static final String KEY_PENDING_DEVICE_NAME = "key_pending_device_name";
@VisibleForTesting
static final int RES_SHOW_DEVICE_NAME_BOOL = R.bool.config_show_device_name;
@@ -79,7 +82,7 @@ private void initializeDeviceName() {
mDeviceName = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DEVICE_NAME);
if (mDeviceName == null) {
- mDeviceName = Build.MODEL;
+ mDeviceName = SystemProperties.get(KEY_MARKET_NAME_PROP, Build.MODEL);
}
}
diff --git a/src/com/android/settings/deviceinfo/FeedbackPreferenceController.java b/src/com/android/settings/deviceinfo/FeedbackPreferenceController.java
index d94586be094..51151c9de32 100644
--- a/src/com/android/settings/deviceinfo/FeedbackPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/FeedbackPreferenceController.java
@@ -41,7 +41,7 @@ public FeedbackPreferenceController(Fragment host, Context context) {
@Override
public boolean isAvailable() {
- return !TextUtils.isEmpty(DeviceInfoUtils.getFeedbackReporterPackage(mContext));
+ return false;
}
@Override
diff --git a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java
index 6df100ca4ec..d1bf80862ca 100644
--- a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java
@@ -55,6 +55,28 @@ public int getAvailabilityStatus() {
AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
+ @Override
+ public CharSequence getSummary() {
+ return mContext.getString(R.string.device_info_protected_single_press);
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ String prefKey = preference.getKey();
+ if (prefKey.startsWith(KEY_PHONE_NUMBER)) {
+ int simSlotNumber = 0;
+ if (!TextUtils.equals(prefKey, KEY_PHONE_NUMBER)) {
+ // Get multisim slot number from preference key.
+ // Multisim preference key is KEY_PHONE_NUMBER + simSlotNumber
+ simSlotNumber = Integer.parseInt(
+ prefKey.replaceAll("[^0-9]", ""));
+ }
+ final Preference simStatusPreference = mPreferenceList.get(simSlotNumber);
+ simStatusPreference.setSummary(getPhoneNumber(simSlotNumber));
+ }
+ return super.handlePreferenceTreeClick(preference);
+ }
+
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
@@ -70,7 +92,6 @@ public void displayPreference(PreferenceScreen screen) {
for (int simSlotNumber = 1; simSlotNumber < mTelephonyManager.getPhoneCount();
simSlotNumber++) {
final Preference multiSimPreference = createNewPreference(screen.getContext());
- multiSimPreference.setSelectable(false);
multiSimPreference.setCopyingEnabled(true);
multiSimPreference.setOrder(phonePreferenceOrder + simSlotNumber);
multiSimPreference.setKey(KEY_PHONE_NUMBER + simSlotNumber);
@@ -84,7 +105,7 @@ public void updateState(Preference preference) {
for (int simSlotNumber = 0; simSlotNumber < mPreferenceList.size(); simSlotNumber++) {
final Preference simStatusPreference = mPreferenceList.get(simSlotNumber);
simStatusPreference.setTitle(getPreferenceTitle(simSlotNumber));
- simStatusPreference.setSummary(getPhoneNumber(simSlotNumber));
+ simStatusPreference.setSummary(getSummary());
}
}
@@ -134,7 +155,7 @@ protected SubscriptionInfo getSubscriptionInfo(int simSlot) {
}
@VisibleForTesting
- protected String getFormattedPhoneNumber(SubscriptionInfo subscriptionInfo) {
+ protected CharSequence getFormattedPhoneNumber(SubscriptionInfo subscriptionInfo) {
final String phoneNumber = SubscriptionUtil.getBidiFormattedPhoneNumber(mContext,
subscriptionInfo);
return TextUtils.isEmpty(phoneNumber) ? mContext.getString(R.string.device_info_default)
@@ -143,6 +164,6 @@ protected String getFormattedPhoneNumber(SubscriptionInfo subscriptionInfo) {
@VisibleForTesting
protected Preference createNewPreference(Context context) {
- return new Preference(context);
+ return new PhoneNumberSummaryPreference(context);
}
}
diff --git a/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceController.java b/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceController.java
index c925c27c411..ac5e2430c2b 100644
--- a/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceController.java
@@ -16,9 +16,8 @@
package com.android.settings.deviceinfo;
import android.content.Context;
-import android.content.Intent;
-import android.provider.Settings;
+import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -26,7 +25,6 @@ public class RegulatoryInfoPreferenceController extends AbstractPreferenceContro
PreferenceControllerMixin {
private static final String KEY_REGULATORY_INFO = "regulatory_info";
- private static final Intent INTENT_PROBE = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
public RegulatoryInfoPreferenceController(Context context) {
super(context);
@@ -34,7 +32,7 @@ public RegulatoryInfoPreferenceController(Context context) {
@Override
public boolean isAvailable() {
- return !mContext.getPackageManager().queryIntentActivities(INTENT_PROBE, 0).isEmpty();
+ return mContext.getResources().getBoolean(R.bool.config_show_regulatory_info);
}
@Override
diff --git a/src/com/android/settings/deviceinfo/SleeptimePreferenceController.java b/src/com/android/settings/deviceinfo/SleeptimePreferenceController.java
new file mode 100644
index 00000000000..0bcefa54759
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/SleeptimePreferenceController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021-2024 crDroid Android 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.deviceinfo;
+
+import android.content.Context;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.deviceinfo.AbstractSleeptimePreferenceController;
+
+/**
+ * Concrete subclass of uptime preference controller
+ */
+public class SleeptimePreferenceController extends AbstractSleeptimePreferenceController
+ implements PreferenceControllerMixin {
+ public SleeptimePreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+
+ // This space intentionally left blank
+}
diff --git a/src/com/android/settings/deviceinfo/StorageWizardInit.java b/src/com/android/settings/deviceinfo/StorageWizardInit.java
index b912ffe4958..bd24fdf54cb 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardInit.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardInit.java
@@ -170,7 +170,7 @@ private void setupHyperlink() {
private Spannable styleFont(String text) {
Spannable s = (Spannable) Html.fromHtml(text);
for (URLSpan span : s.getSpans(0, s.length(), URLSpan.class)) {
- TypefaceSpan typefaceSpan = new TypefaceSpan("sans-serif-medium");
+ TypefaceSpan typefaceSpan = new TypefaceSpan("@*android:string/config_bodyFontFamilyMedium");
s.setSpan(typefaceSpan, s.getSpanStart(span), s.getSpanEnd(span), 0);
}
return s;
diff --git a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java
index 50b60973d5b..bbd447b0e0d 100644
--- a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java
+++ b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java
@@ -23,6 +23,7 @@
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserManager;
+import android.util.Log;
import android.view.View;
import com.android.settings.R;
@@ -37,6 +38,7 @@
import com.android.settings.deviceinfo.ManualPreferenceController;
import com.android.settings.deviceinfo.RegulatoryInfoPreferenceController;
import com.android.settings.deviceinfo.SafetyInfoPreferenceController;
+import com.android.settings.deviceinfo.SleeptimePreferenceController;
import com.android.settings.deviceinfo.UptimePreferenceController;
import com.android.settings.deviceinfo.WifiMacAddressPreferenceController;
import com.android.settings.deviceinfo.imei.ImeiInfoPreferenceController;
@@ -57,6 +59,12 @@
import java.util.concurrent.Executors;
import java.util.function.Consumer;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.TelephonyIntents;
+
@SearchIndexable
public class MyDeviceInfoFragment extends DashboardFragment
implements DeviceNamePreferenceController.DeviceNamePreferenceHost {
@@ -65,6 +73,17 @@ public class MyDeviceInfoFragment extends DashboardFragment
private static final String KEY_EID_INFO = "eid_info";
private static final String KEY_MY_DEVICE_INFO_HEADER = "my_device_info_header";
+ private final BroadcastReceiver mSimStateReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+ String state = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ Log.d(LOG_TAG, "Received ACTION_SIM_STATE_CHANGED: " + state);
+ updatePreferenceStates();
+ }
+ }
+ };
+
private BuildNumberPreferenceController mBuildNumberPreferenceController;
@Override
@@ -91,6 +110,29 @@ public void onStart() {
initHeader();
}
+ @Override
+ public void onPause() {
+ super.onPause();
+ Context context = getContext();
+ if (context != null) {
+ context.unregisterReceiver(mSimStateReceiver);
+ } else {
+ Log.i(LOG_TAG, "context already null, not unregistering SimStateReceiver");
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ Context context = getContext();
+ if (context != null) {
+ context.registerReceiver(mSimStateReceiver,
+ new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED));
+ } else {
+ Log.i(LOG_TAG, "context is null, not registering SimStateReceiver");
+ }
+ }
+
@Override
protected String getLogTag() {
return LOG_TAG;
@@ -124,6 +166,7 @@ private static List buildPreferenceControllers(
controllers.add(new ManualPreferenceController(context));
controllers.add(new FeedbackPreferenceController(fragment, context));
controllers.add(new FccEquipmentIdPreferenceController(context));
+ controllers.add(new SleeptimePreferenceController(context, lifecycle));
controllers.add(new UptimePreferenceController(context, lifecycle));
Consumer imeiInfoList = imeiKey -> {
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java
index b022fcf7df2..f7dff9d3d8f 100644
--- a/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java
@@ -36,7 +36,8 @@ public BatteryCycleCountPreferenceController(Context context,
@Override
public int getAvailabilityStatus() {
- return AVAILABLE;
+ return mContext.getResources().getBoolean(R.bool.config_show_battery_cycle_count)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
@@ -44,7 +45,7 @@ public CharSequence getSummary() {
final Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
final int cycleCount = batteryIntent.getIntExtra(BatteryManager.EXTRA_CYCLE_COUNT, -1);
- return cycleCount == -1
+ return cycleCount <= 0
? mContext.getText(R.string.battery_cycle_count_not_available)
: Integer.toString(cycleCount);
}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryDesignCapacityPreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryDesignCapacityPreferenceController.java
new file mode 100644
index 00000000000..78f415a8c14
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryDesignCapacityPreferenceController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 Paranoid Android
+ *
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+/**
+ * A controller that manages the information about battery design capacity.
+ */
+public class BatteryDesignCapacityPreferenceController extends BasePreferenceController {
+
+ public BatteryDesignCapacityPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ boolean isFeatureEnabled = mContext.getResources().getBoolean(R.bool.config_show_battery_design_capacity);
+ return isFeatureEnabled ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+ final int designCapacityUah =
+ batteryIntent.getIntExtra(BatteryManager.EXTRA_DESIGN_CAPACITY, -1);
+
+ if (designCapacityUah > 0) {
+ int designCapacity = designCapacityUah / 1_000;
+ return mContext.getString(R.string.battery_design_capacity_summary, designCapacity);
+ }
+
+ return mContext.getString(R.string.battery_design_capacity_not_available);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryHealthPreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryHealthPreferenceController.java
new file mode 100644
index 00000000000..71825f884a8
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryHealthPreferenceController.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 Paranoid Android
+ *
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+/**
+ * A controller that manages the information about battery health.
+ */
+public class BatteryHealthPreferenceController extends BasePreferenceController {
+
+ public BatteryHealthPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+ final int health =
+ batteryIntent.getIntExtra(
+ BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
+
+ switch (health) {
+ case BatteryManager.BATTERY_HEALTH_GOOD:
+ return mContext.getString(R.string.battery_health_good);
+ case BatteryManager.BATTERY_HEALTH_OVERHEAT:
+ return mContext.getString(R.string.battery_health_overheat);
+ case BatteryManager.BATTERY_HEALTH_DEAD:
+ return mContext.getString(R.string.battery_health_dead);
+ case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
+ return mContext.getString(R.string.battery_health_over_voltage);
+ case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
+ return mContext.getString(R.string.battery_health_unspecified_failure);
+ case BatteryManager.BATTERY_HEALTH_COLD:
+ return mContext.getString(R.string.battery_health_cold);
+ default:
+ return mContext.getString(R.string.battery_health_unknown);
+ }
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java
index c0170227bae..882901d9b7b 100644
--- a/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java
@@ -18,18 +18,23 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.os.Bundle;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.FooterPreference;
/** A fragment that shows battery hardware information. */
@SearchIndexable
public class BatteryInfoFragment extends DashboardFragment {
public static final String TAG = "BatteryInfo";
+ private static final String KEY_BATTERY_INFO_FOOTER = "battery_info_footer";
+
+ private FooterPreference mFooterPreference;
@Override
public int getMetricsCategory() {
@@ -41,6 +46,19 @@ protected int getPreferenceScreenResId() {
return R.xml.battery_info;
}
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mFooterPreference = findPreference(KEY_BATTERY_INFO_FOOTER);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mFooterPreference.setVisible(
+ getContext().getResources().getBoolean(R.bool.config_show_battery_cycle_count));
+ }
+
@Override
protected String getLogTag() {
return TAG;
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryMaximumCapacityPreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryMaximumCapacityPreferenceController.java
new file mode 100644
index 00000000000..e36f3e8a59f
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryMaximumCapacityPreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 Paranoid Android
+ *
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+/**
+ * A controller that manages the information about battery maximum capacity.
+ */
+public class BatteryMaximumCapacityPreferenceController extends BasePreferenceController {
+
+ public BatteryMaximumCapacityPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ boolean isFeatureEnabled = mContext.getResources().getBoolean(R.bool.config_show_battery_maximum_capacity);
+ return isFeatureEnabled ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+ final int maxCapacityUah =
+ batteryIntent.getIntExtra(BatteryManager.EXTRA_MAXIMUM_CAPACITY, -1);
+ final int designCapacityUah =
+ batteryIntent.getIntExtra(BatteryManager.EXTRA_DESIGN_CAPACITY, -1);
+
+ if (maxCapacityUah > 0 && designCapacityUah > 0) {
+ int maxCapacity = maxCapacityUah / 1_000;
+ int designCapacity = designCapacityUah / 1_000;
+ int percentage = (maxCapacity * 100) / designCapacity;
+
+ return mContext.getString(
+ R.string.battery_maximum_capacity_summary, maxCapacity, percentage);
+ }
+
+ return mContext.getString(R.string.battery_maximum_capacity_not_available);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryTechnologyPreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryTechnologyPreferenceController.java
new file mode 100644
index 00000000000..d9c42511cdf
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryTechnologyPreferenceController.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 Paranoid Android
+ *
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+/**
+ * A controller that manages the information about battery technology.
+ */
+public class BatteryTechnologyPreferenceController extends BasePreferenceController {
+
+ public BatteryTechnologyPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+ final String technology = batteryIntent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
+
+ return technology != null
+ ? technology
+ : mContext.getText(R.string.battery_technology_not_available);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryTemperaturePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryTemperaturePreferenceController.java
new file mode 100644
index 00000000000..8049f325114
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryTemperaturePreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 Paranoid Android
+ *
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.icu.text.MeasureFormat;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+import java.util.Locale;
+
+/**
+ * A controller that manages the information about battery temperature.
+ */
+public class BatteryTemperaturePreferenceController extends BasePreferenceController {
+
+ public BatteryTemperaturePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+ final int temperatureTenths =
+ batteryIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
+
+ if (temperatureTenths > 0) {
+ float temperature = temperatureTenths / 10f;
+
+ return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.SHORT)
+ .format(new Measure(temperature, MeasureUnit.CELSIUS));
+ }
+
+ return mContext.getText(R.string.battery_temperature_not_available);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryVoltagePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryVoltagePreferenceController.java
new file mode 100644
index 00000000000..0fc408f4ec2
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryVoltagePreferenceController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 Paranoid Android
+ *
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.icu.text.MeasureFormat;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+import java.util.Locale;
+
+/**
+ * A controller that manages the information about battery voltage.
+ */
+public class BatteryVoltagePreferenceController extends BasePreferenceController {
+
+ public BatteryVoltagePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+ final int voltageMillivolts = batteryIntent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
+
+ if (voltageMillivolts > 0) {
+ float voltage = voltageMillivolts / 1_000f;
+
+ return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.SHORT)
+ .format(new Measure(voltage, MeasureUnit.VOLT));
+ }
+
+ return mContext.getText(R.string.battery_voltage_not_available);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/AboutDeviceNamePreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/AboutDeviceNamePreferenceController.java
new file mode 100644
index 00000000000..a960249ebde
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/AboutDeviceNamePreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019-2023 crDroid Android 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.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+public class AboutDeviceNamePreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "AboutDeviceNameCtrl";
+
+ private static final String KEY_MARKET_NAME_PROP = "ro.product.marketname";
+ private static final String KEY_BRAND_NAME_PROP = "ro.product.manufacturer";
+ private static final String KEY_DEVICE_NAME_PROP = "ro.product.device";
+
+ public AboutDeviceNamePreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ String deviceBrand = SystemProperties.get(KEY_BRAND_NAME_PROP,
+ mContext.getString(R.string.device_info_default));
+ String deviceCodename = SystemProperties.get(KEY_DEVICE_NAME_PROP,
+ mContext.getString(R.string.device_info_default));
+ String deviceModel = Build.MODEL;
+ String deviceMarketname = SystemProperties.get(KEY_MARKET_NAME_PROP,
+ deviceBrand + " " + deviceModel);
+ return deviceMarketname + " | " + deviceCodename;
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java
index 235638433be..f3f330f1e9c 100644
--- a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.os.SystemProperties;
+import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
@@ -42,8 +43,14 @@ public int getAvailabilityStatus() {
@Override
public CharSequence getSummary() {
- return SystemProperties.get(BASEBAND_PROPERTY,
+ String baseband = SystemProperties.get(BASEBAND_PROPERTY,
mContext.getString(R.string.device_info_default));
+ for (String str : baseband.split(",")) {
+ if (!TextUtils.isEmpty(str)) {
+ return str;
+ }
+ }
+ return baseband;
}
}
// LINT.ThenChange(BasebandVersionPreference.kt)
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java
index 7a3bdafb5e7..16192203217 100644
--- a/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java
@@ -20,10 +20,22 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.DeviceInfoUtils;
+import androidx.preference.Preference;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
// LINT.IfChange
public class KernelVersionPreferenceController extends BasePreferenceController {
+ private static final String KEY_KERNEL_VERSION = "kernel_version";
+ private static final String FILENAME_PROC_VERSION = "/proc/version";
+ private static final String LOG_TAG = "KernelVersionPreferenceController";
+ private boolean fullKernelVersion = false;
+
public KernelVersionPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@@ -37,5 +49,52 @@ public int getAvailabilityStatus() {
public CharSequence getSummary() {
return DeviceInfoUtils.getFormattedKernelVersion(mContext);
}
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_KERNEL_VERSION;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!TextUtils.equals(preference.getKey(), KEY_KERNEL_VERSION)) {
+ return false;
+ }
+ if(fullKernelVersion) {
+ preference.setSummary(DeviceInfoUtils.getFormattedKernelVersion(mContext));
+ fullKernelVersion = false;
+ } else {
+ preference.setSummary(getFullKernelVersion());
+ fullKernelVersion = true;
+ }
+ return false;
+ }
+
+ private String getFullKernelVersion() {
+ String procVersionStr;
+ try {
+ procVersionStr = readLine(FILENAME_PROC_VERSION);
+ return procVersionStr;
+ } catch (IOException e) {
+ Log.e(LOG_TAG,
+ "IO Exception when getting kernel version for Device Info screen", e);
+ return "Unavailable";
+ }
+ }
+
+ /**
+ * Reads a line from the specified file.
+ * @param filename the file to read from
+ * @return the first line, if any.
+ * @throws IOException if the file couldn't be read
+ */
+ private static String readLine(String filename) throws IOException {
+ BufferedReader reader = new BufferedReader(new FileReader(filename), 256);
+ try {
+ return reader.readLine();
+ } finally {
+ reader.close();
+ }
+ }
}
// LINT.ThenChange(KernelVersionPreference.kt)
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/LineageBuildDatePreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/LineageBuildDatePreferenceController.java
new file mode 100644
index 00000000000..468ea5e5dd1
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/LineageBuildDatePreferenceController.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The LineageOS 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.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+public class LineageBuildDatePreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "LineageBuildDateCtrl";
+
+ private static final String KEY_BUILD_DATE_PROP = "ro.build.date";
+
+ public LineageBuildDatePreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return SystemProperties.get(KEY_BUILD_DATE_PROP,
+ mContext.getString(R.string.unknown));
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/LineageVendorSecurityPatchLevelPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/LineageVendorSecurityPatchLevelPreferenceController.java
new file mode 100644
index 00000000000..40887ee5805
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/LineageVendorSecurityPatchLevelPreferenceController.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The LineageOS 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.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.text.format.DateFormat;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class LineageVendorSecurityPatchLevelPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "LineageVendorSecurityPatchCtrl";
+
+ private static final String KEY_AOSP_VENDOR_SECURITY_PATCH =
+ "ro.vendor.build.security_patch";
+
+ private static final String KEY_LINEAGE_VENDOR_SECURITY_PATCH =
+ "ro.lineage.build.vendor_security_patch";
+
+ public LineageVendorSecurityPatchLevelPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ String patchLevel = SystemProperties.get(KEY_AOSP_VENDOR_SECURITY_PATCH);
+
+ if (patchLevel.isEmpty()) {
+ patchLevel = SystemProperties.get(KEY_LINEAGE_VENDOR_SECURITY_PATCH);
+ }
+
+ if (!patchLevel.isEmpty()) {
+ try {
+ SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd");
+ Date patchLevelDate = template.parse(patchLevel);
+ String format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "dMMMMyyyy");
+ patchLevel = DateFormat.format(format, patchLevelDate).toString();
+ } catch (ParseException e) {
+ // parsing failed, use raw string
+ }
+ } else {
+ patchLevel = mContext.getString(R.string.unknown);
+ }
+
+ return patchLevel;
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/LineageVersionDetailPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/LineageVersionDetailPreferenceController.java
new file mode 100644
index 00000000000..3dda08240ed
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/LineageVersionDetailPreferenceController.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019-2024 The LineageOS 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.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.slices.Sliceable;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+
+public class LineageVersionDetailPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "lineageVersionDialogCtrl";
+ private static final int DELAY_TIMER_MILLIS = 500;
+ private static final int ACTIVITY_TRIGGER_COUNT = 3;
+
+ private static final String KEY_LINEAGE_VERSION_PROP = "ro.modversion";
+ private static final String KEY_MATRIXX_BUILD_VERSION_PROP = "ro.modversion";
+ private static final String KEY_MATRIXX_DEVICE_PROP = "ro.matrixx.device";
+ private static final String KEY_MATRIXX_RELEASE_TYPE_PROP = "ro.matrixx.release.type";
+ private static final String KEY_MATRIXX_RELEASE_VERSION_PROP = "ro.matrixx.display.version";
+ private static final String KEY_MATRIXX_RELEASE_CODE_PROP = "ro.matrixx.release.code";
+ private static final String KEY_MATRIXX_VARIANT_PROP = "ro.matrixx.build.variant";
+
+ private static final String PLATLOGO_PACKAGE_NAME = "com.android.egg";
+ private static final String PLATLOGO_ACTIVITY_CLASS =
+ PLATLOGO_PACKAGE_NAME + ".EasterEgg";
+
+ private final UserManager mUserManager;
+ private final long[] mHits = new long[ACTIVITY_TRIGGER_COUNT];
+
+ private RestrictedLockUtils.EnforcedAdmin mFunDisallowedAdmin;
+ private boolean mFunDisallowedBySystem;
+ private boolean fullRomVersion = false;
+
+ public LineageVersionDetailPreferenceController(Context context, String key) {
+ super(context, key);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ initializeAdminPermissions();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean useDynamicSliceSummary() {
+ return true;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return true;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return shortRomVersion();
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ return false;
+ }
+ if (fullRomVersion) {
+ preference.setSummary(shortRomVersion());
+ fullRomVersion = false;
+ } else {
+ preference.setSummary(SystemProperties.get(KEY_MATRIXX_RELEASE_VERSION_PROP,
+ mContext.getString(R.string.unknown)));
+ fullRomVersion = true;
+ }
+ if (Utils.isMonkeyRunning()) {
+ return false;
+ }
+ arrayCopy();
+ mHits[mHits.length - 1] = SystemClock.uptimeMillis();
+ if (mHits[0] >= (SystemClock.uptimeMillis() - DELAY_TIMER_MILLIS)) {
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN)) {
+ if (mFunDisallowedAdmin != null && !mFunDisallowedBySystem) {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext,
+ mFunDisallowedAdmin);
+ }
+ Log.d(TAG, "Sorry, no fun for you!");
+ return true;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_MAIN)
+ .setClassName(PLATLOGO_PACKAGE_NAME, PLATLOGO_ACTIVITY_CLASS);
+ try {
+ mContext.startActivity(intent);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to start activity " + intent.toString());
+ }
+ }
+ return true;
+ }
+
+ private String shortRomVersion() {
+ String romVersion = SystemProperties.get(KEY_MATRIXX_BUILD_VERSION_PROP,
+ this.mContext.getString(R.string.device_info_default));
+ String romReleasetype = SystemProperties.get(KEY_MATRIXX_RELEASE_TYPE_PROP,
+ this.mContext.getString(R.string.device_info_default));
+ String romVariant = SystemProperties.get(KEY_MATRIXX_VARIANT_PROP,
+ this.mContext.getString(R.string.device_info_default));
+ String romReleaseCode = SystemProperties.get(KEY_MATRIXX_RELEASE_CODE_PROP,
+ this.mContext.getString(R.string.device_info_default));
+ String shortVersion = romVersion + " | " + romReleaseCode + " | " + romReleasetype + " | " + romVariant;
+ return shortVersion;
+ }
+
+ /**
+ * Copies the array onto itself to remove the oldest hit.
+ */
+ @VisibleForTesting
+ void arrayCopy() {
+ System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
+ }
+
+ @VisibleForTesting
+ void initializeAdminPermissions() {
+ mFunDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ mContext, UserManager.DISALLOW_FUN, UserHandle.myUserId());
+ mFunDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ mContext, UserManager.DISALLOW_FUN, UserHandle.myUserId());
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/MainlineModuleVersionPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/MainlineModuleVersionPreferenceController.java
index 4c02feb044f..b8f08c86941 100644
--- a/src/com/android/settings/deviceinfo/firmwareversion/MainlineModuleVersionPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/firmwareversion/MainlineModuleVersionPreferenceController.java
@@ -70,7 +70,8 @@ public MainlineModuleVersionPreferenceController(Context context, String key) {
@Override
public int getAvailabilityStatus() {
- return !TextUtils.isEmpty(mModuleVersion) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ return !TextUtils.isEmpty(mModuleVersion) &&
+ parseDateFromVersionName(mModuleVersion).isPresent() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
private void initModules() {
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/MatrixxMaintainerPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/MatrixxMaintainerPreferenceController.java
new file mode 100644
index 00000000000..1fcacad95ec
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/firmwareversion/MatrixxMaintainerPreferenceController.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 VoidUI 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.deviceinfo.firmwareversion;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.TextUtils;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+public class MatrixxMaintainerPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "MatrixxMaintainerPreferenceController";
+
+ public MatrixxMaintainerPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ public CharSequence getSummary() {
+ String maintainer = mContext.getResources().getString(R.string.matrixx_maintainer);
+ return maintainer;
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/PlatformRevisionPreferenceController.java b/src/com/android/settings/deviceinfo/hardwareinfo/PlatformRevisionPreferenceController.java
new file mode 100644
index 00000000000..90818b28509
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/hardwareinfo/PlatformRevisionPreferenceController.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.deviceinfo.hardwareinfo;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.slices.Sliceable;
+
+public class PlatformRevisionPreferenceController extends BasePreferenceController {
+
+ public PlatformRevisionPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mContext.getResources().getBoolean(R.bool.config_show_device_model)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public boolean useDynamicSliceSummary() {
+ return true;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return true;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return SystemProperties.get("ro.board.platform");
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/TotalRAMPreferenceController.java b/src/com/android/settings/deviceinfo/hardwareinfo/TotalRAMPreferenceController.java
new file mode 100644
index 00000000000..5f7680ea9f1
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/hardwareinfo/TotalRAMPreferenceController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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.deviceinfo.hardwareinfo;
+
+import android.content.Context;
+import android.text.format.Formatter;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.applications.ProcStatsData;
+import com.android.settings.applications.ProcessStatsBase;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.utils.ThreadUtils;
+
+public class TotalRAMPreferenceController extends BasePreferenceController implements
+ PreferenceControllerMixin {
+
+ private ProcStatsData mProcStatsData;
+ private PreferenceScreen mPreferenceScreen;
+
+ public TotalRAMPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mContext.getResources().getBoolean(R.bool.config_show_device_model)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mProcStatsData = getProcStatsData();
+ mPreferenceScreen = screen;
+ setDuration();
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ // This is posted on the background thread to speed up fragment launch time for dev options
+ // mProcStasData.refreshStats(true) takes ~20ms to run.
+ ThreadUtils.postOnBackgroundThread(() -> {
+ mProcStatsData.refreshStats(true);
+ final ProcStatsData.MemInfo memInfo = mProcStatsData.getMemInfo();
+ final String totalResult = Formatter.formatShortFileSize(mContext,
+ (long) memInfo.realTotalRam);
+ ThreadUtils.postOnMainThread(
+ () -> mPreferenceScreen.findPreference(mPreferenceKey).setSummary(totalResult));
+ });
+ }
+
+ @VisibleForTesting
+ void setDuration() {
+ mProcStatsData.setDuration(ProcessStatsBase.sDurations[0] /* 3 hours */);
+ }
+
+ @VisibleForTesting
+ ProcStatsData getProcStatsData() {
+ return new ProcStatsData(mContext, false);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java b/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java
index 1ae6b4007b3..16440b25555 100644
--- a/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java
+++ b/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogController.java
@@ -100,8 +100,7 @@ private void updateDialogForCdmaPhone() {
mDialog.setText(ID_PRL_VERSION_VALUE, getCdmaPrlVersion());
- if ((mSubscriptionInfo != null && isCdmaLteEnabled()) ||
- (mSubscriptionInfo == null && isSimPresent(mSlotId))) {
+ if (isCdmaLteEnabled()) {
// Show IMEI for LTE device
mDialog.setText(ID_IMEI_VALUE, mTelephonyManager.getImei(mSlotId));
mDialog.setText(ID_IMEI_SV_VALUE,
diff --git a/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java b/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java
index ff55184741a..2045b9661c0 100644
--- a/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/imei/ImeiInfoPreferenceController.java
@@ -33,6 +33,7 @@
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.deviceinfo.PhoneNumberSummaryPreference;
import com.android.settings.deviceinfo.simstatus.SlotSimStatus;
import com.android.settings.network.SubscriptionUtil;
import com.android.settingslib.Utils;
@@ -102,7 +103,6 @@ public void displayPreference(PreferenceScreen screen) {
multiImeiPreference.setKey(DEFAULT_KEY + (1 + simSlotNumber));
multiImeiPreference.setEnabled(true);
multiImeiPreference.setCopyingEnabled(true);
-
category.addPreference(multiImeiPreference);
}
}
@@ -112,6 +112,11 @@ public void updateState(Preference preference) {
updatePreference(preference, keyToSlotIndex(preference.getKey()));
}
+ @Override
+ public CharSequence getSummary() {
+ return mContext.getString(R.string.device_info_protected_single_press);
+ }
+
private CharSequence getSummary(int simSlot) {
final int phoneType = getPhoneType(simSlot);
return phoneType == PHONE_TYPE_CDMA ? mTelephonyManager.getMeid(simSlot)
@@ -145,12 +150,8 @@ public boolean useDynamicSliceSummary() {
@VisibleForTesting
protected void updatePreference(Preference preference, int simSlot) {
- if (simSlot < 0) {
- preference.setVisible(false);
- return;
- }
preference.setTitle(getTitle(simSlot));
- preference.setSummary(getSummary(simSlot));
+ preference.setSummary(getSummary());
}
private CharSequence getTitleForGsmPhone(int simSlot, boolean isPrimaryImei) {
@@ -194,6 +195,6 @@ public int getPhoneType(int slotIndex) {
@VisibleForTesting
Preference createNewPreference(Context context) {
- return new Preference(context);
+ return new PhoneNumberSummaryPreference(context);
}
}
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimEidPreferenceController.kt b/src/com/android/settings/deviceinfo/simstatus/SimEidPreferenceController.kt
index f765d8c6cca..e0376dc1944 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimEidPreferenceController.kt
+++ b/src/com/android/settings/deviceinfo/simstatus/SimEidPreferenceController.kt
@@ -92,7 +92,6 @@ class SimEidPreferenceController(context: Context, preferenceKey: String) :
}
preference.title = title
preference.dialogTitle = title
- preference.summary = eid
updateDialog()
}
}
@@ -131,6 +130,9 @@ class SimEidPreferenceController(context: Context, preferenceKey: String) :
val qrCodeView = dialog.requireViewById(R.id.esim_id_qrcode)
qrCodeView.setImageBitmap(getEidQrCode(eid))
+
+ // After "Tap to show", eid is displayed on preference.
+ preference.summary = textView.text
}
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index b5ee1d88108..75ab3538e79 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -104,7 +104,15 @@ public class SimStatusDialogController implements DefaultLifecycleObserver {
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
+ SubscriptionInfo oldSubInfo = mSubscriptionInfo;
mSubscriptionInfo = getPhoneSubscriptionInfo(mSlotIndex);
+ if (oldSubInfo != null && mSubscriptionInfo == null) {
+ updateDataState(TelephonyManager.DATA_UNKNOWN);
+ }
+ if (isNeedReregisterTelephonyCallback(oldSubInfo, mSubscriptionInfo)) {
+ unregisterTelephonyCallback();
+ registerTelephonyCallback();
+ }
updateSubscriptionStatus();
}
};
@@ -123,7 +131,6 @@ public void onSubscriptionsChanged() {
private final Context mContext;
private boolean mShowLatestAreaInfo;
- private boolean mIsRegisteredListener = false;
private final BroadcastReceiver mAreaInfoReceiver = new BroadcastReceiver() {
@Override
@@ -183,6 +190,10 @@ public SimStatusDialogController(@NonNull SimStatusDialogFragment dialog, Lifecy
mSubscriptionInfo = getPhoneSubscriptionInfo(slotId);
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ if (mSubscriptionInfo != null) {
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(mSubscriptionInfo.getSubscriptionId());
+ }
mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
mEuiccManager = mContext.getSystemService(EuiccManager.class);
mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
@@ -203,9 +214,6 @@ public void initialize() {
if (mSubscriptionInfo == null) {
return;
}
- mTelephonyManager =
- getTelephonyManager().createForSubscriptionId(mSubscriptionInfo.getSubscriptionId());
- mTelephonyCallback = new SimStatusDialogTelephonyCallback();
updateLatestAreaInfo();
updateSubscriptionStatus();
}
@@ -242,13 +250,7 @@ public void deinitialize() {
*/
@Override
public void onResume(@NonNull LifecycleOwner owner) {
- if (mSubscriptionInfo == null) {
- return;
- }
- mTelephonyManager = getTelephonyManager().createForSubscriptionId(
- mSubscriptionInfo.getSubscriptionId());
- getTelephonyManager()
- .registerTelephonyCallback(mContext.getMainExecutor(), mTelephonyCallback);
+ registerTelephonyCallback();
mSubscriptionManager.addOnSubscriptionsChangedListener(
mContext.getMainExecutor(), mOnSubscriptionsChangedListener);
collectSimStatusDialogInfo(owner);
@@ -259,8 +261,6 @@ public void onResume(@NonNull LifecycleOwner owner) {
new IntentFilter(CellBroadcastIntents.ACTION_AREA_INFO_UPDATED),
Context.RECEIVER_EXPORTED/*UNAUDITED*/);
}
-
- mIsRegisteredListener = true;
}
/**
@@ -268,22 +268,9 @@ public void onResume(@NonNull LifecycleOwner owner) {
*/
@Override
public void onPause(@NonNull LifecycleOwner owner) {
- if (mSubscriptionInfo == null) {
- if (mIsRegisteredListener) {
- mSubscriptionManager.removeOnSubscriptionsChangedListener(
- mOnSubscriptionsChangedListener);
- getTelephonyManager().unregisterTelephonyCallback(mTelephonyCallback);
- if (mShowLatestAreaInfo) {
- mContext.unregisterReceiver(mAreaInfoReceiver);
- }
- mIsRegisteredListener = false;
- }
- return;
- }
-
- mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
- getTelephonyManager().unregisterTelephonyCallback(mTelephonyCallback);
-
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(
+ mOnSubscriptionsChangedListener);
+ unregisterTelephonyCallback();
if (mShowLatestAreaInfo) {
mContext.unregisterReceiver(mAreaInfoReceiver);
}
@@ -555,6 +542,46 @@ private SubscriptionInfo getPhoneSubscriptionInfo(int slotId) {
return SubscriptionManager.from(mContext).getActiveSubscriptionInfoForSimSlotIndex(slotId);
}
+ private void registerTelephonyCallback() {
+ if (mTelephonyCallback != null || mSubscriptionInfo == null) {
+ return;
+ }
+
+ mTelephonyCallback = new SimStatusDialogTelephonyCallback();
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(mSubscriptionInfo.getSubscriptionId());
+ mTelephonyManager.registerTelephonyCallback(
+ mContext.getMainExecutor(), mTelephonyCallback);
+ }
+
+ private void unregisterTelephonyCallback() {
+ if (mTelephonyCallback == null) {
+ return;
+ }
+
+ getTelephonyManager().unregisterTelephonyCallback(mTelephonyCallback);
+ mTelephonyCallback = null;
+ }
+
+ private boolean isNeedReregisterTelephonyCallback(SubscriptionInfo oldSubInfo,
+ SubscriptionInfo newSubInfo) {
+ if (newSubInfo == null) {
+ return false;
+ }
+
+ if (oldSubInfo == null) {
+ return true;
+ }
+
+ int oldSubId = oldSubInfo.getSubscriptionId();
+ int newSubId = newSubInfo.getSubscriptionId();
+ if (oldSubId != newSubId) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
@VisibleForTesting
class SimStatusDialogTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
diff --git a/src/com/android/settings/display/AutoBrightnessOneShotPreferenceController.java b/src/com/android/settings/display/AutoBrightnessOneShotPreferenceController.java
new file mode 100644
index 00000000000..aebcd3d32d3
--- /dev/null
+++ b/src/com/android/settings/display/AutoBrightnessOneShotPreferenceController.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import android.content.Context;
+
+import lineageos.providers.LineageSettings;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+public class AutoBrightnessOneShotPreferenceController extends TogglePreferenceController {
+
+ public AutoBrightnessOneShotPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return LineageSettings.System.getInt(mContext.getContentResolver(),
+ LineageSettings.System.AUTO_BRIGHTNESS_ONE_SHOT, 0) == 1;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ LineageSettings.System.putInt(mContext.getContentResolver(),
+ LineageSettings.System.AUTO_BRIGHTNESS_ONE_SHOT, isChecked ? 1 : 0);
+ return true;
+ }
+
+ @Override
+ @AvailabilityStatus
+ public int getAvailabilityStatus() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available)
+ ? AVAILABLE_UNSEARCHABLE
+ : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return mContext.getText(isChecked()
+ ? R.string.auto_brightness_summary_on
+ : R.string.auto_brightness_summary_off);
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_display;
+ }
+}
diff --git a/src/com/android/settings/display/BrightnessLevelPreferenceController.java b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
index a32c965cb29..afa83a00bd1 100644
--- a/src/com/android/settings/display/BrightnessLevelPreferenceController.java
+++ b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
@@ -36,6 +36,8 @@
import android.os.UserManager;
import android.provider.Settings.System;
import android.text.TextUtils;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -175,7 +177,7 @@ private void updatedSummary(Preference preference) {
private double getCurrentBrightness() {
int value = 0;
- final BrightnessInfo info = mContext.getDisplay().getBrightnessInfo();
+ final BrightnessInfo info = getDisplay().getBrightnessInfo();
if (info != null) {
value = convertLinearToGammaFloat(info.brightness, info.brightnessMinimum,
info.brightnessMaximum);
@@ -183,6 +185,23 @@ private double getCurrentBrightness() {
return getPercentage(value, GAMMA_SPACE_MIN, GAMMA_SPACE_MAX);
}
+ private Display getDisplay() {
+ final Display currentDisplay = mContext.getDisplay();
+ final DisplayInfo info = new DisplayInfo();
+ currentDisplay.getDisplayInfo(info);
+
+ if (info.type == Display.TYPE_OVERLAY) {
+ // try to get the default (internal) display if we are in an overlay display,
+ // otherwise the brightness is always zero.
+ final Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ if (defaultDisplay != null) {
+ return defaultDisplay;
+ }
+ }
+
+ return currentDisplay;
+ }
+
private double getPercentage(double value, int min, int max) {
if (value > max) {
return 1.0;
diff --git a/src/com/android/settings/display/ColorModePreferenceController.java b/src/com/android/settings/display/ColorModePreferenceController.java
index 6cd4867b588..14a2241481c 100644
--- a/src/com/android/settings/display/ColorModePreferenceController.java
+++ b/src/com/android/settings/display/ColorModePreferenceController.java
@@ -28,8 +28,11 @@ public ColorModePreferenceController(Context context, String key) {
@Override
public int getAvailabilityStatus() {
+ final int[] availableColorModes = mContext.getResources().getIntArray(
+ com.android.internal.R.array.config_availableColorModes);
return mContext.getSystemService(ColorDisplayManager.class)
.isDeviceColorManaged()
+ && availableColorModes.length > 0
&& !ColorDisplayManager.areAccessibilityTransformsEnabled(mContext) ?
AVAILABLE : DISABLED_FOR_USER;
}
diff --git a/src/com/android/settings/display/ColorModePreferenceFragment.java b/src/com/android/settings/display/ColorModePreferenceFragment.java
index ce33e673fde..74bd8822f15 100644
--- a/src/com/android/settings/display/ColorModePreferenceFragment.java
+++ b/src/com/android/settings/display/ColorModePreferenceFragment.java
@@ -31,10 +31,12 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.provider.Settings;
import android.provider.Settings.Secure;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
@@ -79,6 +81,9 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment {
private ImageView[] mDotIndicators;
private View[] mViewPagerImages;
+
+ private ContentObserver mDisplayEngineModeObserver;
+ private TextView mDisplayEngineModeText;
@Override
public void onAttach(Context context) {
@@ -105,6 +110,17 @@ public void onChange(boolean selfChange, Uri uri) {
cr.registerContentObserver(
Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
false /* notifyForDescendants */, mContentObserver, mUserId);
+
+ mDisplayEngineModeObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ updateDisplayEngineModeText();
+ }
+ };
+ cr.registerContentObserver(
+ Secure.getUriFor("display_engine_mode"),
+ false /* notifyForDescendants */, mDisplayEngineModeObserver);
}
@Override
@@ -115,6 +131,7 @@ public void onCreate(Bundle savedInstanceState) {
mViewPager.setCurrentItem(selectedPosition);
updateIndicator(selectedPosition);
}
+ updateDisplayEngineModeText();
}
@Override
@@ -123,6 +140,10 @@ public void onDetach() {
getContext().getContentResolver().unregisterContentObserver(mContentObserver);
mContentObserver = null;
}
+ if (mDisplayEngineModeObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mDisplayEngineModeObserver);
+ mDisplayEngineModeObserver = null;
+ }
super.onDetach();
}
@@ -154,6 +175,7 @@ public ArrayList getViewPagerResource() {
void addViewPager(LayoutPreference preview) {
final ArrayList tmpviewPagerList = getViewPagerResource();
+ mDisplayEngineModeText = preview.findViewById(R.id.display_engine_mode_text);
mViewPager = preview.findViewById(R.id.viewpager);
mViewPagerImages = new View[3];
@@ -199,6 +221,16 @@ void addViewPager(LayoutPreference preview) {
updateIndicator(mViewPager.getCurrentItem());
}
+ @Override
+ public void updateCandidates() {
+ super.updateCandidates();
+ PreferenceScreen screen = getPreferenceScreen();
+ if (ColorDisplayManager.isColorTransformAccelerated(screen.getContext())) {
+ getPreferenceManager().inflateFromResource(screen.getContext(), R.xml.color_mode_settings,
+ screen);
+ }
+ }
+
@Override
protected void addStaticPreferences(PreferenceScreen screen) {
final LayoutPreference preview = new LayoutPreference(screen.getContext(),
@@ -385,6 +417,32 @@ public boolean isViewFromObject(View view, Object object) {
}
}
+ private void updateDisplayEngineModeText() {
+ int mode = Secure.getInt(getContext().getContentResolver(), "display_engine_mode", 0);
+ String prefix = getString(R.string.display_engine_category);
+ String modeText = "";
+ switch (mode) {
+ case 0:
+ modeText = getString(R.string.display_engine_default);
+ break;
+ case 1:
+ modeText = getString(R.string.x_reality_engine_mode_title);
+ break;
+ case 2:
+ modeText = getString(R.string.vivid_engine_mode_title);
+ break;
+ case 3:
+ modeText = getString(R.string.triluminous_display_mode_title);
+ break;
+ default:
+ modeText = getString(R.string.display_engine_default);
+ break;
+ }
+ if (mDisplayEngineModeText != null) {
+ mDisplayEngineModeText.setText(prefix + ": " + modeText);
+ }
+ }
+
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.color_mode_settings) {
diff --git a/src/com/android/settings/development/EnableBlursPreferenceController.java b/src/com/android/settings/display/EnableBlursPreferenceController.java
similarity index 80%
rename from src/com/android/settings/development/EnableBlursPreferenceController.java
rename to src/com/android/settings/display/EnableBlursPreferenceController.java
index d3a521a7798..1360f616df2 100644
--- a/src/com/android/settings/development/EnableBlursPreferenceController.java
+++ b/src/com/android/settings/display/EnableBlursPreferenceController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.development;
+package com.android.settings.display;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
@@ -26,12 +26,12 @@
import androidx.preference.TwoStatePreference;
import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+import com.android.settingslib.core.AbstractPreferenceController;
/**
* Controller that toggles window blurs on devices that support it.
*/
-public final class EnableBlursPreferenceController extends DeveloperOptionsPreferenceController
+public final class EnableBlursPreferenceController extends AbstractPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String ENABLE_BLURS_ON_WINDOWS = "enable_blurs_on_windows";
@@ -69,14 +69,6 @@ public boolean isAvailable() {
public void updateState(Preference preference) {
boolean isEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DISABLE_WINDOW_BLURS, 0) == 0;
- ((TwoStatePreference) mPreference).setChecked(isEnabled);
- }
-
- @Override
- protected void onDeveloperOptionsSwitchDisabled() {
- super.onDeveloperOptionsSwitchDisabled();
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.DISABLE_WINDOW_BLURS, 0);
- updateState(null);
+ ((TwoStatePreference) preference).setChecked(isEnabled);
}
}
diff --git a/src/com/android/settings/display/MinRefreshRatePreferenceController.java b/src/com/android/settings/display/MinRefreshRatePreferenceController.java
new file mode 100644
index 00000000000..b9c63040f26
--- /dev/null
+++ b/src/com/android/settings/display/MinRefreshRatePreferenceController.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import static android.provider.Settings.System.MIN_REFRESH_RATE;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.view.Display;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+public class MinRefreshRatePreferenceController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_MIN_REFRESH_RATE = "min_refresh_rate";
+
+ private ListPreference mListPreference;
+
+ private List mEntries = new ArrayList<>();
+ private List mValues = new ArrayList<>();
+
+ public MinRefreshRatePreferenceController(Context context) {
+ super(context, KEY_MIN_REFRESH_RATE);
+
+ if (mContext.getResources().getBoolean(R.bool.config_show_min_refresh_rate_switch)) {
+ Display.Mode mode = mContext.getDisplay().getMode();
+ Display.Mode[] modes = mContext.getDisplay().getSupportedModes();
+ Arrays.sort(modes, (mode1, mode2) ->
+ Float.compare(mode2.getRefreshRate(), mode1.getRefreshRate()));
+ for (Display.Mode m : modes) {
+ if (m.getPhysicalWidth() == mode.getPhysicalWidth() &&
+ m.getPhysicalHeight() == mode.getPhysicalHeight()) {
+ mEntries.add(String.format("%.02fHz", m.getRefreshRate())
+ .replaceAll("[\\.,]00", ""));
+ mValues.add(String.format(Locale.US, "%.02f", m.getRefreshRate()));
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mEntries.size() > 1 ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_MIN_REFRESH_RATE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mListPreference = screen.findPreference(getPreferenceKey());
+ mListPreference.setEntries(mEntries.toArray(new String[mEntries.size()]));
+ mListPreference.setEntryValues(mValues.toArray(new String[mValues.size()]));
+
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final float currentValue = Settings.System.getFloat(mContext.getContentResolver(),
+ MIN_REFRESH_RATE, 60.00f);
+ int index = mListPreference.findIndexOfValue(
+ String.format(Locale.US, "%.02f", currentValue));
+ if (index < 0) index = 0;
+ mListPreference.setValueIndex(index);
+ mListPreference.setSummary(mListPreference.getEntries()[index]);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Settings.System.putFloat(mContext.getContentResolver(), MIN_REFRESH_RATE,
+ Float.valueOf((String) newValue));
+ updateState(preference);
+ return true;
+ }
+
+}
diff --git a/src/com/android/settings/display/NightDisplayActivationPreferenceController.java b/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
index fd6d199a29d..aa963c96a9a 100644
--- a/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayActivationPreferenceController.java
@@ -90,11 +90,6 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
}
}
- @Override
- public final void updateState(Preference preference) {
- updateStateInternal();
- }
-
/** FOR SLICES */
@Override
@@ -112,14 +107,4 @@ public CharSequence getSummary() {
return mTimeFormatter.getAutoModeSummary(mContext, mColorDisplayManager);
}
- private void updateStateInternal() {
- final boolean isActivated = mColorDisplayManager.isNightDisplayActivated();
- final int autoMode = mColorDisplayManager.getNightDisplayAutoMode();
-
- if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME) {
- mTimeFormatter.getFormattedTimeString(isActivated
- ? mColorDisplayManager.getNightDisplayCustomStartTime()
- : mColorDisplayManager.getNightDisplayCustomEndTime());
- }
- }
}
diff --git a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
index ef11e00cebb..68c4b98b908 100644
--- a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java
@@ -20,7 +20,7 @@
import android.hardware.display.ColorDisplayManager;
import android.location.LocationManager;
-import androidx.preference.DropDownPreference;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -31,7 +31,7 @@ public class NightDisplayAutoModePreferenceController extends BasePreferenceCont
implements Preference.OnPreferenceChangeListener {
private final LocationManager mLocationManager;
- private DropDownPreference mPreference;
+ private ListPreference mPreference;
private ColorDisplayManager mColorDisplayManager;
public NightDisplayAutoModePreferenceController(Context context, String key) {
diff --git a/src/com/android/settings/display/PeakRefreshRateListPreferenceController.java b/src/com/android/settings/display/PeakRefreshRateListPreferenceController.java
new file mode 100644
index 00000000000..e51f5d45110
--- /dev/null
+++ b/src/com/android/settings/display/PeakRefreshRateListPreferenceController.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Display;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.List;
+import java.util.Locale;
+
+public class PeakRefreshRateListPreferenceController extends BasePreferenceController
+ implements LifecycleObserver, OnStart, OnStop, Preference.OnPreferenceChangeListener {
+
+ private static float DEFAULT_REFRESH_RATE = 60f;
+
+ private static final String TAG = "PeakRefreshRatePrefCtr";
+ private static final float INVALIDATE_REFRESH_RATE = -1f;
+
+ private final Handler mHandler;
+ private final IDeviceConfigChange mOnDeviceConfigChange;
+ private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
+ private ListPreference mListPreference;
+
+ private List mEntries = new ArrayList<>();
+ private List mValues = new ArrayList<>();
+
+ private interface IDeviceConfigChange {
+ void onDefaultRefreshRateChanged();
+ }
+
+ public PeakRefreshRateListPreferenceController(Context context, String key) {
+ super(context, key);
+ mHandler = new Handler(context.getMainLooper());
+ mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+ mOnDeviceConfigChange =
+ new IDeviceConfigChange() {
+ public void onDefaultRefreshRateChanged() {
+ updateState(mListPreference);
+ }
+ };
+
+ final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+
+ if (display == null) {
+ Log.w(TAG, "No valid default display device");
+ } else {
+ Display.Mode mode = display.getMode();
+ Display.Mode[] modes = display.getSupportedModes();
+ Arrays.sort(modes, (mode1, mode2) ->
+ Float.compare(mode2.getRefreshRate(), mode1.getRefreshRate()));
+ for (Display.Mode m : modes) {
+ if (m.getPhysicalWidth() == mode.getPhysicalWidth() &&
+ m.getPhysicalHeight() == mode.getPhysicalHeight()) {
+ mEntries.add(String.format("%.02fHz", m.getRefreshRate())
+ .replaceAll("[\\.,]00", ""));
+ mValues.add(String.format(Locale.US, "%.02f", m.getRefreshRate()));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mListPreference = screen.findPreference(getPreferenceKey());
+ mListPreference.setEntries(mEntries.toArray(new String[mEntries.size()]));
+ mListPreference.setEntryValues(mValues.toArray(new String[mValues.size()]));
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mContext.getResources().getBoolean(R.bool.config_show_peak_refresh_rate_switch)) {
+ return AVAILABLE;
+ } else {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final float currentValue = Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE, getDefaultPeakRefreshRate());
+ int index = mListPreference.findIndexOfValue(
+ String.format(Locale.US, "%.02f", currentValue));
+ if (index < 0) index = 0;
+ mListPreference.setValueIndex(index);
+ mListPreference.setSummary(mListPreference.getEntries()[index]);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+ Float.valueOf((String) newValue));
+ updateState(preference);
+ return true;
+ }
+
+ @Override
+ public void onStart() {
+ mDeviceConfigDisplaySettings.startListening();
+ }
+
+ @Override
+ public void onStop() {
+ mDeviceConfigDisplaySettings.stopListening();
+ }
+
+ private float findPeakRefreshRate(Display.Mode[] modes) {
+ float peakRefreshRate = DEFAULT_REFRESH_RATE;
+ for (Display.Mode mode : modes) {
+ if (Math.round(mode.getRefreshRate()) > DEFAULT_REFRESH_RATE) {
+ peakRefreshRate = mode.getRefreshRate();
+ }
+ }
+ return peakRefreshRate;
+ }
+
+ private class DeviceConfigDisplaySettings
+ implements DeviceConfig.OnPropertiesChangedListener, Executor {
+ public void startListening() {
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ this /* Executor */,
+ this /* Listener */);
+ }
+
+ public void stopListening() {
+ DeviceConfig.removeOnPropertiesChangedListener(this);
+ }
+
+ public float getDefaultPeakRefreshRate() {
+ float defaultPeakRefreshRate =
+ DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT,
+ INVALIDATE_REFRESH_RATE);
+ Log.d(TAG, "DeviceConfig getDefaultPeakRefreshRate : " + defaultPeakRefreshRate);
+
+ return defaultPeakRefreshRate;
+ }
+
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ // 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.
+ if (mOnDeviceConfigChange != null) {
+ mOnDeviceConfigChange.onDefaultRefreshRateChanged();
+ updateState(mListPreference);
+ }
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ if (mHandler != null) {
+ mHandler.post(runnable);
+ }
+ }
+ }
+
+ private float getDefaultPeakRefreshRate() {
+ float defaultPeakRefreshRate = mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
+ if (defaultPeakRefreshRate == INVALIDATE_REFRESH_RATE) {
+ defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultPeakRefreshRate);
+ }
+
+ return defaultPeakRefreshRate;
+ }
+}
diff --git a/src/com/android/settings/display/QRCodeScannerPreferenceController.java b/src/com/android/settings/display/QRCodeScannerPreferenceController.java
index cb022a74fa8..e15860ec026 100644
--- a/src/com/android/settings/display/QRCodeScannerPreferenceController.java
+++ b/src/com/android/settings/display/QRCodeScannerPreferenceController.java
@@ -64,7 +64,7 @@ public void displayPreference(PreferenceScreen screen) {
@OnLifecycleEvent(ON_START)
public void onStart() {
mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(SHOW_QR_CODE_SCANNER_SETTING), false,
+ Settings.Secure.getUriFor(SHOW_QR_CODE_SCANNER_SETTING), false,
mSettingsObserver);
}
diff --git a/src/com/android/settings/display/RefreshRatePreferenceController.java b/src/com/android/settings/display/RefreshRatePreferenceController.java
new file mode 100644
index 00000000000..b63f12fbc5f
--- /dev/null
+++ b/src/com/android/settings/display/RefreshRatePreferenceController.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 Paranoid Android
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+public class RefreshRatePreferenceController extends BasePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+
+ private static final String TAG = "RefreshRatePreferenceController";
+
+ private RefreshRateUtils mUtils;
+ private Preference mPreference;
+ private final PowerManager mPowerManager;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mPreference != null)
+ updateState(mPreference);
+ }
+ };
+
+ public RefreshRatePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mUtils = new RefreshRateUtils(context);
+ mPowerManager = context.getSystemService(PowerManager.class);
+ }
+
+ @Override
+ public void onStart() {
+ mContext.registerReceiver(mReceiver,
+ new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+ }
+
+ @Override
+ public void onStop() {
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ if (mPowerManager.isPowerSaveMode()) {
+ return mContext.getString(R.string.dark_ui_mode_disabled_summary_dark_theme_on);
+ }
+ return mContext.getString(mUtils.isVrrEnabled() ? R.string.refresh_rate_summary_vrr_on
+ : R.string.refresh_rate_summary_vrr_off, mUtils.getCurrentRefreshRate());
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mUtils.isHighRefreshRateAvailable() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ preference.setEnabled(!mPowerManager.isPowerSaveMode());
+ }
+}
diff --git a/src/com/android/settings/display/RefreshRateSettings.java b/src/com/android/settings/display/RefreshRateSettings.java
new file mode 100644
index 00000000000..3fced007a1a
--- /dev/null
+++ b/src/com/android/settings/display/RefreshRateSettings.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2023 Paranoid Android
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreferenceCompat;
+
+import com.android.settings.R;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.widget.RadioButtonPickerFragment;
+import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.CandidateInfo;
+import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.widget.MainSwitchPreference;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+import com.android.settingslib.widget.TopIntroPreference;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Preference fragment used for switching refresh rate */
+@SearchIndexable
+public class RefreshRateSettings extends RadioButtonPickerFragment {
+
+ private static final String TAG = "RefreshRateSettings";
+ private static final String KEY_VRR_PREF = "refresh_rate_vrr";
+
+ private Context mContext;
+ private RefreshRateUtils mUtils;
+ private SwitchPreferenceCompat mVrrSwitchPref;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mContext = context;
+ mUtils = new RefreshRateUtils(context);
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.refresh_rate_settings;
+ }
+
+ @Override
+ protected void addStaticPreferences(PreferenceScreen screen) {
+ mVrrSwitchPref = new SwitchPreferenceCompat(screen.getContext());
+ mVrrSwitchPref.setKey(KEY_VRR_PREF);
+ mVrrSwitchPref.setTitle(R.string.refresh_rate_vrr_title);
+ mVrrSwitchPref.setSummary(R.string.refresh_rate_vrr_summary);
+ mVrrSwitchPref.setOnPreferenceChangeListener((pref, newValue) -> {
+ mUtils.setVrrEnabled((Boolean) newValue);
+ return true;
+ });
+ screen.addPreference(mVrrSwitchPref);
+ updateVrrPref();
+
+ final FooterPreference footerPreference = new FooterPreference(screen.getContext());
+ footerPreference.setTitle(R.string.refresh_rate_footer);
+ footerPreference.setSelectable(false);
+ footerPreference.setLayoutResource(com.android.settingslib.widget.preference.footer.R.layout.preference_footer);
+ screen.addPreference(footerPreference);
+ }
+
+ @Override
+ protected List extends CandidateInfo> getCandidates() {
+ return mUtils.getRefreshRates().stream()
+ .filter(r -> r >= RefreshRateUtils.DEFAULT_REFRESH_RATE)
+ .map(RefreshRateCandidateInfo::new)
+ .collect(Collectors.toList());
+ }
+
+ private void updateVrrPref() {
+ if (mVrrSwitchPref == null) return;
+ mVrrSwitchPref.setEnabled(mUtils.isVrrPossible());
+ mVrrSwitchPref.setChecked(mUtils.isVrrEnabled());
+ }
+
+ @Override
+ protected String getDefaultKey() {
+ return String.valueOf(mUtils.getCurrentRefreshRate());
+ }
+
+ @Override
+ protected boolean setDefaultKey(final String key) {
+ final int refreshRate = Integer.parseInt(key);
+ mUtils.setCurrentRefreshRate(refreshRate);
+ updateVrrPref();
+ return true;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return -1;
+ }
+
+ private class RefreshRateCandidateInfo extends CandidateInfo {
+ private final CharSequence mLabel;
+ private final String mKey;
+
+ RefreshRateCandidateInfo(Integer refreshRate) {
+ super(true);
+ mLabel = String.format("%d Hz", refreshRate.intValue());
+ mKey = refreshRate.toString();
+ }
+
+ @Override
+ public CharSequence loadLabel() {
+ return mLabel;
+ }
+
+ @Override
+ public Drawable loadIcon() {
+ return null;
+ }
+
+ @Override
+ public String getKey() {
+ return mKey;
+ }
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.refresh_rate_settings) {
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ return new RefreshRateUtils(context).isHighRefreshRateAvailable();
+ }
+ };
+}
diff --git a/src/com/android/settings/display/RefreshRateUtils.java b/src/com/android/settings/display/RefreshRateUtils.java
new file mode 100644
index 00000000000..eeb17d013cd
--- /dev/null
+++ b/src/com/android/settings/display/RefreshRateUtils.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 Paranoid Android
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.android.settings.R;
+
+public class RefreshRateUtils {
+
+ private static final String TAG = "RefreshRateUtils";
+
+ static final int DEFAULT_REFRESH_RATE = 60;
+ static final int DEFAULT_MIN_REFRESH_RATE = 60;
+
+ private Context mContext;
+ private List mRefreshRates;
+ private int mMinRefreshRate, mMaxRefreshRate;
+
+ RefreshRateUtils(Context context) {
+ mContext = context;
+ mRefreshRates = getRefreshRates();
+ mMinRefreshRate = getMinRefreshRateFromConfig();
+ mMaxRefreshRate = Collections.max(mRefreshRates);
+ }
+
+ List getRefreshRates() {
+ return Arrays.stream(mContext.getDisplay().getSupportedModes())
+ .map(m -> Math.round(m.getRefreshRate()))
+ .sorted().distinct().collect(Collectors.toList());
+ }
+
+ boolean isHighRefreshRateAvailable() {
+ return mRefreshRates.stream()
+ .filter(r -> r > DEFAULT_REFRESH_RATE)
+ .count() > 0;
+ }
+
+ private int roundToNearestRefreshRate(int refreshRate, boolean floor) {
+ if (mRefreshRates.contains(refreshRate)) return refreshRate;
+ int findRefreshRate = mMinRefreshRate;
+ for (Integer knownRefreshRate : mRefreshRates) {
+ if (!floor) findRefreshRate = knownRefreshRate;
+ if (knownRefreshRate > refreshRate) break;
+ if (floor) findRefreshRate = knownRefreshRate;
+ }
+ return findRefreshRate;
+ }
+
+ private float getDefaultPeakRefreshRate() {
+ return (float) mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultPeakRefreshRate);
+ }
+
+ private int getPeakRefreshRate() {
+ final int peakRefreshRate = Math.round(Settings.System.getFloat(
+ mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE, getDefaultPeakRefreshRate()));
+ return peakRefreshRate < mMinRefreshRate ? mMaxRefreshRate
+ : roundToNearestRefreshRate(peakRefreshRate, true);
+ }
+
+ private void setPeakRefreshRate(int refreshRate) {
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE, (float) refreshRate);
+ }
+
+ private int getMinRefreshRateFromConfig() {
+ int minRefreshRate;
+ try {
+ minRefreshRate = mContext.getResources().getInteger(R.integer.default_min_refresh_rate);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve default_min_refresh_rate from config, using default", e);
+ minRefreshRate = getMinRefreshRate();
+ }
+ return minRefreshRate;
+ }
+
+ private int getMinRefreshRate() {
+ final int minRefreshRate = Math.round(Settings.System.getFloat(
+ mContext.getContentResolver(), Settings.System.MIN_REFRESH_RATE,
+ (float) DEFAULT_MIN_REFRESH_RATE));
+ return minRefreshRate == mMinRefreshRate ? mMinRefreshRate
+ : roundToNearestRefreshRate(minRefreshRate, false);
+ }
+
+ private void setMinRefreshRate(int refreshRate) {
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, (float) refreshRate);
+ }
+
+ int getCurrentRefreshRate() {
+ return Math.max(getMinRefreshRate(), getPeakRefreshRate());
+ }
+
+ void setCurrentRefreshRate(int refreshRate) {
+ setPeakRefreshRate(refreshRate);
+ setMinRefreshRate(isVrrEnabled() ? mMinRefreshRate : refreshRate);
+ }
+
+ boolean isVrrPossible() {
+ return getCurrentRefreshRate() > DEFAULT_REFRESH_RATE;
+ }
+
+ boolean isVrrEnabled() {
+ return getMinRefreshRate() <= mMinRefreshRate;
+ }
+
+ void setVrrEnabled(boolean enable) {
+ setMinRefreshRate(enable ? mMinRefreshRate : getCurrentRefreshRate());
+ }
+}
diff --git a/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
index e122ad06864..980b8063e95 100644
--- a/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
+++ b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
@@ -20,7 +20,7 @@
import android.location.LocationManager;
import android.os.PowerManager;
-import androidx.preference.DropDownPreference;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -29,7 +29,7 @@
import com.android.settings.display.TwilightLocationDialog;
/**
- * Controller for the dark ui option dropdown
+ * Controller for the dark ui option list
*/
public class DarkModeScheduleSelectorController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
@@ -40,7 +40,7 @@ public class DarkModeScheduleSelectorController extends BasePreferenceController
private final LocationManager mLocationManager;
private final BedtimeSettings mBedtimeSettings;
- private DropDownPreference mPreference;
+ private ListPreference mPreference;
private int mCurrentMode;
public DarkModeScheduleSelectorController(Context context, String key) {
diff --git a/src/com/android/settings/flashlight/FlashlightHandleActivity.java b/src/com/android/settings/flashlight/FlashlightHandleActivity.java
index 6ae07cdcea4..bb3ff2d0477 100644
--- a/src/com/android/settings/flashlight/FlashlightHandleActivity.java
+++ b/src/com/android/settings/flashlight/FlashlightHandleActivity.java
@@ -87,10 +87,7 @@ public List getRawDataToIndex(Context context,
@Override
public List getNonIndexableKeys(Context context) {
List keys = super.getNonIndexableKeys(context);
- if (!FlashlightSlice.isFlashlightAvailable(context)) {
- Log.i(TAG, "Flashlight is unavailable");
- keys.add(DATA_KEY);
- }
+ keys.add(DATA_KEY);
return keys;
}
};
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index e922f7058be..e37d3bc7b66 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -29,6 +29,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import android.widget.CompoundButton;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -48,12 +49,17 @@
import com.android.settings.fuelgauge.batteryusage.BatteryEntry;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.HelpUtils;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.Instrumentable;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.widget.MainSwitchPreference;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.ArrayList;
import java.util.List;
@@ -68,6 +74,7 @@
*/
public class AdvancedPowerUsageDetail extends DashboardFragment
implements ButtonActionDialogFragment.AppButtonsDialogListener,
+ SelectorWithWidgetPreference.OnClickListener,
Preference.OnPreferenceClickListener,
Preference.OnPreferenceChangeListener {
public static final String TAG = "AdvancedPowerDetail";
@@ -86,7 +93,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment
public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
private static final String KEY_PREF_HEADER = "header_view";
+ private static final String KEY_PREF_UNRESTRICTED = "unrestricted_preference";
+ private static final String KEY_PREF_OPTIMIZED = "optimized_preference";
private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage";
+ private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference";
private static final int REQUEST_UNINSTALL = 0;
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
@@ -100,7 +110,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment
@VisibleForTesting ApplicationsState mState;
@VisibleForTesting ApplicationsState.AppEntry mAppEntry;
@VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
- @VisibleForTesting PrimarySwitchPreference mAllowBackgroundUsagePreference;
+ @VisibleForTesting SelectorWithWidgetPreference mOptimizePreference;
+ @VisibleForTesting SelectorWithWidgetPreference mUnrestrictedPreference;
+ @VisibleForTesting MainSwitchPreference mMainSwitchPreference;
+ @VisibleForTesting FooterPreference mFooterPreference;
@VisibleForTesting @BatteryOptimizeUtils.OptimizationMode
int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN;
@@ -362,7 +375,18 @@ void initFooter() {
context.getString(
R.string.manager_battery_usage_allow_background_usage_summary);
}
- mAllowBackgroundUsagePreference.setSummary(detailInfoString);
+ mFooterPreference.setTitle(detailInfoString);
+ final Intent helpIntent =
+ HelpUtils.getHelpIntent(
+ context,
+ context.getString(R.string.help_url_app_usage_settings),
+ /* backupContext= */ "");
+ if (helpIntent != null) {
+ mFooterPreference.setLearnMoreAction(
+ v -> startActivityForResult(helpIntent, /* requestCode= */ 0));
+ mFooterPreference.setLearnMoreText(
+ context.getString(R.string.manager_battery_usage_link_a11y));
+ }
}
@Override
@@ -402,6 +426,8 @@ protected List createPreferenceControllers(Context
}
controllers.add(mAppButtonsPreferenceController);
controllers.add(new AllowBackgroundPreferenceController(context, uid, packageName));
+ controllers.add(new OptimizedPreferenceController(context, uid, packageName));
+ controllers.add(new UnrestrictedPreferenceController(context, uid, packageName));
return controllers;
}
@@ -484,14 +510,51 @@ private void logMetricCategory(int currentOptimizeMode) {
}
private void onCreateBackgroundUsageState(String packageName) {
- mAllowBackgroundUsagePreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE);
- if (mAllowBackgroundUsagePreference != null) {
- mAllowBackgroundUsagePreference.setOnPreferenceClickListener(this);
- mAllowBackgroundUsagePreference.setOnPreferenceChangeListener(this);
- }
+ mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED);
+ mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED);
+ mMainSwitchPreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE);
+ mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE);
+
+ mOptimizePreference.setOnClickListener(this);
+ mUnrestrictedPreference.setOnClickListener(this);
+ mMainSwitchPreference.addOnSwitchChangeListener((view, isChecked) -> {
+ mMainSwitchPreference.setChecked(isChecked);
+ updateSelectorPreference(isChecked);
+ });
mBatteryOptimizeUtils =
new BatteryOptimizeUtils(
getContext(), getArguments().getInt(EXTRA_UID), packageName);
}
+
+ @Override
+ public void onRadioButtonClicked(SelectorWithWidgetPreference selected) {
+ final String selectedKey = selected == null ? null : selected.getKey();
+ updateSelectorPreferenceState(mUnrestrictedPreference, selectedKey);
+ updateSelectorPreferenceState(mOptimizePreference, selectedKey);
+ mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference(), Action.APPLY);
+ }
+
+ void updateSelectorPreference(boolean isEnabled) {
+ mOptimizePreference.setEnabled(isEnabled);
+ mUnrestrictedPreference.setEnabled(isEnabled);
+ onRadioButtonClicked(isEnabled ? mOptimizePreference : null);
+ }
+
+ int getSelectedPreference() {
+ if (!mMainSwitchPreference.isChecked()) {
+ return BatteryOptimizeUtils.MODE_RESTRICTED;
+ } else if (mUnrestrictedPreference.isChecked()) {
+ return BatteryOptimizeUtils.MODE_UNRESTRICTED;
+ } else if (mOptimizePreference.isChecked()) {
+ return BatteryOptimizeUtils.MODE_OPTIMIZED;
+ } else {
+ return BatteryOptimizeUtils.MODE_UNKNOWN;
+ }
+ }
+
+ private static void updateSelectorPreferenceState(
+ SelectorWithWidgetPreference preference, String selectedKey) {
+ preference.setChecked(TextUtils.equals(selectedKey, preference.getKey()));
+ }
}
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
index 891a9977e8f..2d09c6d999e 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
@@ -160,9 +160,17 @@ public void quickUpdateHeaderPreference() {
final int batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
final boolean discharging =
batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;
+ final int chargeCounterUah =
+ batteryBroadcast.getIntExtra(BatteryManager.EXTRA_CHARGE_COUNTER, -1);
mBatteryUsageProgressBarPref.setUsageSummary(formatBatteryPercentageText(batteryLevel));
mBatteryUsageProgressBarPref.setPercent(batteryLevel, BATTERY_MAX_LEVEL);
+
+ if (chargeCounterUah > 0) {
+ int chargeCounter = chargeCounterUah / 1_000;
+ mBatteryUsageProgressBarPref.setTotalSummary(
+ formatBatteryChargeCounterText(chargeCounter));
+ }
}
/** Update summary when battery tips changed. */
@@ -177,4 +185,8 @@ public void updateHeaderByBatteryTips(BatteryTip batteryTip, BatteryInfo battery
private CharSequence formatBatteryPercentageText(int batteryLevel) {
return com.android.settings.Utils.formatPercentage(batteryLevel);
}
+
+ private CharSequence formatBatteryChargeCounterText(int chargeCounter) {
+ return mContext.getString(R.string.battery_charge_counter_summary, chargeCounter);
+ }
}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
index 4b5d9526f16..476cc8b081e 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
@@ -21,6 +21,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.settings.R;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
@@ -42,7 +43,7 @@ public boolean isFirstUseDateAvailable(Context context, long firstUseDateMs) {
@Override
public boolean isBatteryInfoEnabled(Context context) {
- return false;
+ return context.getResources().getBoolean(R.bool.config_show_battery_info);
}
@Override
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
index 066e0fc15b6..5d9d047eaec 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
@@ -26,7 +26,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController;
-import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import java.util.List;
@@ -51,7 +50,6 @@ static void verifyConfiguration(Context context) {
context = context.getApplicationContext();
verifySaverConfiguration(context);
verifyBatteryOptimizeModes(context);
- DynamicDenylistManager.getInstance(context).onBootComplete();
}
/** Avoid users set important apps into the unexpected battery optimize modes */
diff --git a/src/com/android/settings/fuelgauge/FastChargingPreferenceController.java b/src/com/android/settings/fuelgauge/FastChargingPreferenceController.java
new file mode 100644
index 00000000000..a4eea6e11b4
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/FastChargingPreferenceController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The LineageOS 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.fuelgauge;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreferenceCompat;
+
+import com.android.settings.core.BasePreferenceController;
+
+import vendor.lineage.fastcharge.V1_0.IFastCharge;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Controller to change and update the fast charging toggle
+ */
+public class FastChargingPreferenceController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_FAST_CHARGING = "fast_charging";
+ private static final String TAG = "FastChargingPreferenceController";
+
+ private IFastCharge mFastCharge = null;
+
+ public FastChargingPreferenceController(Context context) {
+ super(context, KEY_FAST_CHARGING);
+ try {
+ mFastCharge = IFastCharge.getService();
+ } catch (NoSuchElementException | RemoteException e) {
+ Log.e(TAG, "Failed to get IFastCharge interface", e);
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mFastCharge != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ boolean fastChargingEnabled = false;
+
+ try {
+ fastChargingEnabled = mFastCharge.isEnabled();
+ } catch (RemoteException e) {
+ Log.e(TAG, "isEnabled failed", e);
+ }
+
+ ((SwitchPreferenceCompat) preference).setChecked(fastChargingEnabled);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean shouldEnableFastCharging = (Boolean) newValue;
+
+ try {
+ mFastCharge.setEnabled(shouldEnableFastCharging);
+ updateState(preference);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setEnabled failed", e);
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index bdb236a5192..6e63451a1fd 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -44,6 +44,8 @@
/** Implementation of {@code PowerUsageFeatureProvider} */
public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider {
+ private static final String ADDITIONAL_BATTERY_INFO_ACTION = "com.google.android.apps.turbo.SHOW_ADDITIONAL_BATTERY_INFO";
+ private static final String ADDITIONAL_BATTERY_INFO_PACKAGE = "com.google.android.apps.turbo";
private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
private static final String[] PACKAGES_SYSTEM = {
@@ -125,7 +127,8 @@ public boolean isLocationSettingEnabled(String[] packages) {
@Override
public Intent getAdditionalBatteryInfoIntent() {
- return null;
+ Intent intent = new Intent(ADDITIONAL_BATTERY_INFO_ACTION);
+ return intent.setPackage(ADDITIONAL_BATTERY_INFO_PACKAGE);
}
@Override
diff --git a/src/com/android/settings/fuelgauge/batterydata/BatteryDataBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batterydata/BatteryDataBroadcastReceiver.java
new file mode 100644
index 00000000000..3b8b12fdbf8
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterydata/BatteryDataBroadcastReceiver.java
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2022 The Project Mia
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+package com.android.settings.fuelgauge.batterydata;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public final class BatteryDataBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "BatteryDataBroadcastReceiver";
+
+ public boolean mFetchBatteryUsageData;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String batteryData = intent.getAction();
+ switch (batteryData) {
+ // Fetch device usage data
+ case "settings.intelligence.battery.action.FETCH_BATTERY_USAGE_DATA":
+ mFetchBatteryUsageData = true;
+ BatteryDataFetchService.enqueueWork(context);
+ break;
+ // Fetch bluetooth device usage data
+ case "settings.intelligence.battery.action.FETCH_BLUETOOTH_BATTERY_DATA":
+ try {
+ BluetoothBatteryDataFetch.returnBluetoothDevices(context, intent);
+ } catch (Exception e) {
+ Log.e(TAG, "returnBluetoothDevices() error: ", e);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterydata/BatteryDataFetchService.java b/src/com/android/settings/fuelgauge/batterydata/BatteryDataFetchService.java
new file mode 100644
index 00000000000..19360981e78
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterydata/BatteryDataFetchService.java
@@ -0,0 +1,53 @@
+//
+// Copyright (C) 2022 The Project Mia
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+package com.android.settings.fuelgauge.batterydata;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.JobIntentService;
+
+import java.util.List;
+
+public class BatteryDataFetchService extends JobIntentService {
+
+ private static final String TAG = "BatteryDataFetchService";
+ private static final Intent JOB_INTENT = new Intent("action.LOAD_BATTERY_USAGE_DATA");
+
+ public static void enqueueWork(final Context context) {
+ AsyncTask.execute(() -> {
+ loadUsageDataSafely(context);
+ });
+ }
+
+ @Override
+ protected void onHandleWork(@NonNull Intent intent) {
+ loadUsageDataSafely(this);
+ }
+
+ private static void loadUsageDataSafely(Context context) {
+ try {
+ loadUsageData(context);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Fail load usage data:" + e);
+ }
+ }
+
+ private static void loadUsageData(Context context) {
+ BatteryUsageStats batteryUsageStats = context
+ .getSystemService(BatteryStatsManager.class)
+ .getBatteryUsageStats(new BatteryUsageStatsQuery.Builder()
+ .includeBatteryHistory()
+ .build());
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterydata/BluetoothBatteryDataFetch.java b/src/com/android/settings/fuelgauge/batterydata/BluetoothBatteryDataFetch.java
new file mode 100644
index 00000000000..d437cf5fd80
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterydata/BluetoothBatteryDataFetch.java
@@ -0,0 +1,171 @@
+//
+// Copyright (C) 2022 The Project Mia
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+package com.android.settings.fuelgauge.batterydata;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public final class BluetoothBatteryDataFetch {
+
+ private static final String TAG = "BluetoothBatteryDataFetch";
+
+ @VisibleForTesting
+ public static LocalBluetoothManager mLocalBluetoothManager;
+ private static Context context;
+ private static Intent intent;
+
+ private static String emptyIfNull(String value) {
+ return value == null ? "" : value;
+ }
+
+ public static ContentValues wrapBluetoothData(
+ Context context, CachedBluetoothDevice cachedBluetoothDevice,
+ boolean nonbuds) {
+ BluetoothDevice device = cachedBluetoothDevice.getDevice();
+
+ ContentValues contentValues = new ContentValues();
+ contentValues.put("type", device.getType());
+ contentValues.put("name", emptyIfNull(device.getName()));
+ contentValues.put("alias", emptyIfNull(device.getAlias()));
+ contentValues.put("address", emptyIfNull(device.getAddress()));
+ contentValues.put("batteryLevel", device.getBatteryLevel());
+
+ putStringMetadata(contentValues, "hardwareVersion", device.getMetadata(
+ BluetoothDevice.METADATA_HARDWARE_VERSION));
+ putStringMetadata(contentValues, "batteryLevelRight", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY));
+ putStringMetadata(contentValues, "batteryLevelLeft", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY));
+ putStringMetadata(contentValues, "batteryLevelCase", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY));
+ putStringMetadata(contentValues, "batteryChargingRight", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING));
+ putStringMetadata(contentValues, "batteryChargingLeft", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING));
+ putStringMetadata(contentValues, "batteryChargingCase", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING));
+ putStringMetadata(contentValues, "batteryChargingMain", device.getMetadata(
+ BluetoothDevice.METADATA_MAIN_CHARGING));
+ if (nonbuds) {
+ putStringMetadata(contentValues, "deviceIconMain", device.getMetadata(
+ BluetoothDevice.METADATA_MAIN_ICON));
+ putStringMetadata(contentValues, "deviceIconCase", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_CASE_ICON));
+ putStringMetadata(contentValues, "deviceIconLeft", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON));
+ putStringMetadata(contentValues, "deviceIconRight", device.getMetadata(
+ BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON));
+ }
+ BluetoothClass bluetoothClass = device.getBluetoothClass();
+ if (bluetoothClass != null) {
+ contentValues.put("bluetoothClass", marshall(bluetoothClass));
+ }
+ return contentValues;
+ }
+
+ private static byte[] marshall(Parcelable parcelable) {
+ Parcel obtain = Parcel.obtain();
+ parcelable.writeToParcel(obtain, 0);
+ byte[] marshall = obtain.marshall();
+ obtain.recycle();
+ return marshall;
+ }
+
+ private static void putStringMetadata(
+ ContentValues contentValues, String key, byte[] value) {
+ if (value == null || value.length == 0) {
+ return;
+ }
+ contentValues.put(key, new String(value));
+ }
+
+ public static void returnBluetoothDevices(Context context, Intent intent) {
+ AsyncTask.execute(() -> returnBluetoothDevicesInner(context, intent));
+ }
+
+ public static void returnBluetoothDevicesInner(Context context, Intent intent) {
+ ResultReceiver resultReceiver = intent.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER);
+ if (resultReceiver == null) {
+ Log.w(TAG, "No result receiver found from intent");
+ return;
+ }
+ if (mLocalBluetoothManager == null) {
+ mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, null);
+ }
+ BluetoothAdapter adapter = context.getSystemService(BluetoothManager.class).getAdapter();
+ if (adapter == null || !adapter.isEnabled() || mLocalBluetoothManager == null) {
+ Log.w(TAG, "BluetoothAdapter not present or not enabled");
+ resultReceiver.send(1, null);
+ return;
+ }
+ sendAndFilterBluetoothData(context, resultReceiver, mLocalBluetoothManager,
+ intent.getBooleanExtra("extra_fetch_icon", false));
+ }
+
+ public static void sendAndFilterBluetoothData(Context context,
+ ResultReceiver resultReceiver,
+ LocalBluetoothManager localBluetoothManager,
+ boolean cache) {
+ long start = System.currentTimeMillis();
+ Collection cachedDevicesCopy =
+ localBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy();
+ Log.d(TAG, "cachedDevices:" + cachedDevicesCopy);
+ if (cachedDevicesCopy == null || cachedDevicesCopy.isEmpty()) {
+ resultReceiver.send(0, Bundle.EMPTY);
+ return;
+ }
+ List connectedDevices = cachedDevicesCopy.stream()
+ .filter(CachedBluetoothDevice::isConnected)
+ .collect(Collectors.toList());
+ Log.d(TAG, "Connected devices:" + connectedDevices);
+ if (connectedDevices.isEmpty()) {
+ resultReceiver.send(0, Bundle.EMPTY);
+ return;
+ }
+ ArrayList bluetoothWrapDataListKey = new ArrayList<>();
+ ArrayList bluetoothParcelableList = new ArrayList<>();
+ connectedDevices.forEach(cachedBluetoothDevice -> {
+ BluetoothDevice device = cachedBluetoothDevice.getDevice();
+ bluetoothParcelableList.add(device);
+ try {
+ bluetoothWrapDataListKey.add(
+ wrapBluetoothData(context, cachedBluetoothDevice, cache));
+ } catch (Exception e) {
+ Log.e(TAG, "Wrap bluetooth data failed: " + device, e);
+ }
+ });
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList("bluetoothParcelableListKey", bluetoothParcelableList);
+ if (!bluetoothWrapDataListKey.isEmpty()) {
+ bundle.putParcelableArrayList("bluetoothWrapDataListKey", bluetoothWrapDataListKey);
+ }
+ resultReceiver.send(0, bundle);
+ Log.d(TAG, String.format("Send and filter bluetooth data size=%d in %d/ms",
+ bluetoothWrapDataListKey.size(), (System.currentTimeMillis() - start)));
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
index dbe2722ebea..c9458f782a7 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
@@ -87,26 +87,28 @@ public void updatePreference(Preference preference) {
cardPreference.setSelectable(false);
cardPreference.setIconResId(getIconId());
- cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more));
- cardPreference.setPrimaryButtonAction(
- () -> {
- var helpIntent =
- HelpUtils.getHelpIntent(
- context,
- context.getString(R.string.help_url_battery_defender),
- /* backupContext= */ "");
- ActivityCompat.startActivityForResult(
- (Activity) preference.getContext(),
- helpIntent,
- /* requestCode= */ 0,
- /* options= */ null);
- return Unit.INSTANCE;
- });
- cardPreference.setPrimaryButtonVisibility(true);
- cardPreference.setPrimaryButtonContentDescription(
- context.getString(
- R.string.battery_tip_limited_temporarily_sec_button_content_description));
+ if (getHelpResource() != 0) {
+ cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more));
+ cardPreference.setPrimaryButtonAction(
+ () -> {
+ var helpIntent =
+ HelpUtils.getHelpIntent(
+ context,
+ context.getString(getHelpResource()),
+ /* backupContext= */ "");
+ ActivityCompat.startActivityForResult(
+ (Activity) preference.getContext(),
+ helpIntent,
+ /* requestCode= */ 0,
+ /* options= */ null);
+
+ return Unit.INSTANCE;
+ });
+ cardPreference.setPrimaryButtonVisibility(true);
+ cardPreference.setPrimaryButtonContentDescription(context.getString(
+ R.string.battery_tip_limited_temporarily_sec_button_content_description));
+ }
cardPreference.setSecondaryButtonText(
context.getString(R.string.battery_tip_charge_to_full_button));
@@ -121,6 +123,10 @@ public void updatePreference(Preference preference) {
cardPreference.buildContent();
}
+ private int getHelpResource() {
+ return R.string.help_url_battery_defender;
+ }
+
private void resumeCharging(Context context) {
final Intent intent =
FeatureFactory.getFeatureFactory()
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
index fef30563fe2..6b057484c9f 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
@@ -616,14 +616,10 @@ public static NameAndIcon getNameAndIconFromPowerComponent(
iconId = R.drawable.ic_settings_aod;
break;
default:
- Log.w(
- TAG,
- "unknown attribute:"
- + DebugUtils.constantToString(
- BatteryConsumer.class,
- "POWER_COMPONENT_",
- powerComponentId));
- name = null;
+ String fieldName = DebugUtils.constantToString(
+ BatteryConsumer.class, "POWER_COMPONENT_", powerComponentId);
+ Log.w(TAG, "unknown attribute:" + fieldName);
+ name = context.getResources().getString(R.string.header_category_system) + " (" + fieldName + ")";
iconId = R.drawable.ic_power_system;
break;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
index f710c71c969..5c250ab06a6 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
@@ -106,6 +106,10 @@ public void onReceive(Context context, Intent intent) {
private void tryToFetchUsageData(Context context) {
final Intent batteryIntent = BatteryUtils.getBatteryIntent(context);
+ if (batteryIntent == null) {
+ Log.w(TAG, "tryToFetchUsageData: ignore from null battery intent");
+ return;
+ }
// Returns when battery is not fully charged.
if (!BatteryStatus.isCharged(batteryIntent)) {
return;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index ebe7996d335..a1ebbf82588 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -1446,18 +1446,6 @@ private static BatteryDiffData insertHourlyUsageDiffDataPerSlot(
currentEntry.mCachedUsageConsumePower,
nextEntry.mCachedUsageConsumePower);
}
- if (isSystemConsumer(selectedBatteryEntry.mConsumerType)
- && selectedBatteryEntry.mDrainType == BatteryConsumer.POWER_COMPONENT_SCREEN) {
- // Replace Screen system component time with screen on time.
- foregroundUsageTimeInMs = slotScreenOnTime;
- }
- // Excludes entry since we don't have enough data to calculate.
- if (foregroundUsageTimeInMs == 0
- && foregroundServiceUsageTimeInMs == 0
- && backgroundUsageTimeInMs == 0
- && consumePower == 0) {
- continue;
- }
// Forces refine the cumulative value since it may introduce deviation error since we
// will apply the interpolation arithmetic.
final float totalUsageTimeInMs =
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java b/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java
index 379cef3a22a..01d644c0402 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ScreenOnTimeController.java
@@ -69,7 +69,7 @@ public void displayPreference(PreferenceScreen screen) {
void handleScreenOnTimeUpdated(
Long screenOnTime, String slotTimestamp, String accessibilitySlotTimestamp) {
- if (screenOnTime == null) {
+ if (screenOnTime == null || screenOnTime <= 0) {
mRootPreference.setVisible(false);
mScreenOnTimeTextPreference.setVisible(false);
return;
diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
index ecb2a48fe89..b77d5eb6ddf 100644
--- a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
+++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
@@ -78,11 +78,6 @@ private void syncPolicyIfNeeded() {
return;
}
- if (mNetworkPolicyManager == null) {
- Log.w(TAG, "syncPolicyIfNeeded: invalid mNetworkPolicyManager");
- return;
- }
-
final SharedPreferences.Editor editor = getManualDenylistPref().edit();
final int[] existedUids = mNetworkPolicyManager
.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
@@ -96,11 +91,6 @@ private void syncPolicyIfNeeded() {
/** Set policy flags for specific UID. */
public void setUidPolicyLocked(int uid, int policy) {
- if (mNetworkPolicyManager == null) {
- Log.w(TAG, "setUidPolicyLocked: invalid mNetworkPolicyManager");
- return;
- }
-
Log.i(TAG, "setUidPolicyLocked: uid=" + uid + " policy=" + policy);
synchronized (mLock) {
mNetworkPolicyManager.setUidPolicy(uid, policy);
@@ -110,7 +100,7 @@ public void setUidPolicyLocked(int uid, int policy) {
/** Suggest a list of package to set as POLICY_REJECT. */
public void setDenylist(Set denylistTargetUids) {
- if (denylistTargetUids == null || mNetworkPolicyManager == null) {
+ if (denylistTargetUids == null) {
return;
}
final Set manualDenylistUids = getDenylistAllUids(getManualDenylistPref());
@@ -174,12 +164,6 @@ public void resetDenylistIfNeeded(String packageName, boolean force) {
Log.w(TAG, "resetDenylistIfNeeded: invalid conditions");
return;
}
-
- if (mNetworkPolicyManager == null) {
- Log.w(TAG, "setUidPolicyLocked: invalid mNetworkPolicyManager");
- return;
- }
-
synchronized (mLock) {
final int[] uids = mNetworkPolicyManager
.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
diff --git a/src/com/android/settings/gestures/BackGestureIndicatorView.java b/src/com/android/settings/gestures/BackGestureIndicatorView.java
index c60afd003d3..c698a2b3bd6 100644
--- a/src/com/android/settings/gestures/BackGestureIndicatorView.java
+++ b/src/com/android/settings/gestures/BackGestureIndicatorView.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -37,6 +38,7 @@ public class BackGestureIndicatorView extends LinearLayout {
private ImageView mRightIndicator;
private BackGestureIndicatorDrawable mLeftDrawable;
private BackGestureIndicatorDrawable mRightDrawable;
+ private int mHeightScale;
public BackGestureIndicatorView(Context context) {
super(context);
@@ -87,6 +89,10 @@ public void setIndicatorWidth(int width, boolean leftIndicator) {
indicator.setWidth(width);
}
+ public void setIndicatorHeightScale(int heightScale) {
+ mHeightScale = heightScale;
+ }
+
public WindowManager.LayoutParams getLayoutParams(
WindowManager.LayoutParams parentWindowAttributes) {
int copiedFlags = (parentWindowAttributes.flags
@@ -99,8 +105,33 @@ public WindowManager.LayoutParams getLayoutParams(
| copiedFlags,
PixelFormat.TRANSLUCENT);
+ setCurrentGestureHeight(lp);
lp.setTitle("BackGestureIndicatorView");
lp.token = getContext().getActivityToken();
return lp;
}
+
+ private void setCurrentGestureHeight(WindowManager.LayoutParams lp) {
+ Point displaySize = new Point();
+ getContext().getDisplay().getRealSize(displaySize);
+
+ // mHeightScale cant be range 0 - 3
+ // 0 means full height
+ // 1 measns half of the screen
+ // 2 means lower third of the screen
+ // 3 means lower sicth of the screen
+ if (mHeightScale == 0) {
+ lp.height = displaySize.y;
+ lp.y = 0;
+ } else if (mHeightScale == 1) {
+ lp.height = (displaySize.y * 3) / 4;
+ lp.y = displaySize.y - lp.height;
+ } else if (mHeightScale == 2) {
+ lp.height = displaySize.y / 2;
+ lp.y = displaySize.y - lp.height;
+ } else {
+ lp.height = displaySize.y / 4;
+ lp.y = displaySize.y - lp.height;
+ }
+ }
}
diff --git a/src/com/android/settings/gestures/DoubleTapAmbientSettings.java b/src/com/android/settings/gestures/DoubleTapAmbientSettings.java
new file mode 100644
index 00000000000..decf8ce0504
--- /dev/null
+++ b/src/com/android/settings/gestures/DoubleTapAmbientSettings.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019-2020 The Evolution X 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.gestures;
+
+import android.os.Bundle;
+
+import androidx.preference.Preference;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class DoubleTapAmbientSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.double_tap_ambient_screen_settings);
+
+ getActivity().getActionBar().setTitle(R.string.double_tap_title);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ return false;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.CRDROID_SETTINGS;
+ }
+}
diff --git a/src/com/android/settings/gestures/GestureNavigationCtsController.java b/src/com/android/settings/gestures/GestureNavigationCtsController.java
new file mode 100644
index 00000000000..c1cfc360002
--- /dev/null
+++ b/src/com/android/settings/gestures/GestureNavigationCtsController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 Yet Another AOSP 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.gestures;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+public class GestureNavigationCtsController extends TogglePreferenceController {
+
+ private final boolean mDefaultEnabled;
+ private final String mCtsPackage;
+
+ public GestureNavigationCtsController(Context context, String key) {
+ super(context, key);
+
+ mDefaultEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_searchAllEntrypointsEnabledDefault);
+ mCtsPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_defaultContextualSearchPackageName);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED, mDefaultEnabled ? 1 : 0) == 1;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ return Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED, isChecked ? 1 : 0);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!GestureNavigationLongPressController.isAvailable(mContext)) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ try {
+ if (pm.getApplicationInfo(mCtsPackage, 0).enabled) {
+ return AVAILABLE;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_system;
+ }
+}
diff --git a/src/com/android/settings/gestures/GestureNavigationLongPressController.java b/src/com/android/settings/gestures/GestureNavigationLongPressController.java
new file mode 100644
index 00000000000..c6a902190ff
--- /dev/null
+++ b/src/com/android/settings/gestures/GestureNavigationLongPressController.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 Yet Another AOSP 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.gestures;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+public class GestureNavigationLongPressController extends TogglePreferenceController {
+
+ private static final String GSA_PACKAGE = "com.google.android.googlequicksearchbox";
+ private static final String LENS_SHARE_ACTIVITY = "com.google.android.apps.search.lens.LensShareEntryPointActivity";
+ private static final String LONGPRESS_KEY = "search_all_entrypoints_enabled";
+
+ private Preference mLongPressPref;
+
+ public GestureNavigationLongPressController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mLongPressPref = (Preference) screen.findPreference(LONGPRESS_KEY);
+ mLongPressPref.setEnabled(isChecked());
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.NAVBAR_LONG_PRESS_GESTURE, 1) == 1;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (mLongPressPref != null)
+ mLongPressPref.setEnabled(isChecked);
+ return Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.NAVBAR_LONG_PRESS_GESTURE, isChecked ? 1 : 0);
+ }
+
+ public static boolean isAvailable(Context context) {
+ PackageManager pm = context.getPackageManager();
+ if (pm == null) {
+ return false;
+ }
+ try {
+ if (!pm.getApplicationInfo(GSA_PACKAGE, 0).enabled) {
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ // telling real GSA apart from the google stub
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setComponent(new ComponentName(GSA_PACKAGE, LENS_SHARE_ACTIVITY));
+ return pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_system;
+ }
+}
diff --git a/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java b/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java
index 546581bd128..822105ef57b 100644
--- a/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java
+++ b/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java
@@ -17,10 +17,12 @@
package com.android.settings.gestures;
import android.app.settings.SettingsEnums;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.Settings;
import android.view.WindowManager;
@@ -31,6 +33,10 @@
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.search.SearchIndexable;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+
+import lineageos.providers.LineageSettings;
+
/**
* A fragment to include all the settings related to Gesture Navigation mode.
*/
@@ -45,12 +51,26 @@ public class GestureNavigationSettingsFragment extends DashboardFragment {
private static final String LEFT_EDGE_SEEKBAR_KEY = "gesture_left_back_sensitivity";
private static final String RIGHT_EDGE_SEEKBAR_KEY = "gesture_right_back_sensitivity";
+ private static final String NAVIGATION_BAR_HINT_KEY = "navigation_bar_hint";
+ private static final String NAVIGATION_BAR_LENGTH_KEY = "gesture_navbar_length_preference";
+ private static final String NAVIGATION_BAR_RADIUS_KEY = "gesture_navbar_radius_preference";
+ private static final String GESTURE_BACK_HEIGHT_KEY = "gesture_back_height";
+ private static final String NAVIGATION_BAR_IME_SPACE = "navigation_bar_ime_space";
+
private WindowManager mWindowManager;
private BackGestureIndicatorView mIndicatorView;
private float[] mBackGestureInsetScales;
private float mDefaultBackGestureInset;
+ private LabeledSeekBarPreference mGestureNavbarLengthPreference;
+
+
+
+ private float[] mBackGestureHeightScales = { 0f, 1f, 2f, 3f };
+ private int mCurrentRightWidth;
+ private int mCurrentLefttWidth;
+
public GestureNavigationSettingsFragment() {
super();
}
@@ -75,6 +95,23 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
initSeekBarPreference(LEFT_EDGE_SEEKBAR_KEY);
initSeekBarPreference(RIGHT_EDGE_SEEKBAR_KEY);
+ initSeekBarPreference(GESTURE_BACK_HEIGHT_KEY);
+
+ initGestureNavbarLengthPreference();
+ initGestureBarRadiusPreference();
+
+ boolean isTaskbarEnabled = LineageSettings.System.getInt(getContext().getContentResolver(),
+ LineageSettings.System.ENABLE_TASKBAR, isLargeScreen(getContext()) ? 1 : 0) == 1;
+ if (isTaskbarEnabled) {
+ getPreferenceScreen().removePreference(
+ getPreferenceScreen().findPreference(NAVIGATION_BAR_RADIUS_KEY));
+ getPreferenceScreen().removePreference(
+ getPreferenceScreen().findPreference(NAVIGATION_BAR_LENGTH_KEY));
+ getPreferenceScreen().removePreference(
+ getPreferenceScreen().findPreference(NAVIGATION_BAR_HINT_KEY));
+ getPreferenceScreen().removePreference(
+ getPreferenceScreen().findPreference(NAVIGATION_BAR_IME_SPACE));
+ }
}
@Override
@@ -118,11 +155,42 @@ private void initSeekBarPreference(final String key) {
pref.setContinuousUpdates(true);
pref.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_TICKS);
- final String settingsKey = key == LEFT_EDGE_SEEKBAR_KEY
- ? Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT
- : Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT;
- final float initScale = Settings.Secure.getFloat(
- getContext().getContentResolver(), settingsKey, 1.0f);
+ String settingsKey;
+ float initScale = 0;
+
+ switch(key) {
+ case LEFT_EDGE_SEEKBAR_KEY:
+ settingsKey = Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT;
+ break;
+ case RIGHT_EDGE_SEEKBAR_KEY:
+ settingsKey = Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT;
+ break;
+ case GESTURE_BACK_HEIGHT_KEY:
+ settingsKey = Settings.System.BACK_GESTURE_HEIGHT;
+ break;
+ default:
+ settingsKey = "";
+ break;
+ }
+
+ if (settingsKey != "") {
+ initScale = Settings.Secure.getFloat(
+ getContext().getContentResolver(), settingsKey, 1.0f);
+ }
+
+ // needed if we just change the height
+ float currentWidthScale = Settings.Secure.getFloat(
+ getContext().getContentResolver(), Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT, 1.0f);
+ mCurrentRightWidth = (int) (mDefaultBackGestureInset * currentWidthScale);
+ currentWidthScale = Settings.Secure.getFloat(
+ getContext().getContentResolver(), Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT, 1.0f);
+ mCurrentLefttWidth = (int) (mDefaultBackGestureInset * currentWidthScale);
+
+ if (key == GESTURE_BACK_HEIGHT_KEY) {
+ mBackGestureInsetScales = mBackGestureHeightScales;
+ initScale = Settings.System.getInt(
+ getContext().getContentResolver(), settingsKey, 0);
+ }
// Find the closest value to initScale
float minDistance = Float.MAX_VALUE;
@@ -137,19 +205,65 @@ private void initSeekBarPreference(final String key) {
pref.setProgress(minDistanceIndex);
pref.setOnPreferenceChangeListener((p, v) -> {
- final int width = (int) (mDefaultBackGestureInset * mBackGestureInsetScales[(int) v]);
- mIndicatorView.setIndicatorWidth(width, key == LEFT_EDGE_SEEKBAR_KEY);
+ if (key != GESTURE_BACK_HEIGHT_KEY) {
+ final int width = (int) (mDefaultBackGestureInset * mBackGestureInsetScales[(int) v]);
+ mIndicatorView.setIndicatorWidth(width, key == LEFT_EDGE_SEEKBAR_KEY);
+ if (key == LEFT_EDGE_SEEKBAR_KEY) {
+ mCurrentLefttWidth = width;
+ } else {
+ mCurrentRightWidth = width;
+ }
+ } else {
+ final int heightScale = (int) (mBackGestureInsetScales[(int) v]);
+ mIndicatorView.setIndicatorHeightScale(heightScale);
+ // dont use updateViewLayout else it will animate
+ mWindowManager.removeView(mIndicatorView);
+ mWindowManager.addView(mIndicatorView, mIndicatorView.getLayoutParams(
+ getActivity().getWindow().getAttributes()));
+ // peek the indicators
+ mIndicatorView.setIndicatorWidth(mCurrentRightWidth, false);
+ mIndicatorView.setIndicatorWidth(mCurrentLefttWidth, true);
+ }
return true;
});
pref.setOnPreferenceChangeStopListener((p, v) -> {
- mIndicatorView.setIndicatorWidth(0, key == LEFT_EDGE_SEEKBAR_KEY);
final float scale = mBackGestureInsetScales[(int) v];
- Settings.Secure.putFloat(getContext().getContentResolver(), settingsKey, scale);
+ if (key == GESTURE_BACK_HEIGHT_KEY) {
+ mIndicatorView.setIndicatorWidth(0, false);
+ mIndicatorView.setIndicatorWidth(0, true);
+ Settings.System.putInt(getContext().getContentResolver(), settingsKey, (int) scale);
+ } else {
+ mIndicatorView.setIndicatorWidth(0, key == LEFT_EDGE_SEEKBAR_KEY);
+ Settings.Secure.putFloat(getContext().getContentResolver(), settingsKey, scale);
+ }
return true;
});
}
+ private void initGestureNavbarLengthPreference() {
+ final ContentResolver resolver = getContext().getContentResolver();
+ mGestureNavbarLengthPreference = getPreferenceScreen().findPreference(NAVIGATION_BAR_LENGTH_KEY);
+ mGestureNavbarLengthPreference.setContinuousUpdates(true);
+ mGestureNavbarLengthPreference.setProgress(Settings.System.getIntForUser(
+ resolver, Settings.System.GESTURE_NAVBAR_LENGTH_MODE,
+ 1, UserHandle.USER_CURRENT));
+ mGestureNavbarLengthPreference.setOnPreferenceChangeListener((p, v) ->
+ Settings.System.putIntForUser(resolver, Settings.System.GESTURE_NAVBAR_LENGTH_MODE,
+ (Integer) v, UserHandle.USER_CURRENT));
+ }
+
+ private void initGestureBarRadiusPreference() {
+ final LabeledSeekBarPreference pref = getPreferenceScreen().
+ findPreference(NAVIGATION_BAR_RADIUS_KEY);
+ pref.setContinuousUpdates(true);
+ pref.setProgress(Settings.System.getIntForUser(getContext().getContentResolver(),
+ Settings.System.GESTURE_NAVBAR_RADIUS, 3, UserHandle.USER_CURRENT));
+ pref.setOnPreferenceChangeListener((p, v) ->
+ Settings.System.putIntForUser(getContext().getContentResolver(),
+ Settings.System.GESTURE_NAVBAR_RADIUS, (Integer) v, UserHandle.USER_CURRENT));
+ }
+
private static float[] getFloatArray(TypedArray array) {
int length = array.length();
float[] floatArray = new float[length];
diff --git a/src/com/android/settings/gestures/GestureSettings.java b/src/com/android/settings/gestures/GestureSettings.java
index 8532b162224..5d5d038bb65 100644
--- a/src/com/android/settings/gestures/GestureSettings.java
+++ b/src/com/android/settings/gestures/GestureSettings.java
@@ -55,6 +55,7 @@ public void onAttach(Context context) {
super.onAttach(context);
use(PickupGesturePreferenceController.class).setConfig(getConfig(context));
use(DoubleTapScreenPreferenceController.class).setConfig(getConfig(context));
+ use(ScreenOffUdfpsPreferenceController.class).setConfig(getConfig(context));
}
private AmbientDisplayConfiguration getConfig(Context context) {
diff --git a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
index e8c4b934663..b8b6a198c91 100644
--- a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
+++ b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
@@ -68,6 +68,8 @@ private static List buildAllPreferenceControllers(
controllers.add(new DoubleTapScreenPreferenceController(context, FAKE_PREF_KEY)
.setConfig(ambientDisplayConfiguration));
controllers.add(new PreventRingingParentPreferenceController(context, FAKE_PREF_KEY));
+ controllers.add(new ScreenOffUdfpsPreferenceController(context, FAKE_PREF_KEY)
+ .setConfig(ambientDisplayConfiguration));
return controllers;
}
}
diff --git a/src/com/android/settings/gestures/PickupGestureInsidePreferenceController.java b/src/com/android/settings/gestures/PickupGestureInsidePreferenceController.java
new file mode 100644
index 00000000000..54c14fba11e
--- /dev/null
+++ b/src/com/android/settings/gestures/PickupGestureInsidePreferenceController.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 Yet Another AOSP 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.gestures;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.MainSwitchPreference;
+
+import com.crdroid.settings.preferences.SecureSettingSwitchPreference;
+
+public class PickupGestureInsidePreferenceController extends AbstractPreferenceController
+ implements PreferenceControllerMixin, CompoundButton.OnCheckedChangeListener {
+
+ private static final String KEY = "gesture_pick_up";
+ private static final String AMBIENT_KEY = "doze_pick_up_gesture_ambient";
+
+ private final boolean mDefault;
+ private final Context mContext;
+ private MainSwitchPreference mSwitch;
+ private SecureSettingSwitchPreference mAmbientPref;
+
+ public PickupGestureInsidePreferenceController(Context context) {
+ super(context);
+ mContext = context;
+ mDefault = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_dozePickupGestureEnabled);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mAmbientPref = screen.findPreference(AMBIENT_KEY);
+ mSwitch = screen.findPreference(getPreferenceKey());
+ mSwitch.setOnPreferenceClickListener(preference -> {
+ final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_PICK_UP_GESTURE, mDefault ? 1 : 0) == 1;
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_PICK_UP_GESTURE,
+ enabled ? 0 : 1);
+ updateAmbientEnablement(!enabled);
+ return true;
+ });
+ mSwitch.addOnSwitchChangeListener(this);
+ updateState(mSwitch);
+ }
+
+ public void setChecked(boolean isChecked) {
+ if (mSwitch != null) {
+ mSwitch.updateStatus(isChecked);
+ }
+ updateAmbientEnablement(isChecked);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_PICK_UP_GESTURE, mDefault ? 1 : 0) == 1;
+ setChecked(enabled);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_PICK_UP_GESTURE, isChecked ? 1 : 0);
+ updateAmbientEnablement(isChecked);
+ }
+
+ private void updateAmbientEnablement(boolean enabled) {
+ if (mAmbientPref == null) return;
+ mAmbientPref.setEnabled(enabled);
+ }
+}
diff --git a/src/com/android/settings/gestures/PickupGesturePreferenceController.java b/src/com/android/settings/gestures/PickupGesturePreferenceController.java
index c1590bfd606..a5dcb46b48a 100644
--- a/src/com/android/settings/gestures/PickupGesturePreferenceController.java
+++ b/src/com/android/settings/gestures/PickupGesturePreferenceController.java
@@ -19,31 +19,49 @@
import static android.provider.Settings.Secure.DOZE_PICK_UP_GESTURE;
import android.annotation.UserIdInt;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
+import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.net.Uri;
+import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
-public class PickupGesturePreferenceController extends GesturePreferenceController {
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.PrimarySwitchPreference;
+
+public class PickupGesturePreferenceController extends TogglePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
private static final int ON = 1;
private static final int OFF = 0;
- private static final String PREF_KEY_VIDEO = "gesture_pick_up_video";
- private final String mPickUpPrefKey;
-
private final String SECURE_KEY = DOZE_PICK_UP_GESTURE;
+ private static final String AMBIENT_SECURE_KEY = "doze_pick_up_gesture_ambient";
private AmbientDisplayConfiguration mAmbientConfig;
@UserIdInt
private final int mUserId;
+ private final boolean mDefault;
+
+ private PrimarySwitchPreference mPreference;
+ private SettingObserver mSettingObserver;
public PickupGesturePreferenceController(Context context, String key) {
super(context, key);
mUserId = UserHandle.myUserId();
- mPickUpPrefKey = key;
+ mDefault = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_dozePickupGestureEnabled);
}
public PickupGesturePreferenceController setConfig(AmbientDisplayConfiguration config) {
@@ -68,28 +86,44 @@ public int getAvailabilityStatus() {
}
@Override
- public boolean isSliceable() {
- return TextUtils.equals(getPreferenceKey(), "gesture_pick_up");
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ mSettingObserver = new SettingObserver(mPreference);
+ updateState(mPreference);
}
@Override
- public boolean isPublicSlice() {
- return true;
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ContentResolver resolver = mContext.getContentResolver();
+ final boolean enabled =
+ Settings.Secure.getInt(resolver, SECURE_KEY, mDefault ? ON : OFF) == ON;
+ String summary;
+ if (enabled) {
+ summary = mContext.getString(R.string.gesture_setting_on) + " ("
+ + (Settings.Secure.getInt(resolver, AMBIENT_SECURE_KEY, OFF) == ON
+ ? mContext.getString(R.string.gesture_wake_ambient)
+ : mContext.getString(R.string.gesture_wake)) + ")";
+ } else {
+ summary = mContext.getString(R.string.gesture_setting_off);
+ }
+ preference.setSummary(summary);
}
@Override
- protected String getVideoPrefKey() {
- return PREF_KEY_VIDEO;
+ public boolean isSliceable() {
+ return TextUtils.equals(getPreferenceKey(), "gesture_pick_up");
}
@Override
- public boolean isChecked() {
- return getAmbientConfig().pickupGestureEnabled(mUserId);
+ public boolean isPublicSlice() {
+ return true;
}
@Override
- public String getPreferenceKey() {
- return mPickUpPrefKey;
+ public boolean isChecked() {
+ return getAmbientConfig().pickupGestureEnabled(mUserId);
}
@Override
@@ -105,4 +139,53 @@ private AmbientDisplayConfiguration getAmbientConfig() {
return mAmbientConfig;
}
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return NO_RES;
+ }
+
+ @Override
+ public void onStart() {
+ if (mSettingObserver != null) {
+ mSettingObserver.register(mContext.getContentResolver());
+ mSettingObserver.onChange(false, null);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ if (mSettingObserver != null) {
+ mSettingObserver.unregister(mContext.getContentResolver());
+ }
+ }
+
+ private class SettingObserver extends ContentObserver {
+ private final Uri mUri = Settings.Secure.getUriFor(SECURE_KEY);
+ private final Uri mAmbientUri = Settings.Secure.getUriFor(AMBIENT_SECURE_KEY);
+
+ private final Preference mPreference;
+
+ SettingObserver(Preference preference) {
+ super(Handler.getMain());
+ mPreference = preference;
+ }
+
+ public void register(ContentResolver cr) {
+ cr.registerContentObserver(mUri, false, this);
+ cr.registerContentObserver(mAmbientUri, false, this);
+ }
+
+ public void unregister(ContentResolver cr) {
+ cr.unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ if (uri == null || mUri.equals(uri) || mAmbientUri.equals(uri)) {
+ updateState(mPreference);
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/gestures/PickupGestureSettings.java b/src/com/android/settings/gestures/PickupGestureSettings.java
index bfda405b463..b3d5cfcd93c 100644
--- a/src/com/android/settings/gestures/PickupGestureSettings.java
+++ b/src/com/android/settings/gestures/PickupGestureSettings.java
@@ -26,8 +26,13 @@
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
+import java.util.ArrayList;
+import java.util.List;
+
@SearchIndexable
public class PickupGestureSettings extends DashboardFragment {
@@ -43,9 +48,18 @@ public void onAttach(Context context) {
FeatureFactory.getFeatureFactory().getSuggestionFeatureProvider();
SharedPreferences prefs = suggestionFeatureProvider.getSharedPrefs(context);
prefs.edit().putBoolean(PREF_KEY_SUGGESTION_COMPLETE, true).apply();
+ }
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context, getSettingsLifecycle());
+ }
- use(PickupGesturePreferenceController.class)
- .setConfig(new AmbientDisplayConfiguration(context));
+ private static List buildPreferenceControllers(Context context,
+ Lifecycle lifecycle) {
+ List controllers = new ArrayList<>();
+ controllers.add(new PickupGestureInsidePreferenceController(context));
+ return controllers;
}
@Override
diff --git a/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java b/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java
index d171677d72a..8749c9b12d1 100644
--- a/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java
+++ b/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java
@@ -47,6 +47,8 @@ public class PreventRingingGesturePreferenceController extends AbstractPreferenc
@VisibleForTesting
static final String KEY_MUTE = "prevent_ringing_option_mute";
+ static final String KEY_CYCLE = "prevent_ringing_option_cycle";
+
private final String PREF_KEY_VIDEO = "gesture_prevent_ringing_video";
private final String KEY = "gesture_prevent_ringing_category";
private final Context mContext;
@@ -57,6 +59,7 @@ public class PreventRingingGesturePreferenceController extends AbstractPreferenc
SelectorWithWidgetPreference mVibratePref;
@VisibleForTesting
SelectorWithWidgetPreference mMutePref;
+ SelectorWithWidgetPreference mCyclePref;
private SettingObserver mSettingObserver;
@@ -78,6 +81,7 @@ public void displayPreference(PreferenceScreen screen) {
mPreferenceCategory = screen.findPreference(getPreferenceKey());
mVibratePref = makeRadioPreference(KEY_VIBRATE, R.string.prevent_ringing_option_vibrate);
mMutePref = makeRadioPreference(KEY_MUTE, R.string.prevent_ringing_option_mute);
+ mCyclePref = makeRadioPreference(KEY_CYCLE, R.string.prevent_ringing_option_cycle);
if (mPreferenceCategory != null) {
mSettingObserver = new SettingObserver(mPreferenceCategory);
@@ -115,19 +119,29 @@ public void updateState(Preference preference) {
Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_VIBRATE);
final boolean isVibrate = preventRingingSetting == Settings.Secure.VOLUME_HUSH_VIBRATE;
final boolean isMute = preventRingingSetting == Settings.Secure.VOLUME_HUSH_MUTE;
+ final boolean isCycle = preventRingingSetting == Settings.Secure.VOLUME_HUSH_CYCLE;
if (mVibratePref != null && mVibratePref.isChecked() != isVibrate) {
mVibratePref.setChecked(isVibrate);
}
if (mMutePref != null && mMutePref.isChecked() != isMute) {
mMutePref.setChecked(isMute);
}
+ if (mCyclePref != null && mCyclePref.isChecked() != isCycle) {
+ mCyclePref.setChecked(isCycle);
+ }
if (preventRingingSetting == Settings.Secure.VOLUME_HUSH_OFF) {
mVibratePref.setEnabled(false);
mMutePref.setEnabled(false);
+ if (mCyclePref != null) {
+ mCyclePref.setEnabled(false);
+ }
} else {
mVibratePref.setEnabled(true);
mMutePref.setEnabled(true);
+ if (mCyclePref != null) {
+ mCyclePref.setEnabled(true);
+ }
}
}
@@ -152,6 +166,8 @@ private int keyToSetting(String key) {
return Settings.Secure.VOLUME_HUSH_MUTE;
case KEY_VIBRATE:
return Settings.Secure.VOLUME_HUSH_VIBRATE;
+ case KEY_CYCLE:
+ return Settings.Secure.VOLUME_HUSH_CYCLE;
default:
return Settings.Secure.VOLUME_HUSH_OFF;
}
diff --git a/src/com/android/settings/gestures/PreventRingingParentPreferenceController.java b/src/com/android/settings/gestures/PreventRingingParentPreferenceController.java
index 03b0259449c..2be63fe949c 100644
--- a/src/com/android/settings/gestures/PreventRingingParentPreferenceController.java
+++ b/src/com/android/settings/gestures/PreventRingingParentPreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.gestures;
+import static android.provider.Settings.Secure.VOLUME_HUSH_CYCLE;
import static android.provider.Settings.Secure.VOLUME_HUSH_GESTURE;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
@@ -102,6 +103,9 @@ public void updateState(Preference preference) {
case VOLUME_HUSH_MUTE:
summary = mContext.getText(R.string.prevent_ringing_option_mute_summary);
break;
+ case VOLUME_HUSH_CYCLE:
+ summary = mContext.getText(R.string.prevent_ringing_option_cycle_summary);
+ break;
// VOLUME_HUSH_OFF
default:
summary = mContext.getText(R.string.switch_off_text);
diff --git a/src/com/android/settings/gestures/ScreenOffUdfpsPreferenceController.java b/src/com/android/settings/gestures/ScreenOffUdfpsPreferenceController.java
new file mode 100644
index 00000000000..d1202cb4cac
--- /dev/null
+++ b/src/com/android/settings/gestures/ScreenOffUdfpsPreferenceController.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.gestures;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import androidx.annotation.VisibleForTesting;
+
+public class ScreenOffUdfpsPreferenceController extends GesturePreferenceController {
+
+ private final int ON = 1;
+ private final int OFF = 0;
+
+ private static final String PREF_KEY_VIDEO = "gesture_screen_off_udfps_video";
+
+ private static final String SECURE_KEY = "screen_off_udfps_enabled";
+
+ private AmbientDisplayConfiguration mAmbientConfig;
+ @UserIdInt
+ private final int mUserId;
+
+ public ScreenOffUdfpsPreferenceController(Context context, String key) {
+ super(context, key);
+ mUserId = UserHandle.myUserId();
+ }
+
+ public ScreenOffUdfpsPreferenceController setConfig(AmbientDisplayConfiguration config) {
+ mAmbientConfig = config;
+ return this;
+ }
+
+ private static boolean screenOffUdfpsAvailable(AmbientDisplayConfiguration config) {
+ return !TextUtils.isEmpty(config.udfpsLongPressSensorType());
+ }
+
+ public static boolean isSuggestionComplete(Context context, SharedPreferences prefs) {
+ return isSuggestionComplete(new AmbientDisplayConfiguration(context), prefs);
+ }
+
+ @VisibleForTesting
+ static boolean isSuggestionComplete(AmbientDisplayConfiguration config,
+ SharedPreferences prefs) {
+ return !screenOffUdfpsAvailable(config)
+ || prefs.getBoolean(ScreenOffUdfpsSettings.PREF_KEY_SUGGESTION_COMPLETE, false);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ // No hardware support for Screen-Off UDFPS
+ if (!screenOffUdfpsAvailable(getAmbientConfig())) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return TextUtils.equals(getPreferenceKey(), "gesture_screen_off_udfps");
+ }
+
+ @Override
+ public boolean isPublicSlice() {
+ return true;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ return Settings.Secure.putInt(mContext.getContentResolver(), SECURE_KEY,
+ isChecked ? ON : OFF);
+ }
+
+ @Override
+ protected String getVideoPrefKey() {
+ return PREF_KEY_VIDEO;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return getAmbientConfig().screenOffUdfpsEnabled(mUserId);
+ }
+
+ private AmbientDisplayConfiguration getAmbientConfig() {
+ if (mAmbientConfig == null) {
+ mAmbientConfig = new AmbientDisplayConfiguration(mContext);
+ }
+ return mAmbientConfig;
+ }
+}
diff --git a/src/com/android/settings/gestures/ScreenOffUdfpsSettings.java b/src/com/android/settings/gestures/ScreenOffUdfpsSettings.java
new file mode 100644
index 00000000000..763ffb69bf7
--- /dev/null
+++ b/src/com/android/settings/gestures/ScreenOffUdfpsSettings.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.gestures;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SearchIndexable
+public class ScreenOffUdfpsSettings extends DashboardFragment {
+
+ private static final String TAG = "ScreenOffUdfps";
+
+ public static final String PREF_KEY_SUGGESTION_COMPLETE =
+ "pref_screen_off_udfps_suggestion_complete";
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ SuggestionFeatureProvider suggestionFeatureProvider =
+ FeatureFactory.Companion.getFeatureFactory().getSuggestionFeatureProvider();
+ SharedPreferences prefs = suggestionFeatureProvider.getSharedPrefs(context);
+ prefs.edit().putBoolean(PREF_KEY_SUGGESTION_COMPLETE, true).apply();
+
+ use(ScreenOffUdfpsPreferenceController.class)
+ .setConfig(new AmbientDisplayConfiguration(context));
+ }
+
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.PAGE_UNKNOWN;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.screen_off_udfps_settings;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.screen_off_udfps_settings);
+}
diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
index cfaee006d39..19048794e89 100644
--- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
+++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@@ -59,6 +59,10 @@
import com.android.settingslib.widget.IllustrationPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+
+import lineageos.providers.LineageSettings;
+
import java.util.ArrayList;
import java.util.List;
@@ -209,6 +213,9 @@ protected List extends CandidateInfo> getCandidates() {
final Context c = getContext();
List candidates = new ArrayList<>();
+ boolean isTaskbarEnabled = LineageSettings.System.getInt(getContext().getContentResolver(),
+ LineageSettings.System.ENABLE_TASKBAR, isLargeScreen(getContext()) ? 1 : 0) == 1;
+
if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c,
NAV_BAR_MODE_GESTURAL_OVERLAY)) {
candidates.add(new CandidateInfoExtra(
@@ -216,7 +223,7 @@ protected List extends CandidateInfo> getCandidates() {
c.getText(R.string.edge_to_edge_navigation_summary),
KEY_SYSTEM_NAV_GESTURAL, true /* enabled */));
}
- if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c,
+ if (!isTaskbarEnabled && SystemNavigationPreferenceController.isOverlayPackageAvailable(c,
NAV_BAR_MODE_2BUTTON_OVERLAY)) {
candidates.add(new CandidateInfoExtra(
c.getText(R.string.swipe_up_to_switch_apps_title),
diff --git a/src/com/android/settings/gestures/SystemNavigationPreferenceController.java b/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
index ccdb2577ca2..1bbef2250a4 100644
--- a/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
+++ b/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
@@ -23,6 +23,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
@@ -54,27 +58,6 @@ public CharSequence getSummary() {
/** Returns {@code true} if gesture is available. */
public static boolean isGestureAvailable(Context context) {
- // Skip if the swipe up settings are not available
- if (!context.getResources().getBoolean(
- com.android.internal.R.bool.config_swipe_up_gesture_setting_available)) {
- return false;
- }
-
- // Skip if the recents component is not defined
- final ComponentName recentsComponentName = ComponentName.unflattenFromString(
- context.getString(com.android.internal.R.string.config_recentsComponentName));
- if (recentsComponentName == null) {
- return false;
- }
-
- // Skip if the overview proxy service exists
- final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP)
- .setPackage(recentsComponentName.getPackageName());
- if (context.getPackageManager().resolveService(quickStepIntent,
- PackageManager.MATCH_SYSTEM_ONLY) == null) {
- return false;
- }
-
return true;
}
diff --git a/src/com/android/settings/gestures/TapPreferenceController.java b/src/com/android/settings/gestures/TapPreferenceController.java
new file mode 100644
index 00000000000..1eabaeed7c1
--- /dev/null
+++ b/src/com/android/settings/gestures/TapPreferenceController.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 Yet Another AOSP 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.gestures;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.MainSwitchPreference;
+
+import com.crdroid.settings.preferences.SecureSettingSwitchPreference;
+
+public class TapPreferenceController extends AbstractPreferenceController
+ implements PreferenceControllerMixin, CompoundButton.OnCheckedChangeListener {
+
+ private static final String KEY = "gesture_tap";
+ private static final String AMBIENT_KEY = "doze_tap_gesture_ambient";
+
+ private final Context mContext;
+ private MainSwitchPreference mSwitch;
+ private SecureSettingSwitchPreference mAmbientPref;
+
+ public TapPreferenceController(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mAmbientPref = screen.findPreference(AMBIENT_KEY);
+ mSwitch = screen.findPreference(getPreferenceKey());
+ mSwitch.setOnPreferenceClickListener(preference -> {
+ final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_TAP_SCREEN_GESTURE, 1) == 1;
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
+ enabled ? 0 : 1);
+ updateAmbientEnablement(!enabled);
+ return true;
+ });
+ mSwitch.addOnSwitchChangeListener(this);
+ updateState(mSwitch);
+ }
+
+ public void setChecked(boolean isChecked) {
+ if (mSwitch != null) {
+ mSwitch.updateStatus(isChecked);
+ }
+ updateAmbientEnablement(isChecked);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_TAP_SCREEN_GESTURE, 1) == 1;
+ setChecked(enabled);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.DOZE_TAP_SCREEN_GESTURE, isChecked ? 1 : 0);
+ SystemProperties.set("persist.sys.tap_gesture", isChecked ? "1" : "0");
+ updateAmbientEnablement(isChecked);
+ }
+
+ private void updateAmbientEnablement(boolean enabled) {
+ if (mAmbientPref == null) return;
+ mAmbientPref.setEnabled(enabled);
+ }
+}
diff --git a/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java b/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java
index 86ac713194f..fd3b955c0a9 100644
--- a/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java
+++ b/src/com/android/settings/gestures/TapScreenGesturePreferenceController.java
@@ -19,28 +19,43 @@
import static android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE;
import android.annotation.UserIdInt;
+import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.net.Uri;
+import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
-public class TapScreenGesturePreferenceController extends GesturePreferenceController {
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
- private static final String PREF_KEY_VIDEO = "gesture_tap_screen_video";
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.PrimarySwitchPreference;
+
+public class TapScreenGesturePreferenceController extends TogglePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+
+ private static final String SECURE_KEY = DOZE_TAP_SCREEN_GESTURE;
+ private static final String AMBIENT_SECURE_KEY = "doze_tap_gesture_ambient";
private AmbientDisplayConfiguration mAmbientConfig;
@UserIdInt
private final int mUserId;
+ private PrimarySwitchPreference mPreference;
+ private SettingObserver mSettingObserver;
+
public TapScreenGesturePreferenceController(Context context, String key) {
super(context, key);
mUserId = UserHandle.myUserId();
- }
-
- public TapScreenGesturePreferenceController setConfig(AmbientDisplayConfiguration config) {
- mAmbientConfig = config;
- return this;
+ mAmbientConfig = new AmbientDisplayConfiguration(context);
}
@Override
@@ -54,13 +69,33 @@ public int getAvailabilityStatus() {
}
@Override
- public boolean isPublicSlice() {
- return true;
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ mSettingObserver = new SettingObserver(mPreference);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ContentResolver resolver = mContext.getContentResolver();
+ final boolean enabled =
+ Settings.Secure.getInt(resolver, SECURE_KEY, 1) == 1;
+ String summary;
+ if (enabled) {
+ summary = mContext.getString(R.string.gesture_setting_on) + " ("
+ + (Settings.Secure.getInt(resolver, AMBIENT_SECURE_KEY, 0) == 1
+ ? mContext.getString(R.string.gesture_wake_ambient)
+ : mContext.getString(R.string.gesture_wake)) + ")";
+ } else {
+ summary = mContext.getString(R.string.gesture_setting_off);
+ }
+ preference.setSummary(summary);
}
@Override
- protected String getVideoPrefKey() {
- return PREF_KEY_VIDEO;
+ public boolean isPublicSlice() {
+ return true;
}
@Override
@@ -76,7 +111,7 @@ public boolean isChecked() {
@Override
public boolean setChecked(boolean isChecked) {
boolean success = Settings.Secure.putInt(mContext.getContentResolver(),
- DOZE_TAP_SCREEN_GESTURE, isChecked ? 1 : 0);
+ SECURE_KEY, isChecked ? 1 : 0);
SystemProperties.set("persist.sys.tap_gesture", isChecked ? "1" : "0");
return success;
}
@@ -87,4 +122,53 @@ private AmbientDisplayConfiguration getAmbientConfig() {
}
return mAmbientConfig;
}
-}
\ No newline at end of file
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return NO_RES;
+ }
+
+ @Override
+ public void onStart() {
+ if (mSettingObserver != null) {
+ mSettingObserver.register(mContext.getContentResolver());
+ mSettingObserver.onChange(false, null);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ if (mSettingObserver != null) {
+ mSettingObserver.unregister(mContext.getContentResolver());
+ }
+ }
+
+ private class SettingObserver extends ContentObserver {
+ private final Uri mUri = Settings.Secure.getUriFor(SECURE_KEY);
+ private final Uri mAmbientUri = Settings.Secure.getUriFor(AMBIENT_SECURE_KEY);
+
+ private final Preference mPreference;
+
+ SettingObserver(Preference preference) {
+ super(Handler.getMain());
+ mPreference = preference;
+ }
+
+ public void register(ContentResolver cr) {
+ cr.registerContentObserver(mUri, false, this);
+ cr.registerContentObserver(mAmbientUri, false, this);
+ }
+
+ public void unregister(ContentResolver cr) {
+ cr.unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ if (uri == null || mUri.equals(uri) || mAmbientUri.equals(uri)) {
+ updateState(mPreference);
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/gestures/TapScreenGestureSettings.java b/src/com/android/settings/gestures/TapScreenGestureSettings.java
index 773ef0ef68d..3fa8bb1f7d9 100644
--- a/src/com/android/settings/gestures/TapScreenGestureSettings.java
+++ b/src/com/android/settings/gestures/TapScreenGestureSettings.java
@@ -26,8 +26,13 @@
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
+import java.util.ArrayList;
+import java.util.List;
+
@SearchIndexable
public class TapScreenGestureSettings extends DashboardFragment {
private static final String TAG = "TapScreenGestureSettings";
@@ -42,9 +47,18 @@ public void onAttach(Context context) {
FeatureFactory.getFeatureFactory().getSuggestionFeatureProvider();
SharedPreferences prefs = suggestionFeatureProvider.getSharedPrefs(context);
prefs.edit().putBoolean(PREF_KEY_SUGGESTION_COMPLETE, true).apply();
+ }
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context, getSettingsLifecycle());
+ }
- use(TapScreenGesturePreferenceController.class)
- .setConfig(new AmbientDisplayConfiguration(context));
+ private static List buildPreferenceControllers(Context context,
+ Lifecycle lifecycle) {
+ List controllers = new ArrayList<>();
+ controllers.add(new TapPreferenceController(context));
+ return controllers;
}
@Override
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 4b9f6a8da9d..f1cbd12d4c5 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -26,12 +26,15 @@
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
@@ -41,12 +44,15 @@
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.util.Log;
+import android.util.TypedValue;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.Toolbar;
+import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.Insets;
@@ -60,14 +66,15 @@
import androidx.fragment.app.FragmentTransaction;
import androidx.window.embedding.SplitController;
import androidx.window.embedding.SplitInfo;
+import androidx.window.embedding.ActivityEmbeddingController;
import androidx.window.embedding.SplitRule;
import androidx.window.java.embedding.SplitControllerCallbackAdapter;
+import com.android.internal.util.UserIcons;
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsApplication;
-import com.android.settings.accounts.AvatarViewMixin;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.activityembedding.EmbeddedDeepLinkUtils;
@@ -79,12 +86,15 @@
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settingslib.Utils;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.net.URISyntaxException;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.lang.*;
+
+import com.google.android.material.appbar.CollapsingToolbarLayout;
/** Settings homepage activity */
public class SettingsHomepageActivity extends FragmentActivity implements
@@ -114,11 +124,13 @@ public class SettingsHomepageActivity extends FragmentActivity implements
private View mTwoPaneSuggestionView;
private CategoryMixin mCategoryMixin;
private Set mLoadedListeners;
+ private ActivityEmbeddingController mActivityEmbeddingController;
private boolean mIsEmbeddingActivityEnabled;
private boolean mIsTwoPane;
// A regular layout shows icons on homepage, whereas a simplified layout doesn't.
private boolean mIsRegularLayout = true;
+
private SplitControllerCallbackAdapter mSplitControllerAdapter;
private SplitInfoCallback mCallback;
private boolean mAllowUpdateSuggestion = true;
@@ -189,6 +201,10 @@ public CategoryMixin getCategoryMixin() {
return mCategoryMixin;
}
+ Context context;
+ ImageView avatarView;
+ UserManager mUserManager;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -259,12 +275,43 @@ protected void onCreate(Bundle savedInstanceState) {
updateAppBarMinHeight();
initHomepageContainer();
+
+ Context context = getApplicationContext();
+ mUserManager = context.getSystemService(UserManager.class);
+
updateHomepageAppBar();
updateHomepageBackground();
mLoadedListeners = new ArraySet<>();
+ avatarView = findViewById(R.id.account_avatar);
+
+ if (avatarView != null) {
+ avatarView.setImageDrawable(getCircularUserIcon(getApplicationContext()));
+ avatarView.setVisibility(View.VISIBLE);
+ avatarView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName("com.android.settings","com.android.settings.Settings$UserSettingsActivity"));
+ startActivity(intent);
+ }
+ });
+ }
+
initSearchBarView();
+ avatarView = findViewById(R.id.account_avatar);
+ avatarView.setImageDrawable(getCircularUserIcon(context));
+ avatarView.setVisibility(View.VISIBLE);
+ avatarView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName("com.android.settings","com.android.settings.Settings$UserSettingsActivity"));
+ startActivity(intent);
+ }
+ });
+
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
mCategoryMixin = new CategoryMixin(this);
getLifecycle().addObserver(mCategoryMixin);
@@ -272,7 +319,6 @@ protected void onCreate(Bundle savedInstanceState) {
final String highlightMenuKey = getHighlightMenuKey();
// Only allow features on high ram devices.
if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
- initAvatarView();
final boolean scrollNeeded = mIsEmbeddingActivityEnabled
&& !TextUtils.equals(getString(DEFAULT_HIGHLIGHT_MENU_KEY), highlightMenuKey);
showSuggestionFragment(scrollNeeded);
@@ -283,6 +329,31 @@ protected void onCreate(Bundle savedInstanceState) {
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
}
+
+ setupEdgeToEdge();
+ mActivityEmbeddingController = ActivityEmbeddingController.getInstance(this);
+ mIsTwoPane = mActivityEmbeddingController.isActivityEmbedded(this);
+
+ updateAppBarMinHeight();
+ initHomepageContainer();
+ updateHomepageAppBar();
+ initSearchBarView();
+ initDashboardMessages();
+ // Only allow features on high ram devices.
+ if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
+ final boolean scrollNeeded = mIsEmbeddingActivityEnabled
+ && !TextUtils.equals(getString(DEFAULT_HIGHLIGHT_MENU_KEY), highlightMenuKey);
+ showSuggestionFragment(scrollNeeded);
+ if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) {
+ showFragment(() -> new ContextualCardsFragment(), R.id.contextual_cards_content);
+ ((FrameLayout) findViewById(R.id.main_content))
+ .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
+ }
+ }
+ getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
+ mCategoryMixin = new CategoryMixin(this);
+ getLifecycle().addObserver(mCategoryMixin);
+
mMainFragment = showFragment(() -> {
final TopLevelSettings fragment = new TopLevelSettings();
fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
@@ -308,6 +379,73 @@ protected void onCreate(Bundle savedInstanceState) {
enableTaskLocaleOverride();
}
+ private void initDashboardMessages() {
+ boolean showDashboardMessages = android.provider.Settings.System.getIntForUser(getApplicationContext().getContentResolver(),
+ android.provider.Settings.System.SHOW_CONTEXTUAL_DASHBOARD_MESSAGES, 0,
+ UserHandle.USER_CURRENT) != 0;
+
+ final View root = findViewById(R.id.settings_homepage_container);
+ final TextView textView = root.findViewById(R.id.user_title);
+ final TextView homepageTitle = root.findViewById(R.id.homepage_title);
+ final TextView searchTextView = root.findViewById(R.id.search_action_bar_title);
+ setRandomSearchMessage(searchTextView);
+ if (showDashboardMessages) {
+ setupDashboardMessages(textView, homepageTitle);
+ } else {
+ showDefaultSettingsLabel(textView);
+ homepageTitle.setVisibility(View.GONE);
+ }
+ }
+ private void setRandomSearchMessage(TextView searchTextView) {
+ String[] searchMessages = getResources().getStringArray(R.array.settings_search);
+ searchTextView.setText(searchMessages[new Random().nextInt(searchMessages.length)]);
+ }
+ private void setupDashboardMessages(TextView textView, TextView homepageTitle) {
+ homepageTitle.setVisibility(View.VISIBLE);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.header_text_size_contextual));
+ String[] messages = getMessagesBasedOnTime();
+ String greeting = getGreetingBasedOnTime();
+ textView.setText(greeting + " " + getOwnerName() + ",");
+ homepageTitle.setText(messages[new Random().nextInt(messages.length)]);
+ }
+ private String[] getMessagesBasedOnTime() {
+ int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ if (hour >= 5 && hour < 12) {
+ return getResources().getStringArray(R.array.dashboard_morning);
+ }
+ if (hour >= 12 && hour < 18) {
+ return getResources().getStringArray(R.array.dashboard_daytime);
+ }
+ if (hour >= 18 && hour < 22) {
+ return getResources().getStringArray(R.array.dashboard_evening);
+ }
+ return getResources().getStringArray(R.array.dashboard_night);
+ }
+
+ private String getGreetingBasedOnTime() {
+ int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
+ String[] greetings = getResources().getStringArray(R.array.dashboard_greetings);
+ Random random = new Random();
+ if (random.nextFloat() < 0.4) {
+ return greetings[4]; // Namaste
+ }
+ if (hour >= 5 && hour < 12) {
+ return greetings[0]; // Good morning
+ } else if (hour < 18) {
+ return greetings[1]; // Hello
+ } else if (hour < 22) {
+ return greetings[2]; // Good evening
+ } else {
+ return greetings[3]; // Good night
+ }
+ }
+
+ private void showDefaultSettingsLabel(TextView textView) {
+ textView.setVisibility(View.VISIBLE);
+ textView.setText(R.string.settings_label);
+ textView.setTextAppearance(this, R.style.DefaultHomepageTitleText);
+ }
+
@VisibleForTesting
void initSplitPairRules() {
new ActivityEmbeddingRulesController(getApplicationContext()).initRules();
@@ -426,24 +564,6 @@ private void initSearchBarView() {
}
}
- private void initAvatarView() {
- if (Flags.homepageRevamp()) {
- return;
- }
-
- final ImageView avatarView = findViewById(R.id.account_avatar);
- final ImageView avatarTwoPaneView = findViewById(R.id.account_avatar_two_pane_version);
- if (AvatarViewMixin.isAvatarSupported(this)) {
- avatarView.setVisibility(View.VISIBLE);
- getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
-
- if (mIsEmbeddingActivityEnabled) {
- avatarTwoPaneView.setVisibility(View.VISIBLE);
- getLifecycle().addObserver(new AvatarViewMixin(this, avatarTwoPaneView));
- }
- }
- }
-
private void updateHomepageUI() {
final boolean newTwoPaneState = ActivityEmbeddingUtils.isAlreadyEmbedded(this);
if (mIsTwoPane != newTwoPaneState) {
@@ -859,4 +979,46 @@ public void accept(List splitInfoList) {
}
}
}
+
+ private Drawable getCircularUserIcon(Context context) {
+ if (context == null) {
+ Log.e(TAG, "Context is null while getting user icon. Using application context.");
+ context = getApplicationContext();
+ }
+
+ final UserManager mUserManager = getSystemService(UserManager.class);
+ Bitmap bitmapUserIcon = mUserManager.getUserIcon(UserHandle.myUserId());
+
+ if (bitmapUserIcon == null) {
+ // get default user icon.
+ final Drawable defaultUserIcon = UserIcons.getDefaultUserIcon(
+ context.getResources(), UserHandle.myUserId(), false);
+ bitmapUserIcon = UserIcons.convertToBitmap(defaultUserIcon);
+ }
+ Drawable drawableUserIcon = new CircleFramedDrawable(bitmapUserIcon,
+ (int) context.getResources().getDimension(com.android.internal.R.dimen.user_icon_size));
+
+ return drawableUserIcon;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (avatarView != null) {
+ avatarView.setImageDrawable(getCircularUserIcon(getApplicationContext()));
+ }
+ }
+
+ private String getOwnerName() {
+ final UserManager mUserManager = getSystemService(UserManager.class);
+ final UserInfo userInfo = com.android.settings.Utils.getExistingUser(mUserManager,
+ UserHandle.of(UserHandle.myUserId()));
+ String fullName = userInfo.name != null ? userInfo.name : getString(R.string.default_user);
+ String[] nameParts = fullName.split("\\s+");
+ if (nameParts.length > 0) {
+ return nameParts[0];
+ } else {
+ return getString(R.string.default_user);
+ }
+ }
}
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
index 87e5560c959..361dcf9bc69 100644
--- a/src/com/android/settings/homepage/TopLevelSettings.java
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -71,6 +72,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
private boolean mScrollNeeded = true;
private boolean mFirstStarted = true;
private ActivityEmbeddingController mActivityEmbeddingController;
+ private boolean gAppsExists;
public TopLevelSettings() {
final Bundle args = new Bundle();
@@ -104,6 +106,8 @@ public int getMetricsCategory() {
@Override
public void onAttach(Context context) {
super.onAttach(context);
+ // Check if Google Apps exist and set the gAppsExists flag accordingly
+ gAppsExists = checkIfGoogleAppsExist(context);
HighlightableMenu.fromXml(context, getPreferenceScreenResId());
use(SupportPreferenceController.class).setActivity(getActivity());
}
@@ -201,6 +205,18 @@ private boolean isOnlyOneActivityInTask() {
return taskInfo.numActivities == 1;
}
+ private boolean checkIfGoogleAppsExist(Context context) {
+ // Perform the necessary check to determine if Google Apps exist
+ // For example, you might use PackageManager to check for the existence of a Google app package
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ packageManager.getPackageInfo("com.google.android.gsf", 0);
+ return true; // Google Apps exist
+ } catch (PackageManager.NameNotFoundException e) {
+ return false; // Google Apps do not exist
+ }
+ }
+
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -222,6 +238,47 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
icon.setTint(tintColor);
}
});
+ onSetPrefCard();
+ }
+
+ private void onSetPrefCard() {
+ final PreferenceScreen screen = getPreferenceScreen();
+ final int count = screen.getPreferenceCount();
+ for (int i = 0; i < count; i++) {
+ final Preference preference = screen.getPreference(i);
+
+ String key = preference.getKey();
+ if (key.equals("top_level_network")
+ || key.equals("top_level_apps")
+ || key.equals("top_level_accessibility")
+ || key.equals("top_level_emergency")
+ || key.equals("top_level_system")){
+ preference.setLayoutResource(R.layout.matrixx_dashboard_preference_top);
+ } else if (key.equals("top_level_battery")
+ || key.equals("top_level_display")
+ || key.equals("top_level_security")
+ || key.equals("top_level_privacy")
+ || key.equals("top_level_safety_center")
+ || key.equals("top_level_storage")
+ || key.equals("top_level_wallpaper")
+ || key.equals("top_level_wellbeing")
+ || key.equals("top_level_notifications")){
+ preference.setLayoutResource(R.layout.matrixx_dashboard_preference_middle);
+ } else if (key.equals("top_level_google")){
+ if (gAppsExists && screen.findPreference("dashboard_tile_pref_com.google.android.gms.backup.component.BackupOrRestoreSettingsActivity") != null) {
+ preference.setLayoutResource(R.layout.matrixx_dashboard_preference_google_v2);
+ } else {
+ preference.setLayoutResource(R.layout.matrixx_dashboard_preference_google);
+ }
+ } else if (key.equals("top_level_accounts") && gAppsExists){
+ preference.setLayoutResource(R.layout.matrixx_dashboard_preference_middle);
+ } else if (key.equals("top_level_crdroid")) {
+ preference.setLayoutResource(R.layout.matrixx_dashboard_preference_single);
+ }
+ else {
+ preference.setLayoutResource(R.layout.matrixx_dashboard_preference_bottom);
+ }
+ }
}
@Override
diff --git a/src/com/android/settings/inputmethod/KeyboardSettings.java b/src/com/android/settings/inputmethod/KeyboardSettings.java
index 38f73438cf1..00d5ad5f991 100644
--- a/src/com/android/settings/inputmethod/KeyboardSettings.java
+++ b/src/com/android/settings/inputmethod/KeyboardSettings.java
@@ -36,6 +36,8 @@
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
+import lineageos.hardware.LineageHardwareManager;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -47,6 +49,7 @@ public class KeyboardSettings extends DashboardFragment {
private static final String KEY_KEYBOARDS_CATEGORY = "keyboards_category";
private static final String KEY_POINTER_CATEGORY = "pointer_category";
+ private static final String KEY_TOUCH_HOVERING = "feature_touch_hovering";
@Override
public int getMetricsCategory() {
@@ -110,5 +113,15 @@ private static List buildPreferenceControllers(
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.keyboard_settings);
+ new BaseSearchIndexProvider(R.xml.keyboard_settings) {
+ @Override
+ public List getNonIndexableKeys(Context context) {
+ List keys = super.getNonIndexableKeys(context);
+ LineageHardwareManager hardware = LineageHardwareManager.getInstance(context);
+ if (!hardware.isSupported(LineageHardwareManager.FEATURE_TOUCH_HOVERING)) {
+ keys.add(KEY_TOUCH_HOVERING);
+ }
+ return keys;
+ }
+ };
}
\ No newline at end of file
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index a000f9ede5d..a19862c9708 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -71,6 +71,7 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
private static final String KEYBOARD_OPTIONS_CATEGORY = "keyboard_options_category";
private static final String KEYBOARD_A11Y_CATEGORY = "keyboard_a11y_category";
+ private static final String KEYBOARD_EXTRAS_CATEGORY = "keyboard_extras_category";
private static final String ACCESSIBILITY_BOUNCE_KEYS = "accessibility_bounce_keys";
private static final String ACCESSIBILITY_SLOW_KEYS = "accessibility_slow_keys";
private static final String ACCESSIBILITY_STICKY_KEYS = "accessibility_sticky_keys";
@@ -103,6 +104,8 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
private PreferenceCategory mKeyboardAssistanceCategory;
@Nullable
private PreferenceCategory mKeyboardA11yCategory = null;
+ @NonNull
+ private PreferenceCategory mKeyboardExtrasCategory = null;
@Nullable
private TwoStatePreference mAccessibilityBounceKeys = null;
@Nullable
@@ -132,6 +135,8 @@ public void onCreatePreferences(Bundle bundle, String s) {
mImm = Preconditions.checkNotNull(activity.getSystemService(InputMethodManager.class));
mKeyboardAssistanceCategory = Preconditions.checkNotNull(
findPreference(KEYBOARD_OPTIONS_CATEGORY));
+ mKeyboardExtrasCategory = Preconditions.checkNotNull(
+ findPreference(KEYBOARD_EXTRAS_CATEGORY));
mKeyboardA11yCategory = Objects.requireNonNull(findPreference(KEYBOARD_A11Y_CATEGORY));
mAccessibilityBounceKeys = Objects.requireNonNull(
@@ -330,6 +335,8 @@ private void updateHardKeyboards(@NonNull List newHardKe
}
mKeyboardAssistanceCategory.setOrder(1);
preferenceScreen.addPreference(mKeyboardAssistanceCategory);
+ mKeyboardExtrasCategory.setOrder(99);
+ preferenceScreen.addPreference(mKeyboardExtrasCategory);
if (mSupportsFirmwareUpdate) {
mFeatureProvider.addFirmwareUpdateCategory(getPrefContext(), preferenceScreen);
}
diff --git a/src/com/android/settings/location/AgpsPreferenceController.java b/src/com/android/settings/location/AgpsPreferenceController.java
new file mode 100644
index 00000000000..0b0bb73a6f0
--- /dev/null
+++ b/src/com/android/settings/location/AgpsPreferenceController.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 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.location;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.SwitchPreferenceCompat;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+public class AgpsPreferenceController extends LocationBasePreferenceController {
+ private static final String KEY_ASSISTED_GPS = "assisted_gps";
+
+ private SwitchPreferenceCompat mAgpsPreference;
+
+ public AgpsPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_ASSISTED_GPS;
+ }
+
+ @AvailabilityStatus
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mAgpsPreference = (SwitchPreferenceCompat) screen.findPreference(KEY_ASSISTED_GPS);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mAgpsPreference != null) {
+ mAgpsPreference.setChecked(Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 1) == 1);
+ }
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (KEY_ASSISTED_GPS.equals(preference.getKey())) {
+ final ContentResolver cr = mContext.getContentResolver();
+ final boolean switchState = mAgpsPreference.isChecked();
+ Settings.Global.putInt(cr, Settings.Global.ASSISTED_GPS_ENABLED,
+ switchState ? 1 : 0);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onLocationModeChanged(int mode, boolean restricted) {}
+}
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index 87e8817808f..bcba10f62e6 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -120,6 +120,7 @@ public void onAttach(Context context) {
use(LocationForWorkPreferenceController.class).init(this);
use(LocationSettingsFooterPreferenceController.class).init(this);
use(LocationForPrivateProfilePreferenceController.class).init(this);
+ use(AgpsPreferenceController.class).init(this);
}
@Override
diff --git a/src/com/android/settings/media/AppVolumeSlice.java b/src/com/android/settings/media/AppVolumeSlice.java
new file mode 100644
index 00000000000..b6d0c5d4f4d
--- /dev/null
+++ b/src/com/android/settings/media/AppVolumeSlice.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 Project Kaleidoscope
+ *
+ * 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.media;
+
+import static android.app.slice.Slice.EXTRA_RANGE_VALUE;
+import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
+
+import static com.android.settings.slices.CustomSliceRegistry.APP_VOLUME_SLICE_URI;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.AppVolume;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.InputRangeBuilder;
+import androidx.slice.builders.SliceAction;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.slices.CustomSliceable;
+import com.android.settings.slices.SliceBroadcastReceiver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppVolumeSlice implements CustomSliceable {
+
+ private static final String TAG = "AppVolumeSlice";
+ private static final String PACKAGE_NAME = "package_name";
+ private static final String ACTION_LAUNCH_DIALOG = "action_launch_dialog";
+
+ private final Context mContext;
+
+ private final AudioManager mAudioManager;
+
+ public AppVolumeSlice(Context context) {
+ mContext = context;
+ mAudioManager = context.getSystemService(AudioManager.class);
+ }
+
+ @Override
+ public void onNotifyChange(Intent intent) {
+ final int newPosition = intent.getIntExtra(EXTRA_RANGE_VALUE, -1);
+ final String packageName = intent.getStringExtra(PACKAGE_NAME);
+ if (!TextUtils.isEmpty(packageName)) {
+ mAudioManager.setAppVolume(packageName, newPosition / 100.0f);
+ return;
+ }
+ }
+
+ @Override
+ public Slice getSlice() {
+ final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
+ .setAccentColor(COLOR_NOT_TINTED);
+
+ // Only displaying active tracks
+ final List appVols = new ArrayList<>();
+ for (AppVolume vol : mAudioManager.listAppVolumes()) {
+ if (vol.isActive()) {
+ appVols.add(vol);
+ }
+ }
+ if (appVols.isEmpty()) {
+ Log.d(TAG, "No active tracks");
+ return listBuilder.build();
+ }
+
+ for (AppVolume vol : appVols) {
+ final CharSequence appName = Utils.getApplicationLabel(
+ mContext, vol.getPackageName());
+ IconCompat icon = getApplicationIcon(vol.getPackageName());
+ final SliceAction primarySliceAction = SliceAction.create(
+ getBroadcastIntent(mContext), icon, ListBuilder.ICON_IMAGE, appName);
+ listBuilder.addInputRange(new InputRangeBuilder()
+ .setTitleItem(icon, ListBuilder.ICON_IMAGE)
+ .setTitle(appName)
+ .setInputAction(getSliderInputAction(vol.getPackageName()))
+ .setMax(100)
+ .setValue((int)(vol.getVolume() * 100))
+ .setPrimaryAction(primarySliceAction));
+ }
+ return listBuilder.build();
+ }
+
+ private IconCompat getApplicationIcon(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(packageName, PackageManager.MATCH_ANY_USER);
+ Resources resources = pm.getResourcesForApplication(ai);
+ IconCompat icon = IconCompat.createWithResource(resources, packageName, ai.icon);
+ return icon;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to get icon of " + packageName, e);
+ }
+
+ final Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ return IconCompat.createWithBitmap(bitmap);
+ }
+
+
+ private PendingIntent getSliderInputAction(String packageName) {
+ final int requestCode = packageName.hashCode();
+ final Intent intent = new Intent(getUri().toString())
+ .setData(getUri())
+ .putExtra(PACKAGE_NAME, packageName)
+ .setClass(mContext, SliceBroadcastReceiver.class);
+ return PendingIntent.getBroadcast(mContext, requestCode, intent,
+ PendingIntent.FLAG_MUTABLE);
+ }
+
+ @Override
+ public Uri getUri() {
+ return APP_VOLUME_SLICE_URI;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return null;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_sound;
+ }
+}
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index 69183ff25c0..5b1a8bfc7c8 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.net.NetworkTemplate;
import android.net.wifi.WifiConfiguration;
@@ -45,10 +46,12 @@
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -65,10 +68,13 @@
import com.android.settings.AirplaneModeEnabler;
import com.android.settings.R;
import com.android.settings.RestrictedSettingsFragment;
+import com.android.settings.accessibility.AccessibilitySetupWizardUtils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.datausage.DataUsagePreference;
import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.location.WifiScanningFragment;
+import com.android.settings.network.MobileDataEnabledListener;
+import com.android.settings.network.telephony.MobileDataPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.wifi.AddNetworkFragment;
import com.android.settings.wifi.AddWifiNetworkPreference;
@@ -97,6 +103,10 @@
import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
import com.android.wifitrackerlib.WifiPickerTracker;
+import com.google.android.setupcompat.template.FooterButtonStyleUtils;
+import com.google.android.setupcompat.util.WizardManagerHelper;
+import com.google.android.setupdesign.GlifPreferenceLayout;
+
import java.util.List;
import java.util.Optional;
@@ -107,7 +117,8 @@
public class NetworkProviderSettings extends RestrictedSettingsFragment
implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback,
WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener,
- AirplaneModeEnabler.OnAirplaneModeChangedListener, InternetUpdater.InternetChangeListener {
+ AirplaneModeEnabler.OnAirplaneModeChangedListener, InternetUpdater.InternetChangeListener,
+ MobileDataEnabledListener.Client {
private static final String TAG = "NetworkProviderSettings";
// IDs of context menu
@@ -167,6 +178,8 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
// Enable the Next button when a Wi-Fi network is connected.
private boolean mEnableNextOnConnection;
+ private boolean mIsInSetupWizard;
+
// This string extra specifies a network to open the connect dialog on, so the user can enter
// network credentials. This is used by quick settings for secured networks, among other
// things.
@@ -198,6 +211,9 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
protected WifiManager mWifiManager;
private WifiManager.ActionListener mSaveListener;
+ int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ MobileDataEnabledListener mDataStateListener;
+
protected InternetResetHelper mInternetResetHelper;
/**
@@ -247,6 +263,12 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
@VisibleForTesting
MenuProvider mMenuProvider;
+ /**
+ * Mobile data toggle
+ */
+ private static final String PREF_KEY_MOBILE_DATA_TOGGLE = "main_toggle_mobile_data";
+ private MobileDataPreferenceController mMobileDataPreferenceController;
+
/**
* Mobile networks list for provider model
*/
@@ -261,6 +283,7 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
public NetworkProviderSettings() {
super(DISALLOW_CONFIG_WIFI);
+ mSubId = SubscriptionManager.getActiveDataSubscriptionId();
}
@Override
@@ -271,6 +294,18 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
return;
}
+ if (mIsInSetupWizard) {
+ GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
+ final Drawable icon = getContext().getDrawable(R.drawable.ic_network_setup);
+ final String title = getContext().getString(R.string.provider_internet_settings);
+ AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout,
+ title, "" /* description */, icon);
+ FooterButtonStyleUtils.applyPrimaryButtonPartnerResource(activity, getNextButton(),
+ true);
+
+ return;
+ }
+
setPinnedHeaderView(com.android.settingslib.widget.progressbar.R.layout.progress_header);
setProgressBarVisible(false);
@@ -294,12 +329,15 @@ private boolean hasWifiManager() {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getContext();
+ final Intent intent = this.getIntent();
if (context != null && !context.getResources().getBoolean(
R.bool.config_show_internet_settings)) {
finish();
return;
}
mAirplaneModeEnabler = new AirplaneModeEnabler(getContext(), this);
+ mDataStateListener = new MobileDataEnabledListener(getContext(), this);
+ mIsInSetupWizard = WizardManagerHelper.isAnySetupWizard(intent);
// TODO(b/37429702): Add animations and preference comparator back after initial screen is
// loaded (ODR).
@@ -356,6 +394,17 @@ private void updateUserType() {
mIsGuest = userManager.isGuestUser();
}
+ @Override
+ public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
+ Bundle savedInstanceState) {
+ if (mIsInSetupWizard) {
+ GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
+ return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
+ } else {
+ return super.onCreateRecyclerView(inflater, parent, savedInstanceState);
+ }
+ }
+
private void addPreferences() {
addPreferencesFromResource(R.xml.network_provider_settings);
@@ -378,6 +427,7 @@ private void addPreferences() {
if (mResetInternetPreference != null) {
mResetInternetPreference.setVisible(false);
}
+ addMobileDataPreferenceController();
addNetworkMobileProviderController();
addConnectedEthernetNetworkController();
addWifiSwitchPreferenceController();
@@ -400,6 +450,18 @@ boolean showAnySubscriptionInfo(Context context) {
return (context != null) && SubscriptionUtil.isSimHardwareVisible(context);
}
+ private void addMobileDataPreferenceController() {
+ if (!showAnySubscriptionInfo(getContext())) {
+ return;
+ }
+ if (mMobileDataPreferenceController == null) {
+ mMobileDataPreferenceController = new MobileDataPreferenceController(
+ getContext(), PREF_KEY_MOBILE_DATA_TOGGLE, getSettingsLifecycle(),
+ this, mSubId, mIsInSetupWizard);
+ }
+ mMobileDataPreferenceController.displayPreference(getPreferenceScreen());
+ }
+
private void addNetworkMobileProviderController() {
if (!showAnySubscriptionInfo(getContext())) {
return;
@@ -467,6 +529,11 @@ public void onFailure(int reason) {
}
};
+ if (mIsInSetupWizard) {
+ mConfigureWifiSettingsPreference.setVisible(false);
+ mDataUsagePreference.setVisible(false);
+ }
+
if (savedInstanceState != null) {
mDialogMode = savedInstanceState.getInt(SAVE_DIALOG_MODE);
mDialogWifiEntryKey = savedInstanceState.getString(SAVE_DIALOG_WIFIENTRY_KEY);
@@ -502,6 +569,7 @@ public void onStart() {
return;
}
mAirplaneModeEnabler.start();
+ mDataStateListener.start(mSubId);
}
private void restrictUi() {
@@ -530,7 +598,8 @@ public void onResume() {
}
changeNextButtonState(mWifiPickerTracker != null
- && mWifiPickerTracker.getConnectedWifiEntry() != null);
+ && mWifiPickerTracker.getConnectedWifiEntry() != null
+ || getDataEnabled());
}
@Override
@@ -539,6 +608,7 @@ public void onStop() {
getView().removeCallbacks(mUpdateWifiEntryPreferencesRunnable);
getView().removeCallbacks(mHideProgressBarRunnable);
mAirplaneModeEnabler.stop();
+ mDataStateListener.stop();
super.onStop();
}
@@ -917,7 +987,8 @@ public void onWifiEntriesChanged(@WifiPickerTracker.WifiEntriesChangedReason int
setProgressBarVisible(false);
}
changeNextButtonState(mWifiPickerTracker != null
- && mWifiPickerTracker.getConnectedWifiEntry() != null);
+ && mWifiPickerTracker.getConnectedWifiEntry() != null
+ || getDataEnabled());
// Edit the Wi-Fi network of specified SSID.
if (mOpenSsid != null && mWifiPickerTracker != null) {
@@ -992,7 +1063,9 @@ protected void updateWifiEntryPreferences() {
if (mClickedConnect) {
mClickedConnect = false;
- scrollToPreference(connectedWifiPreferenceCategory);
+ if (!mIsInSetupWizard) {
+ scrollToPreference(connectedWifiPreferenceCategory);
+ }
}
}
} else {
@@ -1117,10 +1190,12 @@ private void removeWifiEntryPreference() {
@VisibleForTesting
void setAdditionalSettingsSummaries() {
- mConfigureWifiSettingsPreference.setSummary(getString(
- isWifiWakeupEnabled()
- ? R.string.wifi_configure_settings_preference_summary_wakeup_on
- : R.string.wifi_configure_settings_preference_summary_wakeup_off));
+ if (!mIsInSetupWizard) {
+ mConfigureWifiSettingsPreference.setSummary(getString(
+ isWifiWakeupEnabled()
+ ? R.string.wifi_configure_settings_preference_summary_wakeup_on
+ : R.string.wifi_configure_settings_preference_summary_wakeup_off));
+ }
final int numSavedNetworks = mWifiPickerTracker == null ? 0 :
mWifiPickerTracker.getNumSavedNetworks();
@@ -1168,7 +1243,9 @@ private boolean isWifiWakeupEnabled() {
}
protected void setProgressBarVisible(boolean visible) {
- showPinnedHeader(visible);
+ if (!mIsInSetupWizard) {
+ showPinnedHeader(visible);
+ }
}
@VisibleForTesting
@@ -1202,7 +1279,7 @@ public int getHelpResource() {
* Renames/replaces "Next" button when appropriate. "Next" button usually exists in
* Wi-Fi setup screens, not in usual wifi settings screen.
*
- * @param enabled true when the device is connected to a wifi network.
+ * @param enabled true when the device is connected to a mobile or wifi network.
*/
@VisibleForTesting
void changeNextButtonState(boolean enabled) {
@@ -1483,6 +1560,17 @@ public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
}
}
+ /**
+ * Implementation of {@code MobileDataEnabledListener.Client}
+ */
+ public void onMobileDataEnabledChange() {
+ changeNextButtonState(getDataEnabled());
+ }
+
+ boolean getDataEnabled() {
+ return getContext().getSystemService(TelephonyManager.class).getDataEnabled(mSubId);
+ }
+
/**
* A Wi-Fi preference for the connected Wi-Fi network without internet access.
*
diff --git a/src/com/android/settings/network/NetworkSetupActivity.java b/src/com/android/settings/network/NetworkSetupActivity.java
new file mode 100644
index 00000000000..3b5fb829315
--- /dev/null
+++ b/src/com/android/settings/network/NetworkSetupActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2021-2024 The LineageOS 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.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+
+import androidx.preference.PreferenceFragmentCompat;
+
+import com.android.settings.ButtonBarHandler;
+import com.android.settings.network.NetworkProviderSettings;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SetupWizardUtils;
+import com.android.settings.wifi.p2p.WifiP2pSettings;
+import com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2;
+
+import com.google.android.setupdesign.util.ThemeHelper;
+
+public class NetworkSetupActivity extends SettingsActivity implements ButtonBarHandler {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTheme(SetupWizardUtils.getTheme(this, getIntent()));
+ setTheme(R.style.SettingsPreferenceTheme_SetupWizard);
+ ThemeHelper.trySetDynamicColor(this);
+ findViewById(R.id.content_parent).setFitsSystemWindows(false);
+ }
+
+ @Override
+ public Intent getIntent() {
+ Intent modIntent = new Intent(super.getIntent());
+ if (!modIntent.hasExtra(EXTRA_SHOW_FRAGMENT)) {
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getNetworkProviderSettingsClass().getName());
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID,
+ R.string.provider_internet_settings);
+ }
+ return modIntent;
+ }
+
+ @Override
+ protected boolean isValidFragment(String fragmentName) {
+ final boolean isSavedAccessPointsWifiSettings =
+ SavedAccessPointsWifiSettings2.class.getName().equals(fragmentName);
+
+ if (NetworkProviderSettings.class.getName().equals(fragmentName)
+ || WifiP2pSettings.class.getName().equals(fragmentName)
+ || isSavedAccessPointsWifiSettings) {
+ return true;
+ }
+ return false;
+ }
+
+ /* package */ Class extends PreferenceFragmentCompat> getNetworkProviderSettingsClass() {
+ return NetworkProviderSettings.class;
+ }
+}
diff --git a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
index 3b99777720a..13182bd6fd9 100644
--- a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
+++ b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
@@ -72,9 +72,21 @@ public class PrivateDnsModeDialogPreference extends CustomDialogPreferenceCompat
// DNS_MODE -> RadioButton id
private static final Map PRIVATE_DNS_MAP;
+ // Only used in Settings, update on additions to ConnectivitySettingsUtils
+ private static final int PRIVATE_DNS_MODE_CLOUDFLARE = 4;
+ private static final int PRIVATE_DNS_MODE_ADGUARD = 5;
+ private static final int PRIVATE_DNS_MODE_OPEN_DNS = 6;
+ private static final int PRIVATE_DNS_MODE_CLEANBROWSING = 7;
+ private static final int PRIVATE_DNS_MODE_QUAD9 = 8;
+
static {
PRIVATE_DNS_MAP = new HashMap<>();
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OFF, R.id.private_dns_mode_off);
+ PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_CLOUDFLARE, R.id.private_dns_mode_cloudflare);
+ PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_ADGUARD, R.id.private_dns_mode_adguard);
+ PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OPEN_DNS, R.id.private_dns_mode_open_dns);
+ PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_CLEANBROWSING, R.id.private_dns_mode_cleanbrowsing);
+ PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_QUAD9, R.id.private_dns_mode_quad9);
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OPPORTUNISTIC, R.id.private_dns_mode_opportunistic);
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, R.id.private_dns_mode_provider);
}
@@ -144,6 +156,31 @@ protected void onBindDialogView(View view) {
final ContentResolver contentResolver = context.getContentResolver();
mMode = ConnectivitySettingsManager.getPrivateDnsMode(context);
+ if (mMode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) {
+ final String privateDnsHostname =
+ ConnectivitySettingsManager.getPrivateDnsHostname(context);
+ final String cloudflareHostname =
+ context.getString(R.string.private_dns_hostname_cloudflare);
+ final String adguardHostname =
+ context.getString(R.string.private_dns_hostname_adguard);
+ final String opendnsHostname =
+ context.getString(R.string.private_dns_hostname_open_dns);
+ final String cleanbrowsingHostname =
+ context.getString(R.string.private_dns_hostname_cleanbrowsing);
+ final String quad9Hostname =
+ context.getString(R.string.private_dns_hostname_quad9);
+ if (privateDnsHostname.equals(cloudflareHostname)) {
+ mMode = PRIVATE_DNS_MODE_CLOUDFLARE;
+ } else if (privateDnsHostname.equals(adguardHostname)) {
+ mMode = PRIVATE_DNS_MODE_ADGUARD;
+ } else if (privateDnsHostname.equals(opendnsHostname)) {
+ mMode = PRIVATE_DNS_MODE_OPEN_DNS;
+ } else if (privateDnsHostname.equals(cleanbrowsingHostname)) {
+ mMode = PRIVATE_DNS_MODE_CLEANBROWSING;
+ } else if (privateDnsHostname.equals(quad9Hostname)) {
+ mMode = PRIVATE_DNS_MODE_QUAD9;
+ }
+ }
mEditText = view.findViewById(R.id.private_dns_mode_provider_hostname);
mEditText.addTextChangedListener(this);
@@ -156,6 +193,21 @@ protected void onBindDialogView(View view) {
// Initial radio button text
final RadioButton offRadioButton = view.findViewById(R.id.private_dns_mode_off);
offRadioButton.setText(com.android.settingslib.R.string.private_dns_mode_off);
+ final RadioButton cloudflareRadioButton =
+ view.findViewById(R.id.private_dns_mode_cloudflare);
+ cloudflareRadioButton.setText(R.string.private_dns_mode_cloudflare);
+ final RadioButton adguardRadioButton =
+ view.findViewById(R.id.private_dns_mode_adguard);
+ adguardRadioButton.setText(R.string.private_dns_mode_adguard);
+ final RadioButton opendnsRadioButton =
+ view.findViewById(R.id.private_dns_mode_open_dns);
+ opendnsRadioButton.setText(R.string.private_dns_mode_open_dns);
+ final RadioButton cleanbrowsingRadioButton =
+ view.findViewById(R.id.private_dns_mode_cleanbrowsing);
+ cleanbrowsingRadioButton.setText(R.string.private_dns_mode_cleanbrowsing);
+ final RadioButton quad9RadioButton =
+ view.findViewById(R.id.private_dns_mode_quad9);
+ quad9RadioButton.setText(R.string.private_dns_mode_quad9);
final RadioButton opportunisticRadioButton =
view.findViewById(R.id.private_dns_mode_opportunistic);
opportunisticRadioButton.setText(
@@ -174,7 +226,7 @@ protected void onBindDialogView(View view) {
helpTextView.setText(AnnotationSpan.linkify(
context.getText(R.string.private_dns_help_message), linkInfo));
} else {
- helpTextView.setText("");
+ helpTextView.setVisibility(View.GONE);
}
}
@@ -182,15 +234,41 @@ protected void onBindDialogView(View view) {
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
final Context context = getContext();
+ int modeToSet = mMode;
if (mMode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) {
// Only clickable if hostname is valid, so we could save it safely
ConnectivitySettingsManager.setPrivateDnsHostname(context,
mEditText.getText().toString());
+ } else if (mMode == PRIVATE_DNS_MODE_CLOUDFLARE) {
+ final String cloudflareHostname =
+ context.getString(R.string.private_dns_hostname_cloudflare);
+ ConnectivitySettingsManager.setPrivateDnsHostname(context, cloudflareHostname);
+ modeToSet = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+ } else if (mMode == PRIVATE_DNS_MODE_ADGUARD) {
+ final String adguardHostname =
+ context.getString(R.string.private_dns_hostname_adguard);
+ ConnectivitySettingsManager.setPrivateDnsHostname(context, adguardHostname);
+ modeToSet = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+ } else if (mMode == PRIVATE_DNS_MODE_OPEN_DNS) {
+ final String opendnsHostname =
+ context.getString(R.string.private_dns_hostname_open_dns);
+ ConnectivitySettingsManager.setPrivateDnsHostname(context, opendnsHostname);
+ modeToSet = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+ } else if (mMode == PRIVATE_DNS_MODE_CLEANBROWSING) {
+ final String cleanbrowsingHostname =
+ context.getString(R.string.private_dns_hostname_cleanbrowsing);
+ ConnectivitySettingsManager.setPrivateDnsHostname(context, cleanbrowsingHostname);
+ modeToSet = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+ } else if (mMode == PRIVATE_DNS_MODE_QUAD9) {
+ final String quad9Hostname =
+ context.getString(R.string.private_dns_hostname_quad9);
+ ConnectivitySettingsManager.setPrivateDnsHostname(context, quad9Hostname);
+ modeToSet = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
}
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().action(context,
- SettingsEnums.ACTION_PRIVATE_DNS_MODE, mMode);
- ConnectivitySettingsManager.setPrivateDnsMode(context, mMode);
+ SettingsEnums.ACTION_PRIVATE_DNS_MODE, modeToSet);
+ ConnectivitySettingsManager.setPrivateDnsMode(context, modeToSet);
}
}
@@ -198,6 +276,16 @@ public void onClick(DialogInterface dialog, int which) {
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.private_dns_mode_off) {
mMode = PRIVATE_DNS_MODE_OFF;
+ } else if (checkedId == R.id.private_dns_mode_cloudflare) {
+ mMode = PRIVATE_DNS_MODE_CLOUDFLARE;
+ } else if (checkedId == R.id.private_dns_mode_adguard) {
+ mMode = PRIVATE_DNS_MODE_ADGUARD;
+ } else if (checkedId == R.id.private_dns_mode_open_dns) {
+ mMode = PRIVATE_DNS_MODE_OPEN_DNS;
+ } else if (checkedId == R.id.private_dns_mode_cleanbrowsing) {
+ mMode = PRIVATE_DNS_MODE_CLEANBROWSING;
+ } else if (checkedId == R.id.private_dns_mode_quad9) {
+ mMode = PRIVATE_DNS_MODE_QUAD9;
} else if (checkedId == R.id.private_dns_mode_opportunistic) {
mMode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
} else if (checkedId == R.id.private_dns_mode_provider) {
diff --git a/src/com/android/settings/network/PrivateDnsPreferenceController.java b/src/com/android/settings/network/PrivateDnsPreferenceController.java
index 21e4926f490..42e324d9b38 100644
--- a/src/com/android/settings/network/PrivateDnsPreferenceController.java
+++ b/src/com/android/settings/network/PrivateDnsPreferenceController.java
@@ -58,6 +58,7 @@
public class PrivateDnsPreferenceController extends BasePreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
private static final String KEY_PRIVATE_DNS_SETTINGS = "private_dns_settings";
+ private static final String KEY_ENFORCE_VPN_SETTINGS = "vpn_enforce_dns";
private static final Uri[] SETTINGS_URIS = new Uri[]{
Settings.Global.getUriFor(PRIVATE_DNS_MODE),
@@ -65,11 +66,19 @@ public class PrivateDnsPreferenceController extends BasePreferenceController
Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER),
};
+ // Only used in Settings, update on additions to ConnectivitySettingsUtils
+ private static final int PRIVATE_DNS_MODE_CLOUDFLARE = 4;
+ private static final int PRIVATE_DNS_MODE_ADGUARD = 5;
+ private static final int PRIVATE_DNS_MODE_OPEN_DNS = 6;
+ private static final int PRIVATE_DNS_MODE_CLEANBROWSING = 7;
+ private static final int PRIVATE_DNS_MODE_QUAD9 = 8;
+
private final Handler mHandler;
private final ContentObserver mSettingsObserver;
private final ConnectivityManager mConnectivityManager;
private LinkProperties mLatestLinkProperties;
private Preference mPreference;
+ private Preference mEnforcePreference;
public PrivateDnsPreferenceController(Context context) {
super(context, KEY_PRIVATE_DNS_SETTINGS);
@@ -98,6 +107,7 @@ public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
+ mEnforcePreference = screen.findPreference(KEY_ENFORCE_VPN_SETTINGS);
}
@Override
@@ -129,15 +139,44 @@ public CharSequence getSummary() {
switch (mode) {
case PRIVATE_DNS_MODE_OFF:
return res.getString(com.android.settingslib.R.string.private_dns_mode_off);
+ case PRIVATE_DNS_MODE_CLOUDFLARE:
+ case PRIVATE_DNS_MODE_ADGUARD:
+ case PRIVATE_DNS_MODE_OPEN_DNS:
+ case PRIVATE_DNS_MODE_CLEANBROWSING:
+ case PRIVATE_DNS_MODE_QUAD9:
case PRIVATE_DNS_MODE_OPPORTUNISTIC:
return dnsesResolved ? res.getString(R.string.private_dns_mode_on)
: res.getString(
com.android.settingslib.R.string.private_dns_mode_opportunistic);
case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
- return dnsesResolved
- ? PrivateDnsModeDialogPreference.getHostnameFromSettings(cr)
- : res.getString(
- com.android.settingslib.R.string.private_dns_mode_provider_failure);
+ if (!dnsesResolved) {
+ return res.getString(
+ com.android.settingslib.R.string.private_dns_mode_provider_failure);
+ }
+ final String privateDnsHostname =
+ ConnectivitySettingsManager.getPrivateDnsHostname(mContext);
+ final String cloudflareHostname =
+ res.getString(R.string.private_dns_hostname_cloudflare);
+ final String adguardHostname =
+ res.getString(R.string.private_dns_hostname_adguard);
+ final String opendnsHostname =
+ res.getString(R.string.private_dns_hostname_open_dns);
+ final String cleanbrowsingHostname =
+ res.getString(R.string.private_dns_hostname_cleanbrowsing);
+ final String quad9Hostname =
+ res.getString(R.string.private_dns_hostname_quad9);
+ if (privateDnsHostname.equals(cloudflareHostname)) {
+ return res.getString(R.string.private_dns_mode_cloudflare);
+ } else if (privateDnsHostname.equals(adguardHostname)) {
+ return res.getString(R.string.private_dns_mode_adguard);
+ } else if (privateDnsHostname.equals(opendnsHostname)) {
+ return res.getString(R.string.private_dns_mode_open_dns);
+ } else if (privateDnsHostname.equals(cleanbrowsingHostname)) {
+ return res.getString(R.string.private_dns_mode_cleanbrowsing);
+ } else if (privateDnsHostname.equals(quad9Hostname)) {
+ return res.getString(R.string.private_dns_mode_quad9);
+ }
+ return PrivateDnsModeDialogPreference.getHostnameFromSettings(cr);
}
return "";
}
@@ -145,7 +184,11 @@ public CharSequence getSummary() {
@Override
public void updateState(Preference preference) {
super.updateState(preference);
- preference.setEnabled(!isManagedByAdmin());
+ final boolean isManaged = isManagedByAdmin();
+ preference.setEnabled(!isManaged);
+ if (mEnforcePreference == null) return;
+ final int mode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
+ mEnforcePreference.setEnabled(!isManaged && mode != PRIVATE_DNS_MODE_OFF);
}
private boolean isManagedByAdmin() {
diff --git a/src/com/android/settings/network/PrivateDnsSettings.java b/src/com/android/settings/network/PrivateDnsSettings.java
new file mode 100644
index 00000000000..086592c971d
--- /dev/null
+++ b/src/com/android/settings/network/PrivateDnsSettings.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 Yet Another AOSP 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.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adaptive connectivity is a feature which automatically manages network connections.
+ */
+@SearchIndexable
+public class PrivateDnsSettings extends DashboardFragment {
+
+ private static final String TAG = "PrivateDnsSettings";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.SETTINGS_NETWORK_CATEGORY;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.private_dns_settings;
+ }
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context, getSettingsLifecycle());
+ }
+
+ private static List buildPreferenceControllers(
+ Context context, Lifecycle lifecycle) {
+ final PrivateDnsPreferenceController privateDnsPreferenceController =
+ new PrivateDnsPreferenceController(context);
+ if (lifecycle != null) {
+ lifecycle.addObserver(privateDnsPreferenceController);
+ }
+ final List controllers = new ArrayList<>();
+ controllers.add(privateDnsPreferenceController);
+ return controllers;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.private_dns_settings) {
+ @Override
+ public List createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context, null);
+ }
+ };
+}
diff --git a/src/com/android/settings/network/telephony/ForceLteCaPreferenceController.java b/src/com/android/settings/network/telephony/ForceLteCaPreferenceController.java
new file mode 100644
index 00000000000..4d078610246
--- /dev/null
+++ b/src/com/android/settings/network/telephony/ForceLteCaPreferenceController.java
@@ -0,0 +1,160 @@
+package com.android.settings.network.telephony;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreferenceCompat;
+import androidx.preference.TwoStatePreference;
+
+import com.android.internal.telephony.util.ArrayUtils;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Preference controller for "Force LTE_CA" hack
+ */
+public class ForceLteCaPreferenceController extends TelephonyTogglePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+
+ private static final String TAG = "ForceLteCaSettings";
+
+ private static final String KEY_PROP = "persist.sys.radio.force_lte_ca";
+
+ @VisibleForTesting
+ Preference mPreference;
+ private TelephonyManager mTelephonyManager;
+ private PhoneCallStateTelephonyCallback mTelephonyCallback;
+ private Integer mCallState;
+
+ public ForceLteCaPreferenceController(Context context, String key) {
+ super(context, key);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ }
+
+ /**
+ * Initial this PreferenceController.
+ * @param subId The subscription Id.
+ * @return This PreferenceController.
+ */
+ public ForceLteCaPreferenceController init(int subId) {
+ Log.d(TAG, "init: ");
+ if (mTelephonyCallback == null) {
+ mTelephonyCallback = new PhoneCallStateTelephonyCallback();
+ }
+
+ mSubId = subId;
+
+ if (mTelephonyManager == null) {
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ }
+
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
+ }
+
+ PersistableBundle carrierConfig = getCarrierConfigForSubId(subId);
+ if (carrierConfig == null) {
+ return this;
+ }
+ return this;
+ }
+
+ @Override
+ public int getAvailabilityStatus(int subId) {
+ init(subId);
+ if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onStart() {
+ if (mTelephonyCallback == null) {
+ return;
+ }
+ mTelephonyCallback.register(mTelephonyManager);
+ }
+
+ @Override
+ public void onStop() {
+ if (mTelephonyCallback == null) {
+ return;
+ }
+ mTelephonyCallback.unregister();
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ if (preference == null) {
+ return;
+ }
+ final TwoStatePreference switchPreference = (TwoStatePreference) preference;
+ switchPreference.setEnabled(isUserControlAllowed());
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+ Log.d(TAG, "setChecked: " + isChecked);
+ SystemProperties.set(KEY_PROP, String.valueOf(isChecked));
+ return true;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return SystemProperties.getBoolean(KEY_PROP, false);
+ }
+
+ private boolean isUserControlAllowed() {
+ return SubscriptionManager.isValidSubscriptionId(mSubId);
+ }
+
+ private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener {
+
+ private TelephonyManager mLocalTelephonyManager;
+
+ @Override
+ public void onCallStateChanged(int state) {
+ mCallState = state;
+ updateState(mPreference);
+ }
+
+ public void register(TelephonyManager telephonyManager) {
+ mLocalTelephonyManager = telephonyManager;
+
+ // assign current call state so that it helps to show correct preference state even
+ // before first onCallStateChanged() by initial registration.
+ mCallState = mLocalTelephonyManager.getCallState();
+ mLocalTelephonyManager.registerTelephonyCallback(
+ mContext.getMainExecutor(), mTelephonyCallback);
+ }
+
+ public void unregister() {
+ mCallState = null;
+ if (mLocalTelephonyManager != null) {
+ mLocalTelephonyManager.unregisterTelephonyCallback(this);
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
index 6adc5052145..8f2051fe6f7 100644
--- a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
+++ b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java
@@ -20,6 +20,7 @@
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import android.content.Context;
+import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -35,6 +36,7 @@
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
+import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.flags.Flags;
import com.android.settings.network.MobileNetworkRepository;
import com.android.settings.wifi.WifiPickerTrackerHelper;
@@ -42,6 +44,8 @@
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
+import com.google.android.setupcompat.util.WizardManagerHelper;
+
import java.util.ArrayList;
import java.util.List;
@@ -61,6 +65,7 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon
int mDialogType;
@VisibleForTesting
boolean mNeedDialog;
+ boolean mIsInSetupWizard;
private WifiPickerTrackerHelper mWifiPickerTrackerHelper;
protected MobileNetworkRepository mMobileNetworkRepository;
@@ -72,9 +77,10 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon
MobileNetworkInfoEntity mMobileNetworkInfoEntity;
public MobileDataPreferenceController(Context context, String key, Lifecycle lifecycle,
- LifecycleOwner lifecycleOwner, int subId) {
+ LifecycleOwner lifecycleOwner, int subId, boolean isInSetupWizard) {
this(context, key);
mSubId = subId;
+ mIsInSetupWizard = isInSetupWizard;
mLifecycleOwner = lifecycleOwner;
if (lifecycle != null) {
lifecycle.addObserver(this);
@@ -89,12 +95,13 @@ public MobileDataPreferenceController(Context context, String key) {
@Override
public int getAvailabilityStatus(int subId) {
- if (Flags.isDualSimOnboardingEnabled()) {
+ if ((Flags.isDualSimOnboardingEnabled() && !mIsInSetupWizard)
+ || mSubscriptionManager.getActiveSubscriptionInfo(subId) == null
+ || !mSubscriptionManager.isUsableSubscriptionId(subId)
+ || !DataUsageUtils.hasMobileData(mContext)) {
return CONDITIONALLY_UNAVAILABLE;
}
- return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
- ? AVAILABLE
- : AVAILABLE_UNSEARCHABLE;
+ return AVAILABLE;
}
@Override
@@ -130,6 +137,14 @@ public boolean handlePreferenceTreeClick(Preference preference) {
public boolean setChecked(boolean isChecked) {
mNeedDialog = isDialogNeeded();
+ // If we are still provisioning we need to allow enabling mobile data first.
+ // By default it is not allowed to use mobile network during provisioning so
+ // we need to allow it.
+ if (!WizardManagerHelper.isDeviceProvisioned(mContext)) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, isChecked ? 1 : 0);
+ }
+
if (!mNeedDialog) {
// Update data directly if we don't need dialog
Log.d(DIALOG_TAG, "setMobileDataEnabled: " + isChecked);
@@ -163,6 +178,7 @@ private void update() {
return;
}
+ mPreference.setVisible(isAvailable());
mPreference.setChecked(isChecked());
if (mSubscriptionInfoEntity.isOpportunistic) {
mPreference.setEnabled(false);
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 91874c4f5ac..fe3a3475dc2 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.ColorStateList;
import android.os.Bundle;
import android.os.UserManager;
import android.provider.Settings;
@@ -197,7 +198,7 @@ protected List createPreferenceControllers(Context
new SmsDefaultSubscriptionController(context, KEY_SMS_PREF, getSettingsLifecycle(),
this),
new MobileDataPreferenceController(context, KEY_MOBILE_DATA_PREF,
- getSettingsLifecycle(), this, mSubId),
+ getSettingsLifecycle(), this, mSubId, false),
new ConvertToEsimPreferenceController(context, KEY_CONVERT_TO_ESIM_PREF,
getSettingsLifecycle(), this, mSubId), eid);
}
@@ -320,7 +321,9 @@ public void onAttach(Context context) {
.addListener(videoCallingPreferenceController);
use(ContactDiscoveryPreferenceController.class).init(getParentFragmentManager(), mSubId);
use(NrAdvancedCallingPreferenceController.class).init(mSubId);
+ use(Smart5gPreferenceController.class).init(mSubId);
use(TransferEsimPreferenceController.class).init(mSubId, mSubscriptionInfoEntity);
+ use(ForceLteCaPreferenceController.class).init(mSubId);
final ConvertToEsimPreferenceController convertToEsimPreferenceController =
use(ConvertToEsimPreferenceController.class);
if (convertToEsimPreferenceController != null) {
@@ -465,6 +468,9 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final MenuItem item = menu.add(Menu.NONE, R.id.edit_sim_name, Menu.NONE,
R.string.mobile_network_sim_name);
item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
+ item.setIconTintList(ColorStateList.valueOf(
+ com.android.settingslib.Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.colorControlNormal)));
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
super.onCreateOptionsMenu(menu, inflater);
diff --git a/src/com/android/settings/network/telephony/Smart5gPreferenceController.java b/src/com/android/settings/network/telephony/Smart5gPreferenceController.java
new file mode 100644
index 00000000000..cfff7a66bb1
--- /dev/null
+++ b/src/com/android/settings/network/telephony/Smart5gPreferenceController.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 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.telephony;
+
+import static android.provider.Settings.System.SMART_5G;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreferenceCompat;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Preference controller for Smart 5G
+ */
+public class Smart5gPreferenceController extends TelephonyTogglePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+
+ private static final String TAG = "Smart5gPreferenceController";
+
+ Preference mPreference;
+ private TelephonyManager mTelephonyManager;
+ private PhoneCallStateTelephonyCallback mTelephonyCallback;
+ private boolean mHas5gCapability = false;
+ private Integer mCallState = TelephonyManager.CALL_STATE_IDLE;
+
+ public Smart5gPreferenceController(Context context, String key) {
+ super(context, key);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ }
+
+ /**
+ * Initialize this PreferenceController.
+ * @param subId The subscription Id.
+ * @return This PreferenceController.
+ */
+ public Smart5gPreferenceController init(int subId) {
+ Log.d(TAG, "init: subId=" + subId);
+ if (mTelephonyCallback == null) {
+ mTelephonyCallback = new PhoneCallStateTelephonyCallback();
+ }
+
+ mSubId = subId;
+
+ if (mTelephonyManager == null) {
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ }
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
+ }
+ long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily();
+ mHas5gCapability =
+ (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0;
+
+ Log.d(TAG, "mHas5gCapability: " + mHas5gCapability);
+ return this;
+ }
+
+ @Override
+ public int getAvailabilityStatus(int subId) {
+ init(subId);
+ return mHas5gCapability ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onStart() {
+ if (mTelephonyCallback == null) {
+ return;
+ }
+ mTelephonyCallback.register(mTelephonyManager);
+ }
+
+ @Override
+ public void onStop() {
+ if (mTelephonyCallback == null) {
+ return;
+ }
+ mTelephonyCallback.unregister();
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ if (preference == null) {
+ return;
+ }
+ final TwoStatePreference switchPreference = (TwoStatePreference) preference;
+ switchPreference.setEnabled(isUserControlAllowed());
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+ return Settings.System.putIntForUser(mContext.getContentResolver(),
+ SMART_5G + mSubId, isChecked ? 1 : 0, UserHandle.USER_CURRENT);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.System.getIntForUser(mContext.getContentResolver(),
+ SMART_5G + mSubId, 0, UserHandle.USER_CURRENT) == 1;
+ }
+
+ protected boolean isCallStateIdle() {
+ return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE);
+ }
+
+ private boolean isUserControlAllowed() {
+ return isCallStateIdle();
+ }
+
+ private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener {
+
+ private TelephonyManager mLocalTelephonyManager;
+
+ @Override
+ public void onCallStateChanged(int state) {
+ mCallState = state;
+ updateState(mPreference);
+ }
+
+ public void register(TelephonyManager telephonyManager) {
+ mLocalTelephonyManager = telephonyManager;
+
+ // assign current call state so that it helps to show correct preference state even
+ // before first onCallStateChanged() by initial registration.
+ mCallState = mLocalTelephonyManager.getCallState();
+ mLocalTelephonyManager.registerTelephonyCallback(
+ mContext.getMainExecutor(), mTelephonyCallback);
+ }
+
+ public void unregister() {
+ mCallState = null;
+ if (mLocalTelephonyManager != null) {
+ mLocalTelephonyManager.unregisterTelephonyCallback(this);
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/IncreasingRingPreferenceController.java b/src/com/android/settings/notification/IncreasingRingPreferenceController.java
new file mode 100644
index 00000000000..2152451f8b3
--- /dev/null
+++ b/src/com/android/settings/notification/IncreasingRingPreferenceController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+
+public class IncreasingRingPreferenceController extends BasePreferenceController {
+ private static final String KEY_INCREASING_RING = "increasing_ring";
+
+ public IncreasingRingPreferenceController(Context context) {
+ super(context, KEY_INCREASING_RING);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return Utils.isVoiceCapable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+}
diff --git a/src/com/android/settings/notification/IncreasingRingVolumePreference.java b/src/com/android/settings/notification/IncreasingRingVolumePreference.java
new file mode 100644
index 00000000000..4c550487e4e
--- /dev/null
+++ b/src/com/android/settings/notification/IncreasingRingVolumePreference.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2014 CyanogenMod 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.ContentResolver;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.provider.Settings;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+
+import lineageos.providers.LineageSettings;
+
+public class IncreasingRingVolumePreference extends Preference
+ implements Handler.Callback, SeekBar.OnSeekBarChangeListener {
+ private static final String TAG = "IncreasingRingMinVolumePreference";
+
+ public interface Callback {
+ void onSampleStarting(IncreasingRingVolumePreference pref);
+ }
+
+ private SeekBar mStartVolumeSeekBar;
+ private SeekBar mRampUpTimeSeekBar;
+ private TextView mRampUpTimeValue;
+
+ private Ringtone mRingtone;
+ private Callback mCallback;
+
+ private Handler mHandler;
+ private final Handler mMainHandler = new Handler(this);
+
+ private static final int MSG_START_SAMPLE = 1;
+ private static final int MSG_STOP_SAMPLE = 2;
+ private static final int MSG_INIT_SAMPLE = 3;
+ private static final int MSG_SET_VOLUME = 4;
+ private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
+
+ public IncreasingRingVolumePreference(Context context) {
+ this(context, null);
+ }
+
+ public IncreasingRingVolumePreference(Context context, AttributeSet attrs) {
+ this(context, attrs, TypedArrayUtils.getAttr(context,
+ androidx.preference.R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle));
+ }
+
+ public IncreasingRingVolumePreference(Context context, AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public IncreasingRingVolumePreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setLayoutResource(R.layout.preference_increasing_ring);
+ initHandler();
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ public void onActivityResume() {
+ initHandler();
+ }
+
+ public void onActivityStop() {
+ if (mHandler != null) {
+ postStopSample();
+ mHandler.getLooper().quitSafely();
+ mHandler = null;
+ }
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_SAMPLE:
+ onStartSample((float) msg.arg1 / 1000F);
+ break;
+ case MSG_STOP_SAMPLE:
+ onStopSample();
+ break;
+ case MSG_INIT_SAMPLE:
+ onInitSample();
+ break;
+ case MSG_SET_VOLUME:
+ onSetVolume((float) msg.arg1 / 1000F);
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ initHandler();
+
+ final SeekBar seekBar = (SeekBar) holder.findViewById(R.id.start_volume);
+ if (seekBar == mStartVolumeSeekBar) return;
+
+ mStartVolumeSeekBar = seekBar;
+ mRampUpTimeSeekBar = (SeekBar) holder.findViewById(R.id.ramp_up_time);
+ mRampUpTimeValue = (TextView) holder.findViewById(R.id.ramp_up_time_value);
+
+ final ContentResolver cr = getContext().getContentResolver();
+ float startVolume = LineageSettings.System.getFloat(cr,
+ LineageSettings.System.INCREASING_RING_START_VOLUME, 0.1f);
+ int rampUpTime = LineageSettings.System.getInt(cr,
+ LineageSettings.System.INCREASING_RING_RAMP_UP_TIME, 10);
+
+ mStartVolumeSeekBar.setProgress(Math.round(startVolume * 1000F));
+ mStartVolumeSeekBar.setOnSeekBarChangeListener(this);
+ mRampUpTimeSeekBar.setOnSeekBarChangeListener(this);
+ mRampUpTimeSeekBar.setProgress((rampUpTime / 5) - 1);
+ mRampUpTimeValue.setText(
+ Formatter.formatShortElapsedTime(getContext(), rampUpTime * 1000));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (seekBar == mStartVolumeSeekBar) {
+ postStartSample(seekBar.getProgress());
+ }
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+ ContentResolver cr = getContext().getContentResolver();
+ if (fromTouch && seekBar == mStartVolumeSeekBar) {
+ LineageSettings.System.putFloat(cr,
+ LineageSettings.System.INCREASING_RING_START_VOLUME, (float) progress / 1000F);
+ } else if (seekBar == mRampUpTimeSeekBar) {
+ int seconds = (progress + 1) * 5;
+ mRampUpTimeValue.setText(
+ Formatter.formatShortElapsedTime(getContext(), seconds * 1000));
+ if (fromTouch) {
+ LineageSettings.System.putInt(cr,
+ LineageSettings.System.INCREASING_RING_RAMP_UP_TIME, seconds);
+ }
+ }
+ }
+
+ private void initHandler() {
+ if (mHandler != null) return;
+
+ HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
+ thread.start();
+
+ mHandler = new Handler(thread.getLooper(), this);
+ mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);
+ }
+
+ private void onInitSample() {
+ mRingtone = RingtoneManager.getRingtone(getContext(),
+ Settings.System.DEFAULT_RINGTONE_URI);
+ if (mRingtone != null) {
+ mRingtone.setStreamType(AudioManager.STREAM_RING);
+ mRingtone.setAudioAttributes(
+ new AudioAttributes.Builder(mRingtone.getAudioAttributes())
+ .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
+ AudioAttributes.FLAG_BYPASS_MUTE)
+ .build());
+ }
+ }
+
+ private void postStartSample(int progress) {
+ boolean playing = isSamplePlaying();
+ mHandler.removeMessages(MSG_START_SAMPLE);
+ mHandler.removeMessages(MSG_SET_VOLUME);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE, progress, 0),
+ playing ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
+ if (playing) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_VOLUME, progress, 0));
+ }
+ }
+
+ private void onStartSample(float volume) {
+ if (mRingtone == null) {
+ return;
+ }
+ if (!isSamplePlaying()) {
+ if (mCallback != null) {
+ mCallback.onSampleStarting(this);
+ }
+ try {
+ mRingtone.play();
+ } catch (Throwable e) {
+ Log.w(TAG, "Error playing ringtone", e);
+ }
+ }
+ mRingtone.setVolume(volume);
+ }
+
+ private void onSetVolume(float volume) {
+ if (mRingtone != null) {
+ mRingtone.setVolume(volume);
+ }
+ }
+
+ private boolean isSamplePlaying() {
+ return mRingtone != null && mRingtone.isPlaying();
+ }
+
+ public void stopSample() {
+ if (mHandler != null) {
+ postStopSample();
+ }
+ }
+
+ private void postStopSample() {
+ // remove pending delayed start messages
+ mHandler.removeMessages(MSG_START_SAMPLE);
+ mHandler.removeMessages(MSG_STOP_SAMPLE);
+ mHandler.sendEmptyMessage(MSG_STOP_SAMPLE);
+ }
+
+ private void onStopSample() {
+ if (mRingtone != null) {
+ mRingtone.stop();
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/IncreasingRingVolumePreferenceController.java b/src/com/android/settings/notification/IncreasingRingVolumePreferenceController.java
new file mode 100644
index 00000000000..78da2485a27
--- /dev/null
+++ b/src/com/android/settings/notification/IncreasingRingVolumePreferenceController.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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 androidx.preference.PreferenceScreen;
+
+import com.android.settings.Utils;
+import com.android.settings.slices.SliceData;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+/**
+ * Base class for preference controller that handles VolumeSeekBarPreference
+ */
+public class IncreasingRingVolumePreferenceController
+ extends AdjustVolumeRestrictedPreferenceController
+ implements LifecycleObserver, OnResume, OnStop {
+
+ private static final String KEY_INCREASING_RING_VOLUME = "increasing_ring_volume";
+
+ private IncreasingRingVolumePreference mPreference;
+ private IncreasingRingVolumePreference.Callback mCallback;
+ private AudioHelper mHelper;
+
+ public IncreasingRingVolumePreferenceController(Context context) {
+ super(context, KEY_INCREASING_RING_VOLUME);
+ mHelper = new AudioHelper(context);
+ }
+
+ public void setCallback(IncreasingRingVolumePreference.Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ if (isAvailable()) {
+ mPreference = screen.findPreference(getPreferenceKey());
+ mPreference.setCallback(mCallback);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ if (mPreference != null) {
+ mPreference.onActivityResume();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ if (mPreference != null) {
+ mPreference.onActivityStop();
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_INCREASING_RING_VOLUME;
+ }
+
+ @Override
+ public int getSliderPosition() {
+ return 0;
+ }
+
+ @Override
+ public boolean setSliderPosition(int position) {
+ return false;
+ }
+
+ @Override
+ public int getMin() {
+ return 0;
+ }
+
+ @Override
+ public int getMax() {
+ return 0;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return Utils.isVoiceCapable(mContext) && !mHelper.isSingleVolume() ?
+ AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public int getSliceType() {
+ return SliceData.SliceType.INTENT;
+ }
+}
diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
index 3f300c0fce3..1adeb644155 100644
--- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
+++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
@@ -30,13 +30,15 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
+import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
import android.util.Slog;
import android.view.WindowManager;
@@ -49,6 +51,8 @@
import com.android.internal.app.AlertController;
import com.android.settings.R;
+import java.util.List;
+
/** @hide */
public class NotificationAccessConfirmationActivity extends Activity
implements DialogInterface {
@@ -113,6 +117,31 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
return;
}
+ // Check NLS service info.
+ String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
+ Intent NLSIntent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
+ List matchedServiceList = getPackageManager().queryIntentServicesAsUser(
+ NLSIntent, /* flags */ 0, mUserId);
+ boolean hasNLSIntentFilter = false;
+ for (ResolveInfo service : matchedServiceList) {
+ if (service.serviceInfo.packageName.equals(mComponentName.getPackageName())) {
+ if (!requiredPermission.equals(service.serviceInfo.permission)) {
+ Slog.e(LOG_TAG, "Service " + mComponentName + " lacks permission "
+ + requiredPermission);
+ finish();
+ return;
+ }
+ hasNLSIntentFilter = true;
+ break;
+ }
+ }
+ if (!hasNLSIntentFilter) {
+ Slog.e(LOG_TAG, "Service " + mComponentName + " lacks an intent-filter action "
+ + "for android.service.notification.NotificationListenerService.");
+ finish();
+ return;
+ }
+
AlertController.AlertParams p = new AlertController.AlertParams(this);
p.mTitle = getString(
R.string.notification_listener_security_warning_title,
@@ -147,19 +176,6 @@ public void onPause() {
}
private void onAllow() {
- String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
- try {
- ServiceInfo serviceInfo = getPackageManager().getServiceInfo(mComponentName, 0);
- if (!requiredPermission.equals(serviceInfo.permission)) {
- Slog.e(LOG_TAG,
- "Service " + mComponentName + " lacks permission " + requiredPermission);
- return;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Failed to get service info for " + mComponentName, e);
- return;
- }
-
mNm.setNotificationListenerAccessGranted(mComponentName, true);
finish();
@@ -170,12 +186,6 @@ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
return AlertActivity.dispatchPopulateAccessibilityEvent(this, event);
}
- @Override
- public void onBackPressed() {
- // Suppress finishing the activity on back button press,
- // consistently with the permission dialog behavior
- }
-
@Override
public void cancel() {
finish();
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index d6f810c99d1..6f495ad09a1 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -98,6 +98,7 @@ public AppRow loadAppRow(Context context, PackageManager pm, ApplicationInfo app
row.userId = UserHandle.getUserId(row.uid);
row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
row.channelCount = getChannelCount(row.pkg, row.uid);
+ row.soundTimeout = getNotificationSoundTimeout(row.pkg, row.uid);
recordAggregatedUsageEvents(context, row);
return row;
}
@@ -656,6 +657,25 @@ void setNm(INotificationManager inm) {
sINM = inm;
}
+ public long getNotificationSoundTimeout(String pkg, int uid) {
+ try {
+ return sINM.getNotificationSoundTimeout(pkg, uid);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return 0;
+ }
+ }
+
+ public boolean setNotificationSoundTimeout(String pkg, int uid, long timeout) {
+ try {
+ sINM.setNotificationSoundTimeout(pkg, uid, timeout);
+ return true;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return false;
+ }
+ }
+
/**
* NotificationsSentState contains how often an app sends notifications and how recently it sent
* one.
@@ -689,6 +709,7 @@ public static class AppRow extends Row {
public int userId;
public int blockedChannelCount;
public int channelCount;
+ public long soundTimeout;
public Map sentByChannel;
public NotificationsSentState sentByApp;
public boolean showAllChannels = true;
diff --git a/src/com/android/settings/notification/PhoneRingtone2PreferenceController.java b/src/com/android/settings/notification/PhoneRingtone2PreferenceController.java
new file mode 100644
index 00000000000..495d9848972
--- /dev/null
+++ b/src/com/android/settings/notification/PhoneRingtone2PreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 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.RingtoneManager;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.DefaultRingtonePreference;
+import com.android.settings.Utils;
+
+import com.android.settings.R;
+
+public class PhoneRingtone2PreferenceController extends RingtonePreferenceControllerBase {
+
+ private static final int SLOT_ID = 1;
+ private static final String KEY_PHONE_RINGTONE2 = "ringtone2";
+
+ public PhoneRingtone2PreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ DefaultRingtonePreference ringtonePreference =
+ (DefaultRingtonePreference) screen.findPreference(KEY_PHONE_RINGTONE2);
+ ringtonePreference.setSlotId(SLOT_ID);
+
+ if (isBuiltInEuiccSlot(SLOT_ID)) {
+ ringtonePreference.setTitle(mContext.getString(R.string.ringtone_title) + " (e-SIM)");
+ } else {
+ ringtonePreference.setTitle(mContext.getString(R.string.ringtone_title) + " - " +
+ String.format(mContext.getString(R.string.sim_card_number_title), 2));
+ }
+
+ ringtonePreference.setEnabled(hasCard());
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_PHONE_RINGTONE2;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return Utils.isVoiceCapable(mContext) && telephonyManager.isMultiSimEnabled();
+ }
+
+ @Override
+ public int getRingtoneType() {
+ return RingtoneManager.TYPE_RINGTONE;
+ }
+
+ private boolean hasCard() {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephonyManager.hasIccCard(SLOT_ID);
+ }
+}
diff --git a/src/com/android/settings/notification/PhoneRingtonePreferenceController.java b/src/com/android/settings/notification/PhoneRingtonePreferenceController.java
index 7bd78feeb3c..70e9d6879e2 100644
--- a/src/com/android/settings/notification/PhoneRingtonePreferenceController.java
+++ b/src/com/android/settings/notification/PhoneRingtonePreferenceController.java
@@ -19,16 +19,39 @@
import android.content.Context;
import android.media.RingtoneManager;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.DefaultRingtonePreference;
+import com.android.settings.R;
+
import com.android.settings.Utils;
public class PhoneRingtonePreferenceController extends RingtonePreferenceControllerBase {
+ private static final int SLOT_ID = 0;
private static final String KEY_PHONE_RINGTONE = "phone_ringtone";
public PhoneRingtonePreferenceController(Context context) {
super(context);
}
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ if (telephonyManager.isMultiSimEnabled()) {
+ DefaultRingtonePreference ringtonePreference =
+ (DefaultRingtonePreference) screen.findPreference(KEY_PHONE_RINGTONE);
+ ringtonePreference.setTitle(mContext.getString(R.string.ringtone_title) + " - " +
+ String.format(mContext.getString(R.string.sim_card_number_title), 1));
+ ringtonePreference.setEnabled(hasCard());
+ }
+ }
+
@Override
public String getPreferenceKey() {
return KEY_PHONE_RINGTONE;
@@ -43,4 +66,10 @@ public boolean isAvailable() {
public int getRingtoneType() {
return RingtoneManager.TYPE_RINGTONE;
}
+
+ private boolean hasCard() {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephonyManager.hasIccCard(SLOT_ID);
+ }
}
diff --git a/src/com/android/settings/notification/PulseNotificationPreferenceController.java b/src/com/android/settings/notification/PulseNotificationPreferenceController.java
index ad53fc3a0a3..0a2a5243d0b 100644
--- a/src/com/android/settings/notification/PulseNotificationPreferenceController.java
+++ b/src/com/android/settings/notification/PulseNotificationPreferenceController.java
@@ -70,9 +70,7 @@ public void onPause() {
@Override
public int getAvailabilityStatus() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_intrusiveNotificationLed) ? AVAILABLE
- : UNSUPPORTED_ON_DEVICE;
+ return UNSUPPORTED_ON_DEVICE;
}
@Override
diff --git a/src/com/android/settings/notification/RingtonePreferenceControllerBase.java b/src/com/android/settings/notification/RingtonePreferenceControllerBase.java
index 29b9266335d..12a156e4b62 100644
--- a/src/com/android/settings/notification/RingtonePreferenceControllerBase.java
+++ b/src/com/android/settings/notification/RingtonePreferenceControllerBase.java
@@ -24,6 +24,7 @@
import androidx.preference.Preference;
+import com.android.settings.RingtonePreference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.utils.ThreadUtils;
@@ -51,8 +52,8 @@ public void updateState(Preference preference) {
}
private void updateSummary(Preference preference) {
- final Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(
- mContext, getRingtoneType());
+ final Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUriBySlot(mContext,
+ getRingtoneType(), ((RingtonePreference)preference).getSlotId());
final CharSequence summary;
try {
@@ -69,4 +70,19 @@ private void updateSummary(Preference preference) {
public abstract int getRingtoneType();
+ public boolean isBuiltInEuiccSlot(int slotIndex) {
+ int[] euiccSlots = mContext.getResources()
+ .getIntArray(com.android.internal.R.array.non_removable_euicc_slots);
+ for (int slot : euiccSlots) {
+ if (slot == slotIndex) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isDeviceSupportsESIM() {
+ return mContext.getResources()
+ .getIntArray(com.android.internal.R.array.non_removable_euicc_slots).length > 0;
+ }
}
diff --git a/src/com/android/settings/notification/SettingPref.java b/src/com/android/settings/notification/SettingPref.java
index 9a374552732..89bbd30af66 100644
--- a/src/com/android/settings/notification/SettingPref.java
+++ b/src/com/android/settings/notification/SettingPref.java
@@ -24,7 +24,7 @@
import android.provider.Settings.Secure;
import android.provider.Settings.System;
-import androidx.preference.DropDownPreference;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.TwoStatePreference;
@@ -45,7 +45,7 @@ public class SettingPref {
private final Uri mUri;
protected TwoStatePreference mTwoState;
- protected DropDownPreference mDropDown;
+ protected ListPreference mDropDown;
public SettingPref(int type, String key, String setting, int def, int... values) {
mType = type;
@@ -73,8 +73,8 @@ public Preference init(SettingsPreferenceFragment settings) {
}
if (p instanceof TwoStatePreference) {
mTwoState = (TwoStatePreference) p;
- } else if (p instanceof DropDownPreference) {
- mDropDown = (DropDownPreference) p;
+ } else if (p instanceof ListPreference) {
+ mDropDown = (ListPreference) p;
CharSequence[] entries = new CharSequence[mValues.length];
CharSequence[] values = new CharSequence[mValues.length];
for (int i = 0; i < mValues.length; i++) {
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index 4575708bbbd..aae0c2d8bfd 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -32,6 +32,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
@@ -60,12 +61,15 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
private static final String EXTRA_OPEN_PHONE_RINGTONE_PICKER =
"EXTRA_OPEN_PHONE_RINGTONE_PICKER";
+ private static final String KEY_NOW_PLAYING = "dashboard_tile_pref_com.google.intelligence.sense.ambientmusic.AmbientMusicSettingsActivity";
@VisibleForTesting
static final int STOP_SAMPLE = 1;
@VisibleForTesting
final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
+ private final IncreasingRingVolumePreferenceCallback mIncreasingRingVolumeCallback =
+ new IncreasingRingVolumePreferenceCallback();
@VisibleForTesting
final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -73,6 +77,7 @@ public void handleMessage(Message msg) {
switch (msg.what) {
case STOP_SAMPLE:
mVolumeCallback.stopSample();
+ mIncreasingRingVolumeCallback.stopSample();
break;
}
}
@@ -111,6 +116,19 @@ public void onCreate(Bundle savedInstanceState) {
if (phoneRingTonePreference != null && openPhoneRingtonePicker) {
onPreferenceTreeClick(phoneRingTonePreference);
}
+ updateAmbientMusicPref();
+ }
+
+ private void updateAmbientMusicPref() {
+ final PreferenceScreen screen = getPreferenceScreen();
+ if (getContext().getResources().getBoolean(R.bool.config_show_now_playing) || screen == null) {
+ return;
+ }
+
+ final Preference preference = screen.findPreference(KEY_NOW_PLAYING);
+ if (preference != null) {
+ screen.removePreference(preference);
+ }
}
@Override
@@ -122,6 +140,7 @@ public int getHelpResource() {
public void onPause() {
super.onPause();
mVolumeCallback.stopSample();
+ mIncreasingRingVolumeCallback.stopSample();
}
@Override
@@ -209,6 +228,11 @@ public void onAttach(Context context) {
controller.setCallback(mVolumeCallback);
getSettingsLifecycle().addObserver(controller);
}
+
+ IncreasingRingVolumePreferenceController irvpc =
+ use(IncreasingRingVolumePreferenceController.class);
+ irvpc.setCallback(mIncreasingRingVolumeCallback);
+ getLifecycle().addObserver(irvpc);
}
// === Volumes ===
@@ -218,6 +242,7 @@ final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback
@Override
public void onSampleStarting(SeekBarVolumizer sbv) {
+ mIncreasingRingVolumeCallback.stopSample();
if (mCurrent != null) {
mHandler.removeMessages(STOP_SAMPLE);
mHandler.sendEmptyMessageDelayed(STOP_SAMPLE, SAMPLE_CUTOFF);
@@ -248,6 +273,26 @@ public void stopSample() {
}
}
+ final class IncreasingRingVolumePreferenceCallback implements
+ IncreasingRingVolumePreference.Callback {
+ private IncreasingRingVolumePreference mPlayingPref;
+
+ @Override
+ public void onSampleStarting(IncreasingRingVolumePreference pref) {
+ mPlayingPref = pref;
+ mVolumeCallback.stopSample();
+ mHandler.removeMessages(STOP_SAMPLE);
+ mHandler.sendEmptyMessageDelayed(STOP_SAMPLE, SAMPLE_CUTOFF);
+ }
+
+ public void stopSample() {
+ if (mPlayingPref != null) {
+ mPlayingPref.stopSample();
+ mPlayingPref = null;
+ }
+ }
+ };
+
private static List buildPreferenceControllers(Context context,
SoundSettings fragment, Lifecycle lifecycle) {
final List controllers = new ArrayList<>();
@@ -256,8 +301,12 @@ private static List buildPreferenceControllers(Con
// === Phone & notification ringtone ===
controllers.add(new PhoneRingtonePreferenceController(context));
+ controllers.add(new PhoneRingtone2PreferenceController(context));
controllers.add(new AlarmRingtonePreferenceController(context));
controllers.add(new NotificationRingtonePreferenceController(context));
+ controllers.add(new IncreasingRingPreferenceController(context));
+ controllers.add(new IncreasingRingVolumePreferenceController(context));
+ controllers.add(new VibrationPatternPreferenceController(context));
// === Other Sound Settings ===
final DialPadTonePreferenceController dialPadTonePreferenceController =
@@ -308,6 +357,17 @@ private static List buildPreferenceControllers(Con
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.sound_settings) {
+ @Override
+ public List getNonIndexableKeys(Context context) {
+ List keys = super.getNonIndexableKeys(context);
+
+ if (!context.getResources().getBoolean(R.bool.config_show_now_playing)) {
+ keys.add(KEY_NOW_PLAYING);
+ }
+
+ return keys;
+ }
+
@Override
public List createPreferenceControllers(
Context context) {
diff --git a/src/com/android/settings/notification/VibrateIconPreferenceController.java b/src/com/android/settings/notification/VibrateIconPreferenceController.java
index d772b47d59e..e538dd12f0f 100644
--- a/src/com/android/settings/notification/VibrateIconPreferenceController.java
+++ b/src/com/android/settings/notification/VibrateIconPreferenceController.java
@@ -40,6 +40,6 @@ public VibrateIconPreferenceController(Context context, SettingsPreferenceFragme
@Override
public boolean isAvailable() {
- return mHasVibrator;
+ return false;
}
}
diff --git a/src/com/android/settings/notification/VibrationPatternPreferenceController.java b/src/com/android/settings/notification/VibrationPatternPreferenceController.java
new file mode 100644
index 00000000000..6ad2cf20127
--- /dev/null
+++ b/src/com/android/settings/notification/VibrationPatternPreferenceController.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 Yet Another AOSP 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.ContentResolver;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This class allows choosing a vibration pattern while ringing
+ */
+public class VibrationPatternPreferenceController extends AbstractPreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_VIB_PATTERN = "vibration_pattern";
+ private static final String KEY_CUSTOM_VIB_CATEGORY = "custom_vibration_pattern";
+
+ private ListPreference mVibPattern;
+ private Preference mCustomVibCategory;
+
+ private static class VibrationEffectProxy {
+ public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
+ return VibrationEffect.createWaveform(timings, amplitudes, repeat);
+ }
+ }
+
+ private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ .build();
+
+ private static final long[] SIMPLE_VIBRATION_PATTERN = {
+ 0, // No delay before starting
+ 1000, // How long to vibrate
+ 1000, // How long to wait before vibrating again
+ 1000, // How long to vibrate
+ 1000, // How long to wait before vibrating again
+ };
+
+ private static final long[] DZZZ_DA_VIBRATION_PATTERN = {
+ 0, // No delay before starting
+ 500, // How long to vibrate
+ 200, // Delay
+ 70, // How long to vibrate
+ 720, // How long to wait before vibrating again
+ };
+
+ private static final long[] MM_MM_MM_VIBRATION_PATTERN = {
+ 0, // No delay before starting
+ 300, // How long to vibrate
+ 400, // Delay
+ 300, // How long to vibrate
+ 400, // Delay
+ 300, // How long to vibrate
+ 1400, // How long to wait before vibrating again
+ };
+
+ private static final long[] DA_DA_DZZZ_VIBRATION_PATTERN = {
+ 0, // No delay before starting
+ 70, // How long to vibrate
+ 80, // Delay
+ 70, // How long to vibrate
+ 180, // Delay
+ 600, // How long to vibrate
+ 1050, // How long to wait before vibrating again
+ };
+
+ private static final long[] DA_DZZZ_DA_VIBRATION_PATTERN = {
+ 0, // No delay before starting
+ 80, // How long to vibrate
+ 200, // Delay
+ 600, // How long to vibrate
+ 150, // Delay
+ 20, // How long to vibrate
+ 1050, // How long to wait before vibrating again
+ };
+
+ private static final int[] SEVEN_ELEMENTS_VIBRATION_AMPLITUDE = {
+ 0, // No delay before starting
+ 255, // Vibrate full amplitude
+ 0, // No amplitude while waiting
+ 255,
+ 0,
+ 255,
+ 0,
+ };
+
+ private static final int[] FIVE_ELEMENTS_VIBRATION_AMPLITUDE = {
+ 0, // No delay before starting
+ 255, // Vibrate full amplitude
+ 0, // No amplitude while waiting
+ 255,
+ 0,
+ };
+
+ public VibrationPatternPreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return Utils.isVoiceCapable(mContext);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_VIB_PATTERN;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mVibPattern = (ListPreference) screen.findPreference(KEY_VIB_PATTERN);
+ int vibPattern = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.RINGTONE_VIBRATION_PATTERN, 0);
+ mVibPattern.setValueIndex(vibPattern);
+ mVibPattern.setSummary(mVibPattern.getEntries()[vibPattern]);
+ mVibPattern.setOnPreferenceChangeListener(this);
+
+ mCustomVibCategory = screen.findPreference(KEY_CUSTOM_VIB_CATEGORY);
+ mCustomVibCategory.setVisible(vibPattern == 5);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mVibPattern) {
+ int vibPattern = Integer.valueOf((String) newValue);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.RINGTONE_VIBRATION_PATTERN, vibPattern);
+ mVibPattern.setSummary(mVibPattern.getEntries()[vibPattern]);
+ boolean isCustom = vibPattern == 5;
+ mCustomVibCategory.setVisible(isCustom);
+ if (!isCustom) previewPattern();
+ return true;
+ }
+ return false;
+ }
+
+ private void previewPattern() {
+ Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ VibrationEffect effect;
+ VibrationEffectProxy vibrationEffectProxy = new VibrationEffectProxy();
+ int vibPattern = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.RINGTONE_VIBRATION_PATTERN, 0);
+ switch (vibPattern) {
+ case 1:
+ effect = vibrationEffectProxy.createWaveform(DZZZ_DA_VIBRATION_PATTERN,
+ FIVE_ELEMENTS_VIBRATION_AMPLITUDE, -1);
+ break;
+ case 2:
+ effect = vibrationEffectProxy.createWaveform(MM_MM_MM_VIBRATION_PATTERN,
+ SEVEN_ELEMENTS_VIBRATION_AMPLITUDE, -1);
+ break;
+ case 3:
+ effect = vibrationEffectProxy.createWaveform(DA_DA_DZZZ_VIBRATION_PATTERN,
+ SEVEN_ELEMENTS_VIBRATION_AMPLITUDE, -1);
+ break;
+ case 4:
+ effect = vibrationEffectProxy.createWaveform(DA_DZZZ_DA_VIBRATION_PATTERN,
+ SEVEN_ELEMENTS_VIBRATION_AMPLITUDE, -1);
+ break;
+ default:
+ effect = vibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
+ FIVE_ELEMENTS_VIBRATION_AMPLITUDE, -1);
+ break;
+ }
+ vibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
+ }
+}
diff --git a/src/com/android/settings/notification/app/AppNotificationSettings.java b/src/com/android/settings/notification/app/AppNotificationSettings.java
index 046f0ce8c0a..a61238a9b6d 100644
--- a/src/com/android/settings/notification/app/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/app/AppNotificationSettings.java
@@ -78,6 +78,7 @@ protected List createPreferenceControllers(Context
mControllers.add(new BlockPreferenceController(context, mDependentFieldListener, mBackend));
mControllers.add(new FullScreenIntentPermissionPreferenceController(context, mBackend));
mControllers.add(new BadgePreferenceController(context, mBackend));
+ mControllers.add(new SoundTimeoutPreferenceController(context, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mDependentFieldListener, mBackend));
mControllers.add(new ImportancePreferenceController(
diff --git a/src/com/android/settings/notification/app/SoundTimeoutPreferenceController.java b/src/com/android/settings/notification/app/SoundTimeoutPreferenceController.java
new file mode 100644
index 00000000000..1fb9a215a88
--- /dev/null
+++ b/src/com/android/settings/notification/app/SoundTimeoutPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The LineageOS 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.app;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+
+import com.android.settings.RestrictedListPreference;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.notification.NotificationBackend;
+
+public class SoundTimeoutPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = "SoundTimeoutPreferenceController";
+ private static final String KEY_SOUND_TIMEOUT = "sound_timeout";
+
+ public SoundTimeoutPreferenceController(Context context,
+ NotificationBackend backend) {
+ super(context, backend);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SOUND_TIMEOUT;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mAppRow == null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ boolean isIncludedInFilter() {
+ return false;
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow != null) {
+ RestrictedListPreference pref = (RestrictedListPreference) preference;
+ pref.setDisabledByAdmin(mAdmin);
+
+ pref.setSummary("%s");
+ pref.setValue(Long.toString(mAppRow.soundTimeout));
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mAppRow != null) {
+ mAppRow.soundTimeout = Long.valueOf((String) newValue);
+ mBackend.setNotificationSoundTimeout(mAppRow.pkg, mAppRow.uid, mAppRow.soundTimeout);
+ }
+ return true;
+ }
+
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java
index 4f45c5c8bb2..7f1cda07ea9 100644
--- a/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java
@@ -25,7 +25,7 @@
import android.service.notification.ZenModeConfig;
import androidx.annotation.NonNull;
-import androidx.preference.DropDownPreference;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -48,8 +48,8 @@ class ZenModeSetCalendarPreferenceController extends AbstractZenModePreferenceCo
@VisibleForTesting
protected static final String KEY_REPLY = "reply";
- private DropDownPreference mCalendar;
- private DropDownPreference mReply;
+ private ListPreference mCalendar;
+ private ListPreference mReply;
private ZenModeConfig.EventInfo mEvent;
diff --git a/src/com/android/settings/notification/zen/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/zen/ZenModeEventRuleSettings.java
index 9b0f3fc88a6..fa243e8cf7f 100644
--- a/src/com/android/settings/notification/zen/ZenModeEventRuleSettings.java
+++ b/src/com/android/settings/notification/zen/ZenModeEventRuleSettings.java
@@ -28,7 +28,7 @@
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.EventInfo;
-import androidx.preference.DropDownPreference;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceScreen;
@@ -50,8 +50,8 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase {
public static final String ACTION = Settings.ACTION_ZEN_MODE_EVENT_RULE_SETTINGS;
- private DropDownPreference mCalendar;
- private DropDownPreference mReply;
+ private ListPreference mCalendar;
+ private ListPreference mReply;
private EventInfo mEvent;
@@ -128,7 +128,7 @@ protected void onCreateInternal() {
mCreate = true;
final PreferenceScreen root = getPreferenceScreen();
- mCalendar = (DropDownPreference) root.findPreference(KEY_CALENDAR);
+ mCalendar = (ListPreference) root.findPreference(KEY_CALENDAR);
mCalendar.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -143,7 +143,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
}
});
- mReply = (DropDownPreference) root.findPreference(KEY_REPLY);
+ mReply = (ListPreference) root.findPreference(KEY_REPLY);
mReply.setEntries(new CharSequence[] {
getString(R.string.zen_mode_event_rule_reply_any_except_no),
getString(R.string.zen_mode_event_rule_reply_yes_or_maybe),
diff --git a/src/com/android/settings/panel/AppVolumePanel.java b/src/com/android/settings/panel/AppVolumePanel.java
new file mode 100644
index 00000000000..876a15cf576
--- /dev/null
+++ b/src/com/android/settings/panel/AppVolumePanel.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 Project Kaleidoscope
+ *
+ * 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.panel;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.slices.CustomSliceRegistry;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppVolumePanel implements PanelContent {
+
+ private final Context mContext;
+
+ public static AppVolumePanel create(Context context) {
+ return new AppVolumePanel(context);
+ }
+
+ private AppVolumePanel(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mContext.getText(R.string.app_volume);
+ }
+
+ @Override
+ public List getSlices() {
+ final List uris = new ArrayList<>();
+ uris.add(CustomSliceRegistry.APP_VOLUME_SLICE_URI);
+ return uris;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.PANEL_VOLUME;
+ }
+
+ @Override
+ public Intent getSeeMoreIntent() {
+ return new Intent(Settings.ACTION_SOUND_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+}
diff --git a/src/com/android/settings/panel/PanelFeatureProviderImpl.java b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
index a0aeec60856..4d7acea49f3 100644
--- a/src/com/android/settings/panel/PanelFeatureProviderImpl.java
+++ b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
@@ -87,6 +87,8 @@ public PanelContent getPanel(Context context, Bundle bundle) {
return VolumePanel.create(context);
}
}
+ case Settings.Panel.ACTION_APP_VOLUME:
+ return AppVolumePanel.create(context);
}
throw new IllegalStateException("No matching panel for: " + panelType);
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index aba9eddce21..87f891f4915 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -229,7 +229,7 @@ public static class ChooseLockPasswordFragment extends InstrumentedFragment
private static final String KEY_IS_AUTO_CONFIRM_CHECK_MANUALLY_CHANGED =
"auto_confirm_option_set_manually";
- private static final int MIN_AUTO_PIN_REQUIREMENT_LENGTH = 6;
+ private static final int MIN_AUTO_PIN_REQUIREMENT_LENGTH = 4;
private LockscreenCredential mCurrentCredential;
private LockscreenCredential mChosenPassword;
@@ -1016,7 +1016,7 @@ private void setAutoPinConfirmOption(boolean enabled, int length) {
mAutoPinConfirmOption.setVisibility(View.VISIBLE);
mAutoConfirmSecurityMessage.setVisibility(View.VISIBLE);
if (!mIsAutoPinConfirmOptionSetManually) {
- mAutoPinConfirmOption.setChecked(length == MIN_AUTO_PIN_REQUIREMENT_LENGTH);
+ mAutoPinConfirmOption.setChecked(length >= MIN_AUTO_PIN_REQUIREMENT_LENGTH);
}
} else {
mAutoPinConfirmOption.setVisibility(View.GONE);
@@ -1098,7 +1098,8 @@ private void startSaveAndFinish() {
mUserId);
mSaveAndFinishWorker.start(mLockPatternUtils,
- mChosenPassword, mCurrentCredential, mUserId);
+ mChosenPassword, mCurrentCredential, mUserId,
+ mLockPatternUtils.getLockPatternSize(mUserId));
}
@Override
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index c331991a486..a3afe19670a 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -102,7 +102,8 @@ public static class IntentBuilder {
private final Intent mIntent;
public IntentBuilder(Context context) {
- mIntent = new Intent(context, ChooseLockPattern.class);
+ mIntent = new Intent(context, ChooseLockPatternSize.class);
+ mIntent.putExtra("className", ChooseLockPattern.class.getName());
mIntent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
}
@@ -211,19 +212,14 @@ public static class ChooseLockPatternFragment extends InstrumentedFragment
protected FooterButton mSkipOrClearButton;
protected FooterButton mNextButton;
@VisibleForTesting protected LockscreenCredential mChosenPattern;
+ private byte mPatternSize = LockPatternUtils.PATTERN_SIZE_DEFAULT;
private ColorStateList mDefaultHeaderColorList;
private View mSudContent;
/**
* The patten used during the help screen to show how to draw a pattern.
*/
- private final List mAnimatePattern =
- Collections.unmodifiableList(Lists.newArrayList(
- LockPatternView.Cell.of(0, 0),
- LockPatternView.Cell.of(0, 1),
- LockPatternView.Cell.of(1, 1),
- LockPatternView.Cell.of(2, 1)
- ));
+ private List mAnimatePattern;
@Override
public void onActivityResult(int requestCode, int resultCode,
@@ -268,12 +264,13 @@ public void onPatternCleared() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
- public void onPatternDetected(List pattern) {
+ public void onPatternDetected(List pattern,
+ byte patternSize) {
if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
if (mChosenPattern == null) throw new IllegalStateException(
"null chosen pattern in stage 'need to confirm");
try (LockscreenCredential confirmPattern =
- LockscreenCredential.createPattern(pattern)) {
+ LockscreenCredential.createPattern(pattern, patternSize)) {
if (mChosenPattern.equals(confirmPattern)) {
updateStage(Stage.ChoiceConfirmed);
} else {
@@ -284,7 +281,8 @@ public void onPatternDetected(List pattern) {
if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
updateStage(Stage.ChoiceTooShort);
} else {
- mChosenPattern = LockscreenCredential.createPattern(pattern);
+ mChosenPattern = LockscreenCredential.createPattern(
+ pattern, patternSize);
updateStage(Stage.FirstChoiceValid);
}
} else {
@@ -542,6 +540,16 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
mSudContent.setPadding(mSudContent.getPaddingLeft(), 0, mSudContent.getPaddingRight(),
0);
+ mPatternSize = getActivity().getIntent().getByteExtra("pattern_size",
+ LockPatternUtils.PATTERN_SIZE_DEFAULT);
+ LockPatternView.Cell.updateSize(mPatternSize);
+ mAnimatePattern = Collections.unmodifiableList(Lists.newArrayList(
+ LockPatternView.Cell.of(0, 0, mPatternSize),
+ LockPatternView.Cell.of(0, 1, mPatternSize),
+ LockPatternView.Cell.of(1, 1, mPatternSize),
+ LockPatternView.Cell.of(2, 1, mPatternSize)
+ ));
+
return layout;
}
@@ -555,6 +563,8 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
mLockPatternView.setFadePattern(false);
+ mLockPatternView.setLockPatternUtils(mLockPatternUtils);
+ mLockPatternView.setLockPatternSize(mPatternSize);
mFooterText = (TextView) view.findViewById(R.id.footerText);
@@ -601,6 +611,9 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
// restore from previous state
mChosenPattern = savedInstanceState.getParcelable(KEY_PATTERN_CHOICE);
mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_PATTERN);
+ mLockPatternView.setPattern(DisplayMode.Correct,
+ LockPatternUtils.byteArrayToPattern(
+ mChosenPattern.getCredential(), mPatternSize));
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
@@ -859,7 +872,7 @@ private void startSaveAndFinish() {
}
}
mSaveAndFinishWorker.start(mLockPatternUtils,
- mChosenPattern, mCurrentCredential, mUserId);
+ mChosenPattern, mCurrentCredential, mUserId, mPatternSize);
}
@Override
diff --git a/src/com/android/settings/password/ChooseLockPatternSize.java b/src/com/android/settings/password/ChooseLockPatternSize.java
new file mode 100644
index 00000000000..eed899a16cb
--- /dev/null
+++ b/src/com/android/settings/password/ChooseLockPatternSize.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012-2013 The CyanogenMod 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.password;
+
+import android.content.Intent;
+import android.content.res.Resources.Theme;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.preference.Preference;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SetupWizardUtils;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.utils.SettingsDividerItemDecoration;
+
+import com.google.android.setupdesign.GlifPreferenceLayout;
+import com.google.android.setupdesign.util.ThemeHelper;
+
+import org.lineageos.internal.logging.LineageMetricsLogger;
+
+public class ChooseLockPatternSize extends SettingsActivity {
+
+ @Override
+ public Intent getIntent() {
+ Intent modIntent = new Intent(super.getIntent());
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternSizeFragment.class.getName());
+ return modIntent;
+ }
+
+ @Override
+ protected boolean isValidFragment(String fragmentName) {
+ if (ChooseLockPatternSizeFragment.class.getName().equals(fragmentName)) return true;
+ return false;
+ }
+
+ @Override
+ protected boolean isToolbarEnabled() {
+ return false;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(SetupWizardUtils.getTheme(this, getIntent()));
+ ThemeHelper.trySetDynamicColor(this);
+ super.onCreate(savedInstanceState);
+ findViewById(R.id.content_parent).setFitsSystemWindows(false);
+ }
+
+ public static class ChooseLockPatternSizeFragment extends SettingsPreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!(getActivity() instanceof ChooseLockPatternSize)) {
+ throw new SecurityException("Fragment contained in wrong activity");
+ }
+ addPreferencesFromResource(R.xml.security_settings_pattern_size);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ final String key = preference.getKey();
+
+ byte patternSize;
+ if ("lock_pattern_size_4".equals(key)) {
+ patternSize = 4;
+ } else if ("lock_pattern_size_5".equals(key)) {
+ patternSize = 5;
+ } else if ("lock_pattern_size_6".equals(key)) {
+ patternSize = 6;
+ } else {
+ patternSize = 3;
+ }
+
+ Bundle extras = getActivity().getIntent().getExtras();
+ Intent intent = new Intent();
+ intent.setClassName(getActivity(), extras.getString("className"));
+ intent.putExtras(extras);
+ intent.putExtra("pattern_size", patternSize);
+ intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(intent);
+
+ finish();
+ return true;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
+ layout.setDividerItemDecoration(new SettingsDividerItemDecoration(getContext()));
+
+ layout.setIcon(getContext().getDrawable(R.drawable.ic_lock));
+
+ if (getActivity() != null) {
+ getActivity().setTitle(R.string.lock_settings_picker_pattern_size_message);
+ }
+
+ layout.setHeaderText(R.string.lock_settings_picker_pattern_size_message);
+
+ // Remove the padding on the start of the header text.
+ if (ThemeHelper.shouldApplyMaterialYouStyle(getContext())) {
+ final LinearLayout headerLayout = layout.findManagedViewById(
+ com.google.android.setupdesign.R.id.sud_layout_header);
+ if (headerLayout != null) {
+ headerLayout.setPadding(0, layout.getPaddingTop(), 0,
+ layout.getPaddingBottom());
+ }
+ }
+
+ // Use the dividers in SetupWizardRecyclerLayout. Suppress the dividers in
+ // PreferenceFragment.
+ setDivider(null);
+ }
+
+ @Override
+ public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
+ Bundle savedInstanceState) {
+ GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
+ return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return LineageMetricsLogger.CHOOSE_LOCK_PATTERN_SIZE;
+ }
+ }
+}
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 91875cc88d6..0f907cd0eb1 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -475,6 +475,9 @@ private boolean launchConfirmationActivity(int request, CharSequence title, Char
intent.setClassName(SETTINGS_PACKAGE_NAME, activityClass.getName());
intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
+ if (userId == LockPatternUtils.USER_FRP) {
+ intent.putExtra("className", ConfirmLockPattern.class.getName());
+ }
Intent inIntent = mFragment != null ? mFragment.getActivity().getIntent() :
mActivity.getIntent();
@@ -555,7 +558,9 @@ private Optional> determineAppropriateActivityClass(boolean returnCrede
? ConfirmLockPassword.InternalActivity.class
: ConfirmLockPassword.class);
case KeyguardManager.PATTERN:
- return Optional.of(returnCredentials || forceVerifyPath
+ return Optional.of(userId == LockPatternUtils.USER_FRP
+ ? ChooseLockPatternSize.class
+ : returnCredentials || forceVerifyPath
? ConfirmLockPattern.InternalActivity.class
: ConfirmLockPattern.class);
}
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index ead79d74d71..136f96f9316 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -629,7 +629,8 @@ public void onRemoteLockscreenValidationResult(
mLockPatternUtils,
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
/* currentCredential= */ null,
- mEffectiveUserId);
+ mEffectiveUserId,
+ mLockPatternUtils.getLockPatternSize(mEffectiveUserId));
} else {
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
/* timeoutMs= */ 0, mEffectiveUserId);
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 28d790e1912..3c9635a9377 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -116,6 +116,7 @@ public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBa
private DisappearAnimationUtils mDisappearAnimationUtils;
private boolean mIsManagedProfile;
+ private byte mPatternSize;
// required constructor for fragments
public ConfirmLockPatternFragment() {
@@ -142,6 +143,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
mSudContent.setPadding(mSudContent.getPaddingLeft(), 0, mSudContent.getPaddingRight(),
0);
mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
+ mPatternSize = mLockPatternUtils.getLockPatternSize(mEffectiveUserId);
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
@@ -156,6 +158,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
mDetailsText = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
+ mPatternSize = intent.getByteExtra("pattern_size", mPatternSize);
}
if (TextUtils.isEmpty(mHeaderText) && mIsManagedProfile) {
mHeaderText = mDevicePolicyManager.getOrganizationNameForUser(mUserId);
@@ -163,6 +166,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
mEffectiveUserId));
+ mLockPatternView.setLockPatternSize(mPatternSize);
mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
mLockPatternView.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
@@ -498,14 +502,15 @@ public void onPatternCellAdded(List pattern) {
}
- public void onPatternDetected(List pattern) {
+ public void onPatternDetected(List pattern, byte patternSize) {
if (mPendingLockCheck != null || mDisappearing) {
return;
}
mLockPatternView.setEnabled(false);
- final LockscreenCredential credential = LockscreenCredential.createPattern(pattern);
+ final LockscreenCredential credential = LockscreenCredential.createPattern(pattern,
+ patternSize);
if (mRemoteValidation) {
validateGuess(credential);
@@ -644,7 +649,8 @@ public void onRemoteLockscreenValidationResult(
mLockPatternUtils,
mRemoteLockscreenValidationFragment.getLockscreenCredential(),
/* currentCredential= */ null,
- mEffectiveUserId);
+ mEffectiveUserId,
+ mLockPatternUtils.getLockPatternSize(mEffectiveUserId));
} else {
mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
/* timeoutMs= */ 0, mEffectiveUserId);
diff --git a/src/com/android/settings/password/SaveAndFinishWorker.java b/src/com/android/settings/password/SaveAndFinishWorker.java
index 40054b77645..5592071e7a4 100644
--- a/src/com/android/settings/password/SaveAndFinishWorker.java
+++ b/src/com/android/settings/password/SaveAndFinishWorker.java
@@ -53,6 +53,7 @@ public class SaveAndFinishWorker extends Fragment {
private LockscreenCredential mUnificationProfileCredential;
private LockscreenCredential mChosenCredential;
private LockscreenCredential mCurrentCredential;
+ private byte mPatternSize;
private boolean mBlocking;
@@ -76,9 +77,10 @@ public SaveAndFinishWorker setListener(Listener listener) {
@VisibleForTesting
void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential,
- LockscreenCredential currentCredential, int userId) {
+ LockscreenCredential currentCredential, int userId, byte patternSize) {
mUtils = utils;
mUserId = userId;
+ mPatternSize = patternSize;
// This will be a no-op for non managed profiles.
mWasSecureBefore = mUtils.isSecure(mUserId);
mFinished = false;
@@ -90,8 +92,8 @@ void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential,
}
public void start(LockPatternUtils utils, LockscreenCredential chosenCredential,
- LockscreenCredential currentCredential, int userId) {
- prepare(utils, chosenCredential, currentCredential, userId);
+ LockscreenCredential currentCredential, int userId, byte patternSize) {
+ prepare(utils, chosenCredential, currentCredential, userId, patternSize);
if (mBlocking) {
finish(saveAndVerifyInBackground().second);
} else {
@@ -107,6 +109,7 @@ public void start(LockPatternUtils utils, LockscreenCredential chosenCredential,
@VisibleForTesting
Pair saveAndVerifyInBackground() {
final int userId = mUserId;
+ mUtils.setLockPatternSize(mPatternSize, userId);
try {
if (!mUtils.setLockCredential(mChosenCredential, mCurrentCredential, userId)) {
return Pair.create(false, null);
diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java
index 55b3847125e..e0e598064ff 100644
--- a/src/com/android/settings/password/SetupChooseLockPattern.java
+++ b/src/com/android/settings/password/SetupChooseLockPattern.java
@@ -47,7 +47,8 @@
public class SetupChooseLockPattern extends ChooseLockPattern {
public static Intent modifyIntentForSetup(Context context, Intent chooseLockPatternIntent) {
- chooseLockPatternIntent.setClass(context, SetupChooseLockPattern.class);
+ chooseLockPatternIntent.setClass(context, ChooseLockPatternSize.class);
+ chooseLockPatternIntent.putExtra("className", SetupChooseLockPattern.class.getName());
return chooseLockPatternIntent;
}
diff --git a/src/com/android/settings/privacy/PrivacyControlsFragment.java b/src/com/android/settings/privacy/PrivacyControlsFragment.java
index e40ed94c82d..c50648aa021 100644
--- a/src/com/android/settings/privacy/PrivacyControlsFragment.java
+++ b/src/com/android/settings/privacy/PrivacyControlsFragment.java
@@ -21,6 +21,7 @@
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.location.LocationIndicatorsPreferenceController;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
@@ -33,12 +34,14 @@ public class PrivacyControlsFragment extends DashboardFragment {
private static final String TAG = "PrivacyDashboardFrag";
private static final String CAMERA_KEY = "privacy_camera_toggle";
private static final String MIC_KEY = "privacy_mic_toggle";
+ private static final String LOCATION_KEY = "location_indicator_settings";
@Override
protected List createPreferenceControllers(Context context) {
final List controllers = new ArrayList<>();
controllers.add(new CameraToggleController(context, CAMERA_KEY));
controllers.add(new MicToggleController(context, MIC_KEY));
+ controllers.add(new LocationIndicatorsPreferenceController(context, LOCATION_KEY));
controllers.add(new ShowClipAccessNotificationPreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/safetycenter/SafetyCenterUtils.java b/src/com/android/settings/safetycenter/SafetyCenterUtils.java
index e0665302fbb..b5685bafb6d 100644
--- a/src/com/android/settings/safetycenter/SafetyCenterUtils.java
+++ b/src/com/android/settings/safetycenter/SafetyCenterUtils.java
@@ -55,6 +55,7 @@ public final class SafetyCenterUtils {
"privacy_work_profile_notifications_category";
private static final String KEY_NOTIFICATION_WORK_PROFILE_NOTIFICATIONS =
"privacy_lock_screen_work_profile_notifications";
+ private static final String APP_LOCK_PREF_KEY = "app_lock";
/**
* Returns preference controllers related to advanced security entries. This is used in {@link
diff --git a/src/com/android/settings/security/LockscreenDashboardFragment.java b/src/com/android/settings/security/LockscreenDashboardFragment.java
index 2e4a1f28917..a88c26e6214 100644
--- a/src/com/android/settings/security/LockscreenDashboardFragment.java
+++ b/src/com/android/settings/security/LockscreenDashboardFragment.java
@@ -30,6 +30,8 @@
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
@@ -37,6 +39,7 @@
import com.android.settings.display.AmbientDisplayNotificationsPreferenceController;
import com.android.settings.gestures.DoubleTapScreenPreferenceController;
import com.android.settings.gestures.PickupGesturePreferenceController;
+import com.android.settings.gestures.ScreenOffUdfpsPreferenceController;
import com.android.settings.notification.LockScreenNotificationPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.security.screenlock.LockScreenPreferenceController;
@@ -69,7 +72,7 @@ public class LockscreenDashboardFragment extends DashboardFragment
@VisibleForTesting
static final String KEY_ADD_USER_FROM_LOCK_SCREEN =
"security_lockscreen_add_users_when_locked";
-
+ private static final String KEY_NOW_PLAYING = "dashboard_tile_pref_com.google.intelligence.sense.ambientmusic.AmbientMusicNotificationsSettingsActivity";
private AmbientDisplayConfiguration mConfig;
private OwnerInfoPreferenceController mOwnerInfoPreferenceController;
@@ -94,6 +97,18 @@ public void onCreate(Bundle icicle) {
R.string.locked_work_profile_notification_title);
replaceEnterpriseStringTitle("security_setting_lock_screen_notif_work_header",
WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER, R.string.profile_section_header);
+ updateAmbientMusicPref();
+ }
+
+ private void updateAmbientMusicPref() {
+ final PreferenceScreen screen = getPreferenceScreen();
+ if (getContext().getResources().getBoolean(R.bool.config_show_now_playing) || screen == null) {
+ return;
+ }
+ final Preference preference = screen.findPreference(KEY_NOW_PLAYING);
+ if (preference != null) {
+ screen.removePreference(preference);
+ }
}
@Override
@@ -113,6 +128,7 @@ public void onAttach(Context context) {
use(AmbientDisplayNotificationsPreferenceController.class).setConfig(getConfig(context));
use(DoubleTapScreenPreferenceController.class).setConfig(getConfig(context));
use(PickupGesturePreferenceController.class).setConfig(getConfig(context));
+ use(ScreenOffUdfpsPreferenceController.class).setConfig(getConfig(context));
mControlsContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@@ -184,6 +200,9 @@ public List createPreferenceControllers(
public List getNonIndexableKeys(Context context) {
final List niks = super.getNonIndexableKeys(context);
niks.add(KEY_ADD_USER_FROM_LOCK_SCREEN);
+ if (!context.getResources().getBoolean(R.bool.config_show_now_playing)) {
+ niks.add(KEY_NOW_PLAYING);
+ }
return niks;
}
diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java
index 797d3ec621e..37714095b02 100644
--- a/src/com/android/settings/security/SecuritySettings.java
+++ b/src/com/android/settings/security/SecuritySettings.java
@@ -32,6 +32,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.security.applock.AppLockSettingsPreferenceController;
import com.android.settings.security.trustagent.TrustAgentListPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -55,6 +56,8 @@ public class SecuritySettings extends DashboardFragment {
@VisibleForTesting
static final String KEY_FACE_SETTINGS = "face_settings";
+ private static final String APP_LOCK_PREF_KEY = "app_lock";
+
@Override
public int getMetricsCategory() {
return SettingsEnums.SECURITY;
@@ -116,6 +119,8 @@ private static List buildPreferenceControllers(Con
securityPreferenceControllers.add(new ChangeScreenLockPreferenceController(context, host));
controllers.add(new PreferenceCategoryController(context, SECURITY_CATEGORY)
.setChildren(securityPreferenceControllers));
+ controllers.add(new AppLockSettingsPreferenceController(
+ context, APP_LOCK_PREF_KEY, host, lifecycle));
controllers.addAll(securityPreferenceControllers);
return controllers;
diff --git a/src/com/android/settings/security/applock/AppLockBiometricPreferenceController.kt b/src/com/android/settings/security/applock/AppLockBiometricPreferenceController.kt
new file mode 100644
index 00000000000..3ce93cef328
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockBiometricPreferenceController.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG
+
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val KEY = "app_lock_biometrics_allowed"
+
+class AppLockBiometricPreferenceController(
+ context: Context,
+ private val coroutineScope: CoroutineScope
+) : AppLockTogglePreferenceController(context, KEY) {
+
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)!!
+ private val biometricManager = context.getSystemService(BiometricManager::class.java)!!
+
+ private var preference: Preference? = null
+ private var isBiometricsAllowed = false
+
+ init {
+ coroutineScope.launch {
+ isBiometricsAllowed = withContext(Dispatchers.Default) {
+ appLockManager.isBiometricsAllowed()
+ }
+ preference?.let {
+ updateState(it)
+ }
+ }
+ }
+
+ override fun getAvailabilityStatus(): Int {
+ val result = biometricManager.canAuthenticate(BIOMETRIC_STRONG)
+ return if (result == BiometricManager.BIOMETRIC_SUCCESS) AVAILABLE else CONDITIONALLY_UNAVAILABLE
+ }
+
+ override fun isChecked() = isBiometricsAllowed
+
+ override fun setChecked(checked: Boolean): Boolean {
+ if (isBiometricsAllowed == checked) return false
+ isBiometricsAllowed = checked
+ coroutineScope.launch(Dispatchers.Default) {
+ appLockManager.setBiometricsAllowed(isBiometricsAllowed)
+ }
+ return true
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockCredentialActivity.kt b/src/com/android/settings/security/applock/AppLockCredentialActivity.kt
new file mode 100644
index 00000000000..e794f5f9104
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockCredentialActivity.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.Activity
+import android.app.AppLockManager
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.hardware.biometrics.BiometricConstants
+import android.hardware.biometrics.BiometricManager.Authenticators
+import android.hardware.biometrics.BiometricPrompt
+import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback
+import android.hardware.biometrics.PromptInfo
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.UserHandle.USER_NULL
+import android.os.UserManager
+import android.util.Log
+import android.view.WindowManager
+
+import androidx.fragment.app.commit
+import androidx.fragment.app.FragmentActivity
+
+import com.android.internal.widget.LockPatternUtils
+import com.android.settings.R
+import com.android.settings.password.BiometricFragment
+import com.android.settings.password.ConfirmDeviceCredentialUtils
+
+class AppLockCredentialActivity : FragmentActivity() {
+
+ private val handler = Handler(Looper.getMainLooper())
+
+ private lateinit var lockPatternUtils: LockPatternUtils
+ private lateinit var userManager: UserManager
+ private lateinit var appLockManager: AppLockManager
+
+ private var packageName: String? = null
+ private var label: String? = null
+ private var userId: Int = USER_NULL
+ private var biometricFragment: BiometricFragment? = null
+ private var goingToBackground = false
+ private var waitingForBiometricCallback = false
+
+ private val authenticationCallback = object : AuthenticationCallback() {
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+ if (!goingToBackground) {
+ waitingForBiometricCallback = false
+ if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED
+ || errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) {
+ finish()
+ }
+ } else if (waitingForBiometricCallback) { // goingToBackground is true
+ waitingForBiometricCallback = false
+ finish()
+ }
+ }
+
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
+ waitingForBiometricCallback = false
+ packageName?.let { nonNullPackageName ->
+ appLockManager.unlockPackage(nonNullPackageName)
+ }
+ ConfirmDeviceCredentialUtils.checkForPendingIntent(this@AppLockCredentialActivity)
+ setResult(Activity.RESULT_OK)
+ finish()
+ }
+
+ override fun onAuthenticationFailed() {
+ waitingForBiometricCallback = false
+ }
+
+ override fun onSystemEvent(event: Int) {
+ if (event == BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL) {
+ finish()
+ }
+ }
+ }
+
+ override protected fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ window.apply {
+ addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+ statusBarColor = Color.TRANSPARENT
+ }
+
+ appLockManager = getSystemService(AppLockManager::class.java)!!
+ userManager = UserManager.get(this)
+ lockPatternUtils = LockPatternUtils(this)
+
+ packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)
+ if (packageName == null) {
+ Log.e(TAG, "Failed to get package name, aborting unlock")
+ finish()
+ return
+ }
+
+ label = intent.getStringExtra(AppLockManager.EXTRA_PACKAGE_LABEL)
+
+ userId = intent.getIntExtra(Intent.EXTRA_USER_ID, USER_NULL)
+ if (userId == USER_NULL) {
+ Log.e(TAG, "Invalid user id, aborting")
+ finish()
+ return
+ }
+
+ val biometricsAllowed = intent.getBooleanExtra(
+ AppLockManager.EXTRA_ALLOW_BIOMETRICS,
+ AppLockManager.DEFAULT_BIOMETRICS_ALLOWED
+ )
+ var allowedAuthenticators = Authenticators.DEVICE_CREDENTIAL
+ if (biometricsAllowed) {
+ allowedAuthenticators = allowedAuthenticators or Authenticators.BIOMETRIC_WEAK
+ }
+
+ val promptInfo = PromptInfo().apply {
+ title = getString(com.android.internal.R.string.unlock_application, label)
+ isDisallowBiometricsIfPolicyExists = true
+ authenticators = allowedAuthenticators
+ isAllowBackgroundAuthentication = true
+ }
+
+ if (isBiometricAllowed()) {
+ // Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
+ // onAuthenticationError and do the right thing automatically.
+ showBiometricPrompt(promptInfo)
+ waitingForBiometricCallback = true
+ } else {
+ finish()
+ }
+ }
+
+ override protected fun onStart() {
+ super.onStart()
+ // Translucent activity that is "visible", so it doesn't complain about finish()
+ // not being called before onResume().
+ setVisible(true)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ if (!isChangingConfigurations()) {
+ goingToBackground = true
+ if (!waitingForBiometricCallback) {
+ finish()
+ }
+ } else {
+ goingToBackground = false
+ }
+ }
+
+ // User could be locked while Effective user is unlocked even though the effective owns the
+ // credential. Otherwise, biometric can't unlock fbe/keystore through
+ // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
+ // biometric is disabled due to device restart.
+ private fun isStrongAuthRequired() =
+ !lockPatternUtils.isBiometricAllowedForUser(userId) ||
+ !userManager.isUserUnlocked(userId)
+
+ private fun isBiometricAllowed() =
+ !isStrongAuthRequired() && !lockPatternUtils.hasPendingEscrowToken(userId)
+
+ private fun showBiometricPrompt(promptInfo: PromptInfo) {
+ biometricFragment = supportFragmentManager.findFragmentByTag(TAG_BIOMETRIC_FRAGMENT)
+ as? BiometricFragment
+ var newFragment = false
+ if (biometricFragment == null) {
+ biometricFragment = BiometricFragment.newInstance(promptInfo)
+ newFragment = true
+ }
+ biometricFragment?.also {
+ it.setCallbacks({
+ handler.post(it)
+ }, authenticationCallback)
+ it.setUser(userId)
+ }
+ if (newFragment) {
+ biometricFragment?.let {
+ supportFragmentManager.commit {
+ add(it, TAG_BIOMETRIC_FRAGMENT)
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "AppLockCredentialActivity"
+ private const val TAG_BIOMETRIC_FRAGMENT = "fragment"
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockHideAppPC.kt b/src/com/android/settings/security/applock/AppLockHideAppPC.kt
new file mode 100644
index 00000000000..b65534e0aed
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockHideAppPC.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val KEY = "hide_from_launcher"
+
+class AppLockHideAppPC(
+ context: Context,
+ private val packageName: String,
+ private val coroutineScope: CoroutineScope
+) : AppLockTogglePreferenceController(context, KEY) {
+
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)!!
+ private var hideFromLauncher = AppLockManager.DEFAULT_HIDE_IN_LAUNCHER
+ private var preference: Preference? = null
+
+ init {
+ coroutineScope.launch {
+ hideFromLauncher = withContext(Dispatchers.Default) {
+ appLockManager.hiddenPackages.any { it == packageName }
+ }
+ preference?.let {
+ updateState(it)
+ }
+ }
+ }
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun isChecked() = hideFromLauncher
+
+ override fun setChecked(checked: Boolean): Boolean {
+ if (hideFromLauncher == checked) return false
+ hideFromLauncher = checked
+ coroutineScope.launch(Dispatchers.Default) {
+ appLockManager.setPackageHidden(packageName, hideFromLauncher)
+ }
+ return true
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockNotificationRedactionPC.kt b/src/com/android/settings/security/applock/AppLockNotificationRedactionPC.kt
new file mode 100644
index 00000000000..8fc0d901d63
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockNotificationRedactionPC.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val KEY = "redact_notifications"
+
+class AppLockNotificationRedactionPC(
+ context: Context,
+ private val packageName: String,
+ private val coroutineScope: CoroutineScope
+) : AppLockTogglePreferenceController(context, KEY) {
+
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)!!
+ private var shouldRedactNotification = AppLockManager.DEFAULT_REDACT_NOTIFICATION
+ private var preference: Preference? = null
+
+ init {
+ coroutineScope.launch {
+ shouldRedactNotification = withContext(Dispatchers.Default) {
+ appLockManager.packageData.find {
+ it.packageName == packageName
+ }?.shouldRedactNotification == true
+ }
+ preference?.let {
+ updateState(it)
+ }
+ }
+ }
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun isChecked() = shouldRedactNotification
+
+ override fun setChecked(checked: Boolean): Boolean {
+ if (shouldRedactNotification == checked) return false
+ shouldRedactNotification = checked
+ coroutineScope.launch(Dispatchers.Default) {
+ appLockManager.setShouldRedactNotification(packageName, checked)
+ }
+ return true
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockPackageConfigFragment.kt b/src/com/android/settings/security/applock/AppLockPackageConfigFragment.kt
new file mode 100644
index 00000000000..3fe14712b57
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockPackageConfigFragment.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.Bundle
+
+import androidx.lifecycle.lifecycleScope
+
+import com.android.internal.logging.nano.MetricsProto
+
+import com.android.settings.R
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.widget.EntityHeaderController
+import com.android.settingslib.applications.ApplicationsState.AppEntry
+import com.android.settingslib.core.AbstractPreferenceController
+import com.android.settingslib.widget.LayoutPreference
+
+private val TAG = AppLockPackageConfigFragment::class.simpleName
+private const val KEY_HEADER = "header_view"
+
+class AppLockPackageConfigFragment : DashboardFragment() {
+
+ private lateinit var packageInfo: PackageInfo
+
+ override fun onAttach(context: Context) {
+ packageInfo = arguments?.getParcelable(PACKAGE_INFO, PackageInfo::class.java)!!
+ super.onAttach(context)
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ super.onCreatePreferences(savedInstanceState, rootKey)
+ val appEntry = AppEntry(requireContext(), packageInfo.applicationInfo, 0)
+ val header = preferenceScreen.findPreference(KEY_HEADER)
+ EntityHeaderController.newInstance(
+ requireActivity(),
+ this,
+ header?.findViewById(R.id.entity_header)
+ ).setPackageName(packageInfo.packageName)
+ .setButtonActions(
+ EntityHeaderController.ActionType.ACTION_NONE,
+ EntityHeaderController.ActionType.ACTION_NONE
+ )
+ .bindHeaderButtons()
+ .setLabel(appEntry)
+ .setIcon(appEntry)
+ .done(false /* rebindActions */)
+ }
+
+ override protected fun createPreferenceControllers(
+ context: Context
+ ) : List = listOf(
+ AppLockPackageProtectionPC(context, packageInfo.packageName, lifecycleScope),
+ AppLockNotificationRedactionPC(context, packageInfo.packageName, lifecycleScope),
+ AppLockHideAppPC(context, packageInfo.packageName, lifecycleScope)
+ )
+
+ override fun getMetricsCategory(): Int = MetricsProto.MetricsEvent.CRDROID_SETTINGS
+
+ override protected fun getPreferenceScreenResId() = R.xml.app_lock_package_config_settings
+
+ override protected fun getLogTag() = TAG
+}
diff --git a/src/com/android/settings/security/applock/AppLockPackageListFragment.kt b/src/com/android/settings/security/applock/AppLockPackageListFragment.kt
new file mode 100644
index 00000000000..930fa7ac537
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockPackageListFragment.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PackageInfoFlags
+import android.os.Bundle
+import android.view.View
+
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.forEach
+
+import com.android.internal.logging.nano.MetricsProto
+import com.android.internal.util.crdroid.Utils
+
+import com.android.settings.R
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settingslib.PrimarySwitchPreference
+import com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private val TAG = AppLockPackageListFragment::class.simpleName
+internal const val PACKAGE_INFO = "package_info"
+
+class AppLockPackageListFragment : DashboardFragment() {
+
+ private lateinit var appLockManager: AppLockManager
+ private lateinit var pm: PackageManager
+ private lateinit var whiteListedPackages: Array
+ private lateinit var launchablePackages: List
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ appLockManager = context.getSystemService(AppLockManager::class.java)!!
+ pm = context.packageManager
+ launchablePackages = Utils.launchablePackages(context)
+ whiteListedPackages = resources.getStringArray(
+ com.android.internal.R.array.config_appLockAllowedSystemApps)
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ super.onCreatePreferences(savedInstanceState, rootKey)
+ lifecycleScope.launch {
+ val selectedPackages = getSelectedPackages()
+ val preferences = withContext(Dispatchers.Default) {
+ pm.getInstalledPackages(
+ PackageInfoFlags.of(PackageManager.MATCH_ALL.toLong())
+ ).filter { packageInfo ->
+ val isSystemApp = packageInfo.applicationInfo?.isSystemApp ?: false
+ !isSystemApp || launchablePackages.contains(packageInfo.packageName) ||
+ whiteListedPackages.contains(packageInfo.packageName)
+ }.sortedWith { first, second ->
+ getLabel(first).compareTo(getLabel(second))
+ }
+ }.map { packageInfo ->
+ createPreference(packageInfo, selectedPackages.contains(packageInfo.packageName))
+ }
+ preferenceScreen?.let {
+ preferences.forEach { pref ->
+ it.addPreference(pref)
+ }
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ lifecycleScope.launch {
+ val selectedPackages = getSelectedPackages()
+ preferenceScreen?.forEach {
+ if (it is PrimarySwitchPreference) {
+ it.isChecked = selectedPackages.contains(it.key)
+ }
+ }
+ }
+ }
+
+ private suspend fun getSelectedPackages(): Set {
+ return withContext(Dispatchers.IO) {
+ appLockManager.packageData.filter {
+ it.shouldProtectApp == true
+ }.map {
+ it.packageName
+ }.toSet()
+ }
+ }
+
+ private fun getLabel(packageInfo: PackageInfo) =
+ packageInfo.applicationInfo?.loadLabel(pm).toString()
+
+ private fun createPreference(packageInfo: PackageInfo, isProtected: Boolean): Preference {
+ val label = getLabel(packageInfo)
+ return PrimarySwitchPreference(requireContext()).apply {
+ key = packageInfo.packageName
+ title = label
+ icon = packageInfo.applicationInfo?.loadIcon(pm)
+ setIconSize(ICON_SIZE_MEDIUM)
+ isChecked = isProtected
+ setOnPreferenceChangeListener { _, newValue ->
+ lifecycleScope.launch(Dispatchers.IO) {
+ appLockManager.setShouldProtectApp(packageInfo.packageName, newValue as Boolean)
+ }
+ return@setOnPreferenceChangeListener true
+ }
+ setOnPreferenceClickListener {
+ SubSettingLauncher(requireContext())
+ .setDestination(AppLockPackageConfigFragment::class.qualifiedName)
+ .setSourceMetricsCategory(metricsCategory)
+ .setTitleText(label)
+ .setArguments(
+ Bundle(1).apply {
+ putParcelable(PACKAGE_INFO, packageInfo)
+ }
+ )
+ .launch()
+ true
+ }
+ }
+ }
+
+ override fun getMetricsCategory(): Int = MetricsProto.MetricsEvent.CRDROID_SETTINGS
+
+ override protected fun getPreferenceScreenResId() = R.xml.app_lock_package_list_settings
+
+ override protected fun getLogTag() = TAG
+}
diff --git a/src/com/android/settings/security/applock/AppLockPackageProtectionPC.kt b/src/com/android/settings/security/applock/AppLockPackageProtectionPC.kt
new file mode 100644
index 00000000000..7b92f68b65c
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockPackageProtectionPC.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val KEY = "main_switch"
+
+class AppLockPackageProtectionPC(
+ context: Context,
+ private val packageName: String,
+ private val coroutineScope: CoroutineScope
+) : AppLockTogglePreferenceController(context, KEY) {
+
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)!!
+ private var isProtected = false
+ private var preference: Preference? = null
+
+ init {
+ coroutineScope.launch {
+ isProtected = withContext(Dispatchers.Default) {
+ appLockManager.packageData.find {
+ it.packageName == packageName
+ }?.shouldProtectApp == true
+ }
+ preference?.let {
+ updateState(it)
+ }
+ }
+ }
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun isChecked() = isProtected
+
+ override fun setChecked(checked: Boolean): Boolean {
+ if (isProtected == checked) return false
+ isProtected = checked
+ coroutineScope.launch(Dispatchers.Default) {
+ appLockManager.setShouldProtectApp(packageName, isProtected)
+ }
+ return true
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockSettingsFragment.kt b/src/com/android/settings/security/applock/AppLockSettingsFragment.kt
new file mode 100644
index 00000000000..43dcc9dc0a0
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockSettingsFragment.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.content.Context
+import android.os.SystemProperties
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+
+import com.android.internal.logging.nano.MetricsProto
+import androidx.lifecycle.lifecycleScope
+
+import com.android.settings.R
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.search.BaseSearchIndexProvider
+import com.android.settingslib.core.AbstractPreferenceController
+import com.android.settingslib.search.SearchIndexable
+
+@SearchIndexable
+class AppLockSettingsFragment : DashboardFragment(),
+ MenuItem.OnMenuItemClickListener {
+
+ private var debugEnabled = SystemProperties.get(DEBUG_PROPERTY, null) == LEVEL_DEBUG
+ private var handledClick = false
+
+ override protected fun getPreferenceScreenResId() = R.xml.app_lock_settings
+
+ override fun getMetricsCategory() = MetricsProto.MetricsEvent.CRDROID_SETTINGS
+
+ override protected fun getLogTag() = TAG
+
+ override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater)
+ menu.add(
+ 0 /* groupId */,
+ MENU_ITEM_DEBUG_ID,
+ 0 /* order */,
+ getDebugMenuItemTitle(),
+ ).setOnMenuItemClickListener(this)
+ }
+
+ private fun getDebugMenuItemTitle(): Int =
+ if (debugEnabled) R.string.disable_debugging else R.string.enable_debugging
+
+ override fun onMenuItemClick(item: MenuItem): Boolean {
+ if (item.itemId == MENU_ITEM_DEBUG_ID) {
+ debugEnabled = !debugEnabled
+ SystemProperties.set(DEBUG_PROPERTY, if (debugEnabled) LEVEL_DEBUG else null)
+ item.setTitle(getDebugMenuItemTitle())
+ return true
+ }
+ return false
+ }
+
+ override protected fun createPreferenceControllers(
+ context: Context
+ ) : List = listOf(
+ AppLockBiometricPreferenceController(context, lifecycleScope)
+ )
+
+ companion object {
+ private const val TAG = "AppLockSettingsFragment"
+
+ private const val DEBUG_PROPERTY = "log.tag.AppLockManagerService"
+ private const val LEVEL_DEBUG = "DEBUG"
+ private const val MENU_ITEM_DEBUG_ID = 101
+
+ @JvmField
+ val SEARCH_INDEX_DATA_PROVIDER = BaseSearchIndexProvider(R.xml.app_lock_settings)
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockSettingsPreferenceController.kt b/src/com/android/settings/security/applock/AppLockSettingsPreferenceController.kt
new file mode 100644
index 00000000000..69b0433d2c3
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockSettingsPreferenceController.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.Activity
+import android.app.AppLockManager
+import android.app.KeyguardManager
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import androidx.lifecycle.Lifecycle.Event
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import com.android.internal.widget.LockPatternUtils
+import com.android.settings.R
+import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.password.ConfirmDeviceCredentialActivity
+import com.android.settingslib.core.lifecycle.Lifecycle
+import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE
+import com.android.settings.core.BasePreferenceController
+
+import com.android.settings.SettingsActivity
+import com.android.settings.core.SettingsBaseActivity
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider
+
+class AppLockSettingsPreferenceController(
+ context: Context,
+ preferenceKey: String,
+ private val host: DashboardFragment?,
+ lifecycle: Lifecycle?,
+) : BasePreferenceController(context, preferenceKey),
+ LifecycleEventObserver {
+
+ private val lockPatternUtils = LockPatternUtils(context)
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)!!
+ private var preference: Preference? = null
+ private val securityPromptLauncher: ActivityResultLauncher?
+
+ init {
+ lifecycle?.addObserver(this)
+ securityPromptLauncher = host?.registerForActivityResult(
+ StartActivityForResult()
+ ) {
+ if (it?.resultCode == Activity.RESULT_OK) {
+ val intent = SubSettingLauncher(mContext)
+ .setDestination(AppLockSettingsFragment::class.qualifiedName)
+ .setSourceMetricsCategory(host.metricsCategory)
+ .setTransitionType(TRANSITION_SLIDE)
+ .toIntent()
+ intent.setClass(mContext, AppLockSubSettings::class.java)
+ mContext.startActivity(intent)
+ }
+ }
+ }
+
+ override fun getAvailabilityStatus() =
+ if (lockPatternUtils.isSecure(UserHandle.myUserId()))
+ AVAILABLE
+ else
+ DISABLED_DEPENDENT_SETTING
+
+ override fun onStateChanged(owner: LifecycleOwner, event: Event) {
+ if (event == Event.ON_START) {
+ preference?.let {
+ updateState(it)
+ }
+ }
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)
+ }
+
+ override fun updateState(preference: Preference) {
+ preference.apply {
+ if (getAvailabilityStatus() == AVAILABLE) {
+ setEnabled(true)
+ summary = getSummaryForListSize(appLockManager.packageData.filter {
+ it.shouldProtectApp == true
+ }.size)
+ } else {
+ setEnabled(false)
+ summary = mContext.getString(R.string.disabled_because_no_backup_security)
+ }
+ }
+ }
+
+ private fun getSummaryForListSize(size: Int): CharSequence? =
+ if (size == 0) {
+ mContext.getString(R.string.app_lock_packages_summary)
+ } else {
+ mContext.resources.getQuantityString(R.plurals.app_lock_summary, size, size)
+ }
+
+ override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+ if (preference.key == preferenceKey && securityPromptLauncher != null) {
+ val title = mContext.getString(R.string.app_lock_authentication_dialog_title)
+ val intent = Intent().apply {
+ setClassName(SETTINGS_PACKAGE_NAME,
+ ConfirmDeviceCredentialActivity::class.qualifiedName!!)
+ putExtra(KeyguardManager.EXTRA_TITLE, title)
+ }
+ securityPromptLauncher.launch(intent)
+ return true
+ }
+ return super.handlePreferenceTreeClick(preference)
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockSubSettings.kt b/src/com/android/settings/security/applock/AppLockSubSettings.kt
new file mode 100644
index 00000000000..e376f1696d0
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockSubSettings.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import com.android.settings.SettingsActivity
+
+class AppLockSubSettings : SettingsActivity() {
+
+ override protected fun isValidFragment(fragmentName: String): Boolean {
+ return AppLockSettingsFragment::class.qualifiedName == fragmentName
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockTimeoutPreferenceController.kt b/src/com/android/settings/security/applock/AppLockTimeoutPreferenceController.kt
new file mode 100644
index 00000000000..00f3a0baaad
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockTimeoutPreferenceController.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+
+import androidx.preference.ListPreference
+import androidx.preference.Preference
+
+import com.android.settings.core.BasePreferenceController
+
+class AppLockTimeoutPreferenceController(
+ context: Context,
+ key: String,
+) : BasePreferenceController(context, key),
+ Preference.OnPreferenceChangeListener {
+
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)!!
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun updateState(preference: Preference) {
+ (preference as ListPreference).value = appLockManager.timeout.takeIf {
+ it != -1L
+ }?.toString()
+ }
+
+ override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
+ appLockManager.timeout = (newValue as String).toLong()
+ return true
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockTogglePreferenceController.kt b/src/com/android/settings/security/applock/AppLockTogglePreferenceController.kt
new file mode 100644
index 00000000000..e5baa5024db
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockTogglePreferenceController.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 FlamingoOS 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.security.applock;
+
+import android.content.Context
+import android.widget.CompoundButton
+import android.widget.CompoundButton.OnCheckedChangeListener
+
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import com.android.settings.R
+import com.android.settings.core.TogglePreferenceController
+import com.android.settingslib.widget.MainSwitchPreference
+
+abstract class AppLockTogglePreferenceController(
+ context: Context,
+ key: String,
+) : TogglePreferenceController(context, key),
+ OnCheckedChangeListener {
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ val preference = screen.findPreference(preferenceKey) ?: return
+ if (preference is MainSwitchPreference) {
+ preference.addOnSwitchChangeListener(this)
+ }
+ }
+
+ override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
+ setChecked(isChecked)
+ }
+
+ override fun getSliceHighlightMenuRes() = R.string.menu_key_security
+}
diff --git a/src/com/android/settings/security/screenlock/AbstractPatternSwitchPreferenceController.java b/src/com/android/settings/security/screenlock/AbstractPatternSwitchPreferenceController.java
new file mode 100644
index 00000000000..e2102aade4e
--- /dev/null
+++ b/src/com/android/settings/security/screenlock/AbstractPatternSwitchPreferenceController.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.security.screenlock;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.TwoStatePreference;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public abstract class AbstractPatternSwitchPreferenceController
+ extends AbstractPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private final String mKey;
+ private final int mUserId;
+ private final LockPatternUtils mLockPatternUtils;
+
+ public AbstractPatternSwitchPreferenceController(Context context, String key,
+ int userId, LockPatternUtils lockPatternUtils) {
+ super(context);
+ mKey = key;
+ mUserId = userId;
+ mLockPatternUtils = lockPatternUtils;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return isPatternLock();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return mKey;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ ((TwoStatePreference) preference).setChecked(isEnabled(mLockPatternUtils, mUserId));
+ }
+
+ private boolean isPatternLock() {
+ return mLockPatternUtils.getCredentialTypeForUser(mUserId)
+ == LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ setEnabled(mLockPatternUtils, mUserId, (Boolean) newValue);
+ return true;
+ }
+
+ protected abstract boolean isEnabled(LockPatternUtils utils, int userId);
+ protected abstract void setEnabled(LockPatternUtils utils, int userId, boolean enabled);
+}
diff --git a/src/com/android/settings/security/screenlock/PatternDotsVisiblePreferenceController.java b/src/com/android/settings/security/screenlock/PatternDotsVisiblePreferenceController.java
new file mode 100644
index 00000000000..8da044d449b
--- /dev/null
+++ b/src/com/android/settings/security/screenlock/PatternDotsVisiblePreferenceController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.security.screenlock;
+
+import android.content.Context;
+
+import com.android.internal.widget.LockPatternUtils;
+
+public class PatternDotsVisiblePreferenceController
+ extends AbstractPatternSwitchPreferenceController {
+ private static final String PREF_KEY = "visibledots";
+
+ public PatternDotsVisiblePreferenceController(Context context, int userId,
+ LockPatternUtils lockPatternUtils) {
+ super(context, PREF_KEY, userId, lockPatternUtils);
+ }
+
+ @Override
+ protected boolean isEnabled(LockPatternUtils utils, int userId) {
+ return utils.isVisibleDotsEnabled(userId);
+ }
+
+ @Override
+ protected void setEnabled(LockPatternUtils utils, int userId, boolean enabled) {
+ utils.setVisibleDotsEnabled(enabled, userId);
+ }
+}
diff --git a/src/com/android/settings/security/screenlock/PatternErrorVisiblePreferenceController.java b/src/com/android/settings/security/screenlock/PatternErrorVisiblePreferenceController.java
new file mode 100644
index 00000000000..b9a18c194fd
--- /dev/null
+++ b/src/com/android/settings/security/screenlock/PatternErrorVisiblePreferenceController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.security.screenlock;
+
+import android.content.Context;
+
+import com.android.internal.widget.LockPatternUtils;
+
+public class PatternErrorVisiblePreferenceController
+ extends AbstractPatternSwitchPreferenceController {
+ private static final String PREF_KEY = "visible_error_pattern";
+
+ public PatternErrorVisiblePreferenceController(Context context, int userId,
+ LockPatternUtils lockPatternUtils) {
+ super(context, PREF_KEY, userId, lockPatternUtils);
+ }
+
+ @Override
+ protected boolean isEnabled(LockPatternUtils utils, int userId) {
+ return utils.isShowErrorPath(userId);
+ }
+
+ @Override
+ protected void setEnabled(LockPatternUtils utils, int userId, boolean enabled) {
+ utils.setShowErrorPath(enabled, userId);
+ }
+}
diff --git a/src/com/android/settings/security/screenlock/PatternVisiblePreferenceController.java b/src/com/android/settings/security/screenlock/PatternVisiblePreferenceController.java
index ea3403bd947..2f8b641a87e 100644
--- a/src/com/android/settings/security/screenlock/PatternVisiblePreferenceController.java
+++ b/src/com/android/settings/security/screenlock/PatternVisiblePreferenceController.java
@@ -18,52 +18,23 @@
import android.content.Context;
-import androidx.preference.Preference;
-import androidx.preference.TwoStatePreference;
-
import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
-
-public class PatternVisiblePreferenceController extends AbstractPreferenceController
- implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+public class PatternVisiblePreferenceController extends AbstractPatternSwitchPreferenceController {
private static final String PREF_KEY = "visiblepattern";
- private final int mUserId;
- private final LockPatternUtils mLockPatternUtils;
-
public PatternVisiblePreferenceController(Context context, int userId,
LockPatternUtils lockPatternUtils) {
- super(context);
- mUserId = userId;
- mLockPatternUtils = lockPatternUtils;
- }
-
- @Override
- public boolean isAvailable() {
- return isPatternLock();
+ super(context, PREF_KEY, userId, lockPatternUtils);
}
@Override
- public String getPreferenceKey() {
- return PREF_KEY;
- }
-
- @Override
- public void updateState(Preference preference) {
- ((TwoStatePreference) preference).setChecked(
- mLockPatternUtils.isVisiblePatternEnabled(mUserId));
- }
-
- private boolean isPatternLock() {
- return mLockPatternUtils.getCredentialTypeForUser(mUserId)
- == LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+ protected boolean isEnabled(LockPatternUtils utils, int userId) {
+ return utils.isVisiblePatternEnabled(userId);
}
@Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- mLockPatternUtils.setVisiblePatternEnabled((Boolean) newValue, mUserId);
- return true;
+ protected void setEnabled(LockPatternUtils utils, int userId, boolean enabled) {
+ utils.setVisiblePatternEnabled(enabled, userId);
}
}
diff --git a/src/com/android/settings/security/screenlock/PinScramblePreferenceController.java b/src/com/android/settings/security/screenlock/PinScramblePreferenceController.java
new file mode 100644
index 00000000000..6d7ae8a20cb
--- /dev/null
+++ b/src/com/android/settings/security/screenlock/PinScramblePreferenceController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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.security.screenlock;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import androidx.preference.Preference;
+import androidx.preference.TwoStatePreference;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import lineageos.providers.LineageSettings;
+
+public class PinScramblePreferenceController extends AbstractPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ static final String KEY_LOCKSCREEN_SCRAMBLE_PIN_LAYOUT = "lockscreen_scramble_pin_layout";
+
+ private final int mUserId;
+ private final LockPatternUtils mLockPatternUtils;
+
+ public PinScramblePreferenceController(Context context, int userId,
+ LockPatternUtils lockPatternUtils) {
+ super(context);
+ mUserId = userId;
+ mLockPatternUtils = lockPatternUtils;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return isPinLock();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_LOCKSCREEN_SCRAMBLE_PIN_LAYOUT;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ ((TwoStatePreference) preference).setChecked(LineageSettings.System.getInt(
+ mContext.getContentResolver(),
+ LineageSettings.System.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT,
+ 0) == 1);
+ }
+
+ private boolean isPinLock() {
+ int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId);
+ boolean hasPin = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC ||
+ quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+ return mLockPatternUtils.isSecure(mUserId) && hasPin;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ LineageSettings.System.putInt(
+ mContext.getContentResolver(),
+ LineageSettings.System.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT,
+ (Boolean) newValue ? 1 : 0);
+ return true;
+ }
+}
diff --git a/src/com/android/settings/security/screenlock/ScreenLockSettings.java b/src/com/android/settings/security/screenlock/ScreenLockSettings.java
index 1c66b711382..f811872ee59 100644
--- a/src/com/android/settings/security/screenlock/ScreenLockSettings.java
+++ b/src/com/android/settings/security/screenlock/ScreenLockSettings.java
@@ -82,12 +82,18 @@ private static List buildPreferenceControllers(Con
context, MY_USER_ID, lockPatternUtils));
controllers.add(new PinPrivacyPreferenceController(
context, MY_USER_ID, lockPatternUtils));
+ controllers.add(new PatternErrorVisiblePreferenceController(
+ context, MY_USER_ID, lockPatternUtils));
+ controllers.add(new PatternDotsVisiblePreferenceController(
+ context, MY_USER_ID, lockPatternUtils));
controllers.add(new PowerButtonInstantLockPreferenceController(
context, MY_USER_ID, lockPatternUtils));
controllers.add(new LockAfterTimeoutPreferenceController(
context, MY_USER_ID, lockPatternUtils));
controllers.add(new AutoPinConfirmPreferenceController(
context, MY_USER_ID, lockPatternUtils, parent));
+ controllers.add(new PinScramblePreferenceController(
+ context, MY_USER_ID, lockPatternUtils));
controllers.add(new OwnerInfoPreferenceController(context, parent));
return controllers;
}
diff --git a/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java b/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
index 0e2e6bcb44c..21a1525b156 100644
--- a/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
+++ b/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
@@ -140,7 +140,7 @@ Intent createResultIntent(ResolveInfo resolveInfo) {
intent = new Intent();
}
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(mContext, R.mipmap.ic_launcher_settings))
+ Intent.ShortcutIconResource.fromContext(mContext, R.drawable.ic_launcher_settings))
.putExtra(Intent.EXTRA_SHORTCUT_INTENT, info.getIntent())
.putExtra(Intent.EXTRA_SHORTCUT_NAME, info.getShortLabel());
diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java
index 8f301066e4a..e4bd841111a 100644
--- a/src/com/android/settings/slices/CustomSliceRegistry.java
+++ b/src/com/android/settings/slices/CustomSliceRegistry.java
@@ -35,6 +35,7 @@
import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice;
import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
import com.android.settings.location.LocationSlice;
+import com.android.settings.media.AppVolumeSlice;
import com.android.settings.media.MediaOutputIndicatorSlice;
import com.android.settings.media.RemoteMediaSlice;
import com.android.settings.network.ProviderModelSlice;
@@ -293,6 +294,16 @@ public class CustomSliceRegistry {
.appendPath("always_on_display")
.build();
+ /**
+ * Backing Uri for the App Volume Slice.
+ */
+ public static Uri APP_VOLUME_SLICE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath("app_volume")
+ .build();
+
@VisibleForTesting
static final Map> sUriToSlice;
@@ -307,6 +318,7 @@ public class CustomSliceRegistry {
sUriToSlice.put(ALWAYS_ON_SLICE_URI, AlwaysOnDisplaySlice.class);
sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class);
+ sUriToSlice.put(APP_VOLUME_SLICE_URI, AppVolumeSlice.class);
// Slices for contextual card.
sUriToSlice.put(FACE_ENROLL_SLICE_URI, FaceSetupSlice.class);
diff --git a/src/com/android/settings/slices/SlicesDatabaseHelper.java b/src/com/android/settings/slices/SlicesDatabaseHelper.java
index cad045e549b..3acbf3284e1 100644
--- a/src/com/android/settings/slices/SlicesDatabaseHelper.java
+++ b/src/com/android/settings/slices/SlicesDatabaseHelper.java
@@ -246,6 +246,6 @@ private boolean isLocaleIndexed() {
@VisibleForTesting
String getBuildTag() {
- return Build.FINGERPRINT;
+ return Build.VERSION.INCREMENTAL;
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/sound/CustomVibrationPreferenceController.java b/src/com/android/settings/sound/CustomVibrationPreferenceController.java
new file mode 100644
index 00000000000..c53c2aa33d1
--- /dev/null
+++ b/src/com/android/settings/sound/CustomVibrationPreferenceController.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 Yet Another AOSP 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.sound;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.sound.CustomVibrationPreferenceController;
+import com.android.settings.Utils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import com.crdroid.settings.preferences.CustomSeekBarPreference;
+
+/**
+ * This class allows choosing a vibration pattern while ringing
+ */
+public class CustomVibrationPreferenceController extends AbstractPreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final String KEY = "custom_vibration_pattern";
+ private static final String KEY_CUSTOM_VIB1 = "custom_vibration_pattern1";
+ private static final String KEY_CUSTOM_VIB2 = "custom_vibration_pattern2";
+ private static final String KEY_CUSTOM_VIB3 = "custom_vibration_pattern3";
+
+ private CustomSeekBarPreference mCustomVib1;
+ private CustomSeekBarPreference mCustomVib2;
+ private CustomSeekBarPreference mCustomVib3;
+
+ private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ .build();
+
+ private static final int[] SEVEN_ELEMENTS_VIBRATION_AMPLITUDE = {
+ 0, // No delay before starting
+ 255, // Vibrate full amplitude
+ 0, // No amplitude while waiting
+ 255,
+ 0,
+ 255,
+ 0,
+ };
+
+ public CustomVibrationPreferenceController(Context context) {
+ super(context);
+ }
+
+ private static class VibrationEffectProxy {
+ public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
+ return VibrationEffect.createWaveform(timings, amplitudes, repeat);
+ }
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mCustomVib1 = (CustomSeekBarPreference) screen.findPreference(KEY_CUSTOM_VIB1);
+ mCustomVib2 = (CustomSeekBarPreference) screen.findPreference(KEY_CUSTOM_VIB2);
+ mCustomVib3 = (CustomSeekBarPreference) screen.findPreference(KEY_CUSTOM_VIB3);
+ updateCustomVibPreferences();
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mCustomVib1) {
+ updateCustomVib(0, (Integer) newValue);
+ return true;
+ } else if (preference == mCustomVib2) {
+ updateCustomVib(1, (Integer) newValue);
+ return true;
+ } else if (preference == mCustomVib3) {
+ updateCustomVib(2, (Integer) newValue);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return Utils.isVoiceCapable(mContext);
+ }
+
+ private void updateCustomVibPreferences() {
+ String value = Settings.System.getString(mContext.getContentResolver(),
+ Settings.System.CUSTOM_RINGTONE_VIBRATION_PATTERN);
+ if (value != null) {
+ String[] customPattern = value.split(",", 3);
+ mCustomVib1.setValue(Integer.parseInt(customPattern[0]));
+ mCustomVib2.setValue(Integer.parseInt(customPattern[1]));
+ mCustomVib3.setValue(Integer.parseInt(customPattern[2]));
+ } else { // set default
+ mCustomVib1.setValue(0);
+ mCustomVib2.setValue(800);
+ mCustomVib3.setValue(800);
+ Settings.System.putString(mContext.getContentResolver(),
+ Settings.System.CUSTOM_RINGTONE_VIBRATION_PATTERN, "0,800,800");
+ }
+ mCustomVib1.setOnPreferenceChangeListener(this);
+ mCustomVib2.setOnPreferenceChangeListener(this);
+ mCustomVib3.setOnPreferenceChangeListener(this);
+ }
+
+ private void updateCustomVib(int index, int value) {
+ String[] customPattern = Settings.System.getString(mContext.getContentResolver(),
+ Settings.System.CUSTOM_RINGTONE_VIBRATION_PATTERN).split(",", 3);
+ customPattern[index] = String.valueOf(value);
+ Settings.System.putString(mContext.getContentResolver(),
+ Settings.System.CUSTOM_RINGTONE_VIBRATION_PATTERN, String.join(
+ ",", customPattern[0], customPattern[1], customPattern[2]));
+ previewPattern();
+ }
+
+ private void previewPattern() {
+ Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ VibrationEffect effect;
+ VibrationEffectProxy vibrationEffectProxy = new VibrationEffectProxy();
+ String[] customVib = Settings.System.getString(
+ mContext.getContentResolver(),
+ Settings.System.CUSTOM_RINGTONE_VIBRATION_PATTERN).split(",", 3);
+ long[] customVibPattern = {
+ 0, // No delay before starting
+ Long.parseLong(customVib[0]), // How long to vibrate
+ 400, // Delay
+ Long.parseLong(customVib[1]), // How long to vibrate
+ 400, // Delay
+ Long.parseLong(customVib[2]), // How long to vibrate
+ 400, // How long to wait before vibrating again
+ };
+ effect = vibrationEffectProxy.createWaveform(customVibPattern,
+ SEVEN_ELEMENTS_VIBRATION_AMPLITUDE, -1);
+ vibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
+ }
+
+}
diff --git a/src/com/android/settings/sound/CustomVibrationPreferenceFragment.java b/src/com/android/settings/sound/CustomVibrationPreferenceFragment.java
new file mode 100644
index 00000000000..290fd4cb448
--- /dev/null
+++ b/src/com/android/settings/sound/CustomVibrationPreferenceFragment.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 Yet Another AOSP 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.sound;
+
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Settings for custom ringtone vibration pattern
+ */
+public class CustomVibrationPreferenceFragment extends DashboardFragment {
+
+ private static final String TAG = "CustomVibrationPreferenceFragment";
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.CRDROID_SETTINGS;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.custom_vibration_pattern;
+ }
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ final List controllers = new ArrayList<>();
+ controllers.add(new CustomVibrationPreferenceController(context));
+ return controllers;
+ }
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
index af4fc176940..957502f8ca4 100644
--- a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
@@ -35,6 +35,7 @@ import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.core.SubSettingLauncher
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
+import com.android.settings.fuelgauge.BatteryOptimizeUtils
import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController
import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry
import com.android.settingslib.spa.widget.preference.Preference
@@ -43,6 +44,7 @@ import com.android.settingslib.spaprivileged.model.app.installed
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.model.app.userId
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -63,7 +65,7 @@ fun AppBatteryPreference(app: ApplicationInfo) {
}
private class AppBatteryPresenter(private val context: Context, private val app: ApplicationInfo) {
- private var batteryDiffEntryState: LoadingState
+ private var batteryDiffEntryState: LoadingState>
by mutableStateOf(LoadingState.Loading)
@Composable
@@ -82,12 +84,17 @@ private class AppBatteryPresenter(private val context: Context, private val app:
}
}
- private suspend fun getBatteryDiffEntry(): BatteryDiffEntry? = withContext(Dispatchers.IO) {
- BatteryChartPreferenceController.getAppBatteryUsageData(
- context, app.packageName, app.userId
- ).also {
- Log.d(TAG, "loadBatteryDiffEntries():\n$it")
+ private suspend fun getBatteryDiffEntry(): Pair = withContext(Dispatchers.IO) {
+ val batteryDiffEntry = async {
+ BatteryChartPreferenceController.getAppBatteryUsageData(
+ context, app.packageName, app.userId
+ ).also {
+ Log.d(TAG, "loadBatteryDiffEntries():\n$it")
+ }
}
+ val optimizationMode = BatteryOptimizeUtils(context, app.uid, app.packageName)
+ .getAppOptimizationMode(false);
+ Pair(batteryDiffEntry.await(), optimizationMode)
}
val enabled = { batteryDiffEntryState is LoadingState.Done }
@@ -97,7 +104,31 @@ private class AppBatteryPresenter(private val context: Context, private val app:
batteryDiffEntryState.let { batteryDiffEntryState ->
when (batteryDiffEntryState) {
is LoadingState.Loading -> context.getString(R.string.summary_placeholder)
- is LoadingState.Done -> batteryDiffEntryState.result.getSummary()
+ is LoadingState.Done -> {
+ val optimizationMode = when (batteryDiffEntryState.result.second) {
+ BatteryOptimizeUtils.MODE_RESTRICTED ->
+ R.string.manager_battery_usage_restricted_title
+ BatteryOptimizeUtils.MODE_UNRESTRICTED ->
+ R.string.manager_battery_usage_unrestricted_title
+ BatteryOptimizeUtils.MODE_OPTIMIZED ->
+ R.string.manager_battery_usage_optimized_title
+ else -> 0
+ }
+ val b = StringBuilder()
+ val bde = batteryDiffEntryState.result.first
+ if (optimizationMode != 0) {
+ b.append(context.getString(optimizationMode))
+ }
+
+ val bdeSummary = bde.getSummary()
+ if (b.isNotEmpty() && bdeSummary.isNotEmpty()) {
+ b.append('\n')
+ b.append(bdeSummary)
+ return@let b.toString()
+ }
+
+ return@let bdeSummary
+ }
}
}
} else ""
@@ -111,7 +142,7 @@ private class AppBatteryPresenter(private val context: Context, private val app:
} ?: context.getString(R.string.no_battery_summary)
fun startActivity() {
- batteryDiffEntryState.resultOrNull?.run {
+ batteryDiffEntryState.resultOrNull?.first?.run {
startBatteryDetailPage()
return
}
diff --git a/src/com/android/settings/spa/preference/ComposePreference.kt b/src/com/android/settings/spa/preference/ComposePreference.kt
index 55a469c492f..9a4c6e4e279 100644
--- a/src/com/android/settings/spa/preference/ComposePreference.kt
+++ b/src/com/android/settings/spa/preference/ComposePreference.kt
@@ -18,6 +18,8 @@ package com.android.settings.spa.preference
import android.content.Context
import android.util.AttributeSet
+import android.view.View
+import android.widget.ImageView
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
@@ -55,7 +57,18 @@ open class ComposePreference @JvmOverloads constructor(
holder.isDividerAllowedAbove = false
holder.isDividerAllowedBelow = false
- (holder.itemView as ComposeView).apply {
+ val iconView = holder.findViewById(android.R.id.icon) as? ImageView
+ iconView?.let {
+ if (icon != null) {
+ it.visibility = View.VISIBLE
+ it.setImageDrawable(icon)
+ } else {
+ it.visibility = View.GONE
+ }
+ }
+
+ val composeView = holder.findViewById(R.id.compose_view) as? ComposeView
+ composeView?.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
SettingsTheme {
diff --git a/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java b/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java
index 868f10f3b50..7dc9ffce2e1 100644
--- a/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java
+++ b/src/com/android/settings/system/AdditionalSystemUpdatePreferenceController.java
@@ -29,9 +29,12 @@ public AdditionalSystemUpdatePreferenceController(Context context) {
@Override
public int getAvailabilityStatus() {
+ /*
return mContext.getResources().getBoolean(
com.android.settings.R.bool.config_additional_system_update_setting_enable)
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
+ */
+ return UNSUPPORTED_ON_DEVICE;
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/development/DevelopmentSystemPropertiesWrapper.java b/src/com/android/settings/system/DevelopmentSystemPropertiesWrapper.java
similarity index 98%
rename from src/com/android/settings/development/DevelopmentSystemPropertiesWrapper.java
rename to src/com/android/settings/system/DevelopmentSystemPropertiesWrapper.java
index e8a64d2beeb..26c28cd30f9 100644
--- a/src/com/android/settings/development/DevelopmentSystemPropertiesWrapper.java
+++ b/src/com/android/settings/system/DevelopmentSystemPropertiesWrapper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.development;
+package com.android.settings.system;
import android.annotation.NonNull;
import android.os.SystemProperties;
diff --git a/src/com/android/settings/development/GameDefaultFrameRatePreferenceController.java b/src/com/android/settings/system/GameDefaultFrameRatePreferenceController.java
similarity index 99%
rename from src/com/android/settings/development/GameDefaultFrameRatePreferenceController.java
rename to src/com/android/settings/system/GameDefaultFrameRatePreferenceController.java
index 00001fb4090..9c69378be7d 100644
--- a/src/com/android/settings/development/GameDefaultFrameRatePreferenceController.java
+++ b/src/com/android/settings/system/GameDefaultFrameRatePreferenceController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.development;
+package com.android.settings.system;
import android.app.IGameManagerService;
diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java
index 678b675e6ad..be688eec890 100644
--- a/src/com/android/settings/system/SystemDashboardFragment.java
+++ b/src/com/android/settings/system/SystemDashboardFragment.java
@@ -15,22 +15,34 @@
*/
package com.android.settings.system;
+import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
+import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
+import androidx.annotation.Nullable;
+
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.ArrayList;
+import java.util.List;
@SearchIndexable
public class SystemDashboardFragment extends DashboardFragment {
private static final String TAG = "SystemDashboardFrag";
+ private List mPreferenceControllers = new ArrayList<>();
@Override
public void onCreate(Bundle icicle) {
@@ -76,6 +88,25 @@ private int getVisiblePreferenceCount(PreferenceGroup group) {
return visibleCount;
}
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ if (Utils.isMonkeyRunning()) {
+ mPreferenceControllers = new ArrayList<>();
+ return null;
+ }
+ mPreferenceControllers = buildPreferenceControllers(context, getActivity(),
+ getSettingsLifecycle(), this);
+ return mPreferenceControllers;
+ }
+
+ private static List buildPreferenceControllers(Context context,
+ @Nullable Activity activity, @Nullable Lifecycle lifecycle,
+ @Nullable SystemDashboardFragment fragment) {
+ final List controllers = new ArrayList<>();
+ controllers.add(new GameDefaultFrameRatePreferenceController(context));
+ return controllers;
+ }
+
/**
* For Search.
*/
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.kt b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
index 87a402e2236..525b52681c3 100644
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.kt
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
@@ -40,9 +40,12 @@ open class SystemUpdatePreferenceController(context: Context, preferenceKey: Str
private lateinit var preference: Preference
override fun getAvailabilityStatus() =
+ /*
if (mContext.resources.getBoolean(R.bool.config_show_system_update_settings) &&
userManager.isAdminUser
) AVAILABLE else UNSUPPORTED_ON_DEVICE
+ */
+ UNSUPPORTED_ON_DEVICE
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index a0137df728f..70de64a91b1 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -465,7 +465,7 @@ public void startActivityForResult(Intent intent, int requestCode) {
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
int pos = 0;
if (!isCurrentUserAdmin() && (canSwitchUserNow() || Flags.newMultiuserSettingsUx())
- && !isCurrentUserGuest()) {
+ && !isCurrentUserGuest() && !mUserManager.isProfile()) {
String nickname = mUserManager.getUserName();
MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++,
getResources().getString(R.string.user_remove_user_menu, nickname));
diff --git a/src/com/android/settings/utils/InsetUtils.java b/src/com/android/settings/utils/InsetUtils.java
new file mode 100644
index 00000000000..fe3a71bc3d8
--- /dev/null
+++ b/src/com/android/settings/utils/InsetUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The LineageOS 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.view.View;
+
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+
+public class InsetUtils {
+ public static void applyWindowInsetsListener(final View rootView) {
+ ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> {
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
+
+ view.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+
+ return WindowInsetsCompat.CONSUMED;
+ });
+ }
+}
diff --git a/src/com/android/settings/utils/SensorPrivacyManagerHelper.kt b/src/com/android/settings/utils/SensorPrivacyManagerHelper.kt
index 3fe3ab0d50c..66211656276 100644
--- a/src/com/android/settings/utils/SensorPrivacyManagerHelper.kt
+++ b/src/com/android/settings/utils/SensorPrivacyManagerHelper.kt
@@ -50,7 +50,7 @@ open class SensorPrivacyManagerHelper(context: Context) :
}
init {
- sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!!
+ sensorPrivacyManager = (context.applicationContext ?: context).getSystemService(SensorPrivacyManager::class.java)!!
sensorPrivacyManager.addSensorPrivacyListener(context.mainExecutor, this)
}
diff --git a/src/com/android/settings/widget/EntityHeaderController.java b/src/com/android/settings/widget/EntityHeaderController.java
index c89b7bb4cd9..208be158c04 100644
--- a/src/com/android/settings/widget/EntityHeaderController.java
+++ b/src/com/android/settings/widget/EntityHeaderController.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -76,6 +77,7 @@ public class EntityHeaderController {
// Required for hearing aid devices.
private CharSequence mSecondSummary;
private String mPackageName;
+ private String mPackageNameReal;
private Intent mAppNotifPrefIntent;
@UserIdInt
private int mUid = UserHandle.USER_NULL;
@@ -134,6 +136,7 @@ public EntityHeaderController setIcon(Drawable icon) {
*/
public EntityHeaderController setIcon(ApplicationsState.AppEntry appEntry) {
mIcon = Utils.getBadgedIcon(mAppContext, appEntry.info);
+ mPackageNameReal = appEntry.info.packageName;
return this;
}
@@ -233,6 +236,18 @@ public View done(boolean rebindActions) {
if (iconView != null) {
iconView.setImageDrawable(mIcon);
iconView.setContentDescription(mIconContentDescription);
+ if (mPackageNameReal != null) {
+ iconView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PackageManager pm = v.getContext().getPackageManager();
+ Intent intent = pm.getLaunchIntentForPackage(mPackageNameReal);
+ if (intent == null)
+ return;
+ v.getContext().startActivity(intent);
+ }
+ });
+ }
}
setText(R.id.entity_header_title, mLabel);
setText(R.id.entity_header_summary, mSummary);
diff --git a/src/com/android/settings/widget/LoadingViewController.java b/src/com/android/settings/widget/LoadingViewController.java
index 66eebf387ba..53d8c7bddd3 100644
--- a/src/com/android/settings/widget/LoadingViewController.java
+++ b/src/com/android/settings/widget/LoadingViewController.java
@@ -128,6 +128,9 @@ public static void handleLoadingContainer(View loading, View content, View empty
}
private static void setViewShown(final View view, boolean shown, boolean animate) {
+ if (view == null) {
+ return;
+ }
if (animate) {
Animation animation = AnimationUtils.loadAnimation(view.getContext(),
shown ? android.R.anim.fade_in : android.R.anim.fade_out);
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 39c77a17924..809ee3e3807 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -73,6 +73,7 @@
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.utils.AndroidKeystoreAliasLoader;
import com.android.settings.wifi.details2.WifiPrivacyPreferenceController;
+import com.android.settings.wifi.details2.WifiPrivacyPreferenceController2;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settingslib.Utils;
import com.android.settingslib.utils.ThreadUtils;
@@ -151,9 +152,10 @@ public class WifiConfigController implements TextWatcher,
UNDESIRED_CERTIFICATE_MACRANDSAPSECRET
};
- // Should be the same index value as wifi_privacy_entries in arrays.xml
- @VisibleForTesting static final int PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC = 0;
- @VisibleForTesting static final int PRIVACY_SPINNER_INDEX_DEVICE_MAC = 1;
+ // Should be the same index value as wifi_privacy_entries_ext in arrays.xml
+ public static final int PRIVACY_PREF_INDEX_PER_CONNECTION_RANDOMIZED_MAC = 0;
+ public static final int PRIVACY_PREF_INDEX_PER_NETWORK_RANDOMIZED_MAC = 1;
+ public static final int PRIVACY_PREF_INDEX_DEVICE_MAC = 2;
// Should be the same index value as wifi_dhcp_entries in arrays.xml
@VisibleForTesting static final int DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_ENABLE = 0;
@@ -326,9 +328,9 @@ private void initWifiConfigController(AccessPoint accessPoint, int mode) {
? HIDDEN_NETWORK
: NOT_HIDDEN_NETWORK);
- mPrivacySettingsSpinner.setSelection(
- config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT
- ? PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC : PRIVACY_SPINNER_INDEX_DEVICE_MAC);
+ int selection = WifiPrivacyPreferenceController2
+ .translateWifiEntryPrivacyToPrefValue(config.macRandomizationSetting);
+ mPrivacySettingsSpinner.setSelection(selection);
mDhcpSettingsSpinner.setSelection(
config.isSendDhcpHostnameEnabled()
@@ -848,10 +850,8 @@ public WifiConfiguration getConfig() {
}
if (mPrivacySettingsSpinner != null) {
- config.macRandomizationSetting = mPrivacySettingsSpinner.getSelectedItemPosition()
- == PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC
- ? WifiConfiguration.RANDOMIZATION_PERSISTENT
- : WifiConfiguration.RANDOMIZATION_NONE;
+ config.macRandomizationSetting = WifiPrivacyPreferenceController2
+ .translatePrefValueToWifiConfigSetting(mPrivacySettingsSpinner.getSelectedItemPosition());
}
if (mDhcpSettingsSpinner != null) {
diff --git a/src/com/android/settings/wifi/WifiEntryPreference.java b/src/com/android/settings/wifi/WifiEntryPreference.java
index e5290f41fea..8177508da92 100644
--- a/src/com/android/settings/wifi/WifiEntryPreference.java
+++ b/src/com/android/settings/wifi/WifiEntryPreference.java
@@ -246,7 +246,13 @@ private StateListDrawable getFrictionStateListDrawable() {
// Fallback for platforms that do not need friction icon resources.
frictionSld = null;
}
- return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
+ if (frictionSld != null) {
+ StateListDrawable val = (StateListDrawable) frictionSld.getDrawable(0);
+ frictionSld.recycle();
+ return val;
+ } else {
+ return null;
+ }
}
/**
diff --git a/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java b/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java
new file mode 100644
index 00000000000..5368d78e443
--- /dev/null
+++ b/src/com/android/settings/wifi/WifiTimeoutPreferenceController.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Calyx Institute
+ *
+ * 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.wifi;
+
+import android.content.Context;
+import android.os.UserManager;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class WifiTimeoutPreferenceController extends BasePreferenceController implements
+ PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+ private static final String TAG = "WifiTimeoutPrefCtrl";
+
+ public static final int FALLBACK_WIFI_TIMEOUT_VALUE = 0;
+
+ private final String mWifiTimeoutKey;
+
+ protected WifiManager mWifiManager;
+
+ public WifiTimeoutPreferenceController(Context context, String key) {
+ super(context, key);
+ mWifiTimeoutKey = key;
+
+ mWifiManager = context.getSystemService(WifiManager.class);
+ if (mWifiManager == null) {
+ Log.e(TAG, "Wifi is not supported on this device");
+ return;
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mWifiManager != null) {
+ return UserManager.get(mContext).isAdminUser() ? AVAILABLE : DISABLED_FOR_USER;
+ }
+ return UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return mWifiTimeoutKey;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final ListPreference timeoutListPreference = (ListPreference) preference;
+ final long currentTimeout = Settings.Global.getLong(mContext.getContentResolver(),
+ Settings.Global.WIFI_OFF_TIMEOUT, FALLBACK_WIFI_TIMEOUT_VALUE);
+ timeoutListPreference.setValue(String.valueOf(currentTimeout));
+ updateTimeoutPreferenceDescription(timeoutListPreference,
+ Long.parseLong(timeoutListPreference.getValue()));
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ try {
+ long value = Long.parseLong((String) newValue);
+ Settings.Global.putLong(mContext.getContentResolver(),
+ Settings.Global.WIFI_OFF_TIMEOUT, value);
+ updateTimeoutPreferenceDescription((ListPreference) preference, value);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "could not persist wifi timeout setting", e);
+ }
+ return true;
+ }
+
+ public static CharSequence getTimeoutDescription(
+ long currentTimeout, CharSequence[] entries, CharSequence[] values) {
+ if (currentTimeout < 0 || entries == null || values == null
+ || values.length != entries.length) {
+ return null;
+ }
+
+ for (int i = 0; i < values.length; i++) {
+ long timeout = Long.parseLong(values[i].toString());
+ if (currentTimeout == timeout) {
+ return entries[i];
+ }
+ }
+ return null;
+ }
+
+ private void updateTimeoutPreferenceDescription(ListPreference preference,
+ long currentTimeout) {
+ final CharSequence[] entries = preference.getEntries();
+ final CharSequence[] values = preference.getEntryValues();
+ final CharSequence timeoutDescription = getTimeoutDescription(
+ currentTimeout, entries, values);
+ String summary = "";
+ if (timeoutDescription != null) {
+ if (currentTimeout != 0)
+ summary = mContext.getString(R.string.wifi_timeout_summary_auto, timeoutDescription);
+ else
+ summary = mContext.getString(R.string.wifi_timeout_summary);
+ }
+ preference.setSummary(summary);
+ }
+}
diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
index 8aab0e1fb6c..63c3f3aa7a8 100644
--- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
+++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
@@ -23,6 +23,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
@@ -213,6 +214,9 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (!mIsUiRestricted && isEditable()) {
MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
+ item.setIconTintList(ColorStateList.valueOf(
+ com.android.settingslib.Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.colorControlNormal)));
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
super.onCreateOptionsMenu(menu, inflater);
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index a8d7f417a4a..4e2ae9b58b9 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -116,6 +116,9 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
private static final String TAG = "WifiDetailsPrefCtrl2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String WIFI_SHARING_KEY_ALIAS = "wifi_sharing_auth_key";
+ private static final int MAX_UNLOCK_SECONDS = 60;
+
@VisibleForTesting
static final String KEY_HEADER = "connection_header";
@VisibleForTesting
@@ -758,7 +761,7 @@ private void refreshWifiType() {
}
private int getMacAddressTitle() {
- if (mWifiEntry.getPrivacy() == WifiEntry.PRIVACY_RANDOMIZED_MAC) {
+ if (mWifiEntry.getPrivacy() != WifiEntry.PRIVACY_DEVICE_MAC) {
return mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED
? R.string.wifi_advanced_randomized_mac_address_title
: R.string.wifi_advanced_randomized_mac_address_disconnected_title;
@@ -980,6 +983,11 @@ private void launchWifiDppConfiguratorActivity() {
* Share the wifi network with QR code.
*/
private void shareNetwork() {
+ if (WifiDppUtils.isUnlockedWithinSeconds(WIFI_SHARING_KEY_ALIAS, MAX_UNLOCK_SECONDS)) {
+ // skip the auth dialog if unlocked last minute
+ launchWifiDppConfiguratorActivity();
+ return;
+ }
WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorActivity());
}
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
index 8888f0dacf5..cb859abdec6 100644
--- a/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
@@ -94,8 +94,8 @@ fun WifiPrivacyPage(wifiEntry: WifiEntry) {
) {
Column {
val title = stringResource(id = R.string.wifi_privacy_mac_settings)
- val wifiPrivacyEntries = stringArrayResource(R.array.wifi_privacy_entries)
- val wifiPrivacyValues = stringArrayResource(R.array.wifi_privacy_values)
+ val wifiPrivacyEntries = stringArrayResource(R.array.wifi_privacy_entries_ext)
+ val wifiPrivacyValues = stringArrayResource(R.array.wifi_privacy_values_ext)
val textsSelectedId = rememberSaveable { mutableIntStateOf(wifiEntry.privacy) }
val dataList = remember {
wifiPrivacyEntries.mapIndexed { index, text ->
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
index 0c67c04622e..cb20ea9c77c 100644
--- a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -30,6 +31,10 @@
import com.android.wifi.flags.Flags;
import com.android.wifitrackerlib.WifiEntry;
+import static com.android.settings.wifi.WifiConfigController.PRIVACY_PREF_INDEX_DEVICE_MAC;
+import static com.android.settings.wifi.WifiConfigController.PRIVACY_PREF_INDEX_PER_CONNECTION_RANDOMIZED_MAC;
+import static com.android.settings.wifi.WifiConfigController.PRIVACY_PREF_INDEX_PER_NETWORK_RANDOMIZED_MAC;
+
/**
* A controller that controls whether the Wi-Fi network is mac randomized or not.
*/
@@ -95,9 +100,6 @@ int getRandomizationValue() {
return mWifiEntry.getPrivacy();
}
- private static final int PREF_RANDOMIZATION_PERSISTENT = 0;
- private static final int PREF_RANDOMIZATION_NONE = 1;
-
/**
* Translates a WifiEntry.Privacy value to the matching preference index value.
*
@@ -105,8 +107,13 @@ int getRandomizationValue() {
* @return index value of preference
*/
public static int translateWifiEntryPrivacyToPrefValue(@WifiEntry.Privacy int privacy) {
- return (privacy == WifiEntry.PRIVACY_RANDOMIZED_MAC)
- ? PREF_RANDOMIZATION_PERSISTENT : PREF_RANDOMIZATION_NONE;
+ Log.d("WifiMacRnd", "translateMacRandomizedValueToPrefValue called from", new Throwable());
+ return switch (privacy) {
+ case WifiEntry.PRIVACY_RANDOMIZATION_ALWAYS -> PRIVACY_PREF_INDEX_PER_CONNECTION_RANDOMIZED_MAC;
+ case WifiEntry.PRIVACY_RANDOMIZED_MAC -> PRIVACY_PREF_INDEX_PER_NETWORK_RANDOMIZED_MAC;
+ case WifiEntry.PRIVACY_DEVICE_MAC -> PRIVACY_PREF_INDEX_DEVICE_MAC;
+ default -> PRIVACY_PREF_INDEX_DEVICE_MAC;
+ };
}
/**
@@ -116,8 +123,13 @@ public static int translateWifiEntryPrivacyToPrefValue(@WifiEntry.Privacy int pr
* @return WifiConfiguration.MacRandomizationSetting value
*/
public static int translatePrefValueToWifiConfigSetting(int prefMacRandomized) {
- return (prefMacRandomized == PREF_RANDOMIZATION_PERSISTENT)
- ? WifiConfiguration.RANDOMIZATION_AUTO : WifiConfiguration.RANDOMIZATION_NONE;
+ Log.d("WifiMacRnd", "translatePrefValueToWifiConfigSetting called from", new Throwable());
+ return switch (prefMacRandomized) {
+ case PRIVACY_PREF_INDEX_DEVICE_MAC -> WifiConfiguration.RANDOMIZATION_NONE;
+ case PRIVACY_PREF_INDEX_PER_NETWORK_RANDOMIZED_MAC -> WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ case PRIVACY_PREF_INDEX_PER_CONNECTION_RANDOMIZED_MAC -> WifiConfiguration.RANDOMIZATION_ALWAYS;
+ default -> WifiConfiguration.RANDOMIZATION_ALWAYS;
+ };
}
private void updateSummary(ListPreference preference, int macRandomized) {
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java b/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java
index 5d73fa464f1..2a5e8c6bace 100644
--- a/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java
+++ b/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java
@@ -15,6 +15,8 @@
*/
package com.android.settings.wifi.p2p;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -92,8 +94,7 @@ public void onPause() {
@Override
public boolean isAvailable() {
- // Always show preference.
- return true;
+ return mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI_DIRECT);
}
@Override
public String getPreferenceKey() {
diff --git a/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java
index 58ad37bddcb..7558b946bee 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherAutoOffPreferenceController.java
@@ -1,5 +1,7 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2022 Project Kaleidoscope
+ * Copyright (C) 2022 FlamingoOS Project
+ * Copyright (C) 2024 crDroid Android Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,9 +23,11 @@
import android.net.wifi.WifiManager;
import androidx.annotation.VisibleForTesting;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
-import androidx.preference.TwoStatePreference;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wifi.repository.WifiHotspotRepository;
@@ -32,10 +36,11 @@ public class WifiTetherAutoOffPreferenceController extends BasePreferenceControl
Preference.OnPreferenceChangeListener {
private final WifiManager mWifiManager;
- private boolean mSettingsOn;
@VisibleForTesting
boolean mNeedShutdownSecondarySap;
+ private ListPreference mPreference;
+
public WifiTetherAutoOffPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
WifiHotspotRepository wifiHotspotRepository = FeatureFactory.getFeatureFactory()
@@ -52,27 +57,63 @@ public int getAvailabilityStatus() {
}
@Override
- public void updateState(Preference preference) {
- SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
- mSettingsOn = softApConfiguration.isAutoShutdownEnabled();
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ updateDisplay();
+ }
- ((TwoStatePreference) preference).setChecked(mSettingsOn);
+ private long getAutoOffTimeout() {
+ final SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
+ final boolean settingsOn = softApConfiguration.isAutoShutdownEnabled();
+ return settingsOn ? softApConfiguration.getShutdownTimeoutMillis() : 0;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
- boolean settingsOn = (Boolean) newValue;
- SoftApConfiguration.Builder configBuilder =
+ final long timeout;
+ try {
+ timeout = Long.parseLong((String) newValue);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ final SoftApConfiguration.Builder configBuilder =
new SoftApConfiguration.Builder(mWifiManager.getSoftApConfiguration());
- configBuilder.setAutoShutdownEnabled(settingsOn);
+ setShutdownTimeout(configBuilder, timeout);
if (mNeedShutdownSecondarySap) {
- configBuilder.setBridgedModeOpportunisticShutdownEnabled(settingsOn);
+ configBuilder.setBridgedModeOpportunisticShutdownEnabled(timeout > 0);
}
- mSettingsOn = settingsOn;
return mWifiManager.setSoftApConfiguration(configBuilder.build());
}
- public boolean isEnabled() {
- return mSettingsOn;
+ public void updateConfig(SoftApConfiguration.Builder builder) {
+ if (builder == null) return;
+ final long timeout = getAutoOffTimeout();
+ setShutdownTimeout(builder, timeout);
+ }
+
+ private void setShutdownTimeout(SoftApConfiguration.Builder builder, long timeout) {
+ builder.setAutoShutdownEnabled(timeout > 0);
+ if (timeout > 0) {
+ builder.setShutdownTimeoutMillis(timeout);
+ }
+ updateDisplay(timeout);
+ }
+
+ public void updateDisplay() {
+ updateDisplay(getAutoOffTimeout());
+ }
+
+ private void updateDisplay(long timeout) {
+ if (mPreference != null) {
+ mPreference.setValue(String.valueOf(timeout));
+ if (timeout > 0) {
+ int index = mPreference.findIndexOfValue(String.valueOf(timeout));
+ mPreference.setSummary(mPreference.getEntries()[index]);
+ } else {
+ mPreference.setSummary(mContext.getResources().getString(
+ R.string.wifi_hotspot_auto_off_summary));
+ }
+ }
}
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherClientManager.java b/src/com/android/settings/wifi/tether/WifiTetherClientManager.java
new file mode 100644
index 00000000000..267c7ef4c29
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherClientManager.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2022 Project Kaleidoscope
+ *
+ * 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.wifi.tether;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.net.MacAddress;
+import android.net.TetheringManager;
+import android.net.TetheredClient;
+import android.net.wifi.SoftApCapability;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settingslib.widget.FooterPreference;
+
+import com.android.settings.wifi.tether.preference.WifiTetherClientLimitPreference;
+
+public class WifiTetherClientManager extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
+ WifiManager.SoftApCallback, TetheringManager.TetheringEventCallback {
+
+ private static final String TAG = "WifiTetherClientManager";
+
+ private static final String PREF_KEY_CLIENT_LIMIT = "client_limit";
+ private static final String PREF_KEY_BLOCKED_CLIENT_LIST = "blocked_client_list";
+ private static final String PREF_KEY_CONNECTED_CLIENT_LIST = "connected_client_list";
+ private static final String PREF_KEY_FOOTER = "footer";
+
+ private WifiManager mWifiManager;
+ private TetheringManager mTetheringManager;
+
+ private WifiTetherClientLimitPreference mClientLimitPref;
+ private PreferenceCategory mConnectedClientsPref;
+ private PreferenceCategory mBlockedClientsPref;
+ private FooterPreference mFooterPref;
+
+ private boolean mSupportForceDisconnect;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mWifiManager = getSystemService(WifiManager.class);
+ mTetheringManager = getSystemService(TetheringManager.class);
+
+ mWifiManager.registerSoftApCallback(getActivity().getMainExecutor(), this);
+
+ addPreferencesFromResource(R.xml.hotspot_client_manager);
+
+ getActivity().setTitle(R.string.wifi_hotspot_client_manager_title);
+
+ mClientLimitPref = findPreference(PREF_KEY_CLIENT_LIMIT);
+ mConnectedClientsPref = findPreference(PREF_KEY_CONNECTED_CLIENT_LIST);
+ mBlockedClientsPref = findPreference(PREF_KEY_BLOCKED_CLIENT_LIST);
+ mFooterPref = findPreference(PREF_KEY_FOOTER);
+
+ mClientLimitPref.setOnPreferenceChangeListener(this);
+
+ updateBlockedClients();
+ updatePreferenceVisible();
+ }
+
+ @Override
+ public void onCapabilityChanged(@NonNull SoftApCapability softApCapability) {
+ mSupportForceDisconnect =
+ softApCapability.areFeaturesSupported(
+ SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT);
+ mWifiManager.unregisterSoftApCallback(this);
+
+ if (mSupportForceDisconnect) {
+ mClientLimitPref.setMin(1);
+ mClientLimitPref.setMax(softApCapability.getMaxSupportedClients());
+ final SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
+ final int maxNumberOfClients = softApConfiguration.getMaxNumberOfClients();
+ mClientLimitPref.setValue(maxNumberOfClients, false);
+ }
+ updatePreferenceVisible();
+ }
+
+ private void updatePreferenceVisible() {
+ if (mBlockedClientsPref == null || mClientLimitPref == null ||
+ mConnectedClientsPref == null || mFooterPref == null) return;
+ boolean hasConnectedClient = mConnectedClientsPref.getPreferenceCount() > 0;
+ boolean hasBlockedClient = mBlockedClientsPref.getPreferenceCount() > 0;
+ mClientLimitPref.setVisible(mSupportForceDisconnect);
+ mBlockedClientsPref.setVisible(mSupportForceDisconnect && hasBlockedClient);
+ mConnectedClientsPref.setVisible(hasConnectedClient);
+ mFooterPref.setVisible(!hasBlockedClient && !hasConnectedClient);
+ }
+
+ private void updateBlockedClients() {
+ final SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
+ final List blockedClientList = softApConfiguration.getBlockedClientList();
+ mBlockedClientsPref.removeAll();
+ for (MacAddress mac : blockedClientList) {
+ BlockedClientPreference preference = new BlockedClientPreference(getActivity(), mac);
+ preference.setOnPreferenceClickListener(this);
+ mBlockedClientsPref.addPreference(preference);
+ }
+ updatePreferenceVisible();
+ }
+
+ @Override
+ public void onClientsChanged(Collection clients) {
+ mConnectedClientsPref.removeAll();
+ for (TetheredClient client : clients) {
+ if (client.getTetheringType() != TetheringManager.TETHERING_WIFI) {
+ continue;
+ }
+ ConnectedClientPreference preference =
+ new ConnectedClientPreference(getActivity(), client);
+ preference.setOnPreferenceClickListener(this);
+ mConnectedClientsPref.addPreference(preference);
+ }
+ updatePreferenceVisible();
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (mSupportForceDisconnect) {
+ if (preference instanceof ConnectedClientPreference) {
+ showBlockClientDialog(
+ ((ConnectedClientPreference)preference).getMacAddress(),
+ preference.getTitle());
+ return true;
+ } else if (preference instanceof BlockedClientPreference) {
+ showUnblockClientDialog(((BlockedClientPreference)preference).getMacAddress());
+ return true;
+ }
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mClientLimitPref) {
+ int value = (int) newValue;
+ SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
+ SoftApConfiguration newSoftApConfiguration =
+ new SoftApConfiguration.Builder(softApConfiguration)
+ .setMaxNumberOfClients(value)
+ .build();
+ return mWifiManager.setSoftApConfiguration(newSoftApConfiguration);
+ }
+ return false;
+ }
+
+ private void blockClient(MacAddress mac, boolean isBlock) {
+ final SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
+ final List blockedClientList = softApConfiguration.getBlockedClientList();
+ if (isBlock) {
+ if (blockedClientList.contains(mac)) return;
+ blockedClientList.add(mac);
+ } else {
+ if (!blockedClientList.contains(mac)) return;
+ blockedClientList.remove(mac);
+ }
+ SoftApConfiguration newSoftApConfiguration =
+ new SoftApConfiguration.Builder(softApConfiguration)
+ .setBlockedClientList(blockedClientList)
+ .build();
+ mWifiManager.setSoftApConfiguration(newSoftApConfiguration);
+ updateBlockedClients();
+ }
+
+ private void showBlockClientDialog(MacAddress mac, CharSequence deviceName) {
+ final Activity activity = getActivity();
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.wifi_hotspot_block_client_dialog_title)
+ .setMessage(activity.getString(
+ R.string.wifi_hotspot_block_client_dialog_text, deviceName))
+ .setPositiveButton(android.R.string.ok,
+ (dialog, which) -> {
+ blockClient(mac, true);
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .create().show();
+ }
+
+ private void showUnblockClientDialog(MacAddress mac) {
+ final Activity activity = getActivity();
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.wifi_hotspot_unblock_client_dialog_title)
+ .setMessage(activity.getString(
+ R.string.wifi_hotspot_unblock_client_dialog_text, mac.toString()))
+ .setPositiveButton(android.R.string.ok,
+ (dialog, which) -> {
+ blockClient(mac, false);
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .create().show();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mTetheringManager.registerTetheringEventCallback(getActivity().getMainExecutor(), this);
+ }
+
+ @Override
+ public void onStop() {
+ mTetheringManager.unregisterTetheringEventCallback(this);
+ super.onStop();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.WIFI_TETHER_SETTINGS;
+ }
+
+ private class ConnectedClientPreference extends Preference {
+ private MacAddress mMacAddress;
+
+ public ConnectedClientPreference(Context context, TetheredClient client) {
+ super(context);
+ mMacAddress = client.getMacAddress();
+
+ String hostName = null;
+ String macAddress = client.getMacAddress().toString();
+
+ for (TetheredClient.AddressInfo addressInfo : client.getAddresses()) {
+ if (!TextUtils.isEmpty(addressInfo.getHostname())) {
+ hostName = addressInfo.getHostname();
+ break;
+ }
+ }
+
+ setKey(macAddress);
+ if (!TextUtils.isEmpty(hostName)) {
+ setTitle(hostName);
+ setSummary(macAddress);
+ } else {
+ setTitle(macAddress);
+ }
+ }
+
+ public MacAddress getMacAddress() {
+ return mMacAddress;
+ }
+ }
+
+ private class BlockedClientPreference extends Preference {
+ private MacAddress mMacAddress;
+
+ public BlockedClientPreference(Context context, MacAddress mac) {
+ super(context);
+ mMacAddress = mac;
+ setKey(mac.toString());
+ setTitle(mac.toString());
+ }
+
+ public MacAddress getMacAddress() {
+ return mMacAddress;
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherClientManagerPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherClientManagerPreferenceController.java
new file mode 100644
index 00000000000..b386152d5db
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherClientManagerPreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 Project Kaleidoscope
+ *
+ * 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.wifi.tether;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.MacAddress;
+import android.net.wifi.SoftApCapability;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
+import android.util.FeatureFlagUtils;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.FeatureFlags;
+import com.android.settings.wifi.tether.WifiTetherBasePreferenceController;
+
+import java.util.List;
+
+public class WifiTetherClientManagerPreferenceController extends WifiTetherBasePreferenceController
+ implements WifiManager.SoftApCallback {
+
+ public static final String DEDUP_POSTFIX = "_2";
+ public static final String PREF_KEY = "wifi_tether_client_manager";
+
+ private boolean mSupportForceDisconnect;
+
+ public WifiTetherClientManagerPreferenceController(Context context,
+ WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) {
+ super(context, listener);
+
+ mWifiManager.registerSoftApCallback(context.getMainExecutor(), this);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)
+ ? PREF_KEY + DEDUP_POSTFIX : PREF_KEY;
+ }
+
+ @Override
+ public void onCapabilityChanged(@NonNull SoftApCapability softApCapability) {
+ mSupportForceDisconnect =
+ softApCapability.areFeaturesSupported(
+ SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT);
+ mWifiManager.unregisterSoftApCallback(this);
+ updateDisplay();
+ }
+
+ @Override
+ public void updateDisplay() {
+ if (mPreference != null) {
+ if (mSupportForceDisconnect) {
+ mPreference.setSummary(R.string.wifi_hotspot_client_manager_summary);
+ } else {
+ mPreference.setSummary(R.string.wifi_hotspot_client_manager_list_only_summary);
+ }
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ return true;
+ }
+
+ public void updateConfig(SoftApConfiguration.Builder builder) {
+ if (builder == null || !mSupportForceDisconnect) return;
+ final SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
+ final int maxNumberOfClients = softApConfiguration.getMaxNumberOfClients();
+ final List blockedClientList = softApConfiguration.getBlockedClientList();
+ builder.setMaxNumberOfClients(maxNumberOfClients)
+ .setBlockedClientList(blockedClientList);
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherHiddenSsidPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherHiddenSsidPreferenceController.java
new file mode 100644
index 00000000000..a4d4667767c
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherHiddenSsidPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 Project Kaleidoscope
+ *
+ * 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.wifi.tether;
+
+import android.content.Context;
+import android.net.wifi.SoftApConfiguration;
+import android.util.FeatureFlagUtils;
+
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreferenceCompat;
+
+import com.android.settings.R;
+import com.android.settings.core.FeatureFlags;
+import com.android.settings.wifi.tether.WifiTetherBasePreferenceController;
+
+/**
+ * This controller helps to manage the state of hide SSID switch preference.
+ */
+public class WifiTetherHiddenSsidPreferenceController extends
+ WifiTetherBasePreferenceController {
+
+ public static final String DEDUP_POSTFIX = "_2";
+ public static final String PREF_KEY = "wifi_tether_hidden_ssid";
+
+ private boolean mHiddenSsid;
+
+ public WifiTetherHiddenSsidPreferenceController(Context context,
+ WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) {
+ super(context, listener);
+
+ if (mWifiManager != null) {
+ final SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
+ if (config != null) {
+ mHiddenSsid = config.isHiddenSsid();
+ }
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)
+ ? PREF_KEY + DEDUP_POSTFIX : PREF_KEY;
+ }
+
+ @Override
+ public void updateDisplay() {
+ if (mPreference == null) {
+ return;
+ }
+ ((SwitchPreferenceCompat) mPreference).setChecked(mHiddenSsid);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mHiddenSsid = (Boolean) newValue;
+ if (mListener != null) {
+ mListener.onTetherConfigUpdated(this);
+ }
+ return true;
+ }
+
+ public boolean isHiddenSsidEnabled() {
+ return mHiddenSsid;
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
index 7f386fdeebc..76c0e50b564 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
@@ -91,7 +91,7 @@ private boolean is5GhzBandSupported() {
if (mWifiManager == null) {
return false;
}
- if (!mWifiManager.is5GHzBandSupported() || mWifiManager.getCountryCode() == null) {
+ if (!mWifiManager.is5GHzBandSupported()) {
return false;
}
return true;
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
index 6f59411cd31..5f43b363a5f 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
@@ -87,7 +87,7 @@ public void updateDisplay() {
// capability of WPA3 hotspot callback will update the preference list here, add null point
// checking to avoid the mPreference is not ready when the fragment is loading for settings
// keyword searching only.
- if (mPreference == null) {
+ if (mPreference == null || mShouldHidePreference) {
return;
}
final ListPreference preference = (ListPreference) mPreference;
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index 980dee57753..c5a4444a89a 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -49,6 +49,9 @@
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
+import com.android.settings.wifi.tether.WifiTetherClientManagerPreferenceController;
+import com.android.settings.wifi.tether.WifiTetherHiddenSsidPreferenceController;
+
import java.util.ArrayList;
import java.util.List;
@@ -77,6 +80,12 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
static final String KEY_WIFI_HOTSPOT_SPEED = "wifi_hotspot_speed";
@VisibleForTesting
static final String KEY_INSTANT_HOTSPOT = "wifi_hotspot_instant";
+ @VisibleForTesting
+ static final String KEY_WIFI_TETHER_CLIENT_MANAGER =
+ WifiTetherClientManagerPreferenceController.PREF_KEY;
+ @VisibleForTesting
+ static final String KEY_WIFI_TETHER_HIDDEN_SSID =
+ WifiTetherHiddenSsidPreferenceController.PREF_KEY;
@VisibleForTesting
SettingsMainSwitchBar mMainSwitchBar;
@@ -91,6 +100,9 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController;
@VisibleForTesting
WifiTetherAutoOffPreferenceController mWifiTetherAutoOffPreferenceController;
+ private WifiTetherClientManagerPreferenceController mClientPrefController;
+ @VisibleForTesting
+ WifiTetherHiddenSsidPreferenceController mHiddenSsidPrefController;
@VisibleForTesting
boolean mUnavailable;
@@ -200,6 +212,8 @@ public void onAttach(Context context) {
mMaxCompatibilityPrefController =
use(WifiTetherMaximizeCompatibilityPreferenceController.class);
mWifiTetherAutoOffPreferenceController = use(WifiTetherAutoOffPreferenceController.class);
+ mClientPrefController = use(WifiTetherClientManagerPreferenceController.class);
+ mHiddenSsidPrefController = use(WifiTetherHiddenSsidPreferenceController.class);
}
@Override
@@ -284,6 +298,8 @@ private static List buildPreferenceControllers(Con
controllers.add(
new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF));
controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener));
+ controllers.add(new WifiTetherClientManagerPreferenceController(context, listener));
+ controllers.add(new WifiTetherHiddenSsidPreferenceController(context, listener));
return controllers;
}
@@ -328,8 +344,9 @@ SoftApConfiguration buildNewConfig() {
if (!mWifiTetherViewModel.isSpeedFeatureAvailable()) {
mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder);
}
- configBuilder.setAutoShutdownEnabled(
- mWifiTetherAutoOffPreferenceController.isEnabled());
+ mWifiTetherAutoOffPreferenceController.updateConfig(configBuilder);
+ mClientPrefController.updateConfig(configBuilder);
+ configBuilder.setHiddenSsid(mHiddenSsidPrefController.isHiddenSsidEnabled());
return configBuilder.build();
}
@@ -338,6 +355,8 @@ private void updateDisplayWithNewConfig() {
use(WifiTetherSecurityPreferenceController.class).updateDisplay();
use(WifiTetherPasswordPreferenceController.class).updateDisplay();
use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay();
+ use(WifiTetherAutoOffPreferenceController.class).updateDisplay();
+ use(WifiTetherHiddenSsidPreferenceController.class).updateDisplay();
}
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
@@ -375,6 +394,8 @@ public List getNonIndexableKeys(Context context) {
keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD);
keys.add(KEY_WIFI_TETHER_AUTO_OFF);
keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
+ keys.add(KEY_WIFI_TETHER_CLIENT_MANAGER);
+ keys.add(KEY_WIFI_TETHER_HIDDEN_SSID);
keys.add(KEY_WIFI_HOTSPOT_SPEED);
keys.add(KEY_INSTANT_HOTSPOT);
} else {
diff --git a/src/com/android/settings/wifi/tether/preference/WifiTetherClientLimitPreference.java b/src/com/android/settings/wifi/tether/preference/WifiTetherClientLimitPreference.java
new file mode 100644
index 00000000000..c53fa6ae0f9
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/preference/WifiTetherClientLimitPreference.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 Project Kaleidoscope
+ *
+ * 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.wifi.tether.preference;
+
+import android.content.Context;
+import android.widget.SeekBar;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.SeekBarDialogPreference;
+
+public class WifiTetherClientLimitPreference extends SeekBarDialogPreference implements
+ SeekBar.OnSeekBarChangeListener {
+
+ private Context mContext;
+ private SeekBar mSeekBar;
+ private int mValue;
+ private int mMin;
+ private int mMax;
+
+ public WifiTetherClientLimitPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+ setText(getSummaryForValue(progress + mMin));
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ mSeekBar = getSeekBar(view);
+ mSeekBar.setOnSeekBarChangeListener(this);
+ mSeekBar.setMax(mMax - mMin);
+ mSeekBar.setProgress(mValue - mMin);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ if (positiveResult) {
+ setValue(mSeekBar.getProgress() + mMin, true);
+ }
+ }
+
+ private String getSummaryForValue(int value) {
+ return mContext.getResources().getQuantityString(
+ R.plurals.wifi_hotspot_client_limit_summary, value, value);
+ }
+
+ public void setMin(int min) {
+ mMin = min;
+ }
+
+ public void setMax(int max) {
+ mMax = max;
+ }
+
+ public void setValue(int value, boolean callListener) {
+ if (value == 0) value = mMax;
+ mValue = value;
+ String summary = getSummaryForValue(value);
+ setSummary(summary);
+ setText(summary);
+ if (callListener) callChangeListener(value);
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+}
diff --git a/src/com/google/android/settings/biometrics/face/FaceEnrollActivityDirector.java b/src/com/google/android/settings/biometrics/face/FaceEnrollActivityDirector.java
new file mode 100644
index 00000000000..446ff54f902
--- /dev/null
+++ b/src/com/google/android/settings/biometrics/face/FaceEnrollActivityDirector.java
@@ -0,0 +1,69 @@
+package com.google.android.settings.biometrics.face;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.fragment.app.FragmentActivity;
+import com.android.settings.R;
+
+import com.android.settings.biometrics.face.FaceEnrollEnrolling;
+
+public class FaceEnrollActivityDirector extends FragmentActivity {
+ private Intent mExtras;
+ private boolean mFirstTime = true;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ mExtras = getIntent();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == 4) {
+ Intent intent2 = new Intent(mExtras);
+ intent2.putExtra("accessibility_diversity", false);
+ intent2.putExtra("from_multi_timeout", true);
+ startEnrollActivity(intent2);
+ } else if (resultCode == 5) {
+ startEnrollActivity(mExtras);
+ } else {
+ setResult(resultCode, data);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mFirstTime) {
+ mFirstTime = false;
+ startEnrollActivity(mExtras);
+ }
+ }
+
+ private void startEnrollActivity(Intent intent) {
+ Intent enrollIntent;
+ boolean trafficLight =
+ getResources().getBoolean(R.bool.config_face_enroll_use_traffic_light);
+ // Use SettingsGoogleFutureFaceEnroll
+ if (trafficLight) {
+ enrollIntent =
+ new Intent(
+ "com.google.android.settings.future.biometrics.faceenroll.action.ENROLL");
+ } else {
+ enrollIntent = new Intent(this, FaceEnrollEnrolling.class);
+ }
+ if (trafficLight) {
+ String packageName = getString(R.string.config_face_enroll_traffic_light_package);
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalStateException("Package name must not be empty");
+ }
+ enrollIntent.setPackage(packageName);
+ }
+ enrollIntent.putExtras(intent);
+ startActivityForResult(enrollIntent, 1);
+ }
+}
diff --git a/src/com/google/android/settings/biometrics/face/FaceEnrollConfirmation.java b/src/com/google/android/settings/biometrics/face/FaceEnrollConfirmation.java
new file mode 100644
index 00000000000..ce8c36722b7
--- /dev/null
+++ b/src/com/google/android/settings/biometrics/face/FaceEnrollConfirmation.java
@@ -0,0 +1,151 @@
+package com.google.android.settings.biometrics.face;
+
+import android.content.Intent;
+import android.content.res.Resources;
+import android.hardware.face.FaceManager;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.fragment.app.FragmentActivity;
+import com.android.settings.R;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupcompat.util.WizardManagerHelper;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.util.ThemeHelper;
+
+public class FaceEnrollConfirmation extends FragmentActivity {
+ private FooterBarMixin mFooterBarMixin;
+ private boolean mNextClicked;
+ protected byte[] mToken;
+ protected int mUserId;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ ThemeHelper.applyTheme(this);
+ ThemeHelper.trySetDynamicColor(this);
+ super.onCreate(bundle);
+ setContentView(R.layout.face_enroll_confirmation);
+ this.mToken = getIntent().getByteArrayExtra("hw_auth_token");
+ this.mUserId =
+ getIntent().getIntExtra("android.intent.extra.USER_ID", UserHandle.myUserId());
+ if (bundle != null) {
+ if (this.mToken == null) {
+ this.mToken = bundle.getByteArray("hw_auth_token");
+ }
+ this.mUserId = bundle.getInt("android.intent.extra.USER_ID", this.mUserId);
+ }
+ setHeaderText(R.string.security_settings_face_enroll_finish_title);
+ getLayout().setDescriptionText(R.string.security_settings_face_enroll_finish_description_with_bp);
+ FooterBarMixin footerBarMixin = (FooterBarMixin) getLayout().getMixin(FooterBarMixin.class);
+ this.mFooterBarMixin = footerBarMixin;
+ footerBarMixin.setPrimaryButton(
+ new FooterButton.Builder(this)
+ .setText(R.string.security_settings_face_enroll_done)
+ .setListener(
+ new View.OnClickListener() {
+ @Override
+ public final void onClick(View view) {
+ FaceEnrollConfirmation.this.onButtonPositive(view);
+ }
+ })
+ .setButtonType(5)
+ .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+ .build());
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (isChangingConfigurations()
+ || WizardManagerHelper.isAnySetupWizard(getIntent())
+ || this.mNextClicked) {
+ return;
+ }
+ setResult(3);
+ finish();
+ }
+
+ @Override
+ protected void onApplyThemeResource(Resources.Theme theme, int i, boolean z) {
+ theme.applyStyle(R.style.SetupWizardPartnerResource, true);
+ super.onApplyThemeResource(theme, i, z);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle bundle) {
+ super.onSaveInstanceState(bundle);
+ bundle.putByteArray("hw_auth_token", this.mToken);
+ bundle.putInt("android.intent.extra.USER_ID", this.mUserId);
+ }
+
+ private GlifLayout getLayout() {
+ return (GlifLayout) findViewById(R.id.face_enroll_confirmation);
+ }
+
+ private void onButtonPositive(View view) {
+ this.mNextClicked = true;
+ if (getIntent().getBooleanExtra("from_settings_summary", false)) {
+ launchFaceSettings();
+ return;
+ }
+ if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
+ revokeChallenge();
+ }
+ setResult(1);
+ finish();
+ }
+
+ private void setHeaderText(int i) {
+ TextView headerTextView = getLayout().getHeaderTextView();
+ CharSequence text = headerTextView.getText();
+ CharSequence text2 = getText(i);
+ if (text != text2) {
+ if (!TextUtils.isEmpty(text2)) {
+ headerTextView.setAccessibilityLiveRegion(1);
+ }
+ getLayout().setHeaderText(text2);
+ setTitle(text2);
+ }
+ }
+
+ private void revokeChallenge() {
+ FaceManager faceManager = (FaceManager) getSystemService(FaceManager.class);
+ if (faceManager != null) {
+ faceManager.revokeChallenge(
+ getIntent().getIntExtra("sensor_id", -1),
+ this.mUserId,
+ getIntent().getLongExtra("challenge", 0L));
+ }
+ }
+
+ private void launchFaceSettings() {
+ Intent intent = new Intent("android.settings.FACE_SETTINGS");
+ intent.setPackage("com.android.settings");
+ intent.putExtra("hw_auth_token", this.mToken);
+ intent.putExtra("android.intent.extra.USER_ID", this.mUserId);
+ intent.setFlags(67239936);
+ intent.putExtra("challenge", getIntent().getLongExtra("challenge", 0L));
+ intent.putExtra("sensor_id", getIntent().getIntExtra("sensor_id", 0));
+ startActivityForResult(intent, 1);
+ }
+
+ @Override
+ protected void onActivityResult(int i, int i2, Intent intent) {
+ super.onActivityResult(i, i2, intent);
+ if (i == 1) {
+ setResult(1, intent);
+ finish();
+ } else if (i != 2) {
+ } else {
+ Log.d("FaceEnrollConfirmation", "Next biometric's result: " + i2);
+ setResult(1, intent);
+ finish();
+ }
+ }
+}
diff --git a/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java b/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java
index df3dd2effd2..05dc113cef8 100644
--- a/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java
+++ b/tests/componenttests/src/com/android/settings/network/telephony/MobileDataPreferenceControllerComponentTest.java
@@ -131,7 +131,7 @@ public void test_disable_mobile_network() {
URL url = new URL("https://www.google.net");
MobileDataPreferenceController controller = new MobileDataPreferenceController(
mInstrumentation.getTargetContext(), "mobile_data", mLifecycle,
- mLifecycleOwner, sSubscriptionId);
+ mLifecycleOwner, sSubscriptionId, false);
FragmentManager manager = ((FragmentActivity) activity).getSupportFragmentManager();
controller.init(manager, sSubscriptionId, mock(SubscriptionInfoEntity.class), mock(
MobileNetworkInfoEntity.class));
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountTypePreferenceLoaderTest.java b/tests/robotests/src/com/android/settings/accounts/AccountTypePreferenceLoaderTest.java
index efa5fea7416..0b9c6c357a4 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountTypePreferenceLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountTypePreferenceLoaderTest.java
@@ -30,8 +30,11 @@
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
+import android.content.ClipData;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.UserHandle;
import androidx.collection.ArraySet;
@@ -250,4 +253,13 @@ public void filterBlockedFragments_nestedPrefGroupWithNoMatchFragment_overrideCl
mPrefLoader.filterBlockedFragments(parent, Set.of("nomatch", "other"));
verify(pref).setOnPreferenceClickListener(any());
}
+
+ @Test
+ public void isSafeIntent_hasContextScheme_returnFalse() {
+ Intent intent = new Intent();
+ intent.setClipData(ClipData.newRawUri(null,
+ Uri.parse("content://com.android.settings.files/my_cache/NOTICE.html")));
+
+ assertThat(mPrefLoader.isSafeIntent(mPackageManager, intent, mAccount.type)).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java
deleted file mode 100644
index 4fb78fbee48..00000000000
--- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsHeaderControllerTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2018 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.connecteddevice.usb;
-
-import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
-import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
-
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.hardware.usb.UsbManager;
-
-import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.PreferenceManager;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.testutils.DrawableTestHelper;
-import com.android.settingslib.widget.LayoutPreference;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {
- ShadowEntityHeaderController.class,
- com.android.settings.testutils.shadow.ShadowFragment.class,
-})
-public class UsbDetailsHeaderControllerTest {
-
- private UsbDetailsHeaderController mDetailsHeaderController;
- private Context mContext;
- private Lifecycle mLifecycle;
- private LifecycleOwner mLifecycleOwner;
- private LayoutPreference mPreference;
- private PreferenceManager mPreferenceManager;
- private PreferenceScreen mScreen;
-
- @Mock
- private UsbBackend mUsbBackend;
- @Mock
- private UsbDetailsFragment mFragment;
- @Mock
- private FragmentActivity mActivity;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private EntityHeaderController mHeaderController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mContext = RuntimeEnvironment.application;
- mLifecycleOwner = () -> mLifecycle;
- mLifecycle = new Lifecycle(mLifecycleOwner);
- mPreferenceManager = new PreferenceManager(mContext);
- mScreen = mPreferenceManager.createPreferenceScreen(mContext);
-
- when(mFragment.getActivity()).thenReturn(mActivity);
- when(mActivity.getApplicationContext()).thenReturn(mContext);
- when(mFragment.getContext()).thenReturn(mContext);
- when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
- when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
-
- ShadowEntityHeaderController.setUseMock(mHeaderController);
- mDetailsHeaderController = new UsbDetailsHeaderController(mContext, mFragment, mUsbBackend);
- mPreference = new LayoutPreference(
- mContext, com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header);
- mPreference.setKey(mDetailsHeaderController.getPreferenceKey());
- mScreen.addPreference(mPreference);
- }
-
- @After
- public void tearDown() {
- ShadowEntityHeaderController.reset();
- }
-
- @Test
- public void displayRefresh_charging_shouldSetHeader() {
- mDetailsHeaderController.displayPreference(mScreen);
- mDetailsHeaderController.refresh(true, UsbManager.FUNCTION_NONE, POWER_ROLE_SINK,
- DATA_ROLE_DEVICE);
- verify(mHeaderController).setLabel(mContext.getString(R.string.usb_pref));
- verify(mHeaderController).setIcon(argThat((ArgumentMatcher) t -> {
- DrawableTestHelper.assertDrawableResId(t, R.drawable.ic_usb);
- return true;
- }));
- verify(mHeaderController).done(true);
- }
-}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogControllerTest.java
index 89cc6d90361..f68f67e112d 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/imei/ImeiInfoDialogControllerTest.java
@@ -142,36 +142,6 @@ public void populateImeiInfo_cdmaSimDisabled_shouldRemoveImeiInfoAndSetMinPrlToE
verify(mDialog).removeViewFromScreen(ID_GSM_SETTINGS);
}
- @Test
- public void populateImeiInfo_cdmaSimPresent_shouldSetImeiInfoAndSetAllCdmaSetting() {
- ReflectionHelpers.setField(mController, "mSubscriptionInfo", null);
- when(mTelephonyManager.getPhoneType()).thenReturn(TelephonyManager.PHONE_TYPE_CDMA);
- when(mTelephonyManager.getSimState(anyInt())).thenReturn(
- TelephonyManager.SIM_STATE_PRESENT);
-
- mController.populateImeiInfo();
-
- verify(mDialog).setText(ID_MEID_NUMBER_VALUE, MEID_NUMBER);
- verify(mDialog).setText(ID_MIN_NUMBER_VALUE, "");
- verify(mDialog).setText(ID_PRL_VERSION_VALUE, "");
- verify(mDialog).setText(eq(ID_IMEI_VALUE), any());
- verify(mDialog).setText(eq(ID_IMEI_SV_VALUE), any());
- }
-
- @Test
- public void populateImeiInfo_cdmaSimABSENT_shouldSetImeiInfoAndSetAllCdmaSetting() {
- ReflectionHelpers.setField(mController, "mSubscriptionInfo", null);
- when(mTelephonyManager.getPhoneType()).thenReturn(TelephonyManager.PHONE_TYPE_CDMA);
- when(mTelephonyManager.getSimState(anyInt())).thenReturn(TelephonyManager.SIM_STATE_ABSENT);
-
- mController.populateImeiInfo();
-
- verify(mDialog).setText(ID_MEID_NUMBER_VALUE, MEID_NUMBER);
- verify(mDialog).setText(ID_MIN_NUMBER_VALUE, "");
- verify(mDialog).setText(ID_PRL_VERSION_VALUE, "");
- verify(mDialog).removeViewFromScreen(ID_GSM_SETTINGS);
- }
-
@Test
public void populateImeiInfo_gsmSimDisabled_shouldSetImeiAndRemoveCdmaSettings() {
ReflectionHelpers.setField(mController, "mSubscriptionInfo", null);
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
index d95635e6437..136a9eda5a6 100644
--- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
@@ -34,7 +34,7 @@
import android.location.LocationManager;
import android.os.PowerManager;
-import androidx.preference.DropDownPreference;
+import androidx.preference.ListPreference;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
@@ -53,7 +53,7 @@ public class DarkModeScheduleSelectorControllerTest {
private DarkModeScheduleSelectorController mController;
private String mPreferenceKey = "key";
@Mock
- private DropDownPreference mPreference;
+ private ListPreference mPreference;
@Mock
private PreferenceScreen mScreen;
private Context mContext;
@@ -94,7 +94,7 @@ public void setUp() {
.thenReturn("custom_bedtime");
when(mResources.getStringArray(R.array.dark_ui_scheduler_with_bedtime_preference_titles))
.thenReturn(new String[]{"never", "auto", "custom", "custom_bedtime"});
- mPreference = spy(new DropDownPreference(mContext));
+ mPreference = spy(new ListPreference(mContext));
mPreference.setEntryValues(new CharSequence[]{"never", "auto", "custom"});
doNothing().when(mPreference).setValueIndex(anyInt());
when(mLocationManager.isLocationEnabled()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
deleted file mode 100644
index d642b3096e8..00000000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2023 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.fuelgauge.datasaver;
-
-import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-
-import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
-import static com.android.settings.fuelgauge.datasaver.DynamicDenylistManager.PREF_KEY_MANUAL_DENYLIST_SYNCED;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.net.NetworkPolicyManager;
-import android.util.ArraySet;
-
-import org.junit.After;
-import org.junit.Before;
-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 java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-@RunWith(RobolectricTestRunner.class)
-public class DynamicDenylistManagerTest {
-
- private static final int[] EMPTY_ARRAY = new int[] {};
- private static final String FAKE_UID_1 = "1001";
- private static final String FAKE_UID_2 = "1002";
- private static final int FAKE_UID_1_INT = Integer.parseInt(FAKE_UID_1);
- private static final int FAKE_UID_2_INT = Integer.parseInt(FAKE_UID_2);
-
- private SharedPreferences mManualDenyListPref;
- private SharedPreferences mDynamicDenyListPref;
- private DynamicDenylistManager mDynamicDenylistManager;
-
- @Mock
- private NetworkPolicyManager mNetworkPolicyManager;
- @Mock
- private PackageManager mPackageManager;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
-
- @After
- public void tearDown() {
- mDynamicDenylistManager.clearSharedPreferences();
- }
-
- @Test
- public void init_withoutExistedRejectPolicy_createWithExpectedValue() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- assertThat(mManualDenyListPref.getAll()).hasSize(1);
- assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
- }
-
- @Test
- public void init_withExistedRejectPolicy_createWithExpectedValue() {
- initDynamicDenylistManager(new int[] {FAKE_UID_1_INT, FAKE_UID_2_INT});
-
- assertThat(mManualDenyListPref.getAll()).hasSize(3);
- assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- assertTrue(mManualDenyListPref.contains(FAKE_UID_2));
- }
-
- @Test
- public void getManualDenylistPref_initiated_containsExpectedValue() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- setupPreference(mManualDenyListPref, FAKE_UID_1);
-
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- }
-
- @Test
- public void getDynamicDenylistPref_initiated_containsExpectedValue() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- setupPreference(mDynamicDenyListPref, FAKE_UID_1);
-
- assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
- }
-
- @Test
- public void updateManualDenylist_policyReject_addsUid() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT,
- POLICY_REJECT_METERED_BACKGROUND);
-
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- }
-
- @Test
- public void updateManualDenylist_policyNone_removesUid() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- setupPreference(mManualDenyListPref, FAKE_UID_1);
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
-
- mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT, POLICY_NONE);
-
- assertFalse(mManualDenyListPref.contains(FAKE_UID_1));
- }
-
- @Test
- public void updateManualDenylist_samePolicy_doNothing() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- setupPreference(mManualDenyListPref, FAKE_UID_1);
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- assertThat(mManualDenyListPref.getAll()).hasSize(2);
-
- mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT,
- POLICY_REJECT_METERED_BACKGROUND);
-
- assertThat(mManualDenyListPref.getAll()).hasSize(2);
- }
-
- @Test
- public void setUidPolicyLocked_invokeSetUidPolicy() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- mDynamicDenylistManager.setUidPolicyLocked(FAKE_UID_1_INT,
- POLICY_REJECT_METERED_BACKGROUND);
-
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_1_INT),
- eq(POLICY_REJECT_METERED_BACKGROUND));
- }
-
- @Test
- public void setDenylist_emptyListAndNoData_doNothing() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- setDenylist(Collections.emptySet());
-
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
- }
-
- @Test
- public void setDenylist_uidDeniedAlready_doNothing() {
- initDynamicDenylistManager(new int[] {FAKE_UID_1_INT});
- final ArraySet uids = new ArraySet<>();
- uids.add(FAKE_UID_1_INT);
- uids.add(null);
-
- setDenylist(uids);
-
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
- }
-
- @Test
- public void setDenylist_sameList_doNothing() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- setupPreference(mDynamicDenyListPref, FAKE_UID_2, FAKE_UID_1);
-
- setDenylist(new ArraySet<>(List.of(FAKE_UID_1_INT, FAKE_UID_2_INT)));
-
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
- }
-
- @Test
- public void setDenylist_newListWithOldData_modifyPolicyNoneAndReject() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- setupPreference(mDynamicDenyListPref, FAKE_UID_2);
-
- setDenylist(new ArraySet<>(List.of(FAKE_UID_1_INT)));
-
- verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE);
- verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT,
- POLICY_REJECT_METERED_BACKGROUND);
- assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
- assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
- }
-
- @Test
- public void setDenylist_newListWithoutOldData_modifyPolicyReject() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- setDenylist(new ArraySet<>(List.of(FAKE_UID_1_INT)));
-
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
- verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT,
- POLICY_REJECT_METERED_BACKGROUND);
- assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
- assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
- }
-
- @Test
- public void setDenylist_emptyListWithOldData_modifyPolicyNone() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- setupPreference(mDynamicDenyListPref, FAKE_UID_2);
-
- setDenylist(Collections.emptySet());
-
- verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE);
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(),
- eq(POLICY_REJECT_METERED_BACKGROUND));
- assertThat(mDynamicDenyListPref.getAll()).isEmpty();
- }
-
- @Test
- public void isInManualDenylist_returnsFalse() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
- }
-
- @Test
- public void isInManualDenylist_incorrectUid_returnsFalse() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- mManualDenyListPref.edit().putInt(FAKE_UID_2, POLICY_REJECT_METERED_BACKGROUND).apply();
-
- assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
- }
-
- @Test
- public void isInManualDenylist_initiated_returnsTrue() {
- initDynamicDenylistManager(EMPTY_ARRAY);
-
- mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
-
- assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
- }
-
- @Test
- public void resetDenylistIfNeeded_nullPackageName_doNothing() {
- initDynamicDenylistManager(new int[0], new int[] {FAKE_UID_1_INT, FAKE_UID_2_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded(null, false);
-
- assertThat(mManualDenyListPref.getAll()).hasSize(1);
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
- }
-
- @Test
- public void resetDenylistIfNeeded_invalidPackageName_doNothing() {
- initDynamicDenylistManager(new int[0], new int[] {FAKE_UID_1_INT, FAKE_UID_2_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", false);
-
- assertThat(mManualDenyListPref.getAll()).hasSize(1);
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
- }
-
- @Test
- public void resetDenylistIfNeeded_denylistUnchanged_doNothingWithPolicy() {
- initDynamicDenylistManager(new int[] {FAKE_UID_1_INT, FAKE_UID_2_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false);
-
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
- }
-
- @Test
- public void resetDenylistIfNeeded_denylistChanged_resetAndClear() {
- initDynamicDenylistManager(new int[0], new int[] {FAKE_UID_1_INT, FAKE_UID_2_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false);
-
- assertThat(mManualDenyListPref.getAll()).isEmpty();
- verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
- }
-
- @Test
- public void resetDenylistIfNeeded_forceResetWithNullPackageName_resetAndClear() {
- initDynamicDenylistManager(new int[0], new int[] {FAKE_UID_2_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded(null, true);
-
- assertThat(mManualDenyListPref.getAll()).isEmpty();
- verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_2_INT), eq(POLICY_NONE));
- }
-
- @Test
- public void resetDenylistIfNeeded_forceResetWithInvalidPackageName_resetAndClear() {
- initDynamicDenylistManager(new int[0], new int[] {FAKE_UID_1_INT, FAKE_UID_2_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", true);
-
- assertThat(mManualDenyListPref.getAll()).isEmpty();
- verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
- }
-
- @Test
- public void resetDenylistIfNeeded_forceResetButDenylistUnchanged_doNothingWithPolicy() {
- initDynamicDenylistManager(new int[] {FAKE_UID_1_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true);
-
- assertThat(mManualDenyListPref.getAll()).isEmpty();
- verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
- }
-
- @Test
- public void resetDenylistIfNeeded_forceResetWithDenylistChanged_resetAndClear() {
- initDynamicDenylistManager(new int[0], new int[] {FAKE_UID_1_INT, FAKE_UID_2_INT});
-
- mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true);
-
- assertThat(mManualDenyListPref.getAll()).isEmpty();
- verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
- }
-
- @Test
- public void clearSharedPreferences_manualDenyListPrefIsEmpty() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
- assertThat(mManualDenyListPref.getAll()).hasSize(2);
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
-
- mDynamicDenylistManager.clearSharedPreferences();
-
- assertThat(mManualDenyListPref.getAll()).isEmpty();
- }
-
- @Test
- public void clearSharedPreferences_dynamicDenyListPrefIsEmpty() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
- assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
- assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
-
- mDynamicDenylistManager.clearSharedPreferences();
-
- assertThat(mDynamicDenyListPref.getAll()).isEmpty();
- }
-
- @Test
- public void dump_dumpExpectedResult() {
- initDynamicDenylistManager(EMPTY_ARRAY);
- setupPreference(mManualDenyListPref, FAKE_UID_1);
- setupPreference(mDynamicDenyListPref, FAKE_UID_2);
- final StringWriter stringWriter = new StringWriter();
- final PrintWriter printWriter = new PrintWriter(stringWriter);
- when(mPackageManager.getNameForUid(FAKE_UID_1_INT)).thenReturn("app1");
- when(mPackageManager.getNameForUid(FAKE_UID_2_INT)).thenReturn("app2");
-
- mDynamicDenylistManager.dump(printWriter);
-
- final String dumpResults = stringWriter.toString();
- assertThat(dumpResults.contains("\tManualDenylist:\n\t\tapp1")).isTrue();
- assertThat(dumpResults.contains("\tDynamicDenylist:\n\t\tapp2")).isTrue();
- }
-
- @Test
- public void dump_withEmptySharedPreferences_dumpExpectedResult() {
- initDynamicDenylistManager(EMPTY_ARRAY, EMPTY_ARRAY);
- mDynamicDenylistManager.clearSharedPreferences();
- final StringWriter stringWriter = new StringWriter();
- final PrintWriter printWriter = new PrintWriter(stringWriter);
-
- mDynamicDenylistManager.dump(printWriter);
-
- final String dumpResults = stringWriter.toString();
- assertThat(dumpResults.contains("Dump of DynamicDenylistManager:")).isTrue();
- assertThat(dumpResults.contains("\tManualDenylist:\n\tDynamicDenylist:")).isTrue();
- }
-
- @Test
- public void onBootComplete_resetIntoManualMode() {
- initDynamicDenylistManager(new int[] {FAKE_UID_1_INT});
- setDenylist(new ArraySet<>(List.of(FAKE_UID_2_INT)));
- // Ensure the testing environment for manual denylist.
- assertThat(mManualDenyListPref.getAll()).hasSize(2);
- assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- // Ensure the testing environment for dynamic denylist.
- assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
-
- mDynamicDenylistManager.onBootComplete();
-
- // Keep the users set uids in the manual denylist.
- assertThat(mManualDenyListPref.getAll()).hasSize(2);
- assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- // Clear the uids in the dynamic denylist.
- assertThat(mDynamicDenyListPref.getAll()).isEmpty();
- }
-
- private void initDynamicDenylistManager(int[] preload) {
- initDynamicDenylistManager(preload, preload);
- }
-
- private void initDynamicDenylistManager(int[] preload1, int[] preload2) {
- final Context context = spy(RuntimeEnvironment.application.getApplicationContext());
- when(context.getApplicationContext()).thenReturn(context);
- when(context.getPackageManager()).thenReturn(mPackageManager);
- when(mNetworkPolicyManager.getUidsWithPolicy(anyInt()))
- .thenReturn(preload1).thenReturn(preload2);
- mDynamicDenylistManager = new DynamicDenylistManager(context, mNetworkPolicyManager);
- mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref();
- mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref();
- }
-
- private void setDenylist(Set packageNameList) {
- mDynamicDenylistManager.setDenylist(packageNameList);
- }
-
- private void setupPreference(SharedPreferences sharedPreferences, String... uids) {
- final SharedPreferences.Editor editor = sharedPreferences.edit();
- for (String uid : uids) {
- editor.putInt(uid, POLICY_REJECT_METERED_BACKGROUND);
- }
- editor.apply();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java
index 9b510fb43e3..0a953615abf 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java
@@ -31,8 +31,6 @@
import androidx.annotation.Nullable;
-import com.android.settings.R;
-
import com.google.common.base.Strings;
import org.junit.Test;
@@ -45,15 +43,14 @@
public class NotificationAccessConfirmationActivityTest {
@Test
- public void start_showsDialog() {
+ public void start_withMissingIntentFilter_finishes() {
ComponentName cn = new ComponentName("com.example", "com.example.SomeService");
installPackage(cn.getPackageName(), "X");
NotificationAccessConfirmationActivity activity = startActivityWithIntent(cn);
- assertThat(activity.isFinishing()).isFalse();
- assertThat(getDialogText(activity)).isEqualTo(
- activity.getString(R.string.notification_listener_security_warning_summary, "X"));
+ assertThat(getDialogText(activity)).isNull();
+ assertThat(activity.isFinishing()).isTrue();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/notification/PhoneRingtone2PreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/PhoneRingtone2PreferenceControllerTest.java
new file mode 100644
index 00000000000..87fa6a5038c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/PhoneRingtone2PreferenceControllerTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.media.RingtoneManager;
+import android.telephony.TelephonyManager;
+
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.DefaultRingtonePreference;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class PhoneRingtone2PreferenceControllerTest {
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private DefaultRingtonePreference mPreference;
+
+ private Context mContext;
+ private PhoneRingtone2PreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowContext = ShadowApplication.getInstance();
+ shadowContext.setSystemService(Context.TELEPHONY_SERVICE, mTelephonyManager);
+ mContext = RuntimeEnvironment.application;
+ mController = new PhoneRingtone2PreferenceController(mContext);
+ }
+
+ @Test
+ public void displayPreference_shouldSetSlotId() {
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreference);
+ mController.displayPreference(mPreferenceScreen);
+
+ verify(mPreference).setSlotId(1);
+ }
+
+ @Test
+ public void isAvailable_notVoiceCapable_shouldReturnFalse() {
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_notMultiSimEnabled_shouldReturnFalse() {
+ when(mTelephonyManager.isMultiSimEnabled()).thenReturn(false);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_VoiceCapable_and_MultiSimEnabled_shouldReturnTrue() {
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+ when(mTelephonyManager.isMultiSimEnabled()).thenReturn(true);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void getRingtoneType_shouldReturnRingtone() {
+ assertThat(mController.getRingtoneType()).isEqualTo(RingtoneManager.TYPE_RINGTONE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/PhoneRingtonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/PhoneRingtonePreferenceControllerTest.java
index 24e845851a4..200d16fa96e 100644
--- a/tests/robotests/src/com/android/settings/notification/PhoneRingtonePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/PhoneRingtonePreferenceControllerTest.java
@@ -18,12 +18,17 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.media.RingtoneManager;
import android.telephony.TelephonyManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.DefaultRingtonePreference;
+import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,6 +43,10 @@ public class PhoneRingtonePreferenceControllerTest {
@Mock
private TelephonyManager mTelephonyManager;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private DefaultRingtonePreference mPreference;
private Context mContext;
private PhoneRingtonePreferenceController mController;
@@ -51,6 +60,16 @@ public void setUp() {
mController = new PhoneRingtonePreferenceController(mContext);
}
+ @Test
+ public void displayPreference_shouldUpdateTitle_for_MultiSimDevice() {
+ when(mTelephonyManager.isMultiSimEnabled()).thenReturn(true);
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreference);
+ mController.displayPreference(mPreferenceScreen);
+
+ verify(mPreference).setTitle(mContext.getString(R.string.ringtone_title) + " - " +
+ String.format(mContext.getString(R.string.sim_card_number_title), 1)));
+
@Test
public void isAvailable_notVoiceCapable_shouldReturnFalse() {
when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
index d80464d5467..2697748af8f 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
@@ -16,11 +16,12 @@
package com.android.settings.wifi;
-import static com.android.settings.wifi.WifiConfigController.PRIVACY_SPINNER_INDEX_DEVICE_MAC;
-import static com.android.settings.wifi.WifiConfigController.PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC;
+import static com.android.settings.wifi.WifiConfigController.PRIVACY_PREF_INDEX_DEVICE_MAC;
+import static com.android.settings.wifi.WifiConfigController.PRIVACY_PREF_INDEX_PER_CONNECTION_RANDOMIZED_MAC;
import static com.android.settings.wifi.WifiConfigController.DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_ENABLE;
import static com.android.settings.wifi.WifiConfigController.DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_DISABLE;
+import static com.android.settings.wifi.details2.WifiPrivacyPreferenceController2.translateMacRandomizedValueToPrefValue;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -407,7 +408,7 @@ public void loadMacRandomizedValue_shouldMandomizedMacAsDefault() {
assertThat(privacySetting.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(privacySetting.getSelectedItemPosition()).isEqualTo(
- PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC);
+ PRIVACY_PREF_INDEX_PER_CONNECTION_RANDOMIZED_MAC);
}
@Test
@@ -432,8 +433,7 @@ private void checkSavedMacRandomizedValue(int macRandomizedValue) {
assertThat(privacySetting.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(privacySetting.getSelectedItemPosition()).isEqualTo(
- macRandomizedValue == WifiConfiguration.RANDOMIZATION_PERSISTENT
- ? PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC : PRIVACY_SPINNER_INDEX_DEVICE_MAC);
+ translateWifiEntryPrivacyToPrefValue(macRandomizedValue));
}
@Test
@@ -446,7 +446,7 @@ public void saveMacRandomizedValue_noChanged_shouldPersistentAsDefault() {
@Test
public void saveMacRandomizedValue_ChangedToDeviceMac_shouldGetNone() {
final Spinner privacySetting = mView.findViewById(R.id.privacy_settings);
- privacySetting.setSelection(PRIVACY_SPINNER_INDEX_DEVICE_MAC);
+ privacySetting.setSelection(PRIVACY_PREF_INDEX_DEVICE_MAC);
WifiConfiguration config = mController.getConfig();
assertThat(config.macRandomizationSetting).isEqualTo(WifiConfiguration.RANDOMIZATION_NONE);
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java
index c63fa60437b..58b3ea918bb 100644
--- a/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java
@@ -22,7 +22,7 @@
import android.content.Context;
-import androidx.preference.DropDownPreference;
+import androidx.preference.ListPreference;
import com.android.settings.R;
import com.android.wifitrackerlib.WifiEntry;
@@ -47,7 +47,7 @@ public class WifiMeteredPreferenceController2Test {
private WifiMeteredPreferenceController2 mPreferenceController;
private Context mContext;
- private DropDownPreference mDropDownPreference;
+ private ListPreference mListPreference;
@Before
public void setUp() {
@@ -56,35 +56,35 @@ public void setUp() {
mPreferenceController = spy(
new WifiMeteredPreferenceController2(mContext, mWifiEntry));
- mDropDownPreference = new DropDownPreference(mContext);
- mDropDownPreference.setEntries(R.array.wifi_metered_entries);
- mDropDownPreference.setEntryValues(R.array.wifi_metered_values);
+ mListPreference = new ListPreference(mContext);
+ mListPreference.setEntries(R.array.wifi_metered_entries);
+ mListPreference.setEntryValues(R.array.wifi_metered_values);
}
@Test
public void testUpdateState_wifiMetered_setCorrectValue() {
doReturn(METERED_OVERRIDE_METERED).when(mPreferenceController).getMeteredOverride();
- mPreferenceController.updateState(mDropDownPreference);
+ mPreferenceController.updateState(mListPreference);
- assertThat(mDropDownPreference.getEntry()).isEqualTo("Treat as metered");
+ assertThat(mListPreference.getEntry()).isEqualTo("Treat as metered");
}
@Test
public void testUpdateState_wifiNotMetered_setCorrectValue() {
doReturn(METERED_OVERRIDE_NOT_METERED).when(mPreferenceController).getMeteredOverride();
- mPreferenceController.updateState(mDropDownPreference);
+ mPreferenceController.updateState(mListPreference);
- assertThat(mDropDownPreference.getEntry()).isEqualTo("Treat as unmetered");
+ assertThat(mListPreference.getEntry()).isEqualTo("Treat as unmetered");
}
@Test
public void testUpdateState_wifiAuto_setCorrectValue() {
doReturn(METERED_OVERRIDE_NONE).when(mPreferenceController).getMeteredOverride();
- mPreferenceController.updateState(mDropDownPreference);
+ mPreferenceController.updateState(mListPreference);
- assertThat(mDropDownPreference.getEntry()).isEqualTo("Detect automatically");
+ assertThat(mListPreference.getEntry()).isEqualTo("Detect automatically");
}
}
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
index 0bc0a32daa6..774f4427530 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
@@ -275,6 +275,7 @@ public void getNonIndexableKeys_tetherAvailable_keysNotReturned() {
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
+ assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_HIDDEN_SSID);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_INSTANT_HOTSPOT);
}
@@ -293,6 +294,7 @@ public void getNonIndexableKeys_tetherNotAvailable_keysReturned() {
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
+ assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_HIDDEN_SSID);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED);
assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT);
}
@@ -311,6 +313,7 @@ public void getNonIndexableKeys_hotspotNotAvailable_keysReturned() {
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
+ assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_HIDDEN_SSID);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED);
assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT);
}
@@ -329,6 +332,7 @@ public void getNonIndexableKeys_tetherAndHotspotNotAvailable_keysReturned() {
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
+ assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_HIDDEN_SSID);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED);
assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT);
}
@@ -348,6 +352,7 @@ public void getNonIndexableKeys_instantHotspotNotAvailableOnly_keysContainInstan
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
+ assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_HIDDEN_SSID);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED);
// contains
assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT);
@@ -367,6 +372,7 @@ public void getNonIndexableKeys_speedFeatureNotAvailableOnly_keysContainInstantH
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
+ assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_HIDDEN_SSID);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_INSTANT_HOTSPOT);
// contains
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY);
diff --git a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPageProviderTest.kt
index 5c9a1a4a932..a3893a85c13 100644
--- a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPageProviderTest.kt
@@ -85,7 +85,7 @@ class WifiPrivacyPageProviderTest {
composeTestRule.setContent {
WifiPrivacyPage(mockWifiEntry)
}
- val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries_ext)
for (entry in wifiPrivacyEntries) {
composeTestRule.onNodeWithText(
entry
@@ -98,7 +98,7 @@ class WifiPrivacyPageProviderTest {
composeTestRule.setContent {
WifiPrivacyPage(mockWifiEntry)
}
- val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries_ext)
for (entry in wifiPrivacyEntries) {
composeTestRule.onNodeWithText(
entry
@@ -111,8 +111,8 @@ class WifiPrivacyPageProviderTest {
composeTestRule.setContent {
WifiPrivacyPage(mockWifiEntry)
}
- val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
- val wifiPrivacyValues = context.resources.getStringArray(R.array.wifi_privacy_values)
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries_ext)
+ val wifiPrivacyValues = context.resources.getStringArray(R.array.wifi_privacy_values_ext)
composeTestRule.onNodeWithText(
wifiPrivacyEntries[wifiPrivacyValues.indexOf("0")]
).assertIsSelected()
@@ -126,7 +126,7 @@ class WifiPrivacyPageProviderTest {
composeTestRule.setContent {
WifiPrivacyPage(mockWifiEntry)
}
- val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ val wifiPrivacyEntries = context.resources.getStringArray(R.array.wifi_privacy_entries_ext)
for (entry in wifiPrivacyEntries) {
composeTestRule.onNodeWithText(entry).assertIsNotEnabled()
}
@@ -176,4 +176,4 @@ class WifiPrivacyPageProviderTest {
context.getString(R.string.wifi_privacy_send_device_name_toggle_title)
).assertIsOff()
}
-}
\ No newline at end of file
+}
diff --git a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt
index 9260409af37..983d5fe1414 100644
--- a/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt
+++ b/tests/spa_unit/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.kt
@@ -45,11 +45,11 @@ class WifiPrivacyPreferenceController2Test {
})
private var preference = ListPreference(context).apply {
- setEntries(R.array.wifi_privacy_entries)
- setEntryValues(R.array.wifi_privacy_values)
+ setEntries(R.array.wifi_privacy_entries_ext)
+ setEntryValues(R.array.wifi_privacy_values_ext)
}
- private var preferenceStrings = context.resources.getStringArray(R.array.wifi_privacy_entries)
+ private var preferenceStrings = context.resources.getStringArray(R.array.wifi_privacy_entries_ext)
@Test
fun updateState_wifiPrivacy_setCorrectValue() {
diff --git a/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java b/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java
index f981f7fe73e..72d4a1140d7 100644
--- a/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java
+++ b/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java
@@ -21,6 +21,7 @@
import android.provider.Settings;
import android.system.helpers.SettingsHelper;
import android.system.helpers.SettingsHelper.SettingsType;
+import android.telephony.TelephonyManager;
import android.test.InstrumentationTestCase;
import androidx.test.filters.MediumTest;
@@ -42,6 +43,7 @@ public class SoundSettingsTest extends InstrumentationTestCase {
private UiDevice mDevice;
private ContentResolver mResolver;
private SettingsHelper mHelper;
+ private TelephonyManager mTelephonyManager;
private final Map ringtoneSounds = Map.of(
@@ -100,6 +102,8 @@ public void setUp() throws Exception {
mDevice.setOrientationNatural();
mResolver = getInstrumentation().getContext().getContentResolver();
mHelper = new SettingsHelper();
+ mTelephonyManager = (TelephonyManager) getInstrumentation().getContext()
+ .getSystemService(Context.TELEPHONY_SERVICE);
}
@Override
@@ -174,26 +178,49 @@ private void launchSoundSettings() throws Exception {
public void testPhoneRingtoneNone() throws Exception {
launchSoundSettings();
mHelper.clickSetting("Phone ringtone");
- verifyRingtone(new RingtoneSetting("None", "null"),
- Settings.System.RINGTONE);
+ if (mTelephonyManager.isMultiSimEnabled()) {
+ mHelper.clickSetting("Phone ringtone - SIM 1");
+ verifyRingtone(new RingtoneSetting("None", "null"), Settings.System.RINGTONE);
+ mHelper.clickSetting("Phone ringtone - SIM 2");
+ verifyRingtone(new RingtoneSetting("None", "null"), Settings.System.RINGTONE2);
+ } else {
+ mHelper.clickSetting("Phone ringtone");
+ verifyRingtone(new RingtoneSetting("None", "null"), Settings.System.RINGTONE);
+ }
}
@MediumTest
@Suppress
public void testPhoneRingtoneHangouts() throws Exception {
launchSoundSettings();
- mHelper.clickSetting("Phone ringtone");
- verifyRingtone(new RingtoneSetting("Hangouts Call", "31"), Settings.System.RINGTONE);
+ if (mTelephonyManager.isMultiSimEnabled()) {
+ mHelper.clickSetting("Phone ringtone - SIM 1");
+ verifyRingtone(new RingtoneSetting("Hangouts Call", "31"), Settings.System.RINGTONE);
+ mHelper.clickSetting("Phone ringtone - SIM 2");
+ verifyRingtone(new RingtoneSetting("Hangouts Call", "31"), Settings.System.RINGTONE2);
+ } else {
+ mHelper.clickSetting("Phone ringtone");
+ verifyRingtone(new RingtoneSetting("Hangouts Call", "31"), Settings.System.RINGTONE);
+ }
}
@MediumTest
public void testPhoneRingtone() throws Exception {
launchSoundSettings();
- mHelper.clickSetting("Phone ringtone");
String ringtone = ringtoneSounds.get(mDevice.getProductName()).toString();
String ringtoneSettingValue = ringtoneCodes.get(mDevice.getProductName()).toString();
- verifyRingtone(new RingtoneSetting(ringtone, ringtoneSettingValue),
- Settings.System.RINGTONE);
+ if (mTelephonyManager.isMultiSimEnabled()) {
+ mHelper.clickSetting("Phone ringtone - SIM 1");
+ verifyRingtone(new RingtoneSetting(ringtone, ringtoneSettingValue),
+ Settings.System.RINGTONE);
+ mHelper.clickSetting("Phone ringtone - SIM 2");
+ verifyRingtone(new RingtoneSetting(ringtone, ringtoneSettingValue),
+ Settings.System.RINGTONE2);
+ } else {
+ mHelper.clickSetting("Phone ringtone");
+ verifyRingtone(new RingtoneSetting(ringtone, ringtoneSettingValue),
+ Settings.System.RINGTONE);
+ }
}
@MediumTest
diff --git a/tests/unit/src/com/android/settings/deviceinfo/PhoneNumberPreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/PhoneNumberPreferenceControllerTest.java
index 9a5399c5f07..5f02b04f7e0 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/PhoneNumberPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/PhoneNumberPreferenceControllerTest.java
@@ -37,6 +37,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.ResourcesUtils;
import org.junit.Before;
@@ -88,9 +89,26 @@ public void setup() {
mCategory.setKey(categoryKey);
mScreen.addPreference(mCategory);
+ doReturn(mSubscriptionInfo).when(mController).getSubscriptionInfo(anyInt());
doReturn(mSecondPreference).when(mController).createNewPreference(mContext);
}
+ @Test
+ public void getAvailabilityStatus_isVoiceCapable_shouldBeAVAILABLE() {
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_isNotVoiceCapable_shouldBeUNSUPPORTED_ON_DEVICE() {
+ when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
@Test
public void displayPreference_multiSim_shouldAddSecondPreference() {
when(mTelephonyManager.getPhoneCount()).thenReturn(2);
@@ -105,7 +123,6 @@ public void displayPreference_multiSim_shouldAddSecondPreference() {
@Test
public void updateState_singleSim_shouldUpdateTitleAndPhoneNumber() {
final String phoneNumber = "1111111111";
- doReturn(mSubscriptionInfo).when(mController).getSubscriptionInfo(anyInt());
doReturn(phoneNumber).when(mController).getFormattedPhoneNumber(mSubscriptionInfo);
when(mTelephonyManager.getPhoneCount()).thenReturn(1);
mController.displayPreference(mScreen);
@@ -119,7 +136,6 @@ public void updateState_singleSim_shouldUpdateTitleAndPhoneNumber() {
@Test
public void updateState_multiSim_shouldUpdateTitleAndPhoneNumberOfMultiplePreferences() {
final String phoneNumber = "1111111111";
- doReturn(mSubscriptionInfo).when(mController).getSubscriptionInfo(anyInt());
doReturn(phoneNumber).when(mController).getFormattedPhoneNumber(mSubscriptionInfo);
when(mTelephonyManager.getPhoneCount()).thenReturn(2);
mController.displayPreference(mScreen);
@@ -137,11 +153,11 @@ public void updateState_multiSim_shouldUpdateTitleAndPhoneNumberOfMultiplePrefer
@Test
public void getSummary_cannotGetActiveSubscriptionInfo_shouldShowUnknown() {
when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(null);
- mController.displayPreference(mScreen);
- mController.updateState(mPreference);
+ CharSequence primaryNumber = mController.getSummary();
- verify(mPreference).setSummary(ResourcesUtils.getResourcesString(
+ assertThat(primaryNumber).isNotNull();
+ assertThat(primaryNumber).isEqualTo(ResourcesUtils.getResourcesString(
mContext, "device_info_default"));
}
@@ -150,10 +166,9 @@ public void getSummary_getEmptySubscriptionInfo_shouldShowUnknown() {
List infos = new ArrayList<>();
when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(infos);
- mController.displayPreference(mScreen);
- mController.updateState(mPreference);
+ CharSequence primaryNumber = mController.getSummary();
- verify(mPreference).setSummary(ResourcesUtils.getResourcesString(
+ assertThat(primaryNumber).isEqualTo(ResourcesUtils.getResourcesString(
mContext, "device_info_default"));
}
}
diff --git a/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
index 6897c018207..7aa3e5c5717 100644
--- a/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
@@ -109,7 +109,7 @@ public void setUp() {
mPreference = new SwitchPreference(mContext);
mController = new MobileDataPreferenceController(mContext, "mobile_data", mLifecycle,
- mLifecycleOwner, SUB_ID);
+ mLifecycleOwner, SUB_ID, false);
mController.init(mFragmentManager, SUB_ID, mSubInfo1, mNetworkInfo1);
mPreference.setKey(mController.getPreferenceKey());
mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
|