Snap for 7995208 from 06504aeb5f to tm-release

Change-Id: If963a52b17333b6e099d35f75de59c1cb6e4e0c8
This commit is contained in:
Android Build Coastguard Worker
2021-12-14 02:09:19 +00:00
26 changed files with 1112 additions and 532 deletions

View File

@@ -111,6 +111,7 @@
<uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" /> <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
<uses-permission android:name="android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS" /> <uses-permission android:name="android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
<application <application
android:name=".SettingsApplication" android:name=".SettingsApplication"
@@ -240,6 +241,7 @@
<activity android:name=".network.telephony.MobileNetworkActivity" <activity android:name=".network.telephony.MobileNetworkActivity"
android:label="@string/network_settings_title" android:label="@string/network_settings_title"
android:exported="true" android:exported="true"
android:taskAffinity="com.android.settings.root"
android:launchMode="singleTask" android:launchMode="singleTask"
android:configChanges="orientation|screenSize|keyboardHidden"> android:configChanges="orientation|screenSize|keyboardHidden">
<!-- Note: Since the framework does not support the multiple requests of network scan <!-- Note: Since the framework does not support the multiple requests of network scan
@@ -257,6 +259,14 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".Settings$SubscriptionSettingsActivity"
android:label="@string/network_settings_title"
android:exported="false">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.network.telephony.MobileNetworkSettings"/>
</activity>
<activity android:name=".Settings$MobileNetworkListActivity" <activity android:name=".Settings$MobileNetworkListActivity"
android:exported="true" android:exported="true"
android:label="@string/network_settings_title"> android:label="@string/network_settings_title">

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal" >
<TextView
android:id="@id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dip"
android:layout_marginRight="6dip"
android:layout_marginTop="6dip"
android:textAlignment="center"/>
</LinearLayout>

View File

@@ -166,6 +166,7 @@
android:id="@+id/esim_id_value" android:id="@+id/esim_id_value"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textIsSelectable="true"
android:text="@string/device_info_not_available"/> android:text="@string/device_info_not_available"/>
<TextView <TextView

View File

@@ -461,7 +461,7 @@
<string name="next_label">Next</string> <string name="next_label">Next</string>
<!-- LocalePicker --> <!-- LocalePicker -->
<!-- Title for the locale picker activity --> <!-- Title for the locale picker activity. [CHAR LIMIT=30]-->
<string name="language_picker_title">Languages</string> <string name="language_picker_title">Languages</string>
<!-- Menu item in the locale menu. Will remove the selected locales. [CHAR LIMIT=30] --> <!-- Menu item in the locale menu. Will remove the selected locales. [CHAR LIMIT=30] -->
@@ -470,6 +470,29 @@
<!-- "Button" that opens a language picker. The selected language gets added to the language list. [CHAR LIMIT=30] --> <!-- "Button" that opens a language picker. The selected language gets added to the language list. [CHAR LIMIT=30] -->
<string name="add_a_language">Add a language</string> <string name="add_a_language">Add a language</string>
<!-- Title of preference for the locale picker activity. [CHAR LIMIT=30]-->
<string name="app_locale_preference_title">Language</string>
<!-- Title for the locale picker category. [CHAR LIMIT=50]-->
<string name="locale_picker_category_title">Preferred Language</string>
<!-- Title for the Apps' locale menu entry [CHAR LIMIT=50]-->
<string name="app_locales_picker_menu_title">App Languages</string>
<!-- Summary for the app's locale picker activity. [CHAR LIMIT=50]-->
<string name="app_locale_picker_summary">Set the language for each app</string>
<!-- Title for the App's locale picker activity. [CHAR LIMIT=50]-->
<string name="app_locale_picker_title">App Language</string>
<!-- Category for the suggested app's locales. [CHAR LIMIT=50]-->
<string name="suggested_app_locales_title">Suggested languages</string>
<!-- Category for the app's locale picker activity. [CHAR LIMIT=50]-->
<string name="all_supported_app_locales_title">All languages</string>
<!-- Description for the app without any supported languages. [CHAR LIMIT=NONE]-->
<string name="no_multiple_language_supported">The app is set to <xliff:g id="default_language" example="English (United States)">%1$s</xliff:g> by default and doesn\u2019t support multiple languages.</string>
<!-- The title of the confirmation dialog shown when the user selects one / several languages and tries to remove them [CHAR LIMIT=60] --> <!-- The title of the confirmation dialog shown when the user selects one / several languages and tries to remove them [CHAR LIMIT=60] -->
<plurals name="dlg_remove_locales_title"> <plurals name="dlg_remove_locales_title">
<item quantity="one">Remove selected language?</item> <item quantity="one">Remove selected language?</item>

View File

@@ -88,6 +88,12 @@
android:title="@string/power_usage_summary_title" android:title="@string/power_usage_summary_title"
android:summary="@string/summary_placeholder" /> android:summary="@string/summary_placeholder" />
<Preference
android:key="app_language_setting"
android:title="@string/app_locale_preference_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppLocalePreferenceController" />
<Preference <Preference
android:key="preferred_settings" android:key="preferred_settings"
android:title="@string/launch_by_default" android:title="@string/launch_by_default"

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/app_locale_picker_title">
<com.android.settingslib.widget.LayoutPreference
android:key="app_locale_description"
android:layout="@layout/app_locale_details_description"
android:selectable="false"
android:visibility="gone"
settings:allowDividerBelow="true"
settings:searchable="false"/>
<PreferenceCategory
android:key="category_key_suggested_languages"
android:title="@string/suggested_app_locales_title" />
<PreferenceCategory
android:key="category_key_all_languages"
android:title="@string/all_supported_app_locales_title" />
</PreferenceScreen>

View File

@@ -19,13 +19,29 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto" xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/language_settings"> android:title="@string/language_settings">
<PreferenceCategory
android:key="languages_category"
android:title="@string/locale_picker_category_title">
<Preference <Preference
android:key="phone_language" android:key="phone_language"
android:title="@string/phone_language" android:title="@string/phone_language"
android:icon="@drawable/ic_translate_24dp" android:icon="@drawable/ic_translate_24dp"
android:fragment="com.android.settings.localepicker.LocaleListEditor" /> android:fragment="com.android.settings.localepicker.LocaleListEditor" />
<Preference
android:key="apps_language"
android:title="@string/app_locales_picker_menu_title"
android:summary="@string/app_locale_picker_summary"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
settings:controller="com.android.settings.applications.appinfo.ManageAppLocalePreferenceController">
<extra
android:name="classname"
android:value="com.android.settings.applications.appinfo.AppLocaleDetails" />
</Preference>
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:key="keyboards_category" android:key="keyboards_category"
android:title="@string/keyboard_and_input_methods_category"> android:title="@string/keyboard_and_input_methods_category">

View File

@@ -304,6 +304,8 @@ public class Settings extends SettingsActivity {
public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ } public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
public static class MobileNetworkListActivity extends SettingsActivity {} public static class MobileNetworkListActivity extends SettingsActivity {}
public static class PowerMenuSettingsActivity extends SettingsActivity {} public static class PowerMenuSettingsActivity extends SettingsActivity {}
public static class SubscriptionSettingsActivity extends SettingsActivity { /* empty */ }
/** /**
* Activity for BugReportHandlerPicker. * Activity for BugReportHandlerPicker.
*/ */

View File

@@ -205,6 +205,10 @@ public class AppInfoDashboardFragment extends DashboardFragment
use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList( use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList(
writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles, writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles,
alarmsAndReminders)); alarmsAndReminders));
final AppLocalePreferenceController appLocale =
use(AppLocalePreferenceController.class);
appLocale.setParentFragment(this);
} }
@Override @Override

View File

