Merge "Check P2P channel before requesting network info" into udc-qpr-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9c5d6c6..4a80dd0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4840,7 +4840,7 @@
</activity>
<activity android:name="Settings$FactoryResetActivity"
- android:permission="android.permission.BACKUP"
+ android:permission="android.permission.MASTER_CLEAR"
android:label="@string/main_clear_title"
android:exported="true"
android:theme="@style/SudThemeGlif.Light">
diff --git a/res/layout/preference_check_icon.xml b/res/layout/preference_check_icon.xml
index 1b759fc..bd0dd79 100644
--- a/res/layout/preference_check_icon.xml
+++ b/res/layout/preference_check_icon.xml
@@ -20,4 +20,5 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
- android:layout_marginHorizontal="16dp"/>
\ No newline at end of file
+ android:layout_marginHorizontal="16dp"
+ android:contentDescription="@*android:string/checked"/>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3184a1f..897d76a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1526,8 +1526,6 @@
<string name="bluetooth_companion_app_remove_association_dialog_title">Disconnect App?</string>
<!-- Bluetooth device details companion apps. The body of confirmation dialog for remove association. [CHAR LIMIT=60] -->
<string name="bluetooth_companion_app_body"><xliff:g id="app_name" example="App Name">%1$s</xliff:g> app will no longer connect to your <xliff:g id="device_name" example="Device Name">%2$s</xliff:g></string>
- <!-- Summary of Bluetooth LE Audio toggle in Device Details. [CHAR LIMIT=40] -->
- <string name="device_details_leaudio_toggle_summary">Experimental. Improves audio quality.</string>
<!-- Bluetooth device details. In the confirmation dialog for unpairing a paired device, this is the label on the button that will complete the unpairing action. -->
<string name="bluetooth_unpair_dialog_forget_confirm_button">Forget device</string>
@@ -6431,7 +6429,7 @@
<!-- Search keywords for the "Delete Guest Activity" section in Multiple Users Screen. [CHAR LIMIT=NONE] -->
<string name="remove_guest_on_exit_keywords">delete, guest, activity, remove, data, visitor, erase</string>
<!-- Title of preference to enable guest calling[CHAR LIMIT=40] -->
- <string name="enable_guest_calling">Allow guest to use phone</string>
+ <string name="enable_guest_calling">Allow guest to make phone calls</string>
<!-- Summary of preference to enable guest calling [CHAR LIMIT=NONE] -->
<string name="enable_guest_calling_summary">Call history will be shared with guest user</string>
@@ -7182,6 +7180,18 @@
<!-- Sound: Title for the option managing notification volume. [CHAR LIMIT=30] -->
<string name="notification_volume_option_title">Notification volume</string>
+ <!-- Sound: Content description of ring volume title in silent mode. [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=8994620163934249882] -->
+ <string name="ringer_content_description_silent_mode">Ringer silent</string>
+
+ <!-- Sound: Content description of ring volume title in vibrate mode. [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6261841170896561364] -->
+ <string name="ringer_content_description_vibrate_mode">Ringer vibrate</string>
+
+ <!-- Sound: Content description of notification volume title in vibrate mode. [CHAR LIMIT=NONE] -->
+ <string name="notification_volume_content_description_vibrate_mode">Notification volume muted, notifications will vibrate</string>
+
+ <!-- Sound: Content description of volume title in silent mode [CHAR LIMIT=NONE] -->
+ <string name="volume_content_description_silent_mode"> <xliff:g id="volume type" example="notification volume">%1$s</xliff:g> muted</string>
+
<!-- Sound: Summary for when notification volume is disabled. [CHAR LIMIT=100] -->
<string name="notification_volume_disabled_summary">Unavailable because ring is muted</string>
@@ -11994,7 +12004,7 @@
<!-- Developer settings: Title for force enabling Notes role. [CHAR LIMIT=50]-->
<string name="enable_notes_role_title">Force enable Notes role</string>
<!-- Developer settings: Summary for disabling phantom process monitoring. [CHAR LIMIT=NONE]-->
- <string name="enable_notes_role_summary">Enable note-taking system integrations via the Notes role. If the Notes role is already enabled, does nothing.</string>
+ <string name="enable_notes_role_summary">Enable note-taking system integrations via the Notes role. If the Notes role is already enabled, does nothing. Requires reboot.</string>
<!-- BT LE Audio Device: Media Broadcast -->
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index a2195df..370b17a 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -63,6 +63,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.RemoteException;
@@ -97,7 +98,6 @@
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -1309,4 +1309,15 @@
com.android.internal.R.bool.config_dreamsOnlyEnabledForDockUser);
return dreamsSupported && (!dreamsOnlyEnabledForDockUser || canCurrentUserDream(context));
}
+
+ /**
+ * Get private total size directly.
+ * Referred from StorageManager
+ */
+ public static long getPrimaryStorageSize() {
+ // TODO(b/288103116): remove this method once support by StorageManager.
+ return Environment.getDataDirectory().getTotalSpace()
+ + Environment.getRootDirectory().getTotalSpace();
+ }
+
}
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
index 0767e65..6bee62c 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java
@@ -81,6 +81,8 @@
final RestrictedSwitchPreference preference =
(RestrictedSwitchPreference) pref;
final CharSequence label = mPkgInfo.applicationInfo.loadLabel(mPm);
+ final boolean isAllowedCn = mCn.flattenToShortString().length()
+ <= NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH;
final boolean isEnabled = isServiceEnabled(mCn);
preference.setChecked(isEnabled);
preference.setOnPreferenceChangeListener((p, newValue) -> {
@@ -105,7 +107,8 @@
return false;
}
});
- preference.updateState(mCn.getPackageName(), mPkgInfo.applicationInfo.uid, isEnabled);
+ preference.updateState(
+ mCn.getPackageName(), mPkgInfo.applicationInfo.uid, isAllowedCn, isEnabled);
}
public void disable(final ComponentName cn) {
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 701967b..00f18e8 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -116,10 +116,6 @@
pref.setTitle(profile.getNameResource(mCachedDevice.getDevice()));
pref.setOnPreferenceClickListener(this);
pref.setOrder(profile.getOrdinal());
-
- if (profile instanceof LeAudioProfile) {
- pref.setSummary(R.string.device_details_leaudio_toggle_summary);
- }
return pref;
}
diff --git a/src/com/android/settings/deviceinfo/StorageCategoryFragment.java b/src/com/android/settings/deviceinfo/StorageCategoryFragment.java
index 52f453d..d1d4cbe 100644
--- a/src/com/android/settings/deviceinfo/StorageCategoryFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageCategoryFragment.java
@@ -211,7 +211,9 @@
setLoading(false /* loading */, false /* animate */);
- final long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
+ // TODO(b/288103116): replace with new API to get TotalBytes before rounding
+ // once support by StorageManager.
+ final long privateUsedBytes = Utils.getPrimaryStorageSize() - mStorageInfo.freeBytes;
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
mPreferenceController.setUsedSize(privateUsedBytes);
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index f31f2be..4138057 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -387,7 +387,9 @@
setLoading(false /* loading */, false /* animate */);
- final long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
+ // TODO(b/288103116): replace with new API to get TotalBytes before rounding
+ // once support by StorageManager.
+ final long privateUsedBytes = Utils.getPrimaryStorageSize() - mStorageInfo.freeBytes;
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
mPreferenceController.setUsedSize(privateUsedBytes);
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
diff --git a/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java b/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
index e682783..43dc9dd 100644
--- a/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
+++ b/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
@@ -74,10 +74,14 @@
return ThreadUtils.postOnBackgroundThread(() -> {
final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(
getStorageManagerVolumeProvider());
- storageCacheHelper.cacheUsedSize(info.totalBytes - info.freeBytes);
+
+ // TODO(b/288103116): replace with new API to get TotalBytes before rounding
+ // once support by StorageManager.
+ long usedBytes = Utils.getPrimaryStorageSize() - info.freeBytes;
+ storageCacheHelper.cacheUsedSize(usedBytes);
ThreadUtils.postOnMainThread(() -> {
preference.setSummary(
- getSummary(info.totalBytes - info.freeBytes, info.totalBytes));
+ getSummary(usedBytes, info.totalBytes));
});
});
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
index be2a64a..23559d4 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
@@ -26,6 +26,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.UsageProgressBarPreference;
@@ -92,7 +93,9 @@
if (mStorageEntry.isPrivate()) {
// StorageStatsManager can only query private storages.
mTotalBytes = mStorageStatsManager.getTotalBytes(mStorageEntry.getFsUuid());
- mUsedBytes = mTotalBytes
+ // TODO(b/288103116): replace with new API to get TotalBytes before rounding
+ // once support by StorageManager.
+ mUsedBytes = Utils.getPrimaryStorageSize()
- mStorageStatsManager.getFreeBytes(mStorageEntry.getFsUuid());
} else {
final File rootFile = mStorageEntry.getPath();
diff --git a/src/com/android/settings/localepicker/LocaleDialogFragment.java b/src/com/android/settings/localepicker/LocaleDialogFragment.java
index ad9e10f..f54446a 100644
--- a/src/com/android/settings/localepicker/LocaleDialogFragment.java
+++ b/src/com/android/settings/localepicker/LocaleDialogFragment.java
@@ -164,15 +164,18 @@
public void onClick(DialogInterface dialog, int which) {
if (mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
int result = Activity.RESULT_CANCELED;
+ boolean changed = false;
if (which == DialogInterface.BUTTON_POSITIVE) {
result = Activity.RESULT_OK;
+ changed = true;
}
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
intent.putExtras(bundle);
mParent.onActivityResult(DIALOG_CONFIRM_SYSTEM_DEFAULT, result, intent);
- mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE);
+ mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE,
+ changed);
}
mShouldKeepDialog = false;
}
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index edd3026..3d7976a 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -16,6 +16,7 @@
package com.android.settings.localepicker;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
@@ -37,6 +38,7 @@
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocaleStore;
import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.shortcut.ShortcutsUpdateTask;
import java.text.NumberFormat;
@@ -210,6 +212,13 @@
Log.e(TAG, String.format(Locale.US,
"Negative position in onItemMove %d -> %d", fromPosition, toPosition));
}
+
+ if (fromPosition != toPosition) {
+ FeatureFactory.getFactory(mContext).getMetricsFeatureProvider()
+ .action(mContext, SettingsEnums.ACTION_REORDER_LANGUAGE,
+ mDragLocale.getLocale().toLanguageTag() + " move to " + toPosition);
+ }
+
notifyItemChanged(fromPosition); // to update the numbers
notifyItemChanged(toPosition);
notifyItemMoved(fromPosition, toPosition);
@@ -244,8 +253,13 @@
void removeChecked() {
int itemCount = mFeedItemList.size();
+ LocaleStore.LocaleInfo localeInfo;
for (int i = itemCount - 1; i >= 0; i--) {
- if (mFeedItemList.get(i).getChecked()) {
+ localeInfo = mFeedItemList.get(i);
+ if (localeInfo.getChecked()) {
+ FeatureFactory.getFactory(mContext).getMetricsFeatureProvider()
+ .action(mContext, SettingsEnums.ACTION_REMOVE_LANGUAGE,
+ localeInfo.getLocale().toLanguageTag());
mFeedItemList.remove(i);
}
}
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 55cff3b..dfdb942 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -199,9 +199,11 @@
localeInfo = (LocaleStore.LocaleInfo) data.getSerializableExtra(INTENT_LOCALE_KEY);
String preferencesTags = Settings.System.getString(
getContext().getContentResolver(), Settings.System.LOCALE_PREFERENCES);
-
- mAdapter.addLocale(mayAppendUnicodeTags(localeInfo, preferencesTags));
+ localeInfo = mayAppendUnicodeTags(localeInfo, preferencesTags);
+ mAdapter.addLocale(localeInfo);
updateVisibilityOfRemoveMenu();
+ mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_ADD_LANGUAGE,
+ localeInfo.getLocale().toLanguageTag());
} else if (requestCode == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
localeInfo = mAdapter.getFeedItemList().get(0);
if (resultCode == Activity.RESULT_OK) {
@@ -214,6 +216,9 @@
LocaleDialogFragment localeDialogFragment = LocaleDialogFragment.newInstance();
localeDialogFragment.setArguments(args);
localeDialogFragment.show(mFragmentManager, TAG_DIALOG_NOT_AVAILABLE);
+ mMetricsFeatureProvider.action(getContext(),
+ SettingsEnums.ACTION_NOT_SUPPORTED_SYSTEM_LANGUAGE,
+ localeInfo.getLocale().toLanguageTag());
}
} else {
mAdapter.notifyListChanged(localeInfo);
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 9d953bf..0cd12fe 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.content.SharedPreferences;
import android.os.ParcelUuid;
import android.provider.Settings;
import android.telephony.PhoneNumberUtils;
@@ -61,6 +62,10 @@
public class SubscriptionUtil {
private static final String TAG = "SubscriptionUtil";
private static final String PROFILE_GENERIC_DISPLAY_NAME = "CARD";
+ @VisibleForTesting
+ static final String SUB_ID = "sub_id";
+ @VisibleForTesting
+ static final String KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME = "unique_subscription_displayName";
private static List<SubscriptionInfo> sAvailableResultsForTesting;
private static List<SubscriptionInfo> sActiveResultsForTesting;
@@ -265,20 +270,21 @@
// Map of SubscriptionId to DisplayName
final Supplier<Stream<DisplayInfo>> originalInfos =
() -> getAvailableSubscriptions(context)
- .stream()
- .filter(i -> {
- // Filter out null values.
- return (i != null && i.getDisplayName() != null);
- })
- .map(i -> {
- DisplayInfo info = new DisplayInfo();
- info.subscriptionInfo = i;
- String displayName = i.getDisplayName().toString();
- info.originalName = TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME)
- ? context.getResources().getString(R.string.sim_card)
- : displayName.trim();
- return info;
- });
+ .stream()
+ .filter(i -> {
+ // Filter out null values.
+ return (i != null && i.getDisplayName() != null);
+ })
+ .map(i -> {
+ DisplayInfo info = new DisplayInfo();
+ info.subscriptionInfo = i;
+ String displayName = i.getDisplayName().toString();
+ info.originalName =
+ TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME)
+ ? context.getResources().getString(R.string.sim_card)
+ : displayName.trim();
+ return info;
+ });
// TODO(goldmanj) consider using a map of DisplayName to SubscriptionInfos.
// A Unique set of display names
@@ -292,6 +298,14 @@
// If a display name is duplicate, append the final 4 digits of the phone number.
// Creates a mapping of Subscription id to original display name + phone number display name
final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
+ String cachedDisplayName = getDisplayNameFromSharedPreference(
+ context, info.subscriptionInfo.getSubscriptionId());
+ if (!TextUtils.isEmpty(cachedDisplayName)) {
+ Log.d(TAG, "use cached display name : " + cachedDisplayName);
+ info.uniqueName = cachedDisplayName;
+ return info;
+ }
+
if (duplicateOriginalNames.contains(info.originalName)) {
// This may return null, if the user cannot view the phone number itself.
final String phoneNumber = getBidiFormattedPhoneNumber(context,
@@ -299,15 +313,17 @@
String lastFourDigits = "";
if (phoneNumber != null) {
lastFourDigits = (phoneNumber.length() > 4)
- ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
+ ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
}
-
if (TextUtils.isEmpty(lastFourDigits)) {
info.uniqueName = info.originalName;
} else {
info.uniqueName = info.originalName + " " + lastFourDigits;
+ Log.d(TAG, "Cache display name [" + info.uniqueName + "] for sub id "
+ + info.subscriptionInfo.getSubscriptionId());
+ saveDisplayNameToSharedPreference(
+ context, info.subscriptionInfo.getSubscriptionId(), info.uniqueName);
}
-
} else {
info.uniqueName = info.originalName;
}
@@ -371,6 +387,27 @@
return getUniqueSubscriptionDisplayName(info.getSubscriptionId(), context);
}
+
+ private static SharedPreferences getDisplayNameSharedPreferences(Context context) {
+ return context.getSharedPreferences(
+ KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE);
+ }
+
+ private static SharedPreferences.Editor getDisplayNameSharedPreferenceEditor(Context context) {
+ return getDisplayNameSharedPreferences(context).edit();
+ }
+
+ private static void saveDisplayNameToSharedPreference(
+ Context context, int subId, CharSequence displayName) {
+ getDisplayNameSharedPreferenceEditor(context)
+ .putString(SUB_ID + subId, String.valueOf(displayName))
+ .apply();
+ }
+
+ private static String getDisplayNameFromSharedPreference(Context context, int subid) {
+ return getDisplayNameSharedPreferences(context).getString(SUB_ID + subid, "");
+ }
+
public static String getDisplayName(SubscriptionInfo info) {
final CharSequence name = info.getDisplayName();
if (name != null) {
diff --git a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
index 245ac83..7addb59 100644
--- a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
@@ -18,7 +18,6 @@
import android.os.SystemClock;
import android.text.TextUtils;
-import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -66,8 +65,7 @@
TelephonyStatusControlSession setTelephonyAvailabilityStatus(
Collection<AbstractPreferenceController> listOfPrefControllers) {
- return (new TelephonyStatusControlSession.Builder(listOfPrefControllers))
- .build();
+ return new TelephonyStatusControlSession(listOfPrefControllers, getLifecycle());
}
@Override
diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
deleted file mode 100644
index 3716f1f..0000000
--- a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2020 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 android.util.Log;
-
-import com.android.settings.core.BasePreferenceController;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-
-/**
- * Session for controlling the status of TelephonyPreferenceController(s).
- *
- * Within this session, result of {@link BasePreferenceController#availabilityStatus()}
- * would be under control.
- */
-public class TelephonyStatusControlSession implements AutoCloseable {
-
- private static final String LOG_TAG = "TelephonyStatusControlSS";
-
- private Collection<AbstractPreferenceController> mControllers;
- private Collection<Future<Boolean>> mResult = new ArrayList<>();
-
- /**
- * Buider of session
- */
- public static class Builder {
- private Collection<AbstractPreferenceController> mControllers;
-
- /**
- * Constructor
- *
- * @param controllers is a collection of {@link AbstractPreferenceController}
- * which would have {@link BasePreferenceController#availabilityStatus()}
- * under control within this session.
- */
- public Builder(Collection<AbstractPreferenceController> controllers) {
- mControllers = controllers;
- }
-
- /**
- * Method to build this session.
- * @return {@link TelephonyStatusControlSession} session been setup.
- */
- public TelephonyStatusControlSession build() {
- return new TelephonyStatusControlSession(mControllers);
- }
- }
-
- private TelephonyStatusControlSession(Collection<AbstractPreferenceController> controllers) {
- mControllers = controllers;
- controllers.forEach(prefCtrl -> mResult
- .add(ThreadUtils.postOnBackgroundThread(() -> setupAvailabilityStatus(prefCtrl))));
-
- }
-
- /**
- * Close the session.
- *
- * No longer control the status.
- */
- public void close() {
- //check the background thread is finished then unset the status of availability.
-
- for (Future<Boolean> result : mResult) {
- try {
- result.get();
- } catch (ExecutionException | InterruptedException exception) {
- Log.e(LOG_TAG, "setup availability status failed!", exception);
- }
- }
- unsetAvailabilityStatus(mControllers);
- }
-
- private Boolean setupAvailabilityStatus(AbstractPreferenceController controller) {
- try {
- if (controller instanceof TelephonyAvailabilityHandler) {
- int status = ((BasePreferenceController) controller)
- .getAvailabilityStatus();
- ((TelephonyAvailabilityHandler) controller).setAvailabilityStatus(status);
- }
- return true;
- } catch (Exception exception) {
- Log.e(LOG_TAG, "Setup availability status failed!", exception);
- return false;
- }
- }
-
- private void unsetAvailabilityStatus(
- Collection<AbstractPreferenceController> controllerLists) {
- controllerLists.stream()
- .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
- .map(TelephonyAvailabilityHandler.class::cast)
- .forEach(controller -> {
- controller.unsetAvailabilityStatus();
- });
- }
-}
diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt
new file mode 100644
index 0000000..0e63c8c
--- /dev/null
+++ b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.network.telephony
+
+import android.util.Log
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.coroutineScope
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.core.AbstractPreferenceController
+import com.google.common.collect.Sets
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.yield
+
+/**
+ * Session for controlling the status of TelephonyPreferenceController(s).
+ *
+ * Within this session, result of [BasePreferenceController.getAvailabilityStatus]
+ * would be under control.
+ */
+class TelephonyStatusControlSession(
+ private val controllers: Collection<AbstractPreferenceController>,
+ lifecycle: Lifecycle,
+) : AutoCloseable {
+ private var job: Job? = null
+ private val controllerSet = Sets.newConcurrentHashSet<TelephonyAvailabilityHandler>()
+
+ init {
+ job = lifecycle.coroutineScope.launch(Dispatchers.Default) {
+ for (controller in controllers) {
+ launch {
+ setupAvailabilityStatus(controller)
+ }
+ }
+ }
+ }
+
+ /**
+ * Close the session.
+ *
+ * No longer control the status.
+ */
+ override fun close() {
+ job?.cancel()
+ unsetAvailabilityStatus()
+ }
+
+ private suspend fun setupAvailabilityStatus(controller: AbstractPreferenceController): Boolean =
+ try {
+ if (controller is TelephonyAvailabilityHandler) {
+ val status = (controller as BasePreferenceController).availabilityStatus
+ yield() // prompt cancellation guarantee
+ if (controllerSet.add(controller)) {
+ controller.setAvailabilityStatus(status)
+ }
+ }
+ true
+ } catch (exception: Exception) {
+ Log.e(LOG_TAG, "Setup availability status failed!", exception)
+ false
+ }
+
+ private fun unsetAvailabilityStatus() {
+ for (controller in controllerSet) {
+ controller.unsetAvailabilityStatus()
+ }
+ }
+
+ companion object {
+ private const val LOG_TAG = "TelephonyStatusControlSS"
+ }
+}
diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java
index e40a2b4..79df55a 100644
--- a/src/com/android/settings/notification/MediaVolumePreferenceController.java
+++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java
@@ -52,6 +52,7 @@
public MediaVolumePreferenceController(Context context) {
super(context, KEY_MEDIA_VOLUME);
+ mVolumePreferenceListener = this::updateContentDescription;
}
@Override
@@ -109,6 +110,18 @@
return false;
}
+ private void updateContentDescription() {
+ if (mPreference != null) {
+ if (mPreference.isMuted()) {
+ mPreference.updateContentDescription(
+ mContext.getString(R.string.volume_content_description_silent_mode,
+ mPreference.getTitle()));
+ } else {
+ mPreference.updateContentDescription(mPreference.getTitle());
+ }
+ }
+ }
+
@Override
public SliceAction getSliceEndItem(Context context) {
if (!isSupportEndItem()) {
diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
index dfe6df2..a6b565a 100644
--- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
+++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
@@ -67,7 +67,9 @@
mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL);
CharSequence mAppLabel;
- if (mComponentName == null || mComponentName.getPackageName() == null) {
+ if (mComponentName == null || mComponentName.getPackageName() == null
+ || mComponentName.flattenToString().length()
+ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
finish();
return;
}
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index 369c4f6..e2ef0dd 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -66,7 +66,6 @@
private static final String TAG = "NotifAccessSettings";
static final String ALLOWED_KEY = "allowed";
static final String NOT_ALLOWED_KEY = "not_allowed";
- private static final int MAX_CN_LENGTH = 500;
private static final ManagedServiceSettings.Config CONFIG =
new ManagedServiceSettings.Config.Builder()
@@ -150,7 +149,8 @@
for (ServiceInfo service : services) {
final ComponentName cn = new ComponentName(service.packageName, service.name);
boolean isAllowed = mNm.isNotificationListenerAccessGranted(cn);
- if (!isAllowed && cn.flattenToString().length() > MAX_CN_LENGTH) {
+ if (!isAllowed && cn.flattenToString().length()
+ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
continue;
}
diff --git a/src/com/android/settings/notification/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
index cf8a33f..fe7b70b 100644
--- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java
+++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java
@@ -26,6 +26,7 @@
import android.os.Looper;
import android.os.Message;
import android.service.notification.NotificationListenerService;
+import android.view.View;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.PreferenceScreen;
@@ -75,6 +76,7 @@
updateEffectsSuppressor();
selectPreferenceIconState();
+ updateContentDescription();
updateEnabledState();
}
@@ -120,23 +122,37 @@
}
@Override
- protected void selectPreferenceIconState() {
+ protected int getEffectiveRingerMode() {
+ if (mVibrator == null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ return AudioManager.RINGER_MODE_SILENT;
+ } else if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+ if (mHelper.getStreamVolume(AudioManager.STREAM_NOTIFICATION) == 0) {
+ // Ring is in normal, but notification is in silent.
+ return AudioManager.RINGER_MODE_SILENT;
+ }
+ }
+ return mRingerMode;
+ }
+
+ @Override
+ protected void updateContentDescription() {
if (mPreference != null) {
- if (mVibrator != null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
- mMuteIcon = mVibrateIconId;
- mPreference.showIcon(mVibrateIconId);
- } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT
- || mVibrator == null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
- mMuteIcon = mSilentIconId;
- mPreference.showIcon(mSilentIconId);
- } else { // ringmode normal: could be that we are still silent
- if (mHelper.getStreamVolume(AudioManager.STREAM_NOTIFICATION) == 0) {
- // ring is in normal, but notification is in silent
- mMuteIcon = mSilentIconId;
- mPreference.showIcon(mSilentIconId);
- } else {
- mPreference.showIcon(mNormalIconId);
- }
+ int ringerMode = getEffectiveRingerMode();
+ if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ mPreference.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
+ mPreference.updateContentDescription(
+ mContext.getString(
+ R.string.notification_volume_content_description_vibrate_mode));
+ } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+ mPreference.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
+ mPreference.updateContentDescription(
+ mContext.getString(R.string.volume_content_description_silent_mode,
+ mPreference.getTitle()));
+ } else {
+ // Set a11y mode to none in order not to trigger talkback while changing
+ // notification volume in normal mode.
+ mPreference.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_NONE);
+ mPreference.updateContentDescription(mPreference.getTitle());
}
}
}
@@ -169,6 +185,7 @@
break;
case NOTIFICATION_VOLUME_CHANGED:
selectPreferenceIconState();
+ updateContentDescription();
updateEnabledState();
break;
}
diff --git a/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java b/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java
index 3687770..ab65f8f 100644
--- a/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java
+++ b/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java
@@ -26,6 +26,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
import java.util.Objects;
@@ -54,6 +55,7 @@
if (mVibrator != null && !mVibrator.hasVibrator()) {
mVibrator = null;
}
+ mVolumePreferenceListener = this::updateContentDescription;
}
protected void updateEffectsSuppressor() {
@@ -123,6 +125,7 @@
}
mRingerMode = ringerMode;
selectPreferenceIconState();
+ updateContentDescription();
return true;
}
@@ -131,10 +134,11 @@
*/
protected void selectPreferenceIconState() {
if (mPreference != null) {
- if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+ int ringerMode = getEffectiveRingerMode();
+ if (ringerMode == AudioManager.RINGER_MODE_NORMAL) {
mPreference.showIcon(mNormalIconId);
} else {
- if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE && mVibrator != null) {
+ if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
mMuteIcon = mVibrateIconId;
} else {
mMuteIcon = mSilentIconId;
@@ -144,6 +148,28 @@
}
}
+ protected int getEffectiveRingerMode() {
+ if (mVibrator == null && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ return AudioManager.RINGER_MODE_SILENT;
+ }
+ return mRingerMode;
+ }
+
+ protected void updateContentDescription() {
+ if (mPreference != null) {
+ int ringerMode = getEffectiveRingerMode();
+ if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ mPreference.updateContentDescription(
+ mContext.getString(R.string.ringer_content_description_vibrate_mode));
+ } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+ mPreference.updateContentDescription(
+ mContext.getString(R.string.ringer_content_description_silent_mode));
+ } else {
+ mPreference.updateContentDescription(mPreference.getTitle());
+ }
+ }
+ }
+
protected abstract boolean hintsMatch(int hints);
}
diff --git a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
index b8a9908..91926e3 100644
--- a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
+++ b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java
@@ -65,6 +65,7 @@
mReceiver.register(true);
updateEffectsSuppressor();
selectPreferenceIconState();
+ updateContentDescription();
if (mPreference != null) {
mPreference.setVisible(getAvailabilityStatus() == AVAILABLE);
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java
index 14955c4..0000eba 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreference.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java
@@ -47,10 +47,13 @@
protected SeekBar mSeekBar;
private int mStream;
- private SeekBarVolumizer mVolumizer;
+ @VisibleForTesting
+ SeekBarVolumizer mVolumizer;
private Callback mCallback;
+ private Listener mListener;
private ImageView mIconView;
private TextView mSuppressionTextView;
+ private TextView mTitle;
private String mSuppressionText;
private boolean mMuted;
private boolean mZenMuted;
@@ -98,6 +101,10 @@
mCallback = callback;
}
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
public void onActivityResume() {
if (mStopped) {
init();
@@ -118,6 +125,7 @@
mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
+ mTitle = (TextView) view.findViewById(com.android.internal.R.id.title);
init();
}
@@ -142,6 +150,9 @@
mMuted = muted;
mZenMuted = zenMuted;
updateIconView();
+ if (mListener != null) {
+ mListener.onUpdateMuteState();
+ }
}
@Override
public void onStartTrackingTouch(SeekBarVolumizer sbv) {
@@ -165,6 +176,9 @@
mVolumizer.setSeekBar(mSeekBar);
updateIconView();
updateSuppressionText();
+ if (mListener != null) {
+ mListener.onUpdateMuteState();
+ }
if (!isEnabled()) {
mSeekBar.setEnabled(false);
mVolumizer.stop();
@@ -175,7 +189,7 @@
if (mIconView == null) return;
if (mIconResId != 0) {
mIconView.setImageResource(mIconResId);
- } else if (mMuteIconResId != 0 && mMuted && !mZenMuted) {
+ } else if (mMuteIconResId != 0 && isMuted()) {
mIconView.setImageResource(mMuteIconResId);
} else {
mIconView.setImageDrawable(getIcon());
@@ -208,6 +222,10 @@
updateSuppressionText();
}
+ protected boolean isMuted() {
+ return mMuted && !mZenMuted;
+ }
+
protected void updateSuppressionText() {
if (mSuppressionTextView != null && mSeekBar != null) {
mSuppressionTextView.setText(mSuppressionText);
@@ -216,6 +234,19 @@
}
}
+ /**
+ * Update content description of title to improve talkback announcements.
+ */
+ protected void updateContentDescription(CharSequence contentDescription) {
+ if (mTitle == null) return;
+ mTitle.setContentDescription(contentDescription);
+ }
+
+ protected void setAccessibilityLiveRegion(int mode) {
+ if (mTitle == null) return;
+ mTitle.setAccessibilityLiveRegion(mode);
+ }
+
public interface Callback {
void onSampleStarting(SeekBarVolumizer sbv);
void onStreamValueChanged(int stream, int progress);
@@ -225,4 +256,15 @@
*/
void onStartTrackingTouch(SeekBarVolumizer sbv);
}
+
+ /**
+ * Listener to view updates in volumeSeekbarPreference.
+ */
+ public interface Listener {
+
+ /**
+ * Listener to mute state updates.
+ */
+ void onUpdateMuteState();
+ }
}
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
index 0414565..285e8dd 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
@@ -36,6 +36,7 @@
protected VolumeSeekBarPreference mPreference;
protected VolumeSeekBarPreference.Callback mVolumePreferenceCallback;
protected AudioHelper mHelper;
+ protected VolumeSeekBarPreference.Listener mVolumePreferenceListener;
public VolumeSeekBarPreferenceController(Context context, String key) {
super(context, key);
@@ -62,6 +63,7 @@
protected void setupVolPreference(PreferenceScreen screen) {
mPreference = screen.findPreference(getPreferenceKey());
mPreference.setCallback(mVolumePreferenceCallback);
+ mPreference.setListener(mVolumePreferenceListener);
mPreference.setStream(getAudioStream());
mPreference.setMuteIcon(getMuteIcon());
}
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 314ce05..f2f6520 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -23,6 +23,7 @@
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PASSWORD;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import android.app.Activity;
import android.app.KeyguardManager;
@@ -32,6 +33,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.graphics.Color;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
@@ -379,6 +381,12 @@
// Translucent activity that is "visible", so it doesn't complain about finish()
// not being called before onResume().
setVisible(true);
+
+ if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ != Configuration.UI_MODE_NIGHT_YES) {
+ getWindow().getInsetsController().setSystemBarsAppearance(
+ APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS);
+ }
}
@Override
diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java
index 2cad181..4424b4f 100644
--- a/src/com/android/settings/password/SetupChooseLockPattern.java
+++ b/src/com/android/settings/password/SetupChooseLockPattern.java
@@ -90,6 +90,12 @@
}
// Show the skip button during SUW but not during Settings > Biometric Enrollment
mSkipOrClearButton.setOnClickListener(this::onSkipOrClearButtonClick);
+
+ final View headerView = view.findViewById(R.id.sud_layout_header);
+ final ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) headerView.getLayoutParams();
+ lp.bottomMargin = 0;
+ view.setLayoutParams(lp);
return view;
}
diff --git a/src/com/android/settings/regionalpreferences/NumberingSystemItemController.java b/src/com/android/settings/regionalpreferences/NumberingSystemItemController.java
index e3a8d23..2a99e99 100644
--- a/src/com/android/settings/regionalpreferences/NumberingSystemItemController.java
+++ b/src/com/android/settings/regionalpreferences/NumberingSystemItemController.java
@@ -153,7 +153,7 @@
private void handleLanguageSelect(Preference preference) {
String selectedLanguage = preference.getKey();
mMetricsFeatureProvider.action(mContext,
- SettingsEnums.ACTION_CHOOSE_LANGUAGE_FOR_NUMBERS_PREFERENCES);
+ SettingsEnums.ACTION_CHOOSE_LANGUAGE_FOR_NUMBERS_PREFERENCES, selectedLanguage);
final Bundle extra = new Bundle();
extra.putString(RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE,
ARG_VALUE_NUMBERING_SYSTEM_SELECT);
@@ -177,7 +177,8 @@
saveNumberingSystemToLocale(Locale.forLanguageTag(mSelectedLanguage),
numberingSystem);
mMetricsFeatureProvider.action(mContext,
- SettingsEnums.ACTION_SET_NUMBERS_PREFERENCES);
+ SettingsEnums.ACTION_SET_NUMBERS_PREFERENCES,
+ updatedLocale.getDisplayName() + ": " + numberingSystem);
// After updated locale to framework, this fragment will recreate,
// so it needs to update the argument of selected language.
Bundle bundle = new Bundle();
diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
index 1e39fff..432ce0e 100644
--- a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
+++ b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
@@ -59,6 +59,8 @@
TickButtonPreference pref = new TickButtonPreference(mContext);
mPreferenceCategory.addPreference(pref);
final String item = unitValues[i];
+ final String value = RegionalPreferencesDataUtils.getDefaultUnicodeExtensionData(
+ mContext, getExtensionTypes());
pref.setTitle(getPreferenceTitle(item));
pref.setKey(item);
pref.setOnPreferenceClickListener(clickedPref -> {
@@ -66,11 +68,10 @@
RegionalPreferencesDataUtils.savePreference(mContext, getExtensionTypes(),
item.equals(RegionalPreferencesDataUtils.DEFAULT_VALUE)
? null : item);
- mMetricsFeatureProvider.action(mContext, getMetricsActionKey());
+ mMetricsFeatureProvider.action(mContext, getMetricsActionKey(),
+ getPreferenceTitle(value) + " > " + getPreferenceTitle(item));
return true;
});
- String value = RegionalPreferencesDataUtils.getDefaultUnicodeExtensionData(mContext,
- getExtensionTypes());
pref.setSelected(!value.isEmpty() && item.equals(value));
}
}
diff --git a/src/com/android/settings/users/GuestTelephonyPreferenceController.java b/src/com/android/settings/users/GuestTelephonyPreferenceController.java
index a935b8a..2aa808f 100644
--- a/src/com/android/settings/users/GuestTelephonyPreferenceController.java
+++ b/src/com/android/settings/users/GuestTelephonyPreferenceController.java
@@ -17,6 +17,7 @@
package com.android.settings.users;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserManager;
@@ -73,6 +74,7 @@
public void updateState(Preference preference) {
super.updateState(preference);
mUserCaps.updateAddUserCapabilities(mContext);
- preference.setVisible(isAvailable() && mUserCaps.mUserSwitcherEnabled);
+ preference.setVisible(isAvailable() && mUserCaps.mUserSwitcherEnabled && mContext
+ .getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
}
}
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index d9fbc42..93b7c78 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -977,10 +977,10 @@
return;
}
try {
- getContext().getSystemService(UserManager.class)
- .removeUserWhenPossible(UserHandle.of(UserHandle.myUserId()),
- /* overrideDevicePolicy= */ false);
- ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
+ mUserManager.removeUserWhenPossible(
+ UserHandle.of(UserHandle.myUserId()), /* overrideDevicePolicy= */ false);
+ ActivityManager.getService().switchUser(
+ mUserManager.getPreviousForegroundUser().getIdentifier());
} catch (RemoteException re) {
Log.e(TAG, "Unable to remove self user");
}
@@ -1099,7 +1099,7 @@
}
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
- switchToUserId(UserHandle.USER_SYSTEM);
+ switchToUserId(mUserManager.getPreviousForegroundUser().getIdentifier());
}
private int createGuest() {
@@ -1139,8 +1139,8 @@
// Create a new guest in the foreground, and then immediately switch to it
int newGuestUserId = createGuest();
if (newGuestUserId == UserHandle.USER_NULL) {
- Log.e(TAG, "Could not create new guest, switching back to system user");
- switchToUserId(UserHandle.USER_SYSTEM);
+ Log.e(TAG, "Could not create new guest, switching back to previous user");
+ switchToUserId(mUserManager.getPreviousForegroundUser().getIdentifier());
mUserManager.removeUser(oldGuestUserId);
WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null);
return;
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java
new file mode 100644
index 0000000..86631ff
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.notification;
+
+import static com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_COMPONENT_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class NotificationAccessConfirmationActivityTest {
+
+ @Test
+ public void start_showsDialog() {
+ 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"));
+ }
+
+ @Test
+ public void start_withMissingPackage_finishes() {
+ ComponentName cn = new ComponentName("com.example", "com.example.SomeService");
+
+ NotificationAccessConfirmationActivity activity = startActivityWithIntent(cn);
+
+ assertThat(getDialogText(activity)).isNull();
+ assertThat(activity.isFinishing()).isTrue();
+ }
+
+ @Test
+ public void start_componentNameTooLong_finishes() {
+ ComponentName longCn = new ComponentName("com.example", Strings.repeat("Blah", 150));
+ installPackage(longCn.getPackageName(), "<Unused>");
+
+ NotificationAccessConfirmationActivity activity = startActivityWithIntent(longCn);
+
+ assertThat(getDialogText(activity)).isNull();
+ assertThat(activity.isFinishing()).isTrue();
+ }
+
+ private static NotificationAccessConfirmationActivity startActivityWithIntent(
+ ComponentName cn) {
+ return Robolectric.buildActivity(
+ NotificationAccessConfirmationActivity.class,
+ new Intent().putExtra(EXTRA_COMPONENT_NAME, cn))
+ .setup()
+ .get();
+ }
+
+ private static void installPackage(String packageName, String appName) {
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = packageName;
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.packageName = packageName;
+ pi.applicationInfo.name = appName;
+ shadowOf(RuntimeEnvironment.application.getPackageManager()).installPackage(pi);
+ }
+
+ @Nullable
+ private static String getDialogText(Activity activity) {
+ TextView tv = activity.getWindow().findViewById(android.R.id.message);
+ CharSequence text = (tv != null ? tv.getText() : null);
+ return text != null ? text.toString() : null;
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java
index 2d54c38..f7e32a2 100644
--- a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java
@@ -49,6 +49,8 @@
@Mock
private VolumeSeekBarPreference.Callback mCallback;
@Mock
+ private VolumeSeekBarPreference.Listener mListener;
+ @Mock
private AudioHelper mHelper;
private VolumeSeekBarPreferenceControllerTestable mController;
@@ -59,7 +61,7 @@
when(mScreen.findPreference(nullable(String.class))).thenReturn(mPreference);
when(mPreference.getKey()).thenReturn("key");
mController = new VolumeSeekBarPreferenceControllerTestable(mContext, mCallback, true,
- mPreference.getKey());
+ mPreference.getKey(), mListener);
mController.setAudioHelper(mHelper);
}
@@ -70,18 +72,20 @@
verify(mPreference).setCallback(mCallback);
verify(mPreference).setStream(VolumeSeekBarPreferenceControllerTestable.AUDIO_STREAM);
verify(mPreference).setMuteIcon(VolumeSeekBarPreferenceControllerTestable.MUTE_ICON);
+ verify(mPreference).setListener(mListener);
}
@Test
public void displayPreference_notAvailable_shouldNotUpdatePreference() {
mController = new VolumeSeekBarPreferenceControllerTestable(mContext, mCallback, false,
- mPreference.getKey());
+ mPreference.getKey(), mListener);
mController.displayPreference(mScreen);
verify(mPreference, never()).setCallback(any(VolumeSeekBarPreference.Callback.class));
verify(mPreference, never()).setStream(anyInt());
verify(mPreference, never()).setMuteIcon(anyInt());
+ verify(mPreference, never()).setListener(mListener);
}
@Test
@@ -157,10 +161,12 @@
private boolean mAvailable;
VolumeSeekBarPreferenceControllerTestable(Context context,
- VolumeSeekBarPreference.Callback callback, boolean available, String key) {
+ VolumeSeekBarPreference.Callback callback, boolean available, String key,
+ VolumeSeekBarPreference.Listener listener) {
super(context, key);
setCallback(callback);
mAvailable = available;
+ mVolumePreferenceListener = listener;
}
@Override
diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
index d74f76a..59f0bcb 100644
--- a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
@@ -18,11 +18,14 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.media.AudioManager;
+import android.preference.SeekBarVolumizer;
+import android.widget.SeekBar;
import org.junit.Before;
import org.junit.Test;
@@ -34,18 +37,28 @@
@RunWith(RobolectricTestRunner.class)
public class VolumeSeekBarPreferenceTest {
+ private static final CharSequence CONTENT_DESCRIPTION = "TEST";
@Mock
private AudioManager mAudioManager;
@Mock
private VolumeSeekBarPreference mPreference;
@Mock
private Context mContext;
+ @Mock
+ private SeekBar mSeekBar;
+ @Mock
+ private SeekBarVolumizer mVolumizer;
+ private VolumeSeekBarPreference.Listener mListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+ doCallRealMethod().when(mPreference).updateContentDescription(CONTENT_DESCRIPTION);
+ mPreference.mSeekBar = mSeekBar;
mPreference.mAudioManager = mAudioManager;
+ mPreference.mVolumizer = mVolumizer;
+ mListener = () -> mPreference.updateContentDescription(CONTENT_DESCRIPTION);
}
@Test
@@ -65,4 +78,24 @@
verify(mPreference).setMin(min);
verify(mPreference).setProgress(progress);
}
+
+ @Test
+ public void init_listenerIsCalled() {
+ doCallRealMethod().when(mPreference).setListener(mListener);
+ doCallRealMethod().when(mPreference).init();
+
+ mPreference.setListener(mListener);
+ mPreference.init();
+
+ verify(mPreference).updateContentDescription(CONTENT_DESCRIPTION);
+ }
+
+ @Test
+ public void init_listenerNotSet_noException() {
+ doCallRealMethod().when(mPreference).init();
+
+ mPreference.init();
+
+ verify(mPreference, never()).updateContentDescription(CONTENT_DESCRIPTION);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
index c5e0813..2f46986 100644
--- a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
@@ -28,6 +28,7 @@
import android.os.UserHandle;
import android.util.TypedValue;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
@@ -114,6 +115,14 @@
assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
}
+ @Test
+ public void headerView_noBottomMargin() {
+ final View header = mActivity.findViewById(R.id.sud_layout_header);
+ final ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) header.getLayoutParams();
+ assertThat(lp.bottomMargin).isEqualTo(0);
+ }
+
private void verifyScreenLockOptionsShown() {
final Button button = mActivity.findViewById(R.id.screen_lock_options);
assertThat(button).isNotNull();
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt
new file mode 100644
index 0000000..7e6a91b
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.network.telephony
+
+import android.content.Context
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.spa.testutils.waitUntil
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class TelephonyStatusControlSessionTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun init() = runTest {
+ val controller = TestController(context)
+
+ val session = TelephonyStatusControlSession(
+ controllers = listOf(controller),
+ lifecycle = TestLifecycleOwner().lifecycle,
+ )
+
+ waitUntil { controller.availabilityStatus == STATUS }
+ session.close()
+ }
+
+ @Test
+ fun close() = runTest {
+ val controller = TestController(context)
+
+ val session = TelephonyStatusControlSession(
+ controllers = listOf(controller),
+ lifecycle = TestLifecycleOwner().lifecycle,
+ )
+ session.close()
+
+ assertThat(controller.availabilityStatus).isNull()
+ }
+
+ private companion object {
+ const val KEY = "key"
+ const val STATUS = BasePreferenceController.AVAILABLE
+ }
+
+ private class TestController(context: Context) : BasePreferenceController(context, KEY),
+ TelephonyAvailabilityHandler {
+
+ var availabilityStatus: Int? = null
+ override fun getAvailabilityStatus(): Int = STATUS
+
+ override fun setAvailabilityStatus(status: Int) {
+ availabilityStatus = status
+ }
+
+ override fun unsetAvailabilityStatus() {
+ availabilityStatus = null
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
index 249b713..4601a1c 100644
--- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java
@@ -84,6 +84,36 @@
}
@Test
+ public void updateState_enabled() {
+ when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
+ AppOpsManager.MODE_ALLOWED);
+ when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
+ mContext);
+ pref.setAppOps(mAppOpsManager);
+
+ mController.updateState(pref);
+
+ assertThat(pref.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void updateState_invalidCn_disabled() {
+ ComponentName longCn = new ComponentName("com.example.package",
+ com.google.common.base.Strings.repeat("Blah", 150));
+ mController.setCn(longCn);
+ when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
+ AppOpsManager.MODE_ALLOWED);
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
+ mContext);
+ pref.setAppOps(mAppOpsManager);
+
+ mController.updateState(pref);
+
+ assertThat(pref.isEnabled()).isFalse();
+ }
+
+ @Test
public void updateState_checked() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ALLOWED);
diff --git a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
index 63dca7e..f063042 100644
--- a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
+++ b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
@@ -16,26 +16,32 @@
package com.android.settings.network;
+import static com.android.settings.network.SubscriptionUtil.KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME;
+import static com.android.settings.network.SubscriptionUtil.SUB_ID;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import com.android.settings.R;
-
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.settings.R;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -445,6 +451,35 @@
}
@Test
+ public void getUniqueDisplayName_hasRecord_useRecordBeTheResult() {
+ final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
+ final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
+ when(info1.getSubscriptionId()).thenReturn(SUBID_1);
+ when(info2.getSubscriptionId()).thenReturn(SUBID_2);
+ when(info1.getDisplayName()).thenReturn(CARRIER_1);
+ when(info2.getDisplayName()).thenReturn(CARRIER_1);
+ when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(
+ Arrays.asList(info1, info2));
+
+ SharedPreferences sp = mock(SharedPreferences.class);
+ when(mContext.getSharedPreferences(
+ KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE)).thenReturn(sp);
+ when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + "6789");
+ when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + "4321");
+
+
+ final CharSequence nameOfSub1 =
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(info1, mContext);
+ final CharSequence nameOfSub2 =
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(info2, mContext);
+
+ assertThat(nameOfSub1).isNotNull();
+ assertThat(nameOfSub2).isNotNull();
+ assertEquals(CARRIER_1 + "6789", nameOfSub1.toString());
+ assertEquals(CARRIER_1 + "4321", nameOfSub2.toString());
+ }
+
+ @Test
public void isInactiveInsertedPSim_nullSubInfo_doesNotCrash() {
assertThat(SubscriptionUtil.isInactiveInsertedPSim(null)).isFalse();
}