diff --git a/res/layout/preference_check_icon.xml b/res/layout/preference_check_icon.xml index 1b759fcc045..bd0dd79b421 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 927035d3095..897d76a8c26 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7180,6 +7180,18 @@ Notification volume + + Ringer silent + + + Ringer vibrate + + + Notification volume muted, notifications will vibrate + + + %1$s muted + Unavailable because ring is muted diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java index 092207bdcf5..d1e113767ff 100644 --- a/src/com/android/settings/localepicker/AppLocalePickerActivity.java +++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java @@ -18,6 +18,7 @@ package com.android.settings.localepicker; import android.app.FragmentTransaction; import android.app.LocaleManager; +import android.app.settings.SettingsEnums; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; @@ -37,15 +38,22 @@ import com.android.settings.R; import com.android.settings.applications.AppLocaleUtil; import com.android.settings.applications.appinfo.AppLocaleDetails; import com.android.settings.core.SettingsBaseActivity; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class AppLocalePickerActivity extends SettingsBaseActivity implements LocalePickerWithRegion.LocaleSelectedListener, MenuItem.OnActionExpandListener { private static final String TAG = AppLocalePickerActivity.class.getSimpleName(); + private static final int SIM_LOCALE = 1 << 0; + private static final int SYSTEM_LOCALE = 1 << 1; + private static final int APP_LOCALE = 1 << 2; + private static final int IME_LOCALE = 1 << 3; private String mPackageName; private LocalePickerWithRegion mLocalePickerWithRegion; private AppLocaleDetails mAppLocaleDetails; private View mAppLocaleDetailContainer; + private MetricsFeatureProvider mMetricsFeatureProvider; @Override public void onCreate(Bundle savedInstanceState) { @@ -71,6 +79,7 @@ public class AppLocalePickerActivity extends SettingsBaseActivity setTitle(R.string.app_locale_picker_title); getActionBar().setDisplayHomeAsUpEnabled(true); + mMetricsFeatureProvider = FeatureFactory.getFactory(this).getMetricsFeatureProvider(); mLocalePickerWithRegion = LocalePickerWithRegion.createLanguagePicker( this, @@ -99,6 +108,7 @@ public class AppLocalePickerActivity extends SettingsBaseActivity if (localeInfo == null || localeInfo.getLocale() == null || localeInfo.isSystemLocale()) { setAppDefaultLocale(""); } else { + logLocaleSource(localeInfo); setAppDefaultLocale(localeInfo.getLocale().toLanguageTag()); } finish(); @@ -177,4 +187,32 @@ public class AppLocalePickerActivity extends SettingsBaseActivity return false; } + + private void logLocaleSource(LocaleStore.LocaleInfo localeInfo) { + if (!localeInfo.isSuggested() || localeInfo.isAppCurrentLocale()) { + return; + } + int localeSource = 0; + if (hasSuggestionType(localeInfo, + LocaleStore.LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE)) { + localeSource |= SYSTEM_LOCALE; + } + if (hasSuggestionType(localeInfo, + LocaleStore.LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE)) { + localeSource |= APP_LOCALE; + } + if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE)) { + localeSource |= IME_LOCALE; + } + if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_SIM)) { + localeSource |= SIM_LOCALE; + } + mMetricsFeatureProvider.action(this, + SettingsEnums.ACTION_CHANGE_APP_LANGUAGE_FROM_SUGGESTED, localeSource); + } + + private static boolean hasSuggestionType(LocaleStore.LocaleInfo localeInfo, + int suggestionType) { + return localeInfo.isSuggestionOfType(suggestionType); + } } diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java index 6c254d954c1..3d7976ab624 100644 --- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -216,7 +216,7 @@ class LocaleDragAndDropAdapter if (fromPosition != toPosition) { FeatureFactory.getFactory(mContext).getMetricsFeatureProvider() .action(mContext, SettingsEnums.ACTION_REORDER_LANGUAGE, - mDragLocale.getLocale().getDisplayName() + " move to " + toPosition); + mDragLocale.getLocale().toLanguageTag() + " move to " + toPosition); } notifyItemChanged(fromPosition); // to update the numbers @@ -259,7 +259,7 @@ class LocaleDragAndDropAdapter if (localeInfo.getChecked()) { FeatureFactory.getFactory(mContext).getMetricsFeatureProvider() .action(mContext, SettingsEnums.ACTION_REMOVE_LANGUAGE, - localeInfo.getLocale().getDisplayName()); + 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 17d3a6aa996..dfdb9428f9c 100644 --- a/src/com/android/settings/localepicker/LocaleListEditor.java +++ b/src/com/android/settings/localepicker/LocaleListEditor.java @@ -203,7 +203,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View mAdapter.addLocale(localeInfo); updateVisibilityOfRemoveMenu(); mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_ADD_LANGUAGE, - localeInfo.getLocale().getDisplayName()); + localeInfo.getLocale().toLanguageTag()); } else if (requestCode == DIALOG_CONFIRM_SYSTEM_DEFAULT) { localeInfo = mAdapter.getFeedItemList().get(0); if (resultCode == Activity.RESULT_OK) { @@ -218,7 +218,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View localeDialogFragment.show(mFragmentManager, TAG_DIALOG_NOT_AVAILABLE); mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_NOT_SUPPORTED_SYSTEM_LANGUAGE, - localeInfo.getLocale().getDisplayName()); + 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 9d953bf3139..0cd12fe6354 100644 --- a/src/com/android/settings/network/SubscriptionUtil.java +++ b/src/com/android/settings/network/SubscriptionUtil.java @@ -23,6 +23,7 @@ import static com.android.internal.util.CollectionUtils.emptyIfNull; 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 @@ import java.util.stream.Stream; 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 sAvailableResultsForTesting; private static List sActiveResultsForTesting; @@ -265,20 +270,21 @@ public class SubscriptionUtil { // Map of SubscriptionId to DisplayName final Supplier> 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 @@ public class SubscriptionUtil { // 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> 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 @@ public class SubscriptionUtil { 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 @@ public class SubscriptionUtil { 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/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java index e40a2b4af98..79df55a0048 100644 --- a/src/com/android/settings/notification/MediaVolumePreferenceController.java +++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java @@ -52,6 +52,7 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont public MediaVolumePreferenceController(Context context) { super(context, KEY_MEDIA_VOLUME); + mVolumePreferenceListener = this::updateContentDescription; } @Override @@ -109,6 +110,18 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont 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/NotificationVolumePreferenceController.java b/src/com/android/settings/notification/NotificationVolumePreferenceController.java index cf8a33f765c..fe7b70bf129 100644 --- a/src/com/android/settings/notification/NotificationVolumePreferenceController.java +++ b/src/com/android/settings/notification/NotificationVolumePreferenceController.java @@ -26,6 +26,7 @@ import android.os.Handler; 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 @@ public class NotificationVolumePreferenceController extends updateEffectsSuppressor(); selectPreferenceIconState(); + updateContentDescription(); updateEnabledState(); } @@ -120,23 +122,37 @@ public class NotificationVolumePreferenceController extends } @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 @@ public class NotificationVolumePreferenceController extends 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 36877707257..ab65f8f5a9d 100644 --- a/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java +++ b/src/com/android/settings/notification/RingerModeAffectedVolumePreferenceController.java @@ -26,6 +26,7 @@ import android.os.Vibrator; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; import java.util.Objects; @@ -54,6 +55,7 @@ public abstract class RingerModeAffectedVolumePreferenceController extends if (mVibrator != null && !mVibrator.hasVibrator()) { mVibrator = null; } + mVolumePreferenceListener = this::updateContentDescription; } protected void updateEffectsSuppressor() { @@ -123,6 +125,7 @@ public abstract class RingerModeAffectedVolumePreferenceController extends } mRingerMode = ringerMode; selectPreferenceIconState(); + updateContentDescription(); return true; } @@ -131,10 +134,11 @@ public abstract class RingerModeAffectedVolumePreferenceController extends */ 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 @@ public abstract class RingerModeAffectedVolumePreferenceController extends } } + 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 b8a99085f6d..91926e3c977 100644 --- a/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java +++ b/src/com/android/settings/notification/SeparateRingVolumePreferenceController.java @@ -65,6 +65,7 @@ public class SeparateRingVolumePreferenceController extends 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 14955c426e1..0000eba2ba7 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreference.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java @@ -47,10 +47,13 @@ public class VolumeSeekBarPreference extends SeekBarPreference { 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 @@ public class VolumeSeekBarPreference extends SeekBarPreference { mCallback = callback; } + public void setListener(Listener listener) { + mListener = listener; + } + public void onActivityResume() { if (mStopped) { init(); @@ -118,6 +125,7 @@ public class VolumeSeekBarPreference extends SeekBarPreference { 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 @@ public class VolumeSeekBarPreference extends SeekBarPreference { mMuted = muted; mZenMuted = zenMuted; updateIconView(); + if (mListener != null) { + mListener.onUpdateMuteState(); + } } @Override public void onStartTrackingTouch(SeekBarVolumizer sbv) { @@ -165,6 +176,9 @@ public class VolumeSeekBarPreference extends SeekBarPreference { mVolumizer.setSeekBar(mSeekBar); updateIconView(); updateSuppressionText(); + if (mListener != null) { + mListener.onUpdateMuteState(); + } if (!isEnabled()) { mSeekBar.setEnabled(false); mVolumizer.stop(); @@ -175,7 +189,7 @@ public class VolumeSeekBarPreference extends SeekBarPreference { 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 @@ public class VolumeSeekBarPreference extends SeekBarPreference { updateSuppressionText(); } + protected boolean isMuted() { + return mMuted && !mZenMuted; + } + protected void updateSuppressionText() { if (mSuppressionTextView != null && mSeekBar != null) { mSuppressionTextView.setText(mSuppressionText); @@ -216,6 +234,19 @@ public class VolumeSeekBarPreference extends SeekBarPreference { } } + /** + * 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 @@ public class VolumeSeekBarPreference extends SeekBarPreference { */ 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 0414565721e..285e8ddbeb9 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java @@ -36,6 +36,7 @@ public abstract class VolumeSeekBarPreferenceController extends 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 @@ public abstract class VolumeSeekBarPreferenceController extends 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/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java index c2111d64e02..1a268f5152b 100644 --- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java +++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java @@ -617,6 +617,9 @@ public class WifiP2pSettings extends DashboardFragment } private void onDeviceAvailable() { + if (mWifiP2pManager == null || sChannel == null) { + return; + } mWifiP2pManager.requestNetworkInfo(sChannel, networkInfo -> { if (sChannel == null) return; mWifiP2pManager.requestConnectionInfo(sChannel, wifip2pinfo -> { diff --git a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java index 48caecdf6f7..8fb3a5d06ab 100644 --- a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java +++ b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java @@ -18,6 +18,8 @@ package com.android.settings.localepicker; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -27,6 +29,7 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.app.ApplicationPackageManager; import android.app.LocaleConfig; +import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -45,6 +48,7 @@ import androidx.annotation.ArrayRes; import com.android.internal.app.LocaleStore; import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppLocaleUtil; +import com.android.settings.testutils.FakeFeatureFactory; import org.junit.After; import org.junit.Before; @@ -79,6 +83,7 @@ import java.util.Locale; public class AppLocalePickerActivityTest { private static final String TEST_PACKAGE_NAME = "com.android.settings"; private static final Uri TEST_PACKAGE_URI = Uri.parse("package:" + TEST_PACKAGE_NAME); + private FakeFeatureFactory mFeatureFactory; @Mock LocaleStore.LocaleInfo mLocaleInfo; @@ -99,6 +104,7 @@ public class AppLocalePickerActivityTest { when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_SUCCESS); when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US")); ReflectionHelpers.setStaticField(AppLocaleUtil.class, "sLocaleConfig", mLocaleConfig); + mFeatureFactory = FakeFeatureFactory.setupForTest(); } @After @@ -210,6 +216,37 @@ public class AppLocalePickerActivityTest { assertThat(controller.get().isFinishing()).isTrue(); } + @Test + public void onLocaleSelected_logLocaleSource() { + ActivityController controller = + initActivityController(true); + LocaleList.setDefault(LocaleList.forLanguageTags("ja-JP,en-CA,en-US")); + Locale locale = new Locale("en", "US"); + when(mLocaleInfo.getLocale()).thenReturn(locale); + when(mLocaleInfo.isSystemLocale()).thenReturn(false); + when(mLocaleInfo.isSuggested()).thenReturn(true); + when(mLocaleInfo.isSuggestionOfType(LocaleStore.LocaleInfo.SUGGESTION_TYPE_SIM)).thenReturn( + true); + when(mLocaleInfo.isSuggestionOfType( + LocaleStore.LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE)).thenReturn( + true); + when(mLocaleInfo.isSuggestionOfType( + LocaleStore.LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE)).thenReturn( + true); + when(mLocaleInfo.isSuggestionOfType( + LocaleStore.LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE)).thenReturn( + true); + + controller.create(); + AppLocalePickerActivity mActivity = controller.get(); + mActivity.onLocaleSelected(mLocaleInfo); + + int localeSource = 15; // SIM_LOCALE | SYSTEM_LOCALE |IME_LOCALE|APP_LOCALE + verify(mFeatureFactory.metricsFeatureProvider).action( + any(), eq(SettingsEnums.ACTION_CHANGE_APP_LANGUAGE_FROM_SUGGESTED), + eq(localeSource)); + } + private ActivityController initActivityController( boolean hasPackageName) { Intent data = new Intent(); diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceControllerTest.java index 2d54c38e249..f7e32a2c9d9 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 @@ public class VolumeSeekBarPreferenceControllerTest { @Mock private VolumeSeekBarPreference.Callback mCallback; @Mock + private VolumeSeekBarPreference.Listener mListener; + @Mock private AudioHelper mHelper; private VolumeSeekBarPreferenceControllerTestable mController; @@ -59,7 +61,7 @@ public class VolumeSeekBarPreferenceControllerTest { 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 @@ public class VolumeSeekBarPreferenceControllerTest { 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 @@ public class VolumeSeekBarPreferenceControllerTest { 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 d74f76a7cea..59f0bcb91b9 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 @@ package com.android.settings.notification; 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 @@ import org.robolectric.RobolectricTestRunner; @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 @@ public class VolumeSeekBarPreferenceTest { 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/wifi/p2p/WifiP2pSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java index fbe184d7270..25a59a9e158 100644 --- a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -150,6 +151,13 @@ public class WifiP2pSettingsTest { verify(mWifiP2pManager, times(1)).requestNetworkInfo(any(), any()); } + @Test + public void onDeviceInfoAvailable_nullChannel_shouldBeIgnored() { + mFragment.sChannel = null; + mFragment.onDeviceInfoAvailable(mock(WifiP2pDevice.class)); + verify(mWifiP2pManager, never()).requestNetworkInfo(any(), any()); + } + @Test public void beSearching_getP2pStateDisabledIntent_shouldBeFalse() { final Bundle bundle = new Bundle(); diff --git a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java index 63dca7e88eb..f0630423953 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; @@ -444,6 +450,35 @@ public class SubscriptionUtilTest { assertTrue(TextUtils.isEmpty(name)); } + @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();