@@ -0,0 +1,314 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import static com.android.settings.widget.EntityHeaderController.ActionType;
import android.app.Activity;
import android.app.LocaleManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.LocaleList;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.widget.RadioButtonPreference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
/**
* A fragment to show the current app locale info and help the user to select the expected locale.
*/
public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreference.OnClickListener {
private static final String TAG = "AppLocaleDetails";
private static final String CATEGORY_KEY_SUGGESTED_LANGUAGES =
"category_key_suggested_languages";
private static final String CATEGORY_KEY_ALL_LANGUAGES =
"category_key_all_languages";
private static final String KEY_APP_DESCRIPTION = "app_locale_description";
private boolean mCreated = false;
private AppLocaleDetailsHelper mAppLocaleDetailsHelper;
private PreferenceGroup mGroupOfSuggestedLocales;
private PreferenceGroup mGroupOfSupportedLocales;
private LayoutPreference mPrefOfDescription;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_locale_details);
mAppLocaleDetailsHelper = new AppLocaleDetailsHelper(getContext(), mPackageName);
mGroupOfSuggestedLocales =
getPreferenceScreen().findPreference(CATEGORY_KEY_SUGGESTED_LANGUAGES);
mGroupOfSupportedLocales =
getPreferenceScreen().findPreference(CATEGORY_KEY_ALL_LANGUAGES);
mPrefOfDescription = getPreferenceScreen().findPreference(KEY_APP_DESCRIPTION);
}
// Override here so we don't have an empty screen
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// if we don't have a package info, show a page saying this is unsupported
if (mPackageInfo == null) {
return inflater.inflate(R.layout.manage_applications_apps_unsupported, null);
}
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onResume() {
// Update Locales first, before refresh ui.
mAppLocaleDetailsHelper.handleAllLocalesData();
super.onResume();
}
@Override
protected boolean refreshUi() {
if (mAppLocaleDetailsHelper.getSupportedLocales().isEmpty()) {
Log.d(TAG, "No supported language.");
mGroupOfSuggestedLocales.setVisible(false);
mGroupOfSupportedLocales.setVisible(false);
mPrefOfDescription.setVisible(true);
TextView description = (TextView) mPrefOfDescription.findViewById(R.id.description);
Locale locale = mAppLocaleDetailsHelper.getCurrentSystemLocales().get(0);
description.setText(getContext().getString(R.string.no_multiple_language_supported,
locale.getDisplayName(locale)));
return true;
}
mGroupOfSuggestedLocales.removeAll();
mGroupOfSupportedLocales.removeAll();
Locale appLocale = AppLocaleDetailsHelper.getAppDefaultLocale(getContext(), mPackageName);
setLanguagesPreference(mGroupOfSuggestedLocales,
mAppLocaleDetailsHelper.getSuggestedLocales(), appLocale);
setLanguagesPreference(mGroupOfSupportedLocales,
mAppLocaleDetailsHelper.getSupportedLocales(), appLocale);
return true;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.APPS_LOCALE_LIST;
}
@Override
protected AlertDialog createDialog(int id, int errorCode) {
return null;
}
@Override
public void onRadioButtonClicked(RadioButtonPreference pref) {
mAppLocaleDetailsHelper.setAppDefaultLocale(pref.getKey());
refreshUi();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mCreated) {
Log.w(TAG, "onActivityCreated: ignoring duplicate call");
return;
}
mCreated = true;
if (mPackageInfo == null) {
return;
}
// Creates a head icon button of app on this page.
final Activity activity = getActivity();
final Preference pref = EntityHeaderController
.newInstance(activity, this, null /* header */)
.setRecyclerView(getListView(), getSettingsLifecycle())
.setIcon(Utils.getBadgedIcon(getContext(), mPackageInfo.applicationInfo))
.setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
.setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
.setPackageName(mPackageName)
.setUid(mPackageInfo.applicationInfo.uid)
.setHasAppInfoLink(true)
.setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
.done(activity, getPrefContext());
getPreferenceScreen().addPreference(pref);
}
/**
* TODO (b209962418) Do a performance test to low end device.
* @return Return the summary to show the current app's language.
*/
public static CharSequence getSummary(Context context, String packageName) {
Locale appLocale =
AppLocaleDetailsHelper.getAppDefaultLocale(context, packageName);
return appLocale == null ? "" : appLocale.getDisplayName(appLocale);
}
private void setLanguagesPreference(PreferenceGroup group,
Collection<Locale> locales, Locale appLocale) {
if (locales == null) {
return;
}
for (Locale locale : locales) {
RadioButtonPreference pref = new RadioButtonPreference(getContext());
pref.setTitle(locale.getDisplayName(locale));
pref.setKey(locale.toLanguageTag());
pref.setChecked(locale.equals(appLocale));
pref.setOnClickListener(this);
group.addPreference(pref);
}
}
@VisibleForTesting
static class AppLocaleDetailsHelper {
private String mPackageName;
private Context mContext;
private TelephonyManager mTelephonyManager;
private LocaleManager mLocaleManager;
private Collection<Locale> mSuggestedLocales = new ArrayList<>();;
private Collection<Locale> mSupportedLocales = new ArrayList<>();;
AppLocaleDetailsHelper(Context context, String packageName) {
mContext = context;
mPackageName = packageName;
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mLocaleManager = context.getSystemService(LocaleManager.class);
}
/** Handle suggested and supported locales for UI display. */
public void handleAllLocalesData() {
clearLocalesData();
handleSuggestedLocales();
handleSupportedLocales();
}
/** Gets suggested locales in the app. */
public Collection<Locale> getSuggestedLocales() {
return mSuggestedLocales;
}
/** Gets supported locales in the app. */
public Collection<Locale> getSupportedLocales() {
return mSupportedLocales;
}
@VisibleForTesting
void handleSuggestedLocales() {
LocaleList currentSystemLocales = getCurrentSystemLocales();
Locale simLocale = mTelephonyManager.getSimLocale();
Locale appLocale = getAppDefaultLocale(mContext, mPackageName);
// 1st locale in suggested languages group.
if (appLocale != null) {
mSuggestedLocales.add(appLocale);
}
// 2nd locale in suggested languages group.
if (simLocale != null && !simLocale.equals(appLocale)) {
mSuggestedLocales.add(simLocale);
}
// Other locales in suggested languages group.
for (int i = 0; i < currentSystemLocales.size(); i++) {
Locale locale = currentSystemLocales.get(i);
if (!locale.equals(appLocale) && !locale.equals(simLocale)) {
mSuggestedLocales.add(locale);
}
}
}
@VisibleForTesting
void handleSupportedLocales() {
//TODO Waiting for PackageManager api
String[] languages = getAssetSystemLocales();
for (String language : languages) {
mSupportedLocales.add(Locale.forLanguageTag(language));
}
if (mSuggestedLocales != null || !mSuggestedLocales.isEmpty()) {
mSupportedLocales.removeAll(mSuggestedLocales);
}
}
private void clearLocalesData() {
mSuggestedLocales.clear();
mSupportedLocales.clear();
}
/** Gets per app's default locale */
public static Locale getAppDefaultLocale(Context context, String packageName) {
LocaleManager localeManager = context.getSystemService(LocaleManager.class);
LocaleList localeList = (localeManager == null)
? new LocaleList() : localeManager.getApplicationLocales(packageName);
return localeList.isEmpty() ? null : localeList.get(0);
}
/** Sets per app's default language to system. */
public void setAppDefaultLocale(String languageTag) {
if (languageTag.isEmpty()) {
Log.w(TAG, "[setAppDefaultLocale] No language tag.");
return;
}
setAppDefaultLocale(LocaleList.forLanguageTags(languageTag));
}
/** Sets per app's default language to system. */
public void setAppDefaultLocale(LocaleList localeList) {
if (mLocaleManager == null) {
Log.w(TAG, "LocaleManager is null, and cannot set the app locale up.");
return;
}
mLocaleManager.setApplicationLocales(mPackageName, localeList);
}
@VisibleForTesting
LocaleList getCurrentSystemLocales() {
return Resources.getSystem().getConfiguration().getLocales();
}
@VisibleForTesting
String[] getAssetSystemLocales() {
try {
PackageManager packageManager = mContext.getPackageManager();
return packageManager.getResourcesForApplication(
packageManager.getPackageInfo(mPackageName, PackageManager.MATCH_ALL)
.applicationInfo).getAssets().getNonSystemLocales();
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Can not found the package name : " + e);
}
return new String[0];
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import android.content.Context;
import android.util.FeatureFlagUtils;
import com.android.settings.SettingsPreferenceFragment;
/**
* A controller to update current locale information of application.
*/
public class AppLocalePreferenceController extends AppInfoPreferenceControllerBase {
public AppLocalePreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return FeatureFlagUtils
.isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION)
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return AppLocaleDetails.class;
}
@Override
public CharSequence getSummary() {
return AppLocaleDetails.getSummary(mContext, mParent.getAppEntry().info.packageName);
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import android.content.Context;
import android.util.FeatureFlagUtils;
import com.android.settings.core.BasePreferenceController;
/**
* A controller to update current locale information of application
* and a entry to launch {@link ManageApplications}.
* TODO(209775925) After feature release, this class may be removed.
*/
public class ManageAppLocalePreferenceController extends BasePreferenceController {
public ManageAppLocalePreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return FeatureFlagUtils
.isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION)
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -108,6 +108,7 @@ import com.android.settings.applications.AppStorageSettings;
import com.android.settings.applications.UsageAccessDetails; import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.appinfo.AlarmsAndRemindersDetails; import com.android.settings.applications.appinfo.AlarmsAndRemindersDetails;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.applications.appinfo.AppLocaleDetails;
import com.android.settings.applications.appinfo.DrawOverlayDetails; import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.ManageExternalStorageDetails; import com.android.settings.applications.appinfo.ManageExternalStorageDetails;
@@ -231,6 +232,7 @@ public class ManageApplications extends InstrumentedFragment
public static final int LIST_MANAGE_EXTERNAL_STORAGE = 11; public static final int LIST_MANAGE_EXTERNAL_STORAGE = 11;
public static final int LIST_TYPE_ALARMS_AND_REMINDERS = 12; public static final int LIST_TYPE_ALARMS_AND_REMINDERS = 12;
public static final int LIST_TYPE_MEDIA_MANAGEMENT_APPS = 13; public static final int LIST_TYPE_MEDIA_MANAGEMENT_APPS = 13;
public static final int LIST_TYPE_APPS_LOCAL = 14;
// List types that should show instant apps. // List types that should show instant apps.
public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList( public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
@@ -318,6 +320,8 @@ public class ManageApplications extends InstrumentedFragment
ServiceManager.getService(Context.USAGE_STATS_SERVICE)); ServiceManager.getService(Context.USAGE_STATS_SERVICE));
mNotificationBackend = new NotificationBackend(); mNotificationBackend = new NotificationBackend();
mSortOrder = R.id.sort_order_recent_notification; mSortOrder = R.id.sort_order_recent_notification;
} else if (className.equals(AppLocaleDetails.class.getName())) {
mListType = LIST_TYPE_APPS_LOCAL;
} else { } else {
mListType = LIST_TYPE_MAIN; mListType = LIST_TYPE_MAIN;
} }
@@ -500,6 +504,8 @@ public class ManageApplications extends InstrumentedFragment
return SettingsEnums.ALARMS_AND_REMINDERS; return SettingsEnums.ALARMS_AND_REMINDERS;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS: case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
return SettingsEnums.MEDIA_MANAGEMENT_APPS; return SettingsEnums.MEDIA_MANAGEMENT_APPS;
case LIST_TYPE_APPS_LOCAL:
return SettingsEnums.APPS_LOCALE_LIST;
default: default:
return SettingsEnums.PAGE_UNKNOWN; return SettingsEnums.PAGE_UNKNOWN;
} }
@@ -623,6 +629,10 @@ public class ManageApplications extends InstrumentedFragment
startAppInfoFragment(MediaManagementAppsDetails.class, startAppInfoFragment(MediaManagementAppsDetails.class,
R.string.media_management_apps_title); R.string.media_management_apps_title);
break; break;
case LIST_TYPE_APPS_LOCAL:
startAppInfoFragment(AppLocaleDetails.class,
R.string.app_locale_picker_title);
break;
// TODO: Figure out if there is a way where we can spin up the profile's settings // TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed // process ahead of time, to avoid a long load of data when user clicks on a managed
// app. Maybe when they load the list of apps that contains managed profile apps. // app. Maybe when they load the list of apps that contains managed profile apps.
@@ -899,6 +909,8 @@ public class ManageApplications extends InstrumentedFragment
screenTitle = R.string.alarms_and_reminders_title; screenTitle = R.string.alarms_and_reminders_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) { } else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
screenTitle = R.string.app_notifications_title; screenTitle = R.string.app_notifications_title;
} else if (className.equals(AppLocaleDetails.class.getName())) {
screenTitle = R.string.app_locales_picker_menu_title;
} else { } else {
if (screenTitle == -1) { if (screenTitle == -1) {
screenTitle = R.string.all_apps; screenTitle = R.string.all_apps;
@@ -1521,6 +1533,10 @@ public class ManageApplications extends InstrumentedFragment
case LIST_TYPE_MEDIA_MANAGEMENT_APPS: case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
holder.setSummary(MediaManagementAppsDetails.getSummary(mContext, entry)); holder.setSummary(MediaManagementAppsDetails.getSummary(mContext, entry));
break; break;
case LIST_TYPE_APPS_LOCAL:
holder.setSummary(AppLocaleDetails
.getSummary(mContext, entry.info.packageName));
break;
default: default:
holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize);
break; break;

View File

@@ -123,6 +123,7 @@ import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.network.NetworkProviderSettings; import com.android.settings.network.NetworkProviderSettings;
import com.android.settings.network.apn.ApnEditor; import com.android.settings.network.apn.ApnEditor;
import com.android.settings.network.apn.ApnSettings; import com.android.settings.network.apn.ApnSettings;
import com.android.settings.network.telephony.MobileNetworkSettings;
import com.android.settings.network.telephony.NetworkSelectSettings; import com.android.settings.network.telephony.NetworkSelectSettings;
import com.android.settings.nfc.AndroidBeam; import com.android.settings.nfc.AndroidBeam;
import com.android.settings.nfc.PaymentSettings; import com.android.settings.nfc.PaymentSettings;
@@ -329,7 +330,8 @@ public class SettingsGateway {
AlarmsAndRemindersDetails.class.getName(), AlarmsAndRemindersDetails.class.getName(),
MediaManagementAppsDetails.class.getName(), MediaManagementAppsDetails.class.getName(),
AutoBrightnessSettings.class.getName(), AutoBrightnessSettings.class.getName(),
OneHandedSettings.class.getName() OneHandedSettings.class.getName(),
MobileNetworkSettings.class.getName()
}; };
public static final String[] SETTINGS_FOR_RESTRICTED = { public static final String[] SETTINGS_FOR_RESTRICTED = {

View File

@@ -36,7 +36,7 @@ import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.network.helper.SubscriptionAnnotation; import com.android.settings.network.helper.SubscriptionAnnotation;
import com.android.settings.network.telephony.MobileNetworkActivity; import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.AddPreference; import com.android.settings.widget.AddPreference;
import com.android.settingslib.Utils; import com.android.settingslib.Utils;
@@ -190,12 +190,8 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
SubscriptionAnnotation info = subs.get(0); SubscriptionAnnotation info = subs.get(0);
if (info.getSubInfo().isEmbedded() || info.isActive() if (info.getSubInfo().isEmbedded() || info.isActive()
|| mStatusCache.isPhysicalSimDisableSupport()) { || mStatusCache.isPhysicalSimDisableSupport()) {
final Intent intent = new Intent(mContext, MobileNetworkActivity.class); MobileNetworkUtils.launchMobileNetworkSettings(mContext,
intent.putExtra(Settings.EXTRA_SUB_ID, info.getSubscriptionId()); info.getSubInfo());
// MobileNetworkActivity is singleTask, set SplitPairRule to show in 2-pane.
MobileNetworkTwoPaneUtils.registerTwoPaneForMobileNetwork(mContext, intent,
null);
mContext.startActivity(intent);
return true; return true;
} }

View File

@@ -36,7 +36,6 @@ import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.network.telephony.MobileNetworkActivity;
import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -128,11 +127,7 @@ public class NetworkProviderDownloadedSimListController extends
pref.setSummary(getSummary(subId)); pref.setSummary(getSummary(subId));
pref.setOnPreferenceClickListener(clickedPref -> { pref.setOnPreferenceClickListener(clickedPref -> {
final Intent intent = new Intent(mContext, MobileNetworkActivity.class); MobileNetworkUtils.launchMobileNetworkSettings(mContext, info);
intent.putExtra(Settings.EXTRA_SUB_ID, info.getSubscriptionId());
// MobileNetworkActivity is singleTask, set SplitPairRule to show in 2-pane.
MobileNetworkTwoPaneUtils.registerTwoPaneForMobileNetwork(mContext, intent, null);
mContext.startActivity(intent);
return true; return true;
}); });
mPreferences.put(subId, pref); mPreferences.put(subId, pref);

View File

@@ -36,7 +36,7 @@ import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.network.telephony.MobileNetworkActivity; import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -126,12 +126,7 @@ public class NetworkProviderSimListController extends AbstractPreferenceControll
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId,
true); true);
} else { } else {
final Intent intent = new Intent(mContext, MobileNetworkActivity.class); MobileNetworkUtils.launchMobileNetworkSettings(mContext, info);
intent.putExtra(Settings.EXTRA_SUB_ID, info.getSubscriptionId());
// MobileNetworkActivity is singleTask, set SplitPairRule to show in 2-pane.
MobileNetworkTwoPaneUtils.registerTwoPaneForMobileNetwork(mContext, intent,
null);
mContext.startActivity(intent);
} }
return true; return true;
}); });

