Merge "Update source metric category of HearingAidDialogFragment and HearingAidPairingDialogFragment"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ec52bbf..938e33d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -250,12 +250,14 @@
android:name=".Settings$MobileNetworkActivity"
android:label="@string/network_settings_title"
android:configChanges="orientation|screenSize|keyboardHidden"
- android:launchMode="singleInstance"
+ android:launchMode="singleTop"
android:exported="true">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.network.telephony.MobileNetworkSettings"/>
<intent-filter android:priority="1">
<!-- Displays the MobileNetworkActivity and opt-in dialog for capability discovery. -->
+ <!-- Please sync with a list created within MobileNetworkIntentConverter.java -->
+ <action android:name="android.intent.action.MAIN" />
<action android:name="android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN" />
<action android:name="android.settings.NETWORK_OPERATOR_SETTINGS" />
<action android:name="android.settings.DATA_ROAMING_SETTINGS" />
diff --git a/OWNERS b/OWNERS
index c2ff692..cc33111 100644
--- a/OWNERS
+++ b/OWNERS
@@ -12,6 +12,7 @@
sunnyshao@google.com
tmfang@google.com
yantingyang@google.com
+ykhung@google.com
# Emergency only
hanxu@google.com
diff --git a/res/drawable-nodpi/auto_awesome_battery b/res/drawable-nodpi/auto_awesome_battery
deleted file mode 100644
index e69de29..0000000
--- a/res/drawable-nodpi/auto_awesome_battery
+++ /dev/null
diff --git a/res/drawable-nodpi/gesture_ambient_tap b/res/drawable-nodpi/gesture_ambient_tap
deleted file mode 100644
index e69de29..0000000
--- a/res/drawable-nodpi/gesture_ambient_tap
+++ /dev/null
diff --git a/res/drawable-nodpi/gesture_ambient_tap_screen b/res/drawable-nodpi/gesture_ambient_tap_screen
deleted file mode 100644
index e69de29..0000000
--- a/res/drawable-nodpi/gesture_ambient_tap_screen
+++ /dev/null
diff --git a/res/raw/auto_awesome_battery.mp4 b/res/raw/auto_awesome_battery.mp4
deleted file mode 100644
index e69de29..0000000
--- a/res/raw/auto_awesome_battery.mp4
+++ /dev/null
diff --git a/res/raw/gesture_ambient_tap.mp4 b/res/raw/gesture_ambient_tap.mp4
deleted file mode 100644
index e69de29..0000000
--- a/res/raw/gesture_ambient_tap.mp4
+++ /dev/null
diff --git a/res/raw/gesture_ambient_tap_screen.mp4 b/res/raw/gesture_ambient_tap_screen.mp4
deleted file mode 100644
index e69de29..0000000
--- a/res/raw/gesture_ambient_tap_screen.mp4
+++ /dev/null
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f397b47..cc7690e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -606,7 +606,7 @@
<!-- Date & time setting screen setting switch title: whether the system clock (Unix epoch time) should be determined automatically [CHAR LIMIT=100] -->
<string name="date_time_auto">Set time automatically</string>
<!-- Date & time setting screen setting switch title: whether the time zone should be determined automatically [CHAR LIMIT=100] -->
- <string name="zone_auto_title">Set time zone automatically</string>
+ <string name="zone_auto_title">Set automatically</string>
<!-- Date & time setting screen setting option summary text for the automatic 24 hour setting checkbox [CHAR LIMIT=100] -->
<string name="date_time_24hour_auto">Use locale default</string>
<!-- Date & time setting screen setting check box title [CHAR LIMIT=30] -->
@@ -4254,7 +4254,7 @@
<string name="managed_profile_location_services">Location services for work</string>
<!-- [CHAR LIMIT=60] Date&Time settings screen, toggle button title -->
- <string name="location_time_zone_detection_toggle_title">Use location to set time zone</string>
+ <string name="location_time_zone_detection_toggle_title">Use location</string>
<!-- [CHAR LIMIT=60] Date&Time settings screen, title of the dialog shown when user tries to
enable GeoTZ when Location toggle is off. -->
<string name="location_time_zone_detection_location_is_off_dialog_title">Device location needed</string>
@@ -4280,6 +4280,8 @@
<!-- [CHAR LIMIT=NONE] Location settings screen, summary when location time zone detection is not
allowed for the user, e.g. because of device policy -->
<string name="location_time_zone_detection_not_allowed">Location time zone detection changes are not allowed</string>
+ <!-- [CHAR LIMIT=NONE] Location settings screen, summary when location time zone detection is enabled. -->
+ <string name="location_time_zone_detection_auto_is_on">Location may be used to set time zone</string>
<!-- [CHAR LIMIT=30] Security & location settings screen, setting check box label for Google location service (cell ID, wifi, etc.) -->
<string name="location_network_based">Wi\u2011Fi & mobile network location</string>
@@ -6835,6 +6837,14 @@
behalf. It comes from the <xliff:g id="voice_input_service_app_name">%s</xliff:g>
application. Enable the use of this service?</string>
+ <!-- On-device recognition settings --><skip />
+ <!-- [CHAR_LIMIT=NONE] Name of the settings item to open the on-device recognition settings. -->
+ <string name="on_device_recognition_settings">On-device recognition settings</string>
+ <!-- [CHAR_LIMIT=NONE] Title of the on-device recognition settings -->
+ <string name="on_device_recognition_settings_title">On-device recognition</string>
+ <!-- [CHAR_LIMIT=NONE] Summary of the on-device recognition settings -->
+ <string name="on_device_recognition_settings_summary">On-device speech recognition</string>
+
<!-- [CHAR LIMIT=50] The text for the settings section that is used to set a preferred text to speech engine -->
<string name="tts_engine_preference_title">Preferred engine</string>
<!-- [CHAR LIMIT=50] The text for a settings screen of the currently set text to speech engine -->
diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml
index a0801f9..e3d0a7e 100644
--- a/res/xml/date_time_prefs.xml
+++ b/res/xml/date_time_prefs.xml
@@ -45,7 +45,6 @@
<com.android.settingslib.RestrictedSwitchPreference
android:key="auto_zone"
android:title="@string/zone_auto_title"
- settings:allowDividerAbove="true"
settings:userRestriction="no_config_date_time"/>
<!-- This preference gets removed if location-based time zone detection is not supported -->
diff --git a/res/xml/language_and_input.xml b/res/xml/language_and_input.xml
index 770a862..64b5003 100644
--- a/res/xml/language_and_input.xml
+++ b/res/xml/language_and_input.xml
@@ -64,6 +64,13 @@
android:fragment="com.android.settings.language.DefaultVoiceInputPicker" />
<Preference
+ android:key="on_device_recognition_settings"
+ android:title="@string/on_device_recognition_settings_title"
+ android:summary="@string/on_device_recognition_settings_summary"
+ settings:controller=
+ "com.android.settings.language.OnDeviceRecognitionPreferenceController" />
+
+ <Preference
android:key="tts_settings_summary"
android:title="@string/tts_settings_title"
android:fragment="com.android.settings.tts.TextToSpeechSettings"
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index ee0743a..c3ab8e2 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -22,8 +22,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsRcsManager;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
@@ -33,8 +31,7 @@
import com.android.settings.biometrics.face.FaceSettings;
import com.android.settings.core.FeatureFlags;
import com.android.settings.enterprise.EnterprisePrivacySettings;
-import com.android.settings.network.SubscriptionUtil;
-import com.android.settings.network.telephony.MobileNetworkUtils;
+import com.android.settings.network.MobileNetworkIntentConverter;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.security.SecuritySettingsFeatureProvider;
@@ -370,41 +367,37 @@
public static class PowerMenuSettingsActivity extends SettingsActivity {}
public static class MobileNetworkActivity extends SettingsActivity {
+ public static final String TAG = "MobileNetworkActivity";
public static final String EXTRA_MMS_MESSAGE = "mms_message";
public static final String EXTRA_SHOW_CAPABILITY_DISCOVERY_OPT_IN =
"show_capability_discovery_opt_in";
+ private MobileNetworkIntentConverter mIntentConverter;
+
+ /**
+ * Override of #onNewIntent() requires Activity to have "singleTop" launch mode within
+ * AndroidManifest.xml
+ */
@Override
- public Intent getIntent() {
- final Intent intent = new Intent(super.getIntent());
- int subId = intent.getIntExtra(android.provider.Settings.EXTRA_SUB_ID,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- SubscriptionInfo subInfo = SubscriptionUtil.getSubscriptionOrDefault(
- getApplicationContext(), subId);
- CharSequence title = SubscriptionUtil.getUniqueSubscriptionDisplayName(
- subInfo, getApplicationContext());
- intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
- intent.putExtra(android.provider.Settings.EXTRA_SUB_ID, subId);
- if (android.provider.Settings.ACTION_MMS_MESSAGE_SETTING.equals(intent.getAction())) {
- // highlight "mms_message" preference.
- intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, EXTRA_MMS_MESSAGE);
- }
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
- if (doesIntentContainOptInAction(intent)) {
- intent.putExtra(EXTRA_SHOW_CAPABILITY_DISCOVERY_OPT_IN,
- maybeShowContactDiscoveryDialog(subId));
- }
+ Log.d(TAG, "Starting onNewIntent");
- return intent;
+ createUiFromIntent(null /* savedState */, convertIntent(intent));
}
- private boolean maybeShowContactDiscoveryDialog(int subId) {
- // If this activity was launched using ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN, show the
- // associated dialog only if the opt-in has not been granted yet.
- return MobileNetworkUtils.isContactDiscoveryVisible(getApplicationContext(), subId)
- // has the user already enabled this configuration?
- && !MobileNetworkUtils.isContactDiscoveryEnabled(
- getApplicationContext(), subId);
+ @Override
+ public Intent getIntent() {
+ return convertIntent(super.getIntent());
+ }
+
+ private Intent convertIntent(Intent copyFrom) {
+ if (mIntentConverter == null) {
+ mIntentConverter = new MobileNetworkIntentConverter(this);
+ }
+ Intent intent = mIntentConverter.apply(copyFrom);
+ return (intent == null) ? copyFrom : intent;
}
public static boolean doesIntentContainOptInAction(Intent intent) {
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 1a9bdc7..4341851 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -70,6 +70,7 @@
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.password.PasswordUtils;
import com.android.settings.wfd.WifiDisplaySettings;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.core.instrumentation.Instrumentable;
@@ -154,6 +155,7 @@
public static final String EXTRA_IS_FROM_SLICE = "is_from_slice";
public static final String EXTRA_USER_HANDLE = "user_handle";
+ public static final String EXTRA_INITIAL_CALLING_PACKAGE = "initial_calling_package";
/**
* Personal or Work profile tab of {@link ProfileSelectFragment}
@@ -264,7 +266,10 @@
super.onCreate(savedState);
Log.d(LOG_TAG, "Starting onCreate");
+ createUiFromIntent(savedState, intent);
+ }
+ protected void createUiFromIntent(Bundle savedState, Intent intent) {
long startTime = System.currentTimeMillis();
final FeatureFactory factory = FeatureFactory.getFactory(this);
@@ -415,6 +420,8 @@
}
private boolean tryStartTwoPaneDeepLink(Intent intent) {
+ intent.putExtra(EXTRA_INITIAL_CALLING_PACKAGE, PasswordUtils.getCallingAppPackageName(
+ getActivityToken()));
final Intent trampolineIntent;
if (intent.getBooleanExtra(EXTRA_IS_FROM_SLICE, false)) {
// Get menu key for slice deep link case.
@@ -502,6 +509,17 @@
return true;
}
+ /** Returns the initial calling package name that launches the activity. */
+ public String getInitialCallingPackage() {
+ String callingPackage = PasswordUtils.getCallingAppPackageName(getActivityToken());
+ if (!TextUtils.equals(callingPackage, getPackageName())) {
+ return callingPackage;
+ }
+
+ String initialCallingPackage = getIntent().getStringExtra(EXTRA_INITIAL_CALLING_PACKAGE);
+ return TextUtils.isEmpty(initialCallingPackage) ? callingPackage : initialCallingPackage;
+ }
+
/** Returns the initial fragment name that the activity will launch. */
@VisibleForTesting
public String getInitialFragmentName(Intent intent) {
diff --git a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java
index 4eea9f5..6c32edd 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java
@@ -33,6 +33,7 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.recyclerview.widget.RecyclerView;
@@ -58,17 +59,21 @@
private static final String SELECT_TO_SPEAK_PREFERENCE = "select_to_speak_preference";
// Package names and service names used to identify screen reader and SelectToSpeak services.
- private static final String SCREEN_READER_PACKAGE_NAME = "com.google.android.marvin.talkback";
- private static final String SCREEN_READER_SERVICE_NAME =
+ @VisibleForTesting
+ static final String SCREEN_READER_PACKAGE_NAME = "com.google.android.marvin.talkback";
+ @VisibleForTesting
+ static final String SCREEN_READER_SERVICE_NAME =
"com.google.android.marvin.talkback.TalkBackService";
- private static final String SELECT_TO_SPEAK_PACKAGE_NAME = "com.google.android.marvin.talkback";
- private static final String SELECT_TO_SPEAK_SERVICE_NAME =
+ @VisibleForTesting
+ static final String SELECT_TO_SPEAK_PACKAGE_NAME = "com.google.android.marvin.talkback";
+ @VisibleForTesting
+ static final String SELECT_TO_SPEAK_SERVICE_NAME =
"com.google.android.accessibility.selecttospeak.SelectToSpeakService";
// Preference controls.
- private Preference mDisplayMagnificationPreference;
- private RestrictedPreference mScreenReaderPreference;
- private RestrictedPreference mSelectToSpeakPreference;
+ protected Preference mDisplayMagnificationPreference;
+ protected RestrictedPreference mScreenReaderPreference;
+ protected RestrictedPreference mSelectToSpeakPreference;
@Override
public int getMetricsCategory() {
diff --git a/src/com/android/settings/applications/AppsPreferenceController.java b/src/com/android/settings/applications/AppsPreferenceController.java
index fad513e..922ba3c 100644
--- a/src/com/android/settings/applications/AppsPreferenceController.java
+++ b/src/com/android/settings/applications/AppsPreferenceController.java
@@ -20,7 +20,6 @@
import android.app.usage.UsageStats;
import android.content.Context;
import android.icu.text.RelativeDateTimeFormatter;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -63,10 +62,9 @@
static final String KEY_SEE_ALL = "see_all_apps";
private final ApplicationsState mApplicationsState;
- private final int mUserId;
@VisibleForTesting
- List<UsageStats> mRecentApps;
+ List<RecentAppStatsMixin.UsageStatsWrapper> mRecentApps;
@VisibleForTesting
PreferenceCategory mRecentAppsCategory;
@VisibleForTesting
@@ -83,7 +81,6 @@
super(context, KEY_RECENT_APPS_CATEGORY);
mApplicationsState = ApplicationsState.getInstance(
(Application) mContext.getApplicationContext());
- mUserId = UserHandle.myUserId();
}
public void setFragment(Fragment fragment) {
@@ -156,7 +153,7 @@
}
@VisibleForTesting
- List<UsageStats> loadRecentApps() {
+ List<RecentAppStatsMixin.UsageStatsWrapper> loadRecentApps() {
final RecentAppStatsMixin recentAppStatsMixin = new RecentAppStatsMixin(mContext,
SHOW_RECENT_APP_COUNT);
recentAppStatsMixin.loadDisplayableRecentApps(SHOW_RECENT_APP_COUNT);
@@ -187,26 +184,28 @@
}
int showAppsCount = 0;
- for (UsageStats stat : mRecentApps) {
- final String pkgName = stat.getPackageName();
+ for (RecentAppStatsMixin.UsageStatsWrapper statsWrapper : mRecentApps) {
+ final UsageStats stats = statsWrapper.mUsageStats;
+ final String pkgName = statsWrapper.mUsageStats.getPackageName();
+ final String key = pkgName + statsWrapper.mUserId;
final ApplicationsState.AppEntry appEntry =
- mApplicationsState.getEntry(pkgName, mUserId);
+ mApplicationsState.getEntry(pkgName, statsWrapper.mUserId);
if (appEntry == null) {
continue;
}
boolean rebindPref = true;
- Preference pref = existedAppPreferences.remove(pkgName);
+ Preference pref = existedAppPreferences.remove(key);
if (pref == null) {
pref = new AppPreference(mContext);
rebindPref = false;
}
- pref.setKey(pkgName);
+ pref.setKey(key);
pref.setTitle(appEntry.label);
pref.setIcon(Utils.getBadgedIcon(mContext, appEntry.info));
pref.setSummary(StringUtil.formatRelativeTime(mContext,
- System.currentTimeMillis() - stat.getLastTimeUsed(), false,
+ System.currentTimeMillis() - stats.getLastTimeUsed(), false,
RelativeDateTimeFormatter.Style.SHORT));
pref.setOrder(showAppsCount++);
pref.setOnPreferenceClickListener(preference -> {
diff --git a/src/com/android/settings/applications/RecentAppStatsMixin.java b/src/com/android/settings/applications/RecentAppStatsMixin.java
index 4bf3864..03b2203 100644
--- a/src/com/android/settings/applications/RecentAppStatsMixin.java
+++ b/src/com/android/settings/applications/RecentAppStatsMixin.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -33,6 +34,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.settings.Utils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -42,26 +44,31 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
-
-public class RecentAppStatsMixin implements Comparator<UsageStats>, LifecycleObserver, OnStart {
+/**
+ * A helper class that loads recent app data in the background and sends it in a callback to a
+ * listener.
+ */
+public class RecentAppStatsMixin implements LifecycleObserver, OnStart {
private static final String TAG = "RecentAppStatsMixin";
private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>();
@VisibleForTesting
- final List<UsageStats> mRecentApps;
- private final int mUserId;
+ List<UsageStatsWrapper> mRecentApps;
+
private final int mMaximumApps;
private final Context mContext;
private final PackageManager mPm;
- private final PowerManager mPowerManager;;
- private final UsageStatsManager mUsageStatsManager;
+ private final PowerManager mPowerManager;
+ private final int mWorkUserId;
+ private final UsageStatsManager mPersonalUsageStatsManager;
+ private final Optional<UsageStatsManager> mWorkUsageStatsManager;
private final ApplicationsState mApplicationsState;
private final List<RecentAppStatsListener> mAppStatsListeners;
private Calendar mCalendar;
@@ -80,10 +87,15 @@
public RecentAppStatsMixin(Context context, int maximumApps) {
mContext = context;
mMaximumApps = maximumApps;
- mUserId = UserHandle.myUserId();
mPm = mContext.getPackageManager();
mPowerManager = mContext.getSystemService(PowerManager.class);
- mUsageStatsManager = mContext.getSystemService(UsageStatsManager.class);
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ mWorkUserId = Utils.getManagedProfileId(userManager, UserHandle.myUserId());
+ mPersonalUsageStatsManager = mContext.getSystemService(UsageStatsManager.class);
+ final UserHandle workUserHandle = Utils.getManagedProfile(userManager);
+ mWorkUsageStatsManager = Optional.ofNullable(workUserHandle).map(
+ handle -> mContext.createContextAsUser(handle, /* flags */ 0)
+ .getSystemService(UsageStatsManager.class));
mApplicationsState = ApplicationsState.getInstance(
(Application) mContext.getApplicationContext());
mRecentApps = new ArrayList<>();
@@ -100,32 +112,56 @@
});
}
- @Override
- public final int compare(UsageStats a, UsageStats b) {
- // return by descending order
- return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed());
- }
-
public void addListener(@NonNull RecentAppStatsListener listener) {
mAppStatsListeners.add(listener);
}
@VisibleForTesting
- void loadDisplayableRecentApps(int number) {
+ void loadDisplayableRecentApps(int limit) {
mRecentApps.clear();
mCalendar = Calendar.getInstance();
mCalendar.add(Calendar.DAY_OF_YEAR, -1);
- final List<UsageStats> mStats = mPowerManager.isPowerSaveMode()
+
+ final int personalUserId = UserHandle.myUserId();
+ final List<UsageStats> personalStats =
+ getRecentAppsStats(mPersonalUsageStatsManager, personalUserId);
+ final List<UsageStats> workStats = mWorkUsageStatsManager
+ .map(statsManager -> getRecentAppsStats(statsManager, mWorkUserId))
+ .orElse(new ArrayList<>());
+
+ // Both lists are already sorted, so we can create a sorted merge in linear time
+ int personal = 0;
+ int work = 0;
+ while (personal < personalStats.size() && work < workStats.size()
+ && mRecentApps.size() < limit) {
+ UsageStats currentPersonal = personalStats.get(personal);
+ UsageStats currentWork = workStats.get(work);
+ if (currentPersonal.getLastTimeUsed() > currentWork.getLastTimeUsed()) {
+ mRecentApps.add(new UsageStatsWrapper(currentPersonal, personalUserId));
+ personal++;
+ } else {
+ mRecentApps.add(new UsageStatsWrapper(currentWork, mWorkUserId));
+ work++;
+ }
+ }
+ while (personal < personalStats.size() && mRecentApps.size() < limit) {
+ mRecentApps.add(new UsageStatsWrapper(personalStats.get(personal++), personalUserId));
+ }
+ while (work < workStats.size() && mRecentApps.size() < limit) {
+ mRecentApps.add(new UsageStatsWrapper(workStats.get(work++), mWorkUserId));
+ }
+ }
+
+ private List<UsageStats> getRecentAppsStats(UsageStatsManager usageStatsManager, int userId) {
+ final List<UsageStats> recentAppStats = mPowerManager.isPowerSaveMode()
? new ArrayList<>()
- : mUsageStatsManager.queryUsageStats(
+ : usageStatsManager.queryUsageStats(
UsageStatsManager.INTERVAL_BEST, mCalendar.getTimeInMillis(),
System.currentTimeMillis());
final Map<String, UsageStats> map = new ArrayMap<>();
- final int statCount = mStats.size();
- for (int i = 0; i < statCount; i++) {
- final UsageStats pkgStats = mStats.get(i);
- if (!shouldIncludePkgInRecents(pkgStats)) {
+ for (final UsageStats pkgStats : recentAppStats) {
+ if (!shouldIncludePkgInRecents(pkgStats, userId)) {
continue;
}
final String pkgName = pkgStats.getPackageName();
@@ -136,28 +172,15 @@
existingStats.add(pkgStats);
}
}
- final List<UsageStats> packageStats = new ArrayList<>();
- packageStats.addAll(map.values());
- Collections.sort(packageStats, this /* comparator */);
- int count = 0;
- for (UsageStats stat : packageStats) {
- final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(
- stat.getPackageName(), mUserId);
- if (appEntry == null) {
- continue;
- }
- mRecentApps.add(stat);
- count++;
- if (count >= number) {
- break;
- }
- }
+ final List<UsageStats> packageStats = new ArrayList<>(map.values());
+ packageStats.sort(Comparator.comparingLong(UsageStats::getLastTimeUsed).reversed());
+ return packageStats;
}
/**
* Whether or not the app should be included in recent list.
*/
- private boolean shouldIncludePkgInRecents(UsageStats stat) {
+ private boolean shouldIncludePkgInRecents(UsageStats stat, int userId) {
final String pkgName = stat.getPackageName();
if (stat.getLastTimeUsed() < mCalendar.getTimeInMillis()) {
Log.d(TAG, "Invalid timestamp (usage time is more than 24 hours ago), skipping "
@@ -169,26 +192,49 @@
Log.d(TAG, "System package, skipping " + pkgName);
return false;
}
+
if (AppUtils.isHiddenSystemModule(mContext, pkgName)) {
return false;
}
+
+ final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(pkgName, userId);
+ if (appEntry == null) {
+ return false;
+ }
+
final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER)
.setPackage(pkgName);
-
- if (mPm.resolveActivity(launchIntent, 0) == null) {
+ if (mPm.resolveActivityAsUser(launchIntent, 0, userId) == null) {
// Not visible on launcher -> likely not a user visible app, skip if non-instant.
- final ApplicationsState.AppEntry appEntry =
- mApplicationsState.getEntry(pkgName, mUserId);
- if (appEntry == null || appEntry.info == null || !AppUtils.isInstant(appEntry.info)) {
+ if (appEntry.info == null || !AppUtils.isInstant(appEntry.info)) {
Log.d(TAG, "Not a user visible or instant app, skipping " + pkgName);
return false;
}
}
+
return true;
}
public interface RecentAppStatsListener {
- void onReloadDataCompleted(List<UsageStats> recentApps);
+ /** A callback after loading the recent app data. */
+ void onReloadDataCompleted(List<UsageStatsWrapper> recentApps);
+ }
+
+ static class UsageStatsWrapper {
+
+ public final UsageStats mUsageStats;
+ public final int mUserId;
+
+ UsageStatsWrapper(UsageStats usageStats, int userId) {
+ mUsageStats = usageStats;
+ mUserId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("UsageStatsWrapper(pkg:%s,uid:%s)",
+ mUsageStats.getPackageName(), mUserId);
+ }
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index df65a69..bf04d5f 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -639,13 +639,17 @@
new Animator.AnimatorListener() {
@Override
- public void onAnimationStart(Animator animation) { }
+ public void onAnimationStart(Animator animation) {
+ startIconAnimation();
+ }
@Override
public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationEnd(Animator animation) {
+ stopIconAnimation();
+
if (mProgressBar.getProgress() >= PROGRESS_BAR_MAX) {
mProgressBar.postDelayed(mDelayedFinishRunnable, getFinishDelay());
}
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index 14c20f1..3d8e148 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -66,7 +66,7 @@
}
boolean isFilterMatched = false;
- if (isDeviceConnected(cachedDevice)) {
+ if (isDeviceConnected(cachedDevice) && isDeviceInCachedDevicesList(cachedDevice)) {
if (DBG) {
Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile);
}
@@ -74,6 +74,8 @@
// It would show in Available Devices group.
if (cachedDevice.isConnectedHearingAidDevice()
|| cachedDevice.isConnectedLeAudioDevice()) {
+ Log.d(TAG, "isFilterMatched() device : " +
+ cachedDevice.getName() + ", the profile is connected.");
return true;
}
// According to the current audio profile type,
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index d65500b..8934676 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -326,4 +326,8 @@
((BluetoothDevicePreference) preference).onPreferenceAttributesChanged();
}
}
+
+ protected boolean isDeviceInCachedDevicesList(CachedBluetoothDevice cachedDevice){
+ return mLocalManager.getCachedDeviceManager().getCachedDevicesCopy().contains(cachedDevice);
+ }
}
diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
index 7d27a37..8542fcd 100644
--- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
@@ -126,7 +126,7 @@
// "Clear All Notifications" button
Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
- deleteIntent.setPackage("com.android.bluetooth.services");
+ deleteIntent.setPackage("com.android.bluetooth");
deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
BluetoothDevice.CONNECTION_ACCESS_NO);
diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
index d1c45b6..5c3dda3 100644
--- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
@@ -66,7 +66,7 @@
}
boolean isFilterMatched = false;
- if (isDeviceConnected(cachedDevice)) {
+ if (isDeviceConnected(cachedDevice) && isDeviceInCachedDevicesList(cachedDevice)) {
if (DBG) {
Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile);
}
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
index dcf89ca..7f58c06 100644
--- a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
@@ -232,8 +232,7 @@
}
private void updateSummary() {
- mSummary.setText(getString(R.string.bt_le_audio_scan_qr_code_scanner,
- null /* broadcast_name*/));;
+ mSummary.setText(getString(R.string.bt_le_audio_scan_qr_code_scanner));
}
@Override
diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
index 3bdf91e..e7a8317 100644
--- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
@@ -106,7 +106,8 @@
+ cachedDevice.isConnected());
}
return device.getBondState() == BluetoothDevice.BOND_BONDED
- && (mDisplayConnected || !device.isConnected());
+ && (mDisplayConnected || (!device.isConnected() && isDeviceInCachedDevicesList(
+ cachedDevice)));
}
@Override
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index 7e6eefe..ea8a5f5 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -25,9 +25,9 @@
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.password.PasswordUtils;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.slices.SlicePreferenceController;
import com.android.settingslib.search.SearchIndexable;
@@ -71,8 +71,8 @@
super.onAttach(context);
final boolean nearbyEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_NEAR_BY_SUGGESTION_ENABLED, true);
- String callingAppPackageName = PasswordUtils.getCallingAppPackageName(
- getActivity().getActivityToken());
+ String callingAppPackageName = ((SettingsActivity) getActivity())
+ .getInitialCallingPackage();
String action = getIntent() != null ? getIntent().getAction() : "";
if (DEBUG) {
Log.d(TAG, "onAttach() calling package name is : " + callingAppPackageName
diff --git a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
index 93e6d0a..36cbc9e 100644
--- a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
+++ b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
@@ -174,7 +174,7 @@
// If capability is possessed, toggle status already tells all the information needed.
// Returning null will make previous text stick on toggling.
// See AbstractPreferenceController#refreshSummary.
- return "";
+ summaryResId = R.string.location_time_zone_detection_auto_is_on;
} else {
// This is unexpected: getAvailabilityStatus() should ensure that the UI element isn't
// even shown for known cases, or the capability is unknown.
diff --git a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java
index af9a295..bd8169a 100644
--- a/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceController.java
@@ -47,10 +47,10 @@
try {
Resources res = context.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth.services");
+ "com.android.bluetooth");
mDefaultMaxConnectedAudioDevices = res.getInteger(res.getIdentifier(
"config_bluetooth_max_connected_audio_devices",
- "integer", "com.android.bluetooth.services"));
+ "integer", "com.android.bluetooth"));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
diff --git a/src/com/android/settings/display/ScreenResolutionFragment.java b/src/com/android/settings/display/ScreenResolutionFragment.java
index a827e92..bc82514 100644
--- a/src/com/android/settings/display/ScreenResolutionFragment.java
+++ b/src/com/android/settings/display/ScreenResolutionFragment.java
@@ -46,6 +46,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/** Preference fragment used for switch screen resolution */
@SearchIndexable
@@ -61,7 +62,7 @@
private String[] mScreenResolutionSummaries;
private IllustrationPreference mImagePreference;
- private DensityRestorer mDensityRestorer;
+ private DisplayObserver mDisplayObserver;
@Override
public void onAttach(Context context) {
@@ -76,7 +77,7 @@
mResources.getStringArray(R.array.config_screen_resolution_summaries_strings);
mResolutions = getAllSupportedResolution();
mImagePreference = new IllustrationPreference(context);
- mDensityRestorer = new DensityRestorer(context);
+ mDisplayObserver = new DisplayObserver(context);
}
@Override
@@ -155,11 +156,7 @@
/** Using display manager to set the display mode. */
@VisibleForTesting
public void setDisplayMode(final int width) {
- if (width == getDisplayMode().getPhysicalWidth()) {
- return;
- }
-
- mDensityRestorer.startObserve();
+ mDisplayObserver.startObserve();
mDefaultDisplay.setUserPreferredDisplayMode(getPreferMode(width));
}
@@ -171,6 +168,13 @@
: width == QHD_WIDTH ? mScreenResolutionOptions[QHD_INDEX] : null;
}
+ /** Get the width corresponding to the resolution key. */
+ int getWidthForResoluitonKey(String key) {
+ return mScreenResolutionOptions[FHD_INDEX].equals(key)
+ ? FHD_WIDTH
+ : mScreenResolutionOptions[QHD_INDEX].equals(key) ? QHD_WIDTH : -1;
+ }
+
@Override
protected String getDefaultKey() {
int physicalWidth = getDisplayMode().getPhysicalWidth();
@@ -180,17 +184,28 @@
@Override
protected boolean setDefaultKey(final String key) {
- if (mScreenResolutionOptions[FHD_INDEX].equals(key)) {
- setDisplayMode(FHD_WIDTH);
-
- } else if (mScreenResolutionOptions[QHD_INDEX].equals(key)) {
- setDisplayMode(QHD_WIDTH);
+ int width = getWidthForResoluitonKey(key);
+ if (width < 0) {
+ return false;
}
+ setDisplayMode(width);
updateIllustrationImage(mImagePreference);
+
return true;
}
+ @Override
+ public void onRadioButtonClicked(SelectorWithWidgetPreference selected) {
+ String selectedKey = selected.getKey();
+ int selectedWidth = getWidthForResoluitonKey(selectedKey);
+ if (!mDisplayObserver.setPendingResolutionChange(selectedWidth)) {
+ return;
+ }
+
+ super.onRadioButtonClicked(selected);
+ }
+
/** Update the resolution image according display mode. */
private void updateIllustrationImage(IllustrationPreference preference) {
String key = getDefaultKey();
@@ -252,12 +267,13 @@
}
};
- private static final class DensityRestorer implements DisplayManager.DisplayListener {
+ private static final class DisplayObserver implements DisplayManager.DisplayListener {
private final @Nullable Context mContext;
private int mDefaultDensity;
private int mCurrentIndex;
+ private AtomicInteger mPreviousWidth = new AtomicInteger(-1);
- DensityRestorer(Context context) {
+ DisplayObserver(Context context) {
mContext = context;
}
@@ -301,26 +317,59 @@
return;
}
+ if (!isDensityChanged() || !isResolutionChangeApplied()) {
+ return;
+ }
+
restoreDensity();
+ stopObserve();
}
private void restoreDensity() {
- if (mContext == null) {
- return;
- }
-
final DisplayDensityUtils density = new DisplayDensityUtils(mContext);
- if (density.getDefaultDensity() == mDefaultDensity) {
- return;
- }
-
if (density.getValues()[mCurrentIndex] != density.getDefaultDensity()) {
DisplayDensityUtils.setForcedDisplayDensity(
Display.DEFAULT_DISPLAY, density.getValues()[mCurrentIndex]);
}
mDefaultDensity = density.getDefaultDensity();
- stopObserve();
+ }
+
+ private boolean isDensityChanged() {
+ final DisplayDensityUtils density = new DisplayDensityUtils(mContext);
+ if (density.getDefaultDensity() == mDefaultDensity) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private int getCurrentWidth() {
+ final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ return dm.getDisplay(Display.DEFAULT_DISPLAY).getMode().getPhysicalWidth();
+ }
+
+ private boolean setPendingResolutionChange(int selectedWidth) {
+ int currentWidth = getCurrentWidth();
+
+ if (selectedWidth == currentWidth) {
+ return false;
+ }
+ if (mPreviousWidth.get() != -1 && !isResolutionChangeApplied()) {
+ return false;
+ }
+
+ mPreviousWidth.set(currentWidth);
+
+ return true;
+ }
+
+ private boolean isResolutionChangeApplied() {
+ if (mPreviousWidth.get() == getCurrentWidth()) {
+ return false;
+ }
+
+ return true;
}
}
}
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
index 92e4f4a..a154ded 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
@@ -77,6 +77,10 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ if (mPrivacySettingsPreference instanceof PrivacySettingsFinancedPreference) {
+ return;
+ }
+
replaceEnterprisePreferenceScreenTitle(
MANAGED_DEVICE_INFO, R.string.enterprise_privacy_settings);
diff --git a/src/com/android/settings/language/LanguageAndInputSettings.java b/src/com/android/settings/language/LanguageAndInputSettings.java
index 71b48f9..2d80da5 100644
--- a/src/com/android/settings/language/LanguageAndInputSettings.java
+++ b/src/com/android/settings/language/LanguageAndInputSettings.java
@@ -50,6 +50,7 @@
private static final String KEY_KEYBOARDS_CATEGORY = "keyboards_category";
private static final String KEY_SPEECH_CATEGORY = "speech_category";
+ private static final String KEY_ON_DEVICE_RECOGNITION = "odsr_settings";
private static final String KEY_TEXT_TO_SPEECH = "tts_settings_summary";
private static final String KEY_POINTER_CATEGORY = "pointer_category";
@@ -123,11 +124,21 @@
new DefaultVoiceInputPreferenceController(context, lifecycle);
final TtsPreferenceController ttsPreferenceController =
new TtsPreferenceController(context, KEY_TEXT_TO_SPEECH);
+ final OnDeviceRecognitionPreferenceController onDeviceRecognitionPreferenceController =
+ new OnDeviceRecognitionPreferenceController(context, KEY_ON_DEVICE_RECOGNITION);
+
controllers.add(defaultVoiceInputPreferenceController);
controllers.add(ttsPreferenceController);
- controllers.add(new PreferenceCategoryController(context,
- KEY_SPEECH_CATEGORY).setChildren(
- Arrays.asList(defaultVoiceInputPreferenceController, ttsPreferenceController)));
+ List<AbstractPreferenceController> speechCategoryChildren = new ArrayList<>(
+ List.of(defaultVoiceInputPreferenceController, ttsPreferenceController));
+
+ if (onDeviceRecognitionPreferenceController.isAvailable()) {
+ controllers.add(onDeviceRecognitionPreferenceController);
+ speechCategoryChildren.add(onDeviceRecognitionPreferenceController);
+ }
+
+ controllers.add(new PreferenceCategoryController(context, KEY_SPEECH_CATEGORY)
+ .setChildren(speechCategoryChildren));
// Pointer
final PointerSpeedController pointerController = new PointerSpeedController(context);
diff --git a/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java b/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java
new file mode 100644
index 0000000..3186639
--- /dev/null
+++ b/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.language;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+import com.android.internal.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+/** Controller of the On-device recognition preference. */
+public class OnDeviceRecognitionPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "OnDeviceRecognitionPreferenceController";
+
+ private Optional<Intent> mIntent;
+
+ public OnDeviceRecognitionPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mIntent == null) {
+ mIntent = Optional.ofNullable(onDeviceRecognitionIntent());
+ }
+ return mIntent.isPresent()
+ ? AVAILABLE
+ : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ if (mIntent != null && mIntent.isPresent()) {
+ preference.setIntent(mIntent.get());
+ }
+ }
+
+ /**
+ * Create an {@link Intent} for the activity in the default on-device recognizer service if
+ * there is a properly defined speech recognition xml meta-data for that service.
+ *
+ * @return {@link Intent} if the proper activity is fount, {@code null} otherwise.
+ */
+ @Nullable
+ private Intent onDeviceRecognitionIntent() {
+ final String resString = mContext.getString(
+ R.string.config_defaultOnDeviceSpeechRecognitionService);
+
+ if (resString == null) {
+ Log.v(TAG, "No on-device recognizer, intent not created.");
+ return null;
+ }
+
+ final ComponentName defaultOnDeviceRecognizerComponentName =
+ ComponentName.unflattenFromString(resString);
+
+ if (defaultOnDeviceRecognizerComponentName == null) {
+ Log.v(TAG, "Invalid on-device recognizer string format, intent not created.");
+ return null;
+ }
+
+ final ArrayList<VoiceInputHelper.RecognizerInfo> validRecognitionServices =
+ VoiceInputHelper.validRecognitionServices(mContext);
+
+ if (validRecognitionServices.isEmpty()) {
+ Log.v(TAG, "No speech recognition services"
+ + "with proper `recognition-service` meta-data found.");
+ return null;
+ }
+
+ // Filter the recognizer services which are in the same package as the default on-device
+ // speech recognizer and have a settings activity defined in the meta-data.
+ final ArrayList<VoiceInputHelper.RecognizerInfo> validOnDeviceRecognitionServices =
+ new ArrayList<>();
+ for (VoiceInputHelper.RecognizerInfo recognizerInfo: validRecognitionServices) {
+ if (!defaultOnDeviceRecognizerComponentName.getPackageName().equals(
+ recognizerInfo.mService.packageName)) {
+ Log.v(TAG, String.format("Recognition service not in the same package as the "
+ + "default on-device recognizer: %s.",
+ recognizerInfo.mComponentName.flattenToString()));
+ } else if (recognizerInfo.mSettings == null) {
+ Log.v(TAG, String.format("Recognition service with no settings activity: %s.",
+ recognizerInfo.mComponentName.flattenToString()));
+ } else {
+ validOnDeviceRecognitionServices.add(recognizerInfo);
+ Log.v(TAG, String.format("Recognition service in the same package as the default "
+ + "on-device recognizer with settings activity: %s.",
+ recognizerInfo.mSettings.flattenToString()));
+ }
+ }
+
+ if (validOnDeviceRecognitionServices.isEmpty()) {
+ Log.v(TAG, "No speech recognition services with proper `recognition-service` "
+ + "meta-data found in the same package as the default on-device recognizer.");
+ return null;
+ }
+
+ // Not more than one proper recognition services should be found in the same
+ // package as the default on-device recognizer. If that happens,
+ // the first one which passed the filter will be selected.
+ if (validOnDeviceRecognitionServices.size() > 1) {
+ Log.w(TAG, "More than one recognition services with proper `recognition-service` "
+ + "meta-data found in the same package as the default on-device recognizer.");
+ }
+ VoiceInputHelper.RecognizerInfo chosenRecognizer = validOnDeviceRecognitionServices.get(0);
+
+ return new Intent(Intent.ACTION_MAIN).setComponent(chosenRecognizer.mSettings);
+ }
+}
diff --git a/src/com/android/settings/language/VoiceInputHelper.java b/src/com/android/settings/language/VoiceInputHelper.java
index 7915ba4..289a2f9 100644
--- a/src/com/android/settings/language/VoiceInputHelper.java
+++ b/src/com/android/settings/language/VoiceInputHelper.java
@@ -29,6 +29,7 @@
import android.speech.RecognitionService;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -44,12 +45,11 @@
static final String TAG = "VoiceInputHelper";
final Context mContext;
- final List<ResolveInfo> mAvailableRecognition;
-
/**
* Base info of the Voice Input provider.
*
* TODO: Remove this superclass as we only have 1 class now (RecognizerInfo).
+ * TODO: Group recognition service xml meta-data attributes in a single class.
*/
public static class BaseInfo implements Comparable<BaseInfo> {
public final ServiceInfo mService;
@@ -90,16 +90,12 @@
}
}
- final ArrayList<RecognizerInfo> mAvailableRecognizerInfos = new ArrayList<>();
+ ArrayList<RecognizerInfo> mAvailableRecognizerInfos = new ArrayList<>();
ComponentName mCurrentRecognizer;
public VoiceInputHelper(Context context) {
mContext = context;
-
- mAvailableRecognition = mContext.getPackageManager().queryIntentServices(
- new Intent(RecognitionService.SERVICE_INTERFACE),
- PackageManager.GET_META_DATA);
}
/** Draws the UI of the Voice Input picker page. */
@@ -113,63 +109,120 @@
mCurrentRecognizer = null;
}
- // Iterate through all the available recognizers and load up their info to show
- // in the preference.
- int size = mAvailableRecognition.size();
- for (int i = 0; i < size; i++) {
- ResolveInfo resolveInfo = mAvailableRecognition.get(i);
- ComponentName comp = new ComponentName(resolveInfo.serviceInfo.packageName,
- resolveInfo.serviceInfo.name);
- ServiceInfo si = resolveInfo.serviceInfo;
- String settingsActivity = null;
- // Always show in voice input settings unless specifically set to False.
- boolean selectableAsDefault = true;
- try (XmlResourceParser parser = si.loadXmlMetaData(mContext.getPackageManager(),
- RecognitionService.SERVICE_META_DATA)) {
- if (parser == null) {
- throw new XmlPullParserException("No " + RecognitionService.SERVICE_META_DATA
- + " meta-data for " + si.packageName);
- }
+ final ArrayList<RecognizerInfo> validRecognitionServices =
+ validRecognitionServices(mContext);
- Resources res = mContext.getPackageManager().getResourcesForApplication(
- si.applicationInfo);
-
- AttributeSet attrs = Xml.asAttributeSet(parser);
-
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG) {
- // Intentionally do nothing.
- }
-
- String nodeName = parser.getName();
- if (!"recognition-service".equals(nodeName)) {
- throw new XmlPullParserException(
- "Meta-data does not start with recognition-service tag");
- }
-
- TypedArray array = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.RecognitionService);
- settingsActivity = array.getString(
- com.android.internal.R.styleable.RecognitionService_settingsActivity);
- selectableAsDefault = array.getBoolean(
- com.android.internal.R.styleable.RecognitionService_selectableAsDefault,
- true);
- array.recycle();
- } catch (XmlPullParserException e) {
- Log.e(TAG, "error parsing recognition service meta-data", e);
- } catch (IOException e) {
- Log.e(TAG, "error parsing recognition service meta-data", e);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "error parsing recognition service meta-data", e);
- }
- // The current recognizer must always be shown in the settings, whatever its
- // selectableAsDefault value is.
- if (selectableAsDefault || comp.equals(mCurrentRecognizer)) {
- mAvailableRecognizerInfos.add(new RecognizerInfo(mContext.getPackageManager(),
- resolveInfo.serviceInfo, settingsActivity, selectableAsDefault));
+ // Filter all recognizers which can be selected as default or are the current recognizer.
+ mAvailableRecognizerInfos = new ArrayList<>();
+ for (RecognizerInfo recognizerInfo: validRecognitionServices) {
+ if (recognizerInfo.mSelectableAsDefault || new ComponentName(
+ recognizerInfo.mService.packageName, recognizerInfo.mService.name)
+ .equals(mCurrentRecognizer)) {
+ mAvailableRecognizerInfos.add(recognizerInfo);
}
}
+
Collections.sort(mAvailableRecognizerInfos);
}
+
+ /**
+ * Query all services with {@link RecognitionService#SERVICE_INTERFACE} intent. Filter only
+ * those which have proper xml meta-data which start with a `recognition-service` tag.
+ * Filtered services are sorted by their labels in the ascending order.
+ *
+ * @param context {@link Context} inside which the settings app is run.
+ *
+ * @return {@link ArrayList}<{@link RecognizerInfo}>
+ * containing info about the filtered speech recognition services.
+ */
+ static ArrayList<RecognizerInfo> validRecognitionServices(Context context) {
+ final List<ResolveInfo> resolvedRecognitionServices =
+ context.getPackageManager().queryIntentServices(
+ new Intent(RecognitionService.SERVICE_INTERFACE),
+ PackageManager.GET_META_DATA);
+
+ final ArrayList<RecognizerInfo> validRecognitionServices = new ArrayList<>();
+
+ for (ResolveInfo resolveInfo: resolvedRecognitionServices) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+
+ final Pair<String, Boolean> recognitionServiceAttributes =
+ parseRecognitionServiceXmlMetadata(context, serviceInfo);
+
+ if (recognitionServiceAttributes != null) {
+ validRecognitionServices.add(new RecognizerInfo(
+ context.getPackageManager(),
+ serviceInfo,
+ recognitionServiceAttributes.first /* settingsActivity */,
+ recognitionServiceAttributes.second /* selectableAsDefault */));
+ }
+ }
+
+ return validRecognitionServices;
+ }
+
+ /**
+ * Load recognition service's xml meta-data and parse it. Return the meta-data attributes,
+ * namely, `settingsActivity` {@link String} and `selectableAsDefault` {@link Boolean}.
+ *
+ * <p>Parsing fails if the meta-data for the given service is not found
+ * or the found meta-data does not start with a `recognition-service`.</p>
+ *
+ * @param context {@link Context} inside which the settings app is run.
+ * @param serviceInfo {@link ServiceInfo} containing info
+ * about the speech recognition service in question.
+ *
+ * @return {@link Pair}<{@link String}, {@link Boolean}> containing `settingsActivity`
+ * and `selectableAsDefault` attributes if the parsing was successful, {@code null} otherwise.
+ */
+ private static Pair<String, Boolean> parseRecognitionServiceXmlMetadata(
+ Context context, ServiceInfo serviceInfo) {
+ // Default recognition service attribute values.
+ // Every recognizer can be selected unless specified otherwise.
+ String settingsActivity;
+ boolean selectableAsDefault = true;
+
+ // Parse xml meta-data.
+ try (XmlResourceParser parser = serviceInfo.loadXmlMetaData(
+ context.getPackageManager(), RecognitionService.SERVICE_META_DATA)) {
+ if (parser == null) {
+ throw new XmlPullParserException(String.format("No %s meta-data for %s package",
+ RecognitionService.SERVICE_META_DATA, serviceInfo.packageName));
+ }
+
+ final Resources res = context.getPackageManager().getResourcesForApplication(
+ serviceInfo.applicationInfo);
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ // Xml meta-data must start with a `recognition-service tag`.
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // Intentionally do nothing.
+ }
+
+ final String nodeName = parser.getName();
+ if (!"recognition-service".equals(nodeName)) {
+ throw new XmlPullParserException(String.format(
+ "%s package meta-data does not start with a `recognition-service` tag",
+ serviceInfo.packageName));
+ }
+
+ final TypedArray array = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.RecognitionService);
+ settingsActivity = array.getString(
+ com.android.internal.R.styleable.RecognitionService_settingsActivity);
+ selectableAsDefault = array.getBoolean(
+ com.android.internal.R.styleable.RecognitionService_selectableAsDefault,
+ selectableAsDefault);
+ array.recycle();
+ } catch (XmlPullParserException | IOException
+ | PackageManager.NameNotFoundException e) {
+ Log.e(TAG, String.format("Error parsing %s package recognition service meta-data",
+ serviceInfo.packageName), e);
+ return null;
+ }
+
+ return Pair.create(settingsActivity, selectableAsDefault);
+ }
}
diff --git a/src/com/android/settings/network/MobileNetworkIntentConverter.java b/src/com/android/settings/network/MobileNetworkIntentConverter.java
new file mode 100644
index 0000000..648ac61
--- /dev/null
+++ b/src/com/android/settings/network/MobileNetworkIntentConverter.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsRcsManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.Settings.MobileNetworkActivity;
+import com.android.settings.SettingsActivity;
+import com.android.settings.network.telephony.MobileNetworkUtils;
+
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+/**
+ * A Java {@link Function} for conversion between {@link Intent} to Settings,
+ * and within Settings itself.
+ */
+public class MobileNetworkIntentConverter implements Function<Intent, Intent> {
+ private static final String TAG = "MobileNetworkIntentConverter";
+
+ private static final ComponentName sTargetComponent = ComponentName
+ .createRelative("com.android.settings",
+ MobileNetworkActivity.class.getTypeName());
+
+ /**
+ * These actions has better aligned with definitions within AndroidManifest.xml
+ */
+ private static final String [] sPotentialActions = new String [] {
+ null,
+ Intent.ACTION_MAIN,
+ android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS,
+ android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS,
+ android.provider.Settings.ACTION_MMS_MESSAGE_SETTING,
+ ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN
+ };
+
+ private static final String RE_ROUTE_TAG = ":reroute:" + TAG;
+ private static final AtomicReference<String> mCachedClassName =
+ new AtomicReference<String>();
+
+ private final Context mAppContext;
+ private final ComponentName mComponent;
+
+ /**
+ * Constructor
+ * @param activity which receiving {@link Intent}
+ */
+ public MobileNetworkIntentConverter(@NonNull Activity activity) {
+ mAppContext = activity.getApplicationContext();
+ mComponent = activity.getComponentName();
+ }
+
+ /**
+ * API defined by {@link Function}.
+ * @param fromIntent is the {@link Intent} for convert.
+ * @return {@link Intent} for sending internally within Settings.
+ * Return {@code null} when failure.
+ */
+ public Intent apply(Intent fromIntent) {
+ long startTime = SystemClock.elapsedRealtimeNanos();
+
+ Intent potentialReqIntent = null;
+ if (isAttachedToExposedComponents()) {
+ potentialReqIntent = convertFromDeepLink(fromIntent);
+ } else if (mayRequireConvert(fromIntent)) {
+ potentialReqIntent = fromIntent;
+ } else {
+ return null;
+ }
+
+ final Intent reqIntent = potentialReqIntent;
+ String action = reqIntent.getAction();
+
+ // Find out the subscription ID of request.
+ final int subId = extractSubscriptionId(reqIntent);
+
+ // Prepare the arguments Bundle.
+ Function<Intent, Intent> ops = Function.identity();
+
+ if (TextUtils.equals(action,
+ android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+ || TextUtils.equals(action,
+ android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)) {
+ // Accepted.
+ ops = ops.andThen(intent -> extractArguments(intent, subId))
+ .andThen(args -> rePackIntent(args, reqIntent))
+ .andThen(intent -> updateFragment(intent, mAppContext, subId));
+ } else if (TextUtils.equals(action,
+ android.provider.Settings.ACTION_MMS_MESSAGE_SETTING)) {
+ ops = ops.andThen(intent -> extractArguments(intent, subId))
+ .andThen(args -> convertMmsArguments(args))
+ .andThen(args -> rePackIntent(args, reqIntent))
+ .andThen(intent -> updateFragment(intent, mAppContext, subId));
+ } else if (TextUtils.equals(action,
+ ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN)) {
+ ops = ops.andThen(intent -> extractArguments(intent, subId))
+ .andThen(args -> supportContactDiscoveryDialog(args, mAppContext, subId))
+ .andThen(args -> rePackIntent(args, reqIntent))
+ .andThen(intent -> updateFragment(intent, mAppContext, subId));
+ } else if ((sTargetComponent.compareTo(mComponent) == 0)
+ && ((action == null) || Intent.ACTION_MAIN.equals(action))) {
+ Log.d(TAG, "Support default actions direct to this component");
+ ops = ops.andThen(intent -> extractArguments(intent, subId))
+ .andThen(args -> rePackIntent(args, reqIntent))
+ .andThen(intent -> replaceIntentAction(intent))
+ .andThen(intent -> updateFragment(intent, mAppContext, subId));
+ } else {
+ return null;
+ }
+
+ if (!isAttachedToExposedComponents()) {
+ ops = ops.andThen(intent -> configForReRoute(intent));
+ }
+
+ Intent result = ops.apply(reqIntent);
+ if (result != null) {
+ long endTime = SystemClock.elapsedRealtimeNanos();
+ Log.d(TAG, mComponent.toString() + " intent conversion: "
+ + (endTime - startTime) + " ns");
+ }
+ return result;
+ }
+
+ @VisibleForTesting
+ protected boolean isAttachedToExposedComponents() {
+ return (sTargetComponent.compareTo(mComponent) == 0);
+ }
+
+ protected int extractSubscriptionId(Intent reqIntent) {
+ return reqIntent.getIntExtra(android.provider.Settings.EXTRA_SUB_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ protected Bundle extractArguments(Intent reqIntent, int subId) {
+ // Duplicate from SettingsActivity#getIntent()
+ Bundle args = reqIntent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ Bundle result = (args != null) ? new Bundle(args) : new Bundle();
+ result.putParcelable("intent", reqIntent);
+ result.putInt(android.provider.Settings.EXTRA_SUB_ID, subId);
+ return result;
+ }
+
+ protected Bundle convertMmsArguments(Bundle args) {
+ // highlight "mms_message" preference.
+ args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
+ MobileNetworkActivity.EXTRA_MMS_MESSAGE);
+ return args;
+ }
+
+ @VisibleForTesting
+ protected boolean mayShowContactDiscoveryDialog(Context context, int subId) {
+ // If this activity was launched using ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN, show the
+ // associated dialog only if the opt-in has not been granted yet.
+ return MobileNetworkUtils.isContactDiscoveryVisible(context, subId)
+ // has the user already enabled this configuration?
+ && !MobileNetworkUtils.isContactDiscoveryEnabled(context, subId);
+ }
+
+ protected Bundle supportContactDiscoveryDialog(Bundle args, Context context, int subId) {
+ boolean showDialog = mayShowContactDiscoveryDialog(context, subId);
+ Log.d(TAG, "maybeShowContactDiscoveryDialog subId=" + subId + ", show=" + showDialog);
+ args.putBoolean(MobileNetworkActivity.EXTRA_SHOW_CAPABILITY_DISCOVERY_OPT_IN,
+ showDialog);
+ return args;
+ }
+
+ protected Intent rePackIntent(Bundle args, Intent reqIntent) {
+ Intent intent = new Intent(reqIntent);
+ intent.setComponent(sTargetComponent);
+ intent.putExtra(android.provider.Settings.EXTRA_SUB_ID,
+ args.getInt(android.provider.Settings.EXTRA_SUB_ID));
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
+ return intent;
+ }
+
+ protected Intent replaceIntentAction(Intent intent) {
+ intent.setAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+ return intent;
+ }
+
+ @VisibleForTesting
+ protected CharSequence getFragmentTitle(Context context, int subId) {
+ SubscriptionInfo subInfo = SubscriptionUtil.getSubscriptionOrDefault(context, subId);
+ return SubscriptionUtil.getUniqueSubscriptionDisplayName(subInfo, context);
+ }
+
+ protected Intent updateFragment(Intent intent, Context context, int subId) {
+ if (intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE) == null) {
+ CharSequence title = getFragmentTitle(context, subId);
+ if (title != null) {
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title.toString());
+ }
+ }
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, getFragmentClass(context));
+ return intent;
+ }
+
+ protected String getFragmentClass(Context context) {
+ String className = mCachedClassName.get();
+ if (className != null) {
+ return className;
+ }
+ try {
+ ActivityInfo ai = context.getPackageManager()
+ .getActivityInfo(sTargetComponent, PackageManager.GET_META_DATA);
+ if (ai != null && ai.metaData != null) {
+ className = ai.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
+ if (className != null) {
+ mCachedClassName.set(className);
+ }
+ return className;
+ }
+ } catch (NameNotFoundException nnfe) {
+ // No recovery
+ Log.d(TAG, "Cannot get Metadata for: " + sTargetComponent.toString());
+ }
+ return null;
+ }
+
+ protected Intent configForReRoute(Intent intent) {
+ if (intent.hasExtra(RE_ROUTE_TAG)) {
+ Log.d(TAG, "Skip re-routed intent " + intent);
+ return null;
+ }
+ return intent.putExtra(RE_ROUTE_TAG, intent.getAction())
+ .setComponent(null);
+ }
+
+ protected static boolean mayRequireConvert(Intent intent) {
+ if (intent == null) {
+ return false;
+ }
+ final String action = intent.getAction();
+ return Arrays.stream(sPotentialActions).anyMatch(potentialAction ->
+ TextUtils.equals(action, potentialAction)
+ );
+ }
+
+ protected Intent convertFromDeepLink(Intent intent) {
+ if (intent == null) {
+ return null;
+ }
+ if (!TextUtils.equals(intent.getAction(),
+ android.provider.Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY)) {
+ return intent;
+ }
+ try {
+ return Intent.parseUri(intent.getStringExtra(
+ android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI),
+ Intent.URI_INTENT_SCHEME);
+ } catch (URISyntaxException exception) {
+ Log.d(TAG, "Intent URI corrupted", exception);
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/settings/network/telephony/RoamingDialogFragment.java b/src/com/android/settings/network/telephony/RoamingDialogFragment.java
index bd45226..3e9875e 100644
--- a/src/com/android/settings/network/telephony/RoamingDialogFragment.java
+++ b/src/com/android/settings/network/telephony/RoamingDialogFragment.java
@@ -72,7 +72,9 @@
.setIconAttribute(android.R.attr.alertDialogIcon)
.setPositiveButton(android.R.string.yes, this)
.setNegativeButton(android.R.string.no, this);
- return builder.create();
+ AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
}
@Override
diff --git a/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java b/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java
index a7185e3..3bf1563 100644
--- a/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java
+++ b/src/com/android/settings/security/ConfirmSimDeletionPreferenceController.java
@@ -19,6 +19,7 @@
import android.app.KeyguardManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.os.UserManager;
import android.provider.Settings;
import androidx.preference.Preference;
@@ -39,6 +40,8 @@
ConfirmationSimDeletionPredicate.KEY_CONFIRM_SIM_DELETION;
private boolean mConfirmationDefaultOn;
private MetricsFeatureProvider mMetricsFeatureProvider;
+ private UserManager mUserManager;
+ private KeyguardManager mKeyguardManager;
public ConfirmSimDeletionPreferenceController(Context context, String key) {
super(context, key);
@@ -46,6 +49,9 @@
context.getResources()
.getBoolean(R.bool.config_sim_deletion_confirmation_default_on);
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+
+ mUserManager = context.getSystemService(UserManager.class);
+ mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
}
@Override
@@ -99,8 +105,7 @@
@Override
public void updateState(Preference preference) {
- final KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
- if (!keyguardManager.isKeyguardSecure()) {
+ if (!mKeyguardManager.isKeyguardSecure() && mUserManager.isGuestUser()) {
preference.setEnabled(false);
if (preference instanceof TwoStatePreference) {
((TwoStatePreference) preference).setChecked(false);
diff --git a/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java b/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
index c871e9f..89ee19b 100644
--- a/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
+++ b/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
@@ -46,6 +46,7 @@
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.Settings.TetherSettingsActivity;
+import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.gestures.OneHandedSettingsUtils;
import com.android.settings.overlay.FeatureFactory;
@@ -127,7 +128,7 @@
return false;
}
final Intent shortcutIntent = createResultIntent(
- buildShortcutIntent(info),
+ buildShortcutIntent(uiContext, info),
info, clickTarget.getTitle());
mHost.setResult(Activity.RESULT_OK, shortcutIntent);
logCreateShortcut(info);
@@ -210,10 +211,14 @@
info.activityInfo.name);
}
- private static Intent buildShortcutIntent(ResolveInfo info) {
- return new Intent(SHORTCUT_PROBE)
+ private static Intent buildShortcutIntent(Context context, ResolveInfo info) {
+ Intent intent = new Intent(SHORTCUT_PROBE)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP)
.setClassName(info.activityInfo.packageName, info.activityInfo.name);
+ if (ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ }
+ return intent;
}
private static ShortcutInfo createShortcutInfo(Context context, Intent shortcutIntent,
@@ -277,8 +282,8 @@
ResolveInfo ri = context.getPackageManager().resolveActivity(si.getIntent(), 0);
if (ri != null) {
- updatedShortcuts.add(createShortcutInfo(context, buildShortcutIntent(ri), ri,
- si.getShortLabel()));
+ updatedShortcuts.add(createShortcutInfo(context,
+ buildShortcutIntent(context, ri), ri, si.getShortLabel()));
}
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
index 473b566..e34767e 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
@@ -16,26 +16,83 @@
package com.android.settings.accessibility;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
import static com.google.common.truth.Truth.assertThat;
-import android.content.Context;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowFragment;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import java.util.List;
/** Tests for {@link AccessibilityButtonFragment}. */
+@Config(shadows = ShadowFragment.class)
@RunWith(RobolectricTestRunner.class)
public class AccessibilityButtonFragmentTest {
- private Context mContext = ApplicationProvider.getApplicationContext();
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Spy
+ private Resources mResources = spy(mContext.getResources());
+ private AccessibilityButtonFragment mFragment;
+
+ @Before
+ public void setUp() {
+ mFragment = spy(new TestAccessibilityButtonFragment(mContext));
+ when(mFragment.getResources()).thenReturn(mResources);
+ when(mFragment.getActivity()).thenReturn(Robolectric.setupActivity(FragmentActivity.class));
+ }
+
+ @Test
+ public void onCreate_navigationGestureEnabled_setCorrectTitle() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_GESTURAL);
+
+ mFragment.onAttach(mContext);
+ mFragment.onCreate(Bundle.EMPTY);
+
+ assertThat(mFragment.getActivity().getTitle().toString()).isEqualTo(
+ mContext.getString(R.string.accessibility_button_gesture_title));
+ }
+
+ @Test
+ public void onCreate_navigationGestureDisabled_setCorrectTitle() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_2BUTTON);
+
+ mFragment.onAttach(mContext);
+ mFragment.onCreate(Bundle.EMPTY);
+
+ assertThat(mFragment.getActivity().getTitle().toString()).isEqualTo(
+ mContext.getString(R.string.accessibility_button_title));
+ }
@Test
public void getNonIndexableKeys_existInXmlLayout() {
@@ -47,4 +104,37 @@
assertThat(keys).containsAtLeastElementsIn(niks);
}
+
+ private static class TestAccessibilityButtonFragment extends AccessibilityButtonFragment {
+
+ private final Context mContext;
+ private final PreferenceManager mPreferenceManager;
+
+ TestAccessibilityButtonFragment(Context context) {
+ super();
+ mContext = context;
+ mPreferenceManager = new PreferenceManager(context);
+ mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context));
+ }
+
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.placeholder_prefs;
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceManager.getPreferenceScreen();
+ }
+
+ @Override
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
new file mode 100644
index 0000000..508b229
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static com.android.settings.accessibility.AccessibilitySettingsForSetupWizard.SCREEN_READER_PACKAGE_NAME;
+import static com.android.settings.accessibility.AccessibilitySettingsForSetupWizard.SCREEN_READER_SERVICE_NAME;
+import static com.android.settings.accessibility.AccessibilitySettingsForSetupWizard.SELECT_TO_SPEAK_PACKAGE_NAME;
+import static com.android.settings.accessibility.AccessibilitySettingsForSetupWizard.SELECT_TO_SPEAK_SERVICE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settingslib.RestrictedPreference;
+
+import com.google.android.setupdesign.GlifPreferenceLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link AccessibilitySettingsForSetupWizard}.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilitySettingsForSetupWizardTest {
+
+ private static final ComponentName TEST_SCREEN_READER_COMPONENT_NAME = new ComponentName(
+ SCREEN_READER_PACKAGE_NAME, SCREEN_READER_SERVICE_NAME);
+ private static final ComponentName TEST_SELECT_TO_SPEAK_COMPONENT_NAME = new ComponentName(
+ SELECT_TO_SPEAK_PACKAGE_NAME, SELECT_TO_SPEAK_SERVICE_NAME);
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final List<AccessibilityServiceInfo> mAccessibilityServices = new ArrayList<>();
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private FragmentActivity mActivity;
+ @Mock
+ private GlifPreferenceLayout mGlifLayoutView;
+ private AccessibilitySettingsForSetupWizard mFragment;
+
+ @Before
+ public void setUp() {
+ mFragment = spy(new TestAccessibilitySettingsForSetupWizard(mContext));
+ doReturn(mAccessibilityManager).when(mActivity).getSystemService(
+ AccessibilityManager.class);
+ when(mAccessibilityManager.getInstalledAccessibilityServiceList()).thenReturn(
+ mAccessibilityServices);
+ doReturn(mActivity).when(mFragment).getActivity();
+ }
+
+ @Test
+ public void onViewCreated_verifyAction() {
+ mFragment.onViewCreated(mGlifLayoutView, null);
+
+ verify(mGlifLayoutView).setHeaderText(
+ mContext.getString(R.string.vision_settings_title));
+ verify(mGlifLayoutView).setDescriptionText(
+ mContext.getString(R.string.vision_settings_description));
+ }
+
+ @Test
+ public void onResume_accessibilityServiceListInstalled_returnExpectedValue() {
+ addEnabledServiceInfo(TEST_SCREEN_READER_COMPONENT_NAME, true);
+ addEnabledServiceInfo(TEST_SELECT_TO_SPEAK_COMPONENT_NAME, true);
+ mFragment.onAttach(mContext);
+ mFragment.onViewCreated(mGlifLayoutView, null);
+
+ mFragment.onResume();
+
+ assertRestrictedPreferenceMatch(mFragment.mScreenReaderPreference,
+ TEST_SCREEN_READER_COMPONENT_NAME.getPackageName(),
+ TEST_SCREEN_READER_COMPONENT_NAME.flattenToString());
+ assertRestrictedPreferenceMatch(mFragment.mSelectToSpeakPreference,
+ TEST_SELECT_TO_SPEAK_COMPONENT_NAME.getPackageName(),
+ TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
+ }
+
+ @Test
+ public void onResume_accessibilityServiceListNotInstalled_returnNull() {
+ mFragment.onAttach(mContext);
+ mFragment.onViewCreated(mGlifLayoutView, null);
+
+ mFragment.onResume();
+
+ assertThat(mFragment.mScreenReaderPreference.getKey()).isNull();
+ assertThat(mFragment.mSelectToSpeakPreference.getKey()).isNull();
+ }
+
+ private void addEnabledServiceInfo(ComponentName componentName, boolean isAccessibilityTool) {
+ final AccessibilityServiceInfo a11yServiceInfo = mock(AccessibilityServiceInfo.class);
+ when(a11yServiceInfo.getComponentName()).thenReturn(componentName);
+ when(a11yServiceInfo.isAccessibilityTool()).thenReturn(isAccessibilityTool);
+ final ResolveInfo resolveInfo = mock(ResolveInfo.class);
+ when(a11yServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
+ resolveInfo.serviceInfo = mock(ServiceInfo.class);
+ resolveInfo.serviceInfo.packageName = componentName.getPackageName();
+ resolveInfo.serviceInfo.name = componentName.getClassName();
+ when(resolveInfo.loadLabel(mActivity.getPackageManager())).thenReturn(
+ componentName.getPackageName());
+ mAccessibilityServices.add(a11yServiceInfo);
+ }
+
+ private void assertRestrictedPreferenceMatch(RestrictedPreference preference, String title,
+ String key) {
+ assertThat(preference.getTitle().toString()).isEqualTo(title);
+ assertThat(preference.getKey()).isEqualTo(key);
+ assertThat(preference.getExtras().getString(AccessibilitySettings.EXTRA_TITLE))
+ .isEqualTo(title);
+ assertThat(preference.getExtras().getString(AccessibilitySettings.EXTRA_PREFERENCE_KEY))
+ .isEqualTo(key);
+ }
+
+ private static class TestAccessibilitySettingsForSetupWizard
+ extends AccessibilitySettingsForSetupWizard {
+
+ private final Context mContext;
+ private final PreferenceManager mPreferenceManager;
+
+ TestAccessibilitySettingsForSetupWizard(Context context) {
+ super();
+ mContext = context;
+ mPreferenceManager = new PreferenceManager(context);
+ mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context));
+ mDisplayMagnificationPreference = new Preference(context);
+ mScreenReaderPreference = new RestrictedPreference(context);
+ mSelectToSpeakPreference = new RestrictedPreference(context);
+ }
+
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.placeholder_prefs;
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceManager.getPreferenceScreen();
+ }
+
+ @Override
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
new file mode 100644
index 0000000..b8bb8d2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment;
+import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.widget.TopIntroPreference;
+
+import com.google.android.setupdesign.GlifPreferenceLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+/**
+ * Tests for {@link ToggleScreenMagnificationPreferenceFragmentForSetupWizard}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowSettingsPreferenceFragment.class})
+public class ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest {
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ private SettingsActivity mActivity;
+ @Mock
+ private GlifPreferenceLayout mGlifLayoutView;
+ @Mock
+ private SettingsMainSwitchBar mSwitchBar;
+ private ToggleScreenMagnificationPreferenceFragmentForSetupWizard mFragment;
+
+ @Before
+ public void setUp() {
+ mFragment =
+ spy(new TestToggleScreenMagnificationPreferenceFragmentForSetupWizard(mContext));
+ doReturn(mActivity).when(mFragment).getActivity();
+ when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
+ }
+
+ @Test
+ public void onViewCreated_verifyAction() {
+ mFragment.onViewCreated(mGlifLayoutView, null);
+
+ verify(mGlifLayoutView).setHeaderText(
+ mContext.getString(R.string.accessibility_screen_magnification_title));
+ verify(mGlifLayoutView).setDescriptionText(
+ mContext.getString(R.string.accessibility_screen_magnification_intro_text));
+ verify(mGlifLayoutView).setDividerInsets(Integer.MAX_VALUE, 0);
+ assertThat(mFragment.mTopIntroPreference.isVisible()).isFalse();
+ assertThat(mFragment.mSettingsPreference.isVisible()).isFalse();
+ assertThat(mFragment.mFollowingTypingSwitchPreference.isVisible()).isFalse();
+ }
+
+ private static class TestToggleScreenMagnificationPreferenceFragmentForSetupWizard
+ extends ToggleScreenMagnificationPreferenceFragmentForSetupWizard {
+
+ private final Context mContext;
+ private final PreferenceManager mPreferenceManager;
+
+ TestToggleScreenMagnificationPreferenceFragmentForSetupWizard(Context context) {
+ super();
+ mContext = context;
+ mPreferenceManager = new PreferenceManager(context);
+ mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context));
+ mTopIntroPreference = new TopIntroPreference(context);
+ mSettingsPreference = new Preference(context);
+ mFollowingTypingSwitchPreference = new SwitchPreference(context);
+ }
+
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.placeholder_prefs;
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceManager.getPreferenceScreen();
+ }
+
+ @Override
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
new file mode 100644
index 0000000..017a781
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settings.widget.SettingsMainSwitchPreference;
+import com.android.settingslib.widget.TopIntroPreference;
+
+import com.google.android.setupdesign.GlifPreferenceLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Tests for {@link ToggleScreenReaderPreferenceFragmentForSetupWizard}.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class ToggleScreenReaderPreferenceFragmentForSetupWizardTest {
+
+ private static final String TEST_TITLE = "test_title";
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ private SettingsActivity mActivity;
+ @Mock
+ private GlifPreferenceLayout mGlifLayoutView;
+ @Mock
+ private SettingsMainSwitchBar mSwitchBar;
+ private ToggleScreenReaderPreferenceFragmentForSetupWizard mFragment;
+
+ @Before
+ public void setUp() {
+ mFragment = spy(new TestToggleScreenReaderPreferenceFragmentForSetupWizard(mContext));
+ doReturn(mActivity).when(mFragment).getActivity();
+ when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
+ }
+
+ @Test
+ public void onViewCreated_verifyAction() {
+ mFragment.onViewCreated(mGlifLayoutView, null);
+
+ verify(mGlifLayoutView).setHeaderText(TEST_TITLE);
+ verify(mGlifLayoutView).setDescriptionText(
+ mContext.getString(R.string.talkback_summary));
+ verify(mGlifLayoutView).setDividerInsets(Integer.MAX_VALUE, 0);
+ assertThat(mFragment.mTopIntroPreference.isVisible()).isFalse();
+ }
+
+ private static class TestToggleScreenReaderPreferenceFragmentForSetupWizard
+ extends ToggleScreenReaderPreferenceFragmentForSetupWizard {
+
+ private final Context mContext;
+ private final PreferenceManager mPreferenceManager;
+
+ TestToggleScreenReaderPreferenceFragmentForSetupWizard(Context context) {
+ super();
+ mContext = context;
+ mPreferenceManager = new PreferenceManager(context);
+ mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context));
+ mTopIntroPreference = new TopIntroPreference(context);
+ mToggleServiceSwitchPreference = new SettingsMainSwitchPreference(context);
+ Bundle bundle = new Bundle();
+ bundle.putString(AccessibilitySettings.EXTRA_TITLE, TEST_TITLE);
+ setArguments(bundle);
+ }
+
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.placeholder_prefs;
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceManager.getPreferenceScreen();
+ }
+
+ @Override
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
new file mode 100644
index 0000000..85abbd6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settings.widget.SettingsMainSwitchPreference;
+import com.android.settingslib.widget.TopIntroPreference;
+
+import com.google.android.setupdesign.GlifPreferenceLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Tests for {@link ToggleSelectToSpeakPreferenceFragmentForSetupWizard}.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest {
+
+ private static final String TEST_TITLE = "test_title";
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ private SettingsActivity mActivity;
+ @Mock
+ private GlifPreferenceLayout mGlifLayoutView;
+ @Mock
+ private SettingsMainSwitchBar mSwitchBar;
+ private ToggleSelectToSpeakPreferenceFragmentForSetupWizard mFragment;
+
+ @Before
+ public void setUp() {
+ mFragment = spy(new TestToggleSelectToSpeakPreferenceFragmentForSetupWizard(mContext));
+ doReturn(mActivity).when(mFragment).getActivity();
+ when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
+ }
+
+ @Test
+ public void onViewCreated_verifyAction() {
+ mFragment.onViewCreated(mGlifLayoutView, null);
+
+ verify(mGlifLayoutView).setHeaderText(TEST_TITLE);
+ verify(mGlifLayoutView).setDescriptionText(
+ mContext.getString(R.string.select_to_speak_summary));
+ verify(mGlifLayoutView).setDividerInsets(Integer.MAX_VALUE, 0);
+ assertThat(mFragment.mTopIntroPreference.isVisible()).isFalse();
+ }
+
+ private static class TestToggleSelectToSpeakPreferenceFragmentForSetupWizard
+ extends ToggleSelectToSpeakPreferenceFragmentForSetupWizard {
+
+ private final Context mContext;
+ private final PreferenceManager mPreferenceManager;
+
+ TestToggleSelectToSpeakPreferenceFragmentForSetupWizard(Context context) {
+ super();
+ mContext = context;
+ mPreferenceManager = new PreferenceManager(context);
+ mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context));
+ mToggleServiceSwitchPreference = new SettingsMainSwitchPreference(context);
+ mTopIntroPreference = new TopIntroPreference(context);
+ Bundle bundle = new Bundle();
+ bundle.putString(AccessibilitySettings.EXTRA_TITLE, TEST_TITLE);
+ setArguments(bundle);
+ }
+
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.placeholder_prefs;
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceManager.getPreferenceScreen();
+ }
+
+ @Override
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/AppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppsPreferenceControllerTest.java
index 75da4d8..d0bb754 100644
--- a/tests/robotests/src/com/android/settings/applications/AppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppsPreferenceControllerTest.java
@@ -70,7 +70,7 @@
private PreferenceScreen mScreen;
private AppsPreferenceController mController;
- private List<UsageStats> mUsageStats;
+ private List<RecentAppStatsMixin.UsageStatsWrapper> mUsageStats;
private PreferenceCategory mRecentAppsCategory;
private PreferenceCategory mGeneralCategory;
private Preference mSeeAllPref;
@@ -147,15 +147,15 @@
final UsageStats stat3 = new UsageStats();
stat1.mLastTimeUsed = System.currentTimeMillis();
stat1.mPackageName = "pkg.class";
- mUsageStats.add(stat1);
+ mUsageStats.add(statsWrapperOf(stat1));
stat2.mLastTimeUsed = System.currentTimeMillis();
stat2.mPackageName = "pkg.class2";
- mUsageStats.add(stat2);
+ mUsageStats.add(statsWrapperOf(stat2));
stat3.mLastTimeUsed = System.currentTimeMillis();
stat3.mPackageName = "pkg.class3";
- mUsageStats.add(stat3);
+ mUsageStats.add(statsWrapperOf(stat3));
when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
.thenReturn(mAppEntry);
when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
@@ -164,4 +164,9 @@
.thenReturn(mAppEntry);
mAppEntry.info = mApplicationInfo;
}
+
+ private static RecentAppStatsMixin.UsageStatsWrapper statsWrapperOf(
+ UsageStats stats) {
+ return new RecentAppStatsMixin.UsageStatsWrapper(stats, /* userId= */ 0);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/applications/RecentAppStatsMixinTest.java b/tests/robotests/src/com/android/settings/applications/RecentAppStatsMixinTest.java
index 0fb2a7e..6b94bce 100644
--- a/tests/robotests/src/com/android/settings/applications/RecentAppStatsMixinTest.java
+++ b/tests/robotests/src/com/android/settings/applications/RecentAppStatsMixinTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -53,6 +54,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
@RunWith(RobolectricTestRunner.class)
public class RecentAppStatsMixinTest {
@@ -60,6 +62,8 @@
@Mock
private UsageStatsManager mUsageStatsManager;
@Mock
+ private UsageStatsManager mWorkUsageStatsManager;
+ @Mock
private UserManager mUserManager;
@Mock
private ApplicationsState mAppState;
@@ -87,6 +91,8 @@
when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
mRecentAppStatsMixin = new RecentAppStatsMixin(context, 3 /* maximumApps */);
+ ReflectionHelpers.setField(mRecentAppStatsMixin, "mWorkUsageStatsManager",
+ Optional.of(mWorkUsageStatsManager));
}
@Test
@@ -99,7 +105,7 @@
// stat1 is valid app.
when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
.thenReturn(mAppEntry);
- when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(new ResolveInfo());
when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
.thenReturn(stats);
@@ -134,7 +140,7 @@
.thenReturn(mAppEntry);
when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId()))
.thenReturn(mAppEntry);
- when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(new ResolveInfo());
when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
.thenReturn(stats);
@@ -170,7 +176,7 @@
.thenReturn(mAppEntry);
when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId()))
.thenReturn(null);
- when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(new ResolveInfo());
when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
.thenReturn(stats);
@@ -272,7 +278,7 @@
when(mPackageManager.getInstalledModules(anyInt() /* flags */))
.thenReturn(modules);
- when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(new ResolveInfo());
when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
.thenReturn(stats);
@@ -296,7 +302,7 @@
// stat1, stat2 are valid apps. stat3 is invalid.
when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
.thenReturn(mAppEntry);
- when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(new ResolveInfo());
when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
.thenReturn(stats);
@@ -306,4 +312,97 @@
assertThat(mRecentAppStatsMixin.mRecentApps).isEmpty();
}
+
+ @Test
+ public void loadDisplayableRecentApps_usePersonalAndWorkApps_shouldBeSortedByLastTimeUse() {
+ final List<UsageStats> personalStats = new ArrayList<>();
+ final UsageStats stats1 = new UsageStats();
+ final UsageStats stats2 = new UsageStats();
+ stats1.mLastTimeUsed = System.currentTimeMillis();
+ stats1.mPackageName = "personal.pkg.class";
+ personalStats.add(stats1);
+
+ stats2.mLastTimeUsed = System.currentTimeMillis() - 5000;
+ stats2.mPackageName = "personal.pkg.class2";
+ personalStats.add(stats2);
+
+ final List<UsageStats> workStats = new ArrayList<>();
+ final UsageStats stat3 = new UsageStats();
+ stat3.mLastTimeUsed = System.currentTimeMillis() - 2000;
+ stat3.mPackageName = "work.pkg.class3";
+ workStats.add(stat3);
+
+ when(mAppState.getEntry(anyString(), anyInt()))
+ .thenReturn(mAppEntry);
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
+ .thenReturn(new ResolveInfo());
+ // personal app stats
+ when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+ .thenReturn(personalStats);
+ // work app stats
+ when(mWorkUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+ .thenReturn(workStats);
+ mAppEntry.info = mApplicationInfo;
+
+ mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+ assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(3);
+ assertThat(mRecentAppStatsMixin.mRecentApps.get(0).mUsageStats.mPackageName).isEqualTo(
+ "personal.pkg.class");
+ assertThat(mRecentAppStatsMixin.mRecentApps.get(1).mUsageStats.mPackageName).isEqualTo(
+ "work.pkg.class3");
+ assertThat(mRecentAppStatsMixin.mRecentApps.get(2).mUsageStats.mPackageName).isEqualTo(
+ "personal.pkg.class2");
+ }
+
+ @Test
+ public void loadDisplayableRecentApps_usePersonalAndWorkApps_shouldBeUniquePerProfile() {
+ final String firstAppPackageName = "app1.pkg.class";
+ final String secondAppPackageName = "app2.pkg.class";
+ final List<UsageStats> personalStats = new ArrayList<>();
+ final UsageStats personalStatsFirstApp = new UsageStats();
+ final UsageStats personalStatsFirstAppOlderUse = new UsageStats();
+ final UsageStats personalStatsSecondApp = new UsageStats();
+ personalStatsFirstApp.mLastTimeUsed = System.currentTimeMillis();
+ personalStatsFirstApp.mPackageName = firstAppPackageName;
+ personalStats.add(personalStatsFirstApp);
+
+ personalStatsFirstAppOlderUse.mLastTimeUsed = System.currentTimeMillis() - 5000;
+ personalStatsFirstAppOlderUse.mPackageName = firstAppPackageName;
+ personalStats.add(personalStatsFirstAppOlderUse);
+
+ personalStatsSecondApp.mLastTimeUsed = System.currentTimeMillis() - 2000;
+ personalStatsSecondApp.mPackageName = secondAppPackageName;
+ personalStats.add(personalStatsSecondApp);
+
+ final List<UsageStats> workStats = new ArrayList<>();
+ final UsageStats workStatsSecondApp = new UsageStats();
+ workStatsSecondApp.mLastTimeUsed = System.currentTimeMillis() - 1000;
+ workStatsSecondApp.mPackageName = secondAppPackageName;
+ workStats.add(workStatsSecondApp);
+
+ when(mAppState.getEntry(anyString(), anyInt()))
+ .thenReturn(mAppEntry);
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
+ .thenReturn(new ResolveInfo());
+ // personal app stats
+ when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+ .thenReturn(personalStats);
+ // work app stats
+ when(mWorkUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+ .thenReturn(workStats);
+ mAppEntry.info = mApplicationInfo;
+
+ mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+ // The output should have the first app once since the duplicate use in the personal profile
+ // is filtered out, and the second app twice - once for each profile.
+ assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(3);
+ assertThat(mRecentAppStatsMixin.mRecentApps.get(0).mUsageStats.mPackageName).isEqualTo(
+ firstAppPackageName);
+ assertThat(mRecentAppStatsMixin.mRecentApps.get(1).mUsageStats.mPackageName).isEqualTo(
+ secondAppPackageName);
+ assertThat(mRecentAppStatsMixin.mRecentApps.get(2).mUsageStats.mPackageName).isEqualTo(
+ secondAppPackageName);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index 3cdff6e..4427127 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -86,6 +86,7 @@
mShadowCachedBluetoothDeviceManager = Shadow.extract(
Utils.getLocalBtManager(mContext).getCachedDeviceManager());
mCachedDevices = new ArrayList<>();
+ mCachedDevices.add(mCachedBluetoothDevice);
mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices);
Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
@@ -109,7 +110,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -122,7 +122,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -135,7 +134,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -148,7 +146,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -261,6 +258,35 @@
}
@Test
+ public void
+ onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovePreference() {
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ when(mBluetoothDeviceUpdater
+ .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ mCachedDevices.clear();
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ when(mBluetoothDeviceUpdater
+ .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ mCachedDevices.clear();
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
+
+ @Test
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java
index 3fa306f..86829b4 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java
@@ -109,6 +109,7 @@
/* deviceProfile */ "",
/* selfManaged */ false,
/* notifyOnDeviceNearby */ true,
+ /* revoked */ false,
/* timeApprovedMs */ System.currentTimeMillis(),
/* lastTimeConnected */ Long.MAX_VALUE);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index 98f1fe3..7472dc1 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -90,6 +90,7 @@
Utils.getLocalBtManager(mContext).getCachedDeviceManager());
doReturn(mContext).when(mDashboardFragment).getContext();
mCachedDevices = new ArrayList<>();
+ mCachedDevices.add(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
@@ -108,7 +109,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -121,7 +121,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -134,7 +133,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -147,7 +145,6 @@
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
- mCachedDevices.add(mCachedBluetoothDevice);
mBluetoothDeviceUpdater.onAudioModeChanged();
@@ -168,6 +165,20 @@
}
@Test
+ public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ when(mBluetoothDeviceUpdater.
+ isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
+ mCachedDevices.clear();
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
+
+ @Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_removePreference() {
mAudioManager.setMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
@@ -260,6 +271,35 @@
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
}
+ @Test
+ public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovesPreference()
+ {
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ when(mBluetoothDeviceUpdater
+ .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ mCachedDevices.clear();
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
+
+ @Test
+ public void onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovesPreference
+ () {
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ when(mBluetoothDeviceUpdater
+ .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
+ when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
+ mCachedDevices.clear();
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
@Test
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
diff --git a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
index da7516b..255a0be 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
@@ -77,6 +77,7 @@
private Context mContext;
private SavedBluetoothDeviceUpdater mBluetoothDeviceUpdater;
private BluetoothDevicePreference mPreference;
+ private List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
@Before
public void setUp() {
@@ -99,6 +100,10 @@
false, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
+ mCachedDevices.add(mCachedBluetoothDevice);
+ when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
+ when(mDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
+
}
@Test
@@ -144,6 +149,31 @@
}
@Test
+ public void
+ onProfileConnectionStateChanged_leDeviceDisconnected_inDeviceList_invokesAddPreference()
+ {
+ when(mBluetoothDevice.isConnected()).thenReturn(false);
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
+ BluetoothDevicePreference.SortType.TYPE_NO_SORT);
+ }
+
+ @Test
+ public void
+ onProfileConnectionStateChanged_deviceDisconnected_notInDeviceList_invokesRemovePreference() {
+ when(mBluetoothDevice.isConnected()).thenReturn(false);
+ mCachedDevices.clear();
+
+ mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
+ BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO);
+
+ verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+ }
+
+ @Test
public void onClick_Preference_setConnect() {
mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
diff --git a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
index b9c4edc..1f50bed 100644
--- a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
@@ -150,7 +150,8 @@
new TimeZoneCapabilitiesAndConfig(capabilities, configuration);
when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
- assertThat(mController.getSummary().toString()).isEmpty();
+ assertThat(mController.getSummary()).isEqualTo(
+ mContext.getString(R.string.location_time_zone_detection_auto_is_on));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java
index 53494ab..72477b9 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothMaxConnectedAudioDevicesPreferenceControllerTest.java
@@ -70,8 +70,8 @@
mPreference.setEntryValues(R.array.bluetooth_max_connected_audio_devices_values);
// Retrieve default max connected audio devices to a test controlled value
try {
- Resources res = mSpyContext.getPackageManager().getResourcesForApplication("com.android.bluetooth.services");
- TEST_MAX_CONNECTED_AUDIO_DEVICES = res.getInteger(res.getIdentifier("config_bluetooth_max_connected_audio_devices", "integer", "com.android.bluetooth.services"));
+ Resources res = mSpyContext.getPackageManager().getResourcesForApplication("com.android.bluetooth");
+ TEST_MAX_CONNECTED_AUDIO_DEVICES = res.getInteger(res.getIdentifier("config_bluetooth_max_connected_audio_devices", "integer", "com.android.bluetooth"));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
index e028c62..b3de184 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
@@ -17,18 +17,21 @@
package com.android.settings.enterprise;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.provider.SearchIndexableResource;
+import android.widget.FrameLayout;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -42,9 +45,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.android.controller.ActivityController;
-import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@@ -54,33 +58,33 @@
@Mock
private DevicePolicyManager mDevicePolicyManager;
- @Mock
- private PrivacySettingsPreference mPrivacySettingsPreference;
private FakeFeatureFactory mFeatureFactory;
private EnterprisePrivacySettings mSettings;
private Context mContext;
+ private TestActivity mActivity;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = spy(ApplicationProvider.getApplicationContext());
+ mContext = ApplicationProvider.getApplicationContext();
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSettings = new EnterprisePrivacySettings();
- mSettings.mPrivacySettingsPreference = mPrivacySettingsPreference;
+ mSettings.mPrivacySettingsPreference = new PrivacySettingsEnterprisePreference(mContext);
- when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDevicePolicyManager);
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
- .thenReturn(DEVICE_OWNER_COMPONENT);
- when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
- .thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
+ ActivityController<TestActivity> controller = Robolectric.buildActivity(
+ TestActivity.class).create();
+ mActivity = controller.get();
+
+ mActivity
+ .getSupportFragmentManager()
+ .beginTransaction()
+ .add(TestActivity.CONTAINER_VIEW_ID, mSettings)
+ .commit();
+ controller.start();
}
@Test
public void verifyConstants() {
- when(mPrivacySettingsPreference.getPreferenceScreenResId())
- .thenReturn(R.xml.enterprise_privacy_settings);
-
assertThat(mSettings.getMetricsCategory())
.isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_SETTINGS);
assertThat(mSettings.getLogTag()).isEqualTo("EnterprisePrivacySettings");
@@ -90,7 +94,7 @@
}
@Test
- public void isPageEnabled_hasDeviceOwner_shouldReturnTrue() {
+ public void isPageEnabled_hasDeviceOwner_returnsTrue() {
when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner())
.thenReturn(true);
@@ -99,7 +103,7 @@
}
@Test
- public void isPageEnabled_noDeviceOwner_shouldReturnFalse() {
+ public void isPageEnabled_noDeviceOwner_returnsFalse() {
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner())
.thenReturn(false);
@@ -109,36 +113,105 @@
}
@Test
- public void getPreferenceControllers() {
- final List<AbstractPreferenceController> controllers = new ArrayList<>();
- controllers.add(new NetworkLogsPreferenceController(mContext));
- when(mPrivacySettingsPreference.createPreferenceControllers(anyBoolean()))
- .thenReturn(controllers);
-
+ public void getPreferenceControllers_returnsEnterprisePreferenceControllers() {
final List<AbstractPreferenceController> privacyControllers =
mSettings.createPreferenceControllers(mContext);
- assertThat(privacyControllers).isNotNull();
- assertThat(privacyControllers.size()).isEqualTo(1);
- assertThat(controllers.get(0)).isInstanceOf(NetworkLogsPreferenceController.class);
+ verifyEnterprisePreferenceControllers(privacyControllers);
}
@Test
public void
getSearchIndexProviderPreferenceControllers_returnsEnterpriseSearchIndexPreferenceControllers() {
+ Context context = spy(ApplicationProvider.getApplicationContext());
+ setupPrivacyPreference(context, DEVICE_OWNER_TYPE_DEFAULT);
+
final List<AbstractPreferenceController> controllers =
EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
- .getPreferenceControllers(mContext);
+ .getPreferenceControllers(context);
verifyEnterprisePreferenceControllers(controllers);
}
@Test
+ public void
+ getSearchIndexProviderPreferenceControllers_returnsFinancedSearchIndexPreferenceControllers() {
+ Context context = spy(ApplicationProvider.getApplicationContext());
+ setupPrivacyPreference(context, DEVICE_OWNER_TYPE_FINANCED);
+
+ final List<AbstractPreferenceController> controllers =
+ EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
+ .getPreferenceControllers(context);
+
+ verifyFinancedPreferenceControllers(controllers);
+ }
+
+ @Test
public void getXmlResourcesToIndex_returnsEnterpriseXmlResources() {
+ Context context = spy(ApplicationProvider.getApplicationContext());
+ setupPrivacyPreference(context, DEVICE_OWNER_TYPE_DEFAULT);
+
final List<SearchIndexableResource> searchIndexableResources =
EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
- .getXmlResourcesToIndex(mContext, true);
+ .getXmlResourcesToIndex(context, true);
verifyEnterpriseSearchIndexableResources(searchIndexableResources);
}
+
+ @Test
+ public void getXmlResourcesToIndex_returnsFinancedXmlResources() {
+ Context context = spy(ApplicationProvider.getApplicationContext());
+ setupPrivacyPreference(context, DEVICE_OWNER_TYPE_FINANCED);
+
+ final List<SearchIndexableResource> searchIndexableResources =
+ EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
+ .getXmlResourcesToIndex(context, true);
+
+ verifyFinancedSearchIndexableResources(searchIndexableResources);
+ }
+
+ @Test
+ public void onCreate_enterprisePrivacyPreference_updatesTitle() {
+ mSettings.onCreate(new Bundle());
+
+ assertThat(mActivity.getTitle())
+ .isEqualTo(mContext.getText(R.string.enterprise_privacy_settings));
+ }
+
+ @Test
+ public void onCreate_financedPrivacyPreference_doesNotUpdateTitle() {
+ mSettings.mPrivacySettingsPreference = new PrivacySettingsFinancedPreference(mContext);
+
+ mSettings.onCreate(new Bundle());
+
+ assertThat(mActivity.getTitle())
+ .isEqualTo(mContext.getText(R.string.financed_privacy_settings));
+ }
+
+ private void setupPrivacyPreference(Context context, int deviceOwnerType) {
+ when(context.getSystemService(DevicePolicyManager.class)).thenReturn(mDevicePolicyManager);
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(DEVICE_OWNER_COMPONENT);
+ when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(deviceOwnerType);
+ }
+
+ private static final class TestActivity extends AppCompatActivity {
+
+ private static final int CONTAINER_VIEW_ID = 1234;
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ FrameLayout frameLayout = new FrameLayout(this);
+ frameLayout.setId(CONTAINER_VIEW_ID);
+
+ // Need to set the Theme.AppCompat theme (or descendant) with this activity, otherwise
+ // a {@link IllegalStateException} is thrown when setting the content view.
+ setTheme(R.style.Theme_AppCompat_Light);
+ setContentView(frameLayout);
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
index cbd4390..2d5a0e9 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
@@ -46,8 +46,10 @@
import androidx.slice.widget.SliceLiveData;
import com.android.settings.R;
+import com.android.settings.bluetooth.Utils;
import com.android.settings.testutils.SliceTester;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import org.junit.After;
@@ -67,7 +69,7 @@
import java.util.List;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowBluetoothAdapter.class)
+@Config(shadows = {ShadowBluetoothAdapter.class, ShadowCachedBluetoothDeviceManager.class})
public class BluetoothDevicesSliceTest {
private static final String BLUETOOTH_MOCK_ADDRESS = "00:11:00:11:00:11";
@@ -111,6 +113,9 @@
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_ON);
}
+ final ShadowCachedBluetoothDeviceManager shadowCachedBluetoothDeviceManager =
+ Shadow.extract(Utils.getLocalBtManager(mContext).getCachedDeviceManager());
+ shadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mBluetoothDeviceList);
}
@After
diff --git a/tests/unit/src/com/android/settings/network/MobileNetworkIntentConverterTest.java b/tests/unit/src/com/android/settings/network/MobileNetworkIntentConverterTest.java
new file mode 100644
index 0000000..1800156
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/MobileNetworkIntentConverterTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.telephony.ims.ImsRcsManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.Settings.MobileNetworkActivity;
+import com.android.settings.SettingsActivity;
+import com.android.settings.Utils;
+import com.android.settings.network.telephony.MobileNetworkSettings;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class MobileNetworkIntentConverterTest {
+
+ private static final String ACTIONS_ALLOWED [] = {
+ Intent.ACTION_MAIN,
+ Settings.ACTION_NETWORK_OPERATOR_SETTINGS,
+ Settings.ACTION_DATA_ROAMING_SETTINGS,
+ Settings.ACTION_MMS_MESSAGE_SETTING,
+ ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN
+ };
+
+ private static final int TEST_SUBSCRIPTION_ID = 3;
+
+ private static final CharSequence TEST_TITLE_CHAR_SEQUENCE =
+ "Test Title".subSequence(0, 10);
+
+ @Mock
+ private Activity mActivity;
+
+ private MobileNetworkIntentConverter mIntentConverter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Context context = spy(ApplicationProvider.getApplicationContext());
+ ComponentName componentName = ComponentName.createRelative(Utils.SETTINGS_PACKAGE_NAME,
+ MobileNetworkActivity.class.getTypeName());
+
+ doReturn(context).when(mActivity).getApplicationContext();
+ doReturn(componentName).when(mActivity).getComponentName();
+
+ mIntentConverter = new MobileNetworkIntentConverter(mActivity) {
+ @Override
+ protected boolean isAttachedToExposedComponents() {
+ return false;
+ }
+ };
+ }
+
+ @Test
+ public void converter_returnNull_whenNotInterested() {
+ Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ assertThat(mIntentConverter.apply(intent)).isEqualTo(null);
+ }
+
+ @Test
+ public void converter_acceptableIntent_whenInterested() {
+ Arrays.stream(ACTIONS_ALLOWED).forEach(action -> {
+ Intent intent = new Intent(action);
+ assertThat(mIntentConverter.apply(intent)).isNotEqualTo(null);
+ });
+ }
+
+ @Test
+ public void convertSubscriptionId_fromIntentExtra_copyToBundleArgument() {
+ Intent intent = new Intent(ACTIONS_ALLOWED[0]);
+ intent.putExtra(Settings.EXTRA_SUB_ID, TEST_SUBSCRIPTION_ID);
+
+ Intent convertedIntent = mIntentConverter.apply(intent);
+
+ Bundle args = convertedIntent.getBundleExtra(
+ SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(args.getInt(Settings.EXTRA_SUB_ID)).isEqualTo(TEST_SUBSCRIPTION_ID);
+
+ int intExtra = convertedIntent.getIntExtra(
+ Settings.EXTRA_SUB_ID, TEST_SUBSCRIPTION_ID - 1);
+ assertThat(intExtra).isEqualTo(TEST_SUBSCRIPTION_ID);
+ }
+
+ @Test
+ public void supportMms_addExtra_whenIntentForMms() {
+ Intent intent = new Intent(Settings.ACTION_MMS_MESSAGE_SETTING);
+
+ Intent convertedIntent = mIntentConverter.apply(intent);
+
+ Bundle args = convertedIntent.getBundleExtra(
+ SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(args).isNotEqualTo(null);
+ assertThat(args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY)).isEqualTo(
+ MobileNetworkActivity.EXTRA_MMS_MESSAGE);
+ }
+
+ @Test
+ public void supportContacts_addExtra_whenIntentForContacts() {
+ Intent intent = new Intent(ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN);
+ MobileNetworkIntentConverter converter = new MobileNetworkIntentConverter(mActivity) {
+ @Override
+ protected boolean isAttachedToExposedComponents() {
+ return false;
+ }
+ @Override
+ protected boolean mayShowContactDiscoveryDialog(Context context, int subId) {
+ return true;
+ }
+ };
+
+ Intent convertedIntent = converter.apply(intent);
+
+ Bundle args = convertedIntent.getBundleExtra(
+ SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(args).isNotEqualTo(null);
+ assertThat(args.getBoolean(MobileNetworkActivity.EXTRA_SHOW_CAPABILITY_DISCOVERY_OPT_IN))
+ .isEqualTo(true);
+ }
+
+ @Test
+ public void convertFormat_forSettings_fragmentPresentation() {
+ MobileNetworkIntentConverter converter = new MobileNetworkIntentConverter(mActivity) {
+ @Override
+ protected boolean isAttachedToExposedComponents() {
+ return false;
+ }
+ @Override
+ protected CharSequence getFragmentTitle(Context context, int subId) {
+ return TEST_TITLE_CHAR_SEQUENCE;
+ }
+ };
+
+ Intent intent = new Intent(ACTIONS_ALLOWED[0]);
+
+ Intent convertedIntent = converter.apply(intent);
+
+ assertThat(convertedIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE))
+ .isEqualTo(TEST_TITLE_CHAR_SEQUENCE.toString());
+ assertThat(convertedIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(MobileNetworkSettings.class.getTypeName());
+ }
+
+ @Test
+ public void convertFormat_deepLink_unwrapIntent() {
+ MobileNetworkIntentConverter converter = new MobileNetworkIntentConverter(mActivity) {
+ @Override
+ protected boolean isAttachedToExposedComponents() {
+ return true;
+ }
+ };
+
+ Intent intent = new Intent(ACTIONS_ALLOWED[0]);
+ String intentUri = intent.toUri(Intent.URI_INTENT_SCHEME);
+
+ Intent deepLinkIntent = new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
+ deepLinkIntent.putExtra(Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI, intentUri);
+
+ assertThat(converter.apply(deepLinkIntent)).isNotEqualTo(null);
+ }
+}