View File

@@ -24,6 +24,7 @@ import static com.android.internal.util.CollectionUtils.emptyIfNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.provider.Settings;
import android.telephony.PhoneNumberUtils; import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager;
@@ -37,6 +38,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.telephony.MccTable; import com.android.internal.telephony.MccTable;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.network.helper.SelectableSubscriptions;
import com.android.settings.network.helper.SubscriptionAnnotation;
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity; import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity; import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
import com.android.settingslib.DeviceInfoUtils; import com.android.settingslib.DeviceInfoUtils;
@@ -48,6 +51,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -643,4 +647,49 @@ public class SubscriptionUtil {
private static int getDefaultDataSubscriptionId() { private static int getDefaultDataSubscriptionId() {
return SubscriptionManager.getDefaultDataSubscriptionId(); return SubscriptionManager.getDefaultDataSubscriptionId();
} }
/**
* Select one of the subscription as the default subscription.
* @param subAnnoList a list of {@link SubscriptionAnnotation}
* @return ideally the {@link SubscriptionAnnotation} as expected
*/
private static SubscriptionAnnotation getDefaultSubscriptionSelection(
List<SubscriptionAnnotation> subAnnoList) {
return (subAnnoList == null) ? null :
subAnnoList.stream()
.filter(SubscriptionAnnotation::isDisplayAllowed)
.filter(SubscriptionAnnotation::isActive)
.findFirst().orElse(null);
}
public static SubscriptionInfo getSubscriptionOrDefault(Context context, int subscriptionId) {
return getSubscription(context, subscriptionId,
(subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) ? null : (
subAnnoList -> getDefaultSubscriptionSelection(subAnnoList)
));
}
/**
* Get the current subscription to display. First check whether intent has {@link
* Settings#EXTRA_SUB_ID} and if so find the subscription with that id.
* If not, select default one based on {@link Function} provided.
*
* @param preferredSubscriptionId preferred subscription id
* @param selectionOfDefault when true current subscription is absent
*/
private static SubscriptionInfo getSubscription(Context context, int preferredSubscriptionId,
Function<List<SubscriptionAnnotation>, SubscriptionAnnotation> selectionOfDefault) {
List<SubscriptionAnnotation> subList =
(new SelectableSubscriptions(context, true)).call();
Log.d(TAG, "get subId=" + preferredSubscriptionId + " from " + subList);
SubscriptionAnnotation currentSubInfo = subList.stream()
.filter(SubscriptionAnnotation::isDisplayAllowed)
.filter(subAnno -> (subAnno.getSubscriptionId() == preferredSubscriptionId))
.findFirst().orElse(null);
if ((currentSubInfo == null) && (selectionOfDefault != null)) {
currentSubInfo = selectionOfDefault.apply(subList);
}
return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo();
}
} }

View File

@@ -55,7 +55,6 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.network.telephony.DataConnectivityListener; import com.android.settings.network.telephony.DataConnectivityListener;
import com.android.settings.network.telephony.MobileNetworkActivity;
import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.network.telephony.SignalStrengthListener; import com.android.settings.network.telephony.SignalStrengthListener;
import com.android.settings.network.telephony.TelephonyDisplayInfoListener; import com.android.settings.network.telephony.TelephonyDisplayInfoListener;
@@ -237,7 +236,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
}); });
mSubsGearPref.setOnGearClickListener(p -> mSubsGearPref.setOnGearClickListener(p ->
startMobileNetworkActivity(mContext, subInfo.getSubscriptionId())); MobileNetworkUtils.launchMobileNetworkSettings(mContext, subInfo));
} }
if (!(mContext.getSystemService(UserManager.class)).isAdminUser()) { if (!(mContext.getSystemService(UserManager.class)).isAdminUser()) {
@@ -335,14 +334,6 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
mSubsGearPref.setSummary(""); mSubsGearPref.setSummary("");
} }
private static void startMobileNetworkActivity(Context context, int subId) {
final Intent intent = new Intent(context, MobileNetworkActivity.class);
intent.putExtra(Settings.EXTRA_SUB_ID, subId);
// MobileNetworkActivity is singleTask, set SplitPairRule to show in 2-pane.
MobileNetworkTwoPaneUtils.registerTwoPaneForMobileNetwork(context, intent, null);
context.startActivity(intent);
}
@VisibleForTesting @VisibleForTesting
boolean shouldInflateSignalStrength(int subId) { boolean shouldInflateSignalStrength(int subId) {
return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);

View File

@@ -18,8 +18,8 @@ package com.android.settings.network.telephony;
import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY; import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
import android.app.ActionBar; import android.app.Activity;
import android.content.Context; import android.app.settings.SettingsEnums;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserManager; import android.os.UserManager;
@@ -29,84 +29,27 @@ import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsRcsManager; import android.telephony.ims.ImsRcsManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
import com.android.settings.R; import com.android.settings.SettingsActivity;
import com.android.settings.core.SettingsBaseActivity; import com.android.settings.Utils;
import com.android.settings.network.ProxySubscriptionManager; import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.network.SubscriptionUtil; import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.helper.SelectableSubscriptions;
import com.android.settings.network.helper.SubscriptionAnnotation;
import java.util.List;
import java.util.function.Function;
/** /**
* Activity for displaying MobileNetworkSettings * Activity for displaying MobileNetworkSettings.
*
* @Deprecated The MobileNetworkActivity should be removed in Android U. Instead of using the
* singleTask activity which will cause an additional window transition when users launch the SIMs
* page, using the {@link com.android.settings.Settings.SubscriptionSettingsActivity} which can be
* managed by {@link SettingsActivity} and be migrated into the Settings architecture.
*/ */
public class MobileNetworkActivity extends SettingsBaseActivity @Deprecated
implements ProxySubscriptionManager.OnActiveSubscriptionChangedListener { public class MobileNetworkActivity extends Activity {
private static final String TAG = "MobileNetworkActivity"; private static final String TAG = "MobileNetworkActivity";
@VisibleForTesting public static final String SHOW_CAPABILITY_DISCOVERY_OPT_IN =
static final String MOBILE_SETTINGS_TAG = "mobile_settings:"; "show_capability_discovery_opt_in";
@VisibleForTesting
static final int SUB_ID_NULL = Integer.MIN_VALUE;
@VisibleForTesting
ProxySubscriptionManager mProxySubscriptionMgr;
private int mCurSubscriptionId = SUB_ID_NULL;
// This flag forces subscription information fragment to be re-created.
// Otherwise, fragment will be kept when subscription id has not been changed.
//
// Set initial value to true allows subscription information fragment to be re-created when
// Activity re-create occur.
private boolean mPendingSubscriptionChange = true;
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
validate(intent);
setIntent(intent);
int updateSubscriptionIndex = mCurSubscriptionId;
if (intent != null) {
updateSubscriptionIndex = intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL);
}
SubscriptionInfo info = getSubscriptionOrDefault(updateSubscriptionIndex);
if (info == null) {
Log.d(TAG, "Invalid subId request " + mCurSubscriptionId
+ " -> " + updateSubscriptionIndex);
return;
}
int oldSubId = mCurSubscriptionId;
updateSubscriptions(info, null);
// If the subscription has changed or the new intent doesnt contain the opt in action,
// remove the old discovery dialog. If the activity is being recreated, we will see
// onCreate -> onNewIntent, so the dialog will first be recreated for the old subscription
// and then removed.
if (mCurSubscriptionId != oldSubId || !doesIntentContainOptInAction(intent)) {
removeContactDiscoveryDialog(oldSubId);
}
// evaluate showing the new discovery dialog if this intent contains an action to show the
// opt-in.
if (doesIntentContainOptInAction(intent)) {
maybeShowContactDiscoveryDialog(info);
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -116,318 +59,63 @@ public class MobileNetworkActivity extends SettingsBaseActivity
return; return;
} }
final Toolbar toolbar = findViewById(R.id.action_bar); // TODO: Move these intent's extra into SubscriptionSettingsActivity if the
toolbar.setVisibility(View.VISIBLE); // MobileNetworkActivity is removed in Android U.
setActionBar(toolbar); Intent intent = getIntent();
if (intent == null) {
final ActionBar actionBar = getActionBar(); Log.d(TAG, "onCreate(), intent = null");
if (actionBar != null) { this.finish();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowTitleEnabled(true);
}
getProxySubscriptionManager().setLifecycle(getLifecycle());
final Intent startIntent = getIntent();
validate(startIntent);
mCurSubscriptionId = savedInstanceState != null
? savedInstanceState.getInt(Settings.EXTRA_SUB_ID, SUB_ID_NULL)
: ((startIntent != null)
? startIntent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL)
: SUB_ID_NULL);
// perform registration after mCurSubscriptionId been configured.
registerActiveSubscriptionsListener();
SubscriptionInfo subscription = getSubscriptionOrDefault(mCurSubscriptionId);
if (subscription == null) {
Log.d(TAG, "Invalid subId request " + mCurSubscriptionId);
tryToFinishActivity();
return; return;
} }
maybeShowContactDiscoveryDialog(subscription); Intent trampolineIntent;
final Intent subscriptionSettingsIntent = new Intent(this,
updateSubscriptions(subscription, null); com.android.settings.Settings.SubscriptionSettingsActivity.class);
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this) || !isTaskRoot()) {
trampolineIntent = subscriptionSettingsIntent;
} else {
trampolineIntent = new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY)
.setPackage(Utils.SETTINGS_PACKAGE_NAME);
trampolineIntent.putExtra(
android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
subscriptionSettingsIntent.toUri(Intent.URI_INTENT_SCHEME));
} }
@VisibleForTesting int subId = intent.getIntExtra(Settings.EXTRA_SUB_ID,
ProxySubscriptionManager getProxySubscriptionManager() { SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (mProxySubscriptionMgr == null) { SubscriptionInfo subInfo = SubscriptionUtil.getSubscriptionOrDefault(this, subId);
mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(this); CharSequence title = SubscriptionUtil.getUniqueSubscriptionDisplayName(subInfo, this);
} trampolineIntent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
return mProxySubscriptionMgr; trampolineIntent.putExtra(Settings.EXTRA_SUB_ID, subId);
if (Settings.ACTION_MMS_MESSAGE_SETTING.equals(intent.getAction())) {
// highlight "mms_message" preference.
trampolineIntent.putExtra(EXTRA_FRAGMENT_ARG_KEY, "mms_message");
} }
@VisibleForTesting if (doesIntentContainOptInAction(intent)) {
void registerActiveSubscriptionsListener() { trampolineIntent.putExtra(SHOW_CAPABILITY_DISCOVERY_OPT_IN,
getProxySubscriptionManager().addActiveSubscriptionsListener(this); maybeShowContactDiscoveryDialog(subId));
} }
/** startActivity(trampolineIntent);
* Implementation of ProxySubscriptionManager.OnActiveSubscriptionChangedListener if (isTaskRoot()) {
*/ finishAndRemoveTask();
public void onChanged() { } else {
mPendingSubscriptionChange = false;
if (mCurSubscriptionId == SUB_ID_NULL) {
return;
}
if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
mPendingSubscriptionChange = true;
return;
}
SubscriptionInfo subInfo = getSubscription(mCurSubscriptionId, null);
if (subInfo != null) {
if (mCurSubscriptionId != subInfo.getSubscriptionId()) {
// update based on subscription status change
removeContactDiscoveryDialog(mCurSubscriptionId);
updateSubscriptions(subInfo, null);
}
return;
}
Log.w(TAG, "subId missing: " + mCurSubscriptionId);
// When UI is not the active one, avoid from destroy it immediately
// but wait until onResume() to see if subscription back online again.
// This is to avoid from glitch behavior of subscription which changes
// the UI when UI is considered as in the background or only partly
// visible.
if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
mPendingSubscriptionChange = true;
return;
}
// Subscription could be missing
tryToFinishActivity();
}
protected void runSubscriptionUpdate(Runnable onUpdateRemaining) {
SubscriptionInfo subInfo = getSubscription(mCurSubscriptionId, null);
if (subInfo == null) {
onUpdateRemaining.run();
tryToFinishActivity();
return;
}
if (mCurSubscriptionId != subInfo.getSubscriptionId()) {
removeContactDiscoveryDialog(mCurSubscriptionId);
updateSubscriptions(subInfo, null);
}
onUpdateRemaining.run();
}
protected void tryToFinishActivity() {
if ((!isFinishing()) && (!isDestroyed())) {
finish(); finish();
} }
} }
@Override private boolean maybeShowContactDiscoveryDialog(int subId) {
protected void onStart() {
getProxySubscriptionManager().setLifecycle(getLifecycle());
if (mPendingSubscriptionChange) {
mPendingSubscriptionChange = false;
runSubscriptionUpdate(() -> super.onStart());
return;
}
super.onStart();
}
@Override
protected void onResume() {
if (mPendingSubscriptionChange) {
mPendingSubscriptionChange = false;
runSubscriptionUpdate(() -> super.onResume());
return;
}
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mProxySubscriptionMgr == null) {
return;
}
mProxySubscriptionMgr.removeActiveSubscriptionsListener(this);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
saveInstanceState(outState);
}
@VisibleForTesting
void saveInstanceState(@NonNull Bundle outState) {
outState.putInt(Settings.EXTRA_SUB_ID, mCurSubscriptionId);
}
private void updateTitleAndNavigation(SubscriptionInfo subscription) {
// Set the title to the name of the subscription. If we don't have subscription info, the
// title will just default to the label for this activity that's already specified in
// AndroidManifest.xml.
if (subscription != null) {
setTitle(SubscriptionUtil.getUniqueSubscriptionDisplayName(subscription, this));
}
}
@VisibleForTesting
void updateSubscriptions(SubscriptionInfo subscription, Bundle savedInstanceState) {
if (subscription == null) {
return;
}
final int subscriptionIndex = subscription.getSubscriptionId();
updateTitleAndNavigation(subscription);
if (savedInstanceState == null) {
switchFragment(subscription);
}
mCurSubscriptionId = subscriptionIndex;
}
/**
* Select one of the subscription as the default subscription.
* @param subAnnoList a list of {@link SubscriptionAnnotation}
* @return ideally the {@link SubscriptionAnnotation} as expected
*/
protected SubscriptionAnnotation defaultSubscriptionSelection(
List<SubscriptionAnnotation> subAnnoList) {
return (subAnnoList == null) ? null :
subAnnoList.stream()
.filter(SubscriptionAnnotation::isDisplayAllowed)
.filter(SubscriptionAnnotation::isActive)
.findFirst().orElse(null);
}
protected SubscriptionInfo getSubscriptionOrDefault(int subscriptionId) {
return getSubscription(subscriptionId,
(subscriptionId != SUB_ID_NULL) ? null : (
subAnnoList -> defaultSubscriptionSelection(subAnnoList)
));
}
/**
* Get the current subscription to display. First check whether intent has {@link
* Settings#EXTRA_SUB_ID} and if so find the subscription with that id.
* If not, select default one based on {@link Function} provided.
*
* @param preferredSubscriptionId preferred subscription id
* @param selectionOfDefault when true current subscription is absent
*/
@VisibleForTesting
protected SubscriptionInfo getSubscription(int preferredSubscriptionId,
Function<List<SubscriptionAnnotation>, SubscriptionAnnotation> selectionOfDefault) {
List<SubscriptionAnnotation> subList =
(new SelectableSubscriptions(this, true)).call();
Log.d(TAG, "get subId=" + preferredSubscriptionId + " from " + subList);
SubscriptionAnnotation currentSubInfo = subList.stream()
.filter(SubscriptionAnnotation::isDisplayAllowed)
.filter(subAnno -> (subAnno.getSubscriptionId() == preferredSubscriptionId))
.findFirst().orElse(null);
if ((currentSubInfo == null) && (selectionOfDefault != null)) {
currentSubInfo = selectionOfDefault.apply(subList);
}
return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo();
}
@VisibleForTesting
SubscriptionInfo getSubscriptionForSubId(int subId) {
return SubscriptionUtil.getAvailableSubscription(this,
getProxySubscriptionManager(), subId);
}
@VisibleForTesting
void switchFragment(SubscriptionInfo subInfo) {
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
final int subId = subInfo.getSubscriptionId();
final Intent intent = getIntent();
final Bundle bundle = new Bundle();
bundle.putInt(Settings.EXTRA_SUB_ID, subId);
if (intent != null && Settings.ACTION_MMS_MESSAGE_SETTING.equals(intent.getAction())) {
// highlight "mms_message" preference.
bundle.putString(EXTRA_FRAGMENT_ARG_KEY, "mms_message");
}
final String fragmentTag = buildFragmentTag(subId);
if (fragmentManager.findFragmentByTag(fragmentTag) != null) {
Log.d(TAG, "Construct fragment: " + fragmentTag);
}
final Fragment fragment = new MobileNetworkSettings();
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.content_frame, fragment, fragmentTag);
fragmentTransaction.commitAllowingStateLoss();
}
private void removeContactDiscoveryDialog(int subId) {
ContactDiscoveryDialogFragment fragment = getContactDiscoveryFragment(subId);
if (fragment != null) {
fragment.dismiss();
}
}
private ContactDiscoveryDialogFragment getContactDiscoveryFragment(int subId) {
// In the case that we are rebuilding this activity after it has been destroyed and
// recreated, look up the dialog in the fragment manager.
return (ContactDiscoveryDialogFragment) getSupportFragmentManager()
.findFragmentByTag(ContactDiscoveryDialogFragment.getFragmentTag(subId));
}
private void maybeShowContactDiscoveryDialog(SubscriptionInfo info) {
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
CharSequence carrierName = "";
if (info != null) {
subId = info.getSubscriptionId();
carrierName = SubscriptionUtil.getUniqueSubscriptionDisplayName(info, this);
}
// If this activity was launched using ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN, show the // 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. // associated dialog only if the opt-in has not been granted yet.
boolean showOptInDialog = doesIntentContainOptInAction(getIntent()) return MobileNetworkUtils.isContactDiscoveryVisible(this, subId)
// has the carrier config enabled capability discovery?
&& MobileNetworkUtils.isContactDiscoveryVisible(this, subId)
// has the user already enabled this configuration? // has the user already enabled this configuration?
&& !MobileNetworkUtils.isContactDiscoveryEnabled(this, subId); && !MobileNetworkUtils.isContactDiscoveryEnabled(this, subId);
ContactDiscoveryDialogFragment fragment = getContactDiscoveryFragment(subId);
if (showOptInDialog) {
if (fragment == null) {
fragment = ContactDiscoveryDialogFragment.newInstance(subId, carrierName);
}
// Only try to show the dialog if it has not already been added, otherwise we may
// accidentally add it multiple times, causing multiple dialogs.
if (!fragment.isAdded()) {
fragment.show(getSupportFragmentManager(),
ContactDiscoveryDialogFragment.getFragmentTag(subId));
}
}
} }
private boolean doesIntentContainOptInAction(Intent intent) { public static boolean doesIntentContainOptInAction(Intent intent) {
String intentAction = (intent != null ? intent.getAction() : null); String intentAction = (intent != null ? intent.getAction() : null);
return TextUtils.equals(intentAction, return TextUtils.equals(intentAction,
ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN); ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN);
} }
private void validate(Intent intent) {
// Do not allow ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN without a subscription id specified,
// since we do not want the user to accidentally turn on capability polling for the wrong
// subscription.
if (doesIntentContainOptInAction(intent)) {
if (SUB_ID_NULL == intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL)) {
throw new IllegalArgumentException("Intent with action "
+ "SHOW_CAPABILITY_DISCOVERY_OPT_IN must also include the extra "
+ "Settings#EXTRA_SUB_ID");
}
}
}
@VisibleForTesting
String buildFragmentTag(int subscriptionId) {
return MOBILE_SETTINGS_TAG + subscriptionId;
}
} }

View File

@@ -23,8 +23,10 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.telephony.ims.ImsRcsManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@@ -39,6 +41,7 @@ import com.android.settings.datausage.BillingCyclePreferenceController;
import com.android.settings.datausage.DataUsageSummaryPreferenceController; import com.android.settings.datausage.DataUsageSummaryPreferenceController;
import com.android.settings.network.ActiveSubscriptionsListener; import com.android.settings.network.ActiveSubscriptionsListener;
import com.android.settings.network.CarrierWifiTogglePreferenceController; import com.android.settings.network.CarrierWifiTogglePreferenceController;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.telephony.cdma.CdmaSubscriptionPreferenceController; import com.android.settings.network.telephony.cdma.CdmaSubscriptionPreferenceController;
import com.android.settings.network.telephony.cdma.CdmaSystemSelectPreferenceController; import com.android.settings.network.telephony.cdma.CdmaSystemSelectPreferenceController;
import com.android.settings.network.telephony.gsm.AutoSelectPreferenceController; import com.android.settings.network.telephony.gsm.AutoSelectPreferenceController;
@@ -115,9 +118,16 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings {
@Override @Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
Intent intent = getIntent();
if (intent != null) {
mSubId = intent.getIntExtra(Settings.EXTRA_SUB_ID,
MobileNetworkUtils.getSearchableSubscriptionId(context));
Log.i(LOG_TAG, "display subId from intent: " + mSubId);
} else {
mSubId = getArguments().getInt(Settings.EXTRA_SUB_ID, mSubId = getArguments().getInt(Settings.EXTRA_SUB_ID,
MobileNetworkUtils.getSearchableSubscriptionId(context)); MobileNetworkUtils.getSearchableSubscriptionId(context));
Log.i(LOG_TAG, "display subId: " + mSubId); Log.i(LOG_TAG, "display subId from getArguments(): " + mSubId);
}
if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
return Arrays.asList(); return Arrays.asList();
@@ -131,6 +141,30 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings {
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
Intent intent = getIntent();
SubscriptionInfo info = SubscriptionUtil.getSubscriptionOrDefault(context, mSubId);
if (info == null) {
Log.d(LOG_TAG, "Invalid subId request " + mSubId);
return;
}
int oldSubId = mSubId;
updateSubscriptions(info);
// If the subscription has changed or the new intent does not contain the opt in action,
// remove the old discovery dialog. If the activity is being recreated, we will see
// onCreate -> onNewIntent, so the dialog will first be recreated for the old subscription
// and then removed.
if (!MobileNetworkActivity.doesIntentContainOptInAction(intent)) {
removeContactDiscoveryDialog(oldSubId);
}
// evaluate showing the new discovery dialog if this intent contains an action to show the
// opt-in.
if (MobileNetworkActivity.doesIntentContainOptInAction(intent)) {
showContactDiscoveryDialog(
SubscriptionUtil.getSubscriptionOrDefault(context, mSubId));
}
final DataUsageSummaryPreferenceController dataUsageSummaryPreferenceController = final DataUsageSummaryPreferenceController dataUsageSummaryPreferenceController =
use(DataUsageSummaryPreferenceController.class); use(DataUsageSummaryPreferenceController.class);
if (dataUsageSummaryPreferenceController != null) { if (dataUsageSummaryPreferenceController != null) {
@@ -339,4 +373,49 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings {
return context.getSystemService(UserManager.class).isAdminUser(); return context.getSystemService(UserManager.class).isAdminUser();
} }
}; };
private ContactDiscoveryDialogFragment getContactDiscoveryFragment(int subId) {
// In the case that we are rebuilding this activity after it has been destroyed and
// recreated, look up the dialog in the fragment manager.
return (ContactDiscoveryDialogFragment) getChildFragmentManager()
.findFragmentByTag(ContactDiscoveryDialogFragment.getFragmentTag(subId));
}
private void removeContactDiscoveryDialog(int subId) {
ContactDiscoveryDialogFragment fragment = getContactDiscoveryFragment(subId);
if (fragment != null) {
fragment.dismiss();
}
}
private void showContactDiscoveryDialog(SubscriptionInfo info) {
if (info == null) {
Log.d(LOG_TAG, "Invalid subId request " + mSubId);
onDestroy();
return;
}
CharSequence carrierName = SubscriptionUtil.getUniqueSubscriptionDisplayName(info,
getContext());
ContactDiscoveryDialogFragment fragment = getContactDiscoveryFragment(mSubId);
if (fragment == null) {
fragment = ContactDiscoveryDialogFragment.newInstance(mSubId, carrierName);
}
// Only try to show the dialog if it has not already been added, otherwise we may
// accidentally add it multiple times, causing multiple dialogs.
if (!fragment.isAdded()) {
fragment.show(getChildFragmentManager(),
ContactDiscoveryDialogFragment.getFragmentTag(mSubId));
}
}
private void updateSubscriptions(SubscriptionInfo subscription) {
if (subscription == null) {
return;
}
final int subscriptionIndex = subscription.getSubscriptionId();
mSubId = subscriptionIndex;
}
} }

View File

@@ -45,6 +45,7 @@ import android.graphics.drawable.LayerDrawable;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.os.Bundle;
import android.os.PersistableBundle; import android.os.PersistableBundle;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
@@ -73,9 +74,11 @@ import com.android.internal.util.ArrayUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.network.SubscriptionUtil; import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.ims.WifiCallingQueryImsState; import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants; import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.development.DevelopmentSettingsEnabler; import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
@@ -1008,4 +1011,21 @@ public class MobileNetworkUtils {
return context.getResources().getString(resId); return context.getResources().getString(resId);
} }
public static void launchMobileNetworkSettings(Context context, SubscriptionInfo info) {
final int subId = info.getSubscriptionId();
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
Log.d(TAG, "launchMobileNetworkSettings fail, subId is invalid");
return;
}
final Bundle extra = new Bundle();
extra.putInt(Settings.EXTRA_SUB_ID, subId);
new SubSettingLauncher(context)
.setTitleText(SubscriptionUtil.getUniqueSubscriptionDisplayName(info, context))
.setDestination(MobileNetworkSettings.class.getCanonicalName())
.setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN)
.setArguments(extra)
.launch();
}
} }

View File

@@ -16,13 +16,10 @@
package com.android.settings.network.telephony; package com.android.settings.network.telephony;
import static androidx.lifecycle.Lifecycle.State;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
import android.content.Context; import android.content.Context;
@@ -30,21 +27,17 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.test.core.app.ActivityScenario; import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.telephony.TelephonyIntents; import com.android.settings.SettingsActivity;
import com.android.settings.network.ProxySubscriptionManager;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@@ -58,22 +51,16 @@ import org.robolectric.shadows.ShadowSubscriptionManager.SubscriptionInfoBuilder
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class MobileNetworkActivityTest { public class MobileNetworkActivityTest {
private static final int CURRENT_SUB_ID = 3; private static final int SUB_ID = 1;
private static final int PREV_SUB_ID = 1; private static final String DISPLAY_NAME = "SUB_ID";
private Context mContext; private Context mContext;
private ShadowContextImpl mShadowContextImpl; private ShadowContextImpl mShadowContextImpl;
private Intent mTestIntent; private Intent mTestIntent;
@Mock @Mock
private UserManager mUserManager; private UserManager mUserManager;
@Mock
private TelephonyManager mTelephonyManager;
private ShadowSubscriptionManager mSubscriptionManager; private ShadowSubscriptionManager mSubscriptionManager;
private SubscriptionInfo mSubscriptionInfo1; private SubscriptionInfo mSubscriptionInfo;
private SubscriptionInfo mSubscriptionInfo2;
private ActivityScenario<MobileNetworkActivity> mMobileNetworkActivity; private ActivityScenario<MobileNetworkActivity> mMobileNetworkActivity;
@Before @Before
@@ -82,20 +69,16 @@ public class MobileNetworkActivityTest {
mContext = ApplicationProvider.getApplicationContext(); mContext = ApplicationProvider.getApplicationContext();
mShadowContextImpl = Shadow.extract(RuntimeEnvironment.application.getBaseContext()); mShadowContextImpl = Shadow.extract(RuntimeEnvironment.application.getBaseContext());
mSubscriptionManager = shadowOf(mContext.getSystemService(SubscriptionManager.class));
mShadowContextImpl.setSystemService(Context.USER_SERVICE, mUserManager); mShadowContextImpl.setSystemService(Context.USER_SERVICE, mUserManager);
doReturn(true).when(mUserManager).isAdminUser(); doReturn(true).when(mUserManager).isAdminUser();
mShadowContextImpl.setSystemService(Context.TELEPHONY_SERVICE, mTelephonyManager); mTestIntent = new Intent(mContext, MobileNetworkActivity.class);
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt()); mSubscriptionInfo = SubscriptionInfoBuilder.newBuilder()
.setId(SUB_ID).setDisplayName(DISPLAY_NAME).buildSubscriptionInfo();
mTestIntent = new Intent(mContext, MockMobileNetworkActivity.class); mTestIntent.putExtra(Settings.EXTRA_SUB_ID, SUB_ID);
mTestIntent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE,
mSubscriptionManager = shadowOf(mContext.getSystemService(SubscriptionManager.class)); mSubscriptionInfo.getDisplayName());
mSubscriptionInfo1 = SubscriptionInfoBuilder.newBuilder()
.setId(PREV_SUB_ID).buildSubscriptionInfo();
mSubscriptionInfo2 = SubscriptionInfoBuilder.newBuilder()
.setId(CURRENT_SUB_ID).buildSubscriptionInfo();
} }
@After @After
@@ -105,116 +88,21 @@ public class MobileNetworkActivityTest {
} }
} }
private static class MockMobileNetworkActivity extends MobileNetworkActivity {
private MockMobileNetworkActivity() {
super();
}
private SubscriptionInfo mSubscriptionInFragment;
@Override
ProxySubscriptionManager getProxySubscriptionManager() {
if (mProxySubscriptionMgr == null) {
mProxySubscriptionMgr = mock(ProxySubscriptionManager.class);
}
return mProxySubscriptionMgr;
}
@Override
void registerActiveSubscriptionsListener() {
onChanged();
}
@Override
void switchFragment(SubscriptionInfo subInfo) {
mSubscriptionInFragment = subInfo;
}
}
private ActivityScenario<MobileNetworkActivity> createTargetActivity(Intent activityIntent) { private ActivityScenario<MobileNetworkActivity> createTargetActivity(Intent activityIntent) {
return ActivityScenario.launch(activityIntent); return ActivityScenario.launch(activityIntent);
} }
@Test @Test
@Ignore public void onCreate_getExtraFromIntent() {
public void updateBottomNavigationView_oneSubscription_shouldNotCrash() { mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo);
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1);
mMobileNetworkActivity = createTargetActivity(mTestIntent); mMobileNetworkActivity = createTargetActivity(mTestIntent);
mMobileNetworkActivity.moveToState(State.STARTED);
}
@Test
@Ignore
public void updateBottomNavigationView_twoSubscription_shouldNotCrash() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
mMobileNetworkActivity = createTargetActivity(mTestIntent);
mMobileNetworkActivity.moveToState(State.STARTED);
}
@Test
@Ignore
public void switchFragment_switchBetweenTwoSubscriptions() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
mTestIntent.putExtra(Settings.EXTRA_SUB_ID, PREV_SUB_ID);
mMobileNetworkActivity = createTargetActivity(mTestIntent);
mMobileNetworkActivity.moveToState(State.STARTED);
mMobileNetworkActivity.onActivity(activity -> {
final MockMobileNetworkActivity mockActivity = (MockMobileNetworkActivity) activity;
mockActivity.switchFragment(mSubscriptionInfo1);
assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo1);
});
}
@Test
@Ignore
public void switchFragment_subscriptionsUpdate_notifyByIntent() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
mTestIntent.putExtra(Settings.EXTRA_SUB_ID, PREV_SUB_ID);
mMobileNetworkActivity = createTargetActivity(mTestIntent);
mMobileNetworkActivity.moveToState(State.STARTED);
mMobileNetworkActivity.onActivity(activity -> {
final MockMobileNetworkActivity mockActivity = (MockMobileNetworkActivity) activity;
mockActivity.switchFragment(mSubscriptionInfo1);
assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo1);
mContext.sendBroadcast(new Intent(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), null);
mockActivity.switchFragment(mSubscriptionInfo2);
assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo2);
mContext.sendBroadcast(new Intent(
TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED), null);
mockActivity.switchFragment(mSubscriptionInfo1);
assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo1);
});
}
@Test
@Ignore
public void onSaveInstanceState_saveCurrentSubId() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
mTestIntent.putExtra(Settings.EXTRA_SUB_ID, PREV_SUB_ID);
mMobileNetworkActivity = createTargetActivity(mTestIntent);
mMobileNetworkActivity.moveToState(State.STARTED);
mMobileNetworkActivity.onActivity(activity -> { mMobileNetworkActivity.onActivity(activity -> {
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
activity.saveInstanceState(bundle); activity.onCreate(bundle);
assertThat(bundle.getInt(Settings.EXTRA_SUB_ID)).isEqualTo(PREV_SUB_ID); assertThat(bundle.getInt(Settings.EXTRA_SUB_ID)).isEqualTo(SUB_ID);
assertThat(bundle.getString(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE)).isEqualTo(
DISPLAY_NAME);
}); });
} }
} }

View File

@@ -0,0 +1,198 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.LocaleManager;
import android.content.Context;
import android.os.LocaleList;
import android.os.Looper;
import android.telephony.TelephonyManager;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.Iterables;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Locale;
@RunWith(AndroidJUnit4.class)
public class AppLocaleDetailsTest {
private static final String APP_PACKAGE_NAME = "app_package_name";
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private LocaleManager mLocaleManager;
private Context mContext;
private LocaleList mSystemLocales;
private Locale mSimLocale;
private LocaleList mAppLocale;
private String[] mAssetLocales;
@Before
@UiThreadTest
public void setUp() {
MockitoAnnotations.initMocks(this);
if (Looper.myLooper() == null) {
Looper.prepare();
}
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(LocaleManager.class)).thenReturn(mLocaleManager);
setupInitialLocales("en",
"uk",
"en, uk, jp, ne",
new String[]{"en", "ne", "ms", "pa"});
}
@Test
@UiThreadTest
public void handleAllLocalesData_localeManagerIsNull_noCrash() {
when(mContext.getSystemService(LocaleManager.class)).thenReturn(null);
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
}
@Test
@UiThreadTest
public void handleAllLocalesData_1stLocaleOfSuggestedLocaleListIsAppLocale() {
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 0);
assertTrue(locale.equals(mAppLocale.get(0)));
}
@Test
@UiThreadTest
public void handleAllLocalesData_2ndLocaleOfSuggestedLocaleListIsSimLocale() {
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 1);
assertTrue(locale.equals(mSimLocale));
}
@Test
@UiThreadTest
public void handleAllLocalesData_withoutAppLocale_1stLocaleOfSuggestedLocaleListIsSimLocal() {
setupInitialLocales("",
"uk",
"en, uk, jp, ne",
new String[]{"en", "ne", "ms", "pa"});
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 0);
assertTrue(locale.equals(mSimLocale));
}
@Test
@UiThreadTest
public void handleAllLocalesData_noAppAndSimLocale_1stLocaleIsFirstOneInSystemLocales() {
setupInitialLocales("",
"",
"en, uk, jp, ne",
new String[]{"en", "ne", "ms", "pa"});
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 0);
assertTrue(locale.equals(mSystemLocales.get(0)));
}
@Test
@UiThreadTest
public void handleAllLocalesData_supportLocaleListIsNotEmpty() {
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
assertFalse(helper.getSupportedLocales().isEmpty());
}
/**
* Sets the initial Locale data
*
* @param appLocale Application locale, it shall be a language tag.
* example: "en"
* @param simLocale SIM carrier locale, it shall be a language tag.
* example: "en"
* @param systemLocales System locales, a locale list by a multiple language tags with comma.
* example: "en, uk, jp"
* @param assetLocales Asset locales, a locale list by a multiple language tags with String
* array.
* example: new String[] {"en", "ne", "ms", "pa"}
*/
private void setupInitialLocales(String appLocale,
String simLocale,
String systemLocales,
String[] assetLocales) {
mAppLocale = LocaleList.forLanguageTags(appLocale);
mSimLocale = Locale.forLanguageTag(simLocale);
mSystemLocales = LocaleList.forLanguageTags(systemLocales);
mAssetLocales = assetLocales;
when(mTelephonyManager.getSimLocale()).thenReturn(simLocale.isEmpty() ? null : mSimLocale);
when(mLocaleManager.getApplicationLocales(anyString())).thenReturn(mAppLocale);
}
private class DummyAppLocaleDetailsHelper
extends AppLocaleDetails.AppLocaleDetailsHelper {
DummyAppLocaleDetailsHelper(Context context, String packageName) {
super(context, packageName);
}
@Override
String[] getAssetSystemLocales() {
return mAssetLocales;
}
@Override
LocaleList getCurrentSystemLocales() {
return mSystemLocales;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.util.FeatureFlagUtils;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class AppLocalePreferenceControllerTest {
private Context mContext;
private AppLocalePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mController = spy(new AppLocalePreferenceController(mContext, "test_key"));
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, true);
}
@Test
public void getAvailabilityStatus_featureFlagOff_shouldReturnUnavailable() {
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, false);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_featureFlagOn_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.util.FeatureFlagUtils;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class ManageAppLocalePreferenceControllerTest {
private Context mContext;
private ManageAppLocalePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mController = spy(new ManageAppLocalePreferenceController(mContext, "a key"));
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, true);
}
@Test
public void getAvailabilityStatus_featureFlagOff_shouldReturnUnavailable() {
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, false);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_featureFlagOn_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
}
}