diff --git a/res/values/strings.xml b/res/values/strings.xml
index f9e1517249c..927f84d046f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -354,6 +354,24 @@
Only apps that support language selection are shown here.
+
+
+ Regional preferences
+
+ Set units and number preferences
+
+ Apps can use your regional preferences to personalize your experience
+
+ Temperature
+
+ Calendar
+
+ First day of week
+
+ Numbers
+
+ [No preference]
+
{count, plural,
=1 {Remove selected language?}
@@ -492,10 +510,10 @@
Use location
-
- - On - %1$d app has access to location
- - On - %1$d apps have access to location
-
+ {count, plural,
+ =1 {On - # app has access to location}
+ other {On - # apps have access to location}
+ }
Loading\u2026
@@ -677,10 +695,10 @@
Add fingerprint
-
- - Fingerprint added
- - %1$d fingerprints added
-
+ {count, plural,
+ =1 {Fingerprint added}
+ other {# fingerprints added}
+ }
@@ -4453,27 +4471,26 @@
Keep on after device restarts
-
- - Short (%1$s second)
- - Short (%1$s seconds)
-
+ {count, plural,
+ =1 {Short ({time} second)}
+ other {Short ({time} seconds)}
+ }
-
- - Medium (%1$s second)
- - Medium (%1$s seconds)
-
+ {count, plural,
+ =1 {Medium ({time} second)}
+ other {Medium ({time} seconds)}
+ }
-
- - Long (%1$s second)
- - Long (%1$s seconds)
-
+ {count, plural,
+ =1 {Long ({time} second)}
+ other {Long ({time} seconds)}
+ }
-
- - %1$s second
- - %1$s seconds
- - %1$s seconds
-
+ {count, plural,
+ =1 {{time} second}
+ other {{time} seconds}
+ }
Settings
@@ -4672,16 +4689,16 @@
Printing
-
- - 1 print service on
- - %1$d print services on
-
+ {count, plural,
+ =1 {1 print service on}
+ other {# print services on}
+ }
-
- - 1 print job
- - %1$d print jobs
-
+ {count, plural,
+ =1 {1 print job}
+ other {# print jobs}
+ }
Print services
@@ -4878,31 +4895,31 @@
Includes high-power background activity
-
- - Restrict %1$d app
- - Restrict %1$d apps
-
+ {count, plural,
+ =1 {Restrict # app}
+ other {Restrict # apps}
+ }
-
- - %1$s recently restricted
- - %2$d apps recently restricted
-
+ {count, plural,
+ =1 {{label} recently restricted}
+ other {# apps recently restricted}
+ }
-
- - %1$s has high background battery usage
- - %2$d apps have high background battery usage
-
+ {count, plural,
+ =1 {{label} has high background battery usage}
+ other {# apps have high background battery usage}
+ }
-
- - This app can\'t run in the background
- - These apps can\'t run in the background
-
+ {count, plural,
+ =1 {This app can\'t run in the background}
+ other {These apps can\'t run in the background}
+ }
-
- - Restrict app?
- - Restrict %1$d apps?
-
+ {count, plural,
+ =1 {Restrict app?}
+ other {Restrict # apps?}
+ }
To save battery, stop %1$s from using battery in the background. This app may not work properly and notifications may be delayed.
@@ -4931,10 +4948,10 @@
Restricted apps
-
- - Limiting battery usage for %1$d app
- - Limiting battery usage for %1$d apps
-
+ {count, plural,
+ =1 {Limiting battery usage for # app}
+ other {Limiting battery usage for # apps}
+ }
Restricted %1$s
@@ -4953,10 +4970,10 @@
Detecting when apps drain battery
-
- - %1$d app restricted
- - %1$d apps restricted
-
+ {count, plural,
+ =1 {# app restricted}
+ other {# apps restricted}
+ }
^1 %
@@ -5406,10 +5423,10 @@
This app won\u2019t manage certificates, but it will stay on your device. Any certificates installed by the app will be uninstalled.
-
- - %d URL
- - %d URLs
-
+ {count, plural,
+ =1 {# URL}
+ other {# URLs}
+ }
Emergency dialing signal
@@ -5510,10 +5527,10 @@
Recently dismissed
-
- - %d notification
- - %d notifications
-
+ {count, plural,
+ =1 {# notification}
+ other {# notifications}
+ }
@@ -5954,10 +5971,10 @@
Done
-
- - Trust or remove certificate
- - Trust or remove certificates
-
+ {count, plural,
+ =1 {Trust or remove certificate}
+ other {Trust or remove certificates}
+ }
{numberOfCertificates, plural,
=1 {{orgName} has installed a certificate authority on your device, which may allow them to monitor your device network activity, including emails, apps, and secure websites.\n\nFor more information about this certificate, contact your admin.}
@@ -5971,10 +5988,10 @@
A third party is capable of monitoring your network activity, including emails, apps, and secure websites.\n\nA trusted credential installed on your device is making this possible.
-
- - Check certificate
- - Check certificates
-
+ {count, plural,
+ =1 {Check certificate}
+ other {Check certificates}
+ }
Multiple users
@@ -7434,10 +7451,10 @@
No priority conversations
-
- - %d priority conversation
- - %d priority conversations
-
+ {count, plural,
+ =1 {# priority conversation}
+ other {# priority conversations}
+ }
Priority conversations
@@ -7530,14 +7547,14 @@
All %1$s notifications
-
- - About %d notification per day
- - About %,d notifications per day
-
-
- - About %d notification per week
- - About %,d notifications per week
-
+ {count, plural,
+ =1 {About # notification per day}
+ other {About # notifications per day}
+ }
+ {count, plural,
+ =1 {About # notification per week}
+ other {About # notifications per week}
+ }
Never
@@ -7734,10 +7751,10 @@
No apps connected
-
- - %d app connected
- - %d apps connected
-
+ {count, plural,
+ =1 {# app connected}
+ other {# apps connected}
+ }
-
- - %d category deleted
- - %d categories deleted
-
+ {count, plural,
+ =1 {# category deleted}
+ other {# categories deleted}
+ }
Block all
diff --git a/res/xml/language_and_input.xml b/res/xml/language_and_input.xml
index 03614bce26a..601ba8e3537 100644
--- a/res/xml/language_and_input.xml
+++ b/res/xml/language_and_input.xml
@@ -38,6 +38,15 @@
android:name="classname"
android:value="com.android.settings.applications.appinfo.AppLocaleDetails" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/android/settings/MonitoringCertInfoActivity.java b/src/com/android/settings/MonitoringCertInfoActivity.java
index 5c15eb3a73b..4225d724fd8 100644
--- a/src/com/android/settings/MonitoringCertInfoActivity.java
+++ b/src/com/android/settings/MonitoringCertInfoActivity.java
@@ -33,6 +33,7 @@ import android.provider.Settings;
import androidx.appcompat.app.AlertDialog;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.utils.StringUtil;
import java.util.HashMap;
import java.util.Locale;
@@ -65,16 +66,17 @@ public class MonitoringCertInfoActivity extends Activity implements OnClickListe
final int numberOfCertificates = getIntent().getIntExtra(
Settings.EXTRA_NUMBER_OF_CERTIFICATES, 1);
final int titleId = RestrictedLockUtils.getProfileOrDeviceOwner(this, user) != null
- ? R.plurals.ssl_ca_cert_settings_button // Check certificate
- : R.plurals.ssl_ca_cert_dialog_title; // Trust or remove certificate
- final CharSequence title = getResources().getQuantityText(titleId, numberOfCertificates);
+ ? R.string.ssl_ca_cert_settings_button // Check certificate
+ : R.string.ssl_ca_cert_dialog_title; // Trust or remove certificate
+ final CharSequence title = StringUtil.getIcuPluralsString(this, numberOfCertificates,
+ titleId);
setTitle(title);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(title);
builder.setCancelable(true);
- builder.setPositiveButton(getResources().getQuantityText(
- R.plurals.ssl_ca_cert_settings_button, numberOfCertificates) , this);
+ builder.setPositiveButton(StringUtil.getIcuPluralsString(this, numberOfCertificates,
+ R.string.ssl_ca_cert_settings_button) , this);
builder.setNeutralButton(R.string.cancel, null);
builder.setOnDismissListener(this);
diff --git a/src/com/android/settings/accessibility/AutoclickPreferenceController.java b/src/com/android/settings/accessibility/AutoclickPreferenceController.java
index b118718b34f..741035207a5 100644
--- a/src/com/android/settings/accessibility/AutoclickPreferenceController.java
+++ b/src/com/android/settings/accessibility/AutoclickPreferenceController.java
@@ -34,9 +34,9 @@ public class AutoclickPreferenceController extends BasePreferenceController {
* placeholder for integer delay value.
*/
private static final int[] AUTOCLICK_PREFERENCE_SUMMARIES = {
- R.plurals.accessibilty_autoclick_preference_subtitle_short_delay,
- R.plurals.accessibilty_autoclick_preference_subtitle_medium_delay,
- R.plurals.accessibilty_autoclick_preference_subtitle_long_delay
+ R.string.accessibilty_autoclick_preference_subtitle_short_delay,
+ R.string.accessibilty_autoclick_preference_subtitle_medium_delay,
+ R.string.accessibilty_autoclick_preference_subtitle_long_delay
};
public AutoclickPreferenceController(Context context, String preferenceKey) {
@@ -59,7 +59,7 @@ public class AutoclickPreferenceController extends BasePreferenceController {
Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
final int summaryIndex = getAutoclickPreferenceSummaryIndex(delayMillis);
- return AutoclickUtils.getAutoclickDelaySummary(mContext.getResources(),
+ return AutoclickUtils.getAutoclickDelaySummary(mContext,
AUTOCLICK_PREFERENCE_SUMMARIES[summaryIndex], delayMillis);
}
diff --git a/src/com/android/settings/accessibility/AutoclickUtils.java b/src/com/android/settings/accessibility/AutoclickUtils.java
index cd7d662118f..89f2ceb57f9 100644
--- a/src/com/android/settings/accessibility/AutoclickUtils.java
+++ b/src/com/android/settings/accessibility/AutoclickUtils.java
@@ -20,9 +20,16 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.PluralsRes;
+import android.annotation.StringRes;
+import android.content.Context;
import android.content.res.Resources;
+import com.android.settings.R;
+import com.android.settingslib.utils.StringUtil;
+
import java.lang.annotation.Retention;
+import java.util.HashMap;
+import java.util.Map;
/** Provides utility methods related auto click. */
public final class AutoclickUtils {
@@ -58,20 +65,23 @@ public final class AutoclickUtils {
/**
* Gets string that should be used for provided autoclick delay.
*
- * @param resources Resources from which string should be retrieved.
+ * @param context context from which string should be retrieved.
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @param delayMillis Delay for whose value summary should be retrieved.
*/
- public static CharSequence getAutoclickDelaySummary(Resources resources,
- @PluralsRes int id, int delayMillis) {
+ public static CharSequence getAutoclickDelaySummary(Context context,
+ @StringRes int id, int delayMillis) {
final int quantity = (delayMillis == 1000) ? Quantity.ONE : Quantity.FEW;
final float delaySecond = (float) delayMillis / 1000;
// Only show integer when delay time is 1.
final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f";
- return resources.getQuantityString(id, quantity, String.format(decimalFormat, delaySecond));
+ Map arguments = new HashMap<>();
+ arguments.put("count", quantity);
+ arguments.put("time", String.format(decimalFormat, delaySecond));
+ return StringUtil.getIcuPluralsString(context, arguments, id);
}
private AutoclickUtils(){}
diff --git a/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java b/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java
index 372c6f54303..6a04863fd4b 100644
--- a/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java
+++ b/src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java
@@ -176,7 +176,7 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro
}
}
private CharSequence delayTimeToString(int delayMillis) {
- return AutoclickUtils.getAutoclickDelaySummary(mContext.getResources(),
- R.plurals.accessibilty_autoclick_delay_unit_second, delayMillis);
+ return AutoclickUtils.getAutoclickDelaySummary(mContext,
+ R.string.accessibilty_autoclick_delay_unit_second, delayMillis);
}
}
diff --git a/src/com/android/settings/applications/AppStateNotificationBridge.java b/src/com/android/settings/applications/AppStateNotificationBridge.java
index 964eae42d4c..f0e447e17b4 100644
--- a/src/com/android/settings/applications/AppStateNotificationBridge.java
+++ b/src/com/android/settings/applications/AppStateNotificationBridge.java
@@ -110,11 +110,11 @@ public class AppStateNotificationBridge extends AppStateBaseBridge {
context, System.currentTimeMillis() - state.lastSent, true);
} else if (sortOrder == R.id.sort_order_frequent_notification) {
if (state.avgSentDaily > 0) {
- return context.getResources().getQuantityString(
- R.plurals.notifications_sent_daily, state.avgSentDaily, state.avgSentDaily);
+ return StringUtil.getIcuPluralsString(context, state.avgSentDaily,
+ R.string.notifications_sent_daily);
}
- return context.getResources().getQuantityString(R.plurals.notifications_sent_weekly,
- state.avgSentWeekly, state.avgSentWeekly);
+ return StringUtil.getIcuPluralsString(context, state.avgSentWeekly,
+ R.string.notifications_sent_weekly);
} else {
return "";
}
diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesController.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesController.java
index bc8542e551a..4d49a225ccc 100644
--- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesController.java
+++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesController.java
@@ -25,6 +25,7 @@ import android.os.UserManager;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.utils.StringUtil;
import java.util.List;
@@ -66,9 +67,7 @@ public class InteractAcrossProfilesController extends BasePreferenceController {
return connectedApps == 0
? mContext.getResources().getString(
R.string.interact_across_profiles_number_of_connected_apps_none)
- : mContext.getResources().getQuantityString(
- R.plurals.interact_across_profiles_number_of_connected_apps,
- connectedApps,
- connectedApps);
+ : StringUtil.getIcuPluralsString(mContext, connectedApps,
+ R.string.interact_across_profiles_number_of_connected_apps);
}
}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
index f5ad76b2d9d..1ca4278d0b0 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
@@ -28,6 +28,7 @@ import com.android.settings.Settings;
import com.android.settings.Utils;
import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.utils.StringUtil;
/**
* Utilities for combined biometric details shared between Security Settings and Safety Center.
@@ -102,9 +103,8 @@ public class CombinedBiometricStatusUtils {
} else if (faceEnrolled) {
return mContext.getString(R.string.security_settings_face_preference_summary);
} else if (numFingerprintsEnrolled > 0) {
- return mContext.getResources().getQuantityString(
- R.plurals.security_settings_fingerprint_preference_summary,
- numFingerprintsEnrolled, numFingerprintsEnrolled);
+ return StringUtil.getIcuPluralsString(mContext, numFingerprintsEnrolled,
+ R.string.security_settings_fingerprint_preference_summary);
} else {
return mContext.getString(
R.string.security_settings_biometric_preference_summary_none_enrolled);
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
index 32b83004c74..b82e8b615f6 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
@@ -28,6 +28,7 @@ import com.android.settings.Utils;
import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.utils.StringUtil;
/**
* Utilities for fingerprint details shared between Security Settings and Safety Center.
@@ -69,9 +70,8 @@ public class FingerprintStatusUtils {
public String getSummary() {
if (hasEnrolled()) {
final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size();
- return mContext.getResources().getQuantityString(
- R.plurals.security_settings_fingerprint_preference_summary,
- numEnrolled, numEnrolled);
+ return StringUtil.getIcuPluralsString(mContext, numEnrolled,
+ R.string.security_settings_fingerprint_preference_summary);
} else {
return mContext.getString(
R.string.security_settings_fingerprint_preference_summary_none);
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index a8c43fb3e10..8eaff45b400 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -341,9 +341,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
@VisibleForTesting
void setTitleForInputDevice() {
- // TODO(b/254835745) once source filter for bt stylus merged
- // && mInputDevice.supportsSource(InputDevice.SOURCE_BLUETOOTH_STYLUS))
- if (mInputDevice != null) {
+ if (StylusDevicesController.isDeviceStylus(mInputDevice, mCachedDevice)) {
// This will override the default R.string.device_details_title "Device Details"
// that will show on non-stylus bluetooth devices.
// That title is set via the manifest and also from BluetoothDeviceUpdater.
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
index 08ae760684b..d6bdb77d04b 100644
--- a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
+++ b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
@@ -17,8 +17,6 @@
package com.android.settings.development.featureflags;
import android.content.Context;
-import android.provider.Settings;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import androidx.preference.SwitchPreference;
@@ -51,13 +49,5 @@ public class FeatureFlagPreference extends SwitchPreference {
} else {
FeatureFlagUtils.setEnabled(getContext(), mKey, isChecked);
}
-
- // A temporary logic for settings_hide_second_layer_page_navigate_up_button_in_two_pane
- // Remove it before Android T release.
- if (TextUtils.equals(mKey,
- FeatureFlagUtils.SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE)) {
- Settings.Global.putString(getContext().getContentResolver(),
- mKey, String.valueOf(isChecked));
- }
}
}
diff --git a/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java b/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
index b85db40d954..d43a488ad01 100644
--- a/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/RestrictAppPreferenceController.java
@@ -31,6 +31,7 @@ import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipUtils;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.utils.StringUtil;
import java.util.List;
@@ -75,9 +76,8 @@ public class RestrictAppPreferenceController extends BasePreferenceController {
final int num = mAppInfos.size();
// Fragment change RestrictedAppsList after onPause(), UI needs to be updated in onResume()
preference.setVisible(num > 0);
- preference.setSummary(
- mContext.getResources().getQuantityString(R.plurals.restricted_app_summary, num,
- num));
+ preference.setSummary(StringUtil.getIcuPluralsString(mContext, num,
+ R.string.restricted_app_summary));
}
@Override
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java
index a156395f44a..1c4c67c8781 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java
@@ -27,6 +27,7 @@ import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.utils.StringUtil;
/** Preference controller to control the battery manager */
public class BatteryManagerPreferenceController extends BasePreferenceController {
@@ -66,8 +67,8 @@ public class BatteryManagerPreferenceController extends BasePreferenceController
@VisibleForTesting
void updateSummary(Preference preference, int num) {
if (num > 0) {
- preference.setSummary(mContext.getResources().getQuantityString(
- R.plurals.battery_manager_app_restricted, num, num));
+ preference.setSummary(StringUtil.getIcuPluralsString(mContext, num,
+ R.string.battery_manager_app_restricted));
} else {
preference.setSummary(
mPowerUsageFeatureProvider.isAdaptiveChargingSupported()
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
index 5fd3905649f..3989e892818 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -42,6 +42,7 @@ import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
+import com.android.settingslib.utils.StringUtil;
import java.util.List;
@@ -107,8 +108,8 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
restrictedAppList.get(0).packageName);
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
- .setTitle(context.getResources().getQuantityString(
- R.plurals.battery_tip_restrict_app_dialog_title, num, num))
+ .setTitle(StringUtil.getIcuPluralsString(context, num,
+ R.string.battery_tip_restrict_app_dialog_title))
.setPositiveButton(R.string.battery_tip_restrict_app_dialog_ok, this)
.setNegativeButton(android.R.string.cancel, null);
if (num == 1) {
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
index c5f1171a8c7..6fb1c9a891f 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
@@ -28,9 +28,12 @@ import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.utils.StringUtil;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Tip to suggest user to restrict some bad apps
@@ -62,12 +65,15 @@ public class RestrictAppTip extends BatteryTip {
final int num = mRestrictAppList.size();
final CharSequence appLabel = num > 0 ? Utils.getApplicationLabel(context,
mRestrictAppList.get(0).packageName) : "";
- final Resources resources = context.getResources();
+ Map arguments = new HashMap<>();
+ arguments.put("count", num);
+ arguments.put("label", appLabel);
return mState == StateType.HANDLED
- ? resources.getQuantityString(R.plurals.battery_tip_restrict_handled_title, num,
- appLabel, num)
- : resources.getQuantityString(R.plurals.battery_tip_restrict_title, num, num);
+ ? StringUtil.getIcuPluralsString(context, arguments,
+ R.string.battery_tip_restrict_handled_title)
+ : StringUtil.getIcuPluralsString(context, arguments,
+ R.string.battery_tip_restrict_title);
}
@Override
@@ -76,9 +82,12 @@ public class RestrictAppTip extends BatteryTip {
final CharSequence appLabel = num > 0 ? Utils.getApplicationLabel(context,
mRestrictAppList.get(0).packageName) : "";
final int resId = mState == StateType.HANDLED
- ? R.plurals.battery_tip_restrict_handled_summary
- : R.plurals.battery_tip_restrict_summary;
- return context.getResources().getQuantityString(resId, num, appLabel, num);
+ ? R.string.battery_tip_restrict_handled_summary
+ : R.string.battery_tip_restrict_summary;
+ Map arguments = new HashMap<>();
+ arguments.put("count", num);
+ arguments.put("label", appLabel);
+ return StringUtil.getIcuPluralsString(context, arguments, resId);
}
@Override
diff --git a/src/com/android/settings/location/TopLevelLocationPreferenceController.java b/src/com/android/settings/location/TopLevelLocationPreferenceController.java
index 91100b7628d..464344e9ee4 100644
--- a/src/com/android/settings/location/TopLevelLocationPreferenceController.java
+++ b/src/com/android/settings/location/TopLevelLocationPreferenceController.java
@@ -21,6 +21,7 @@ import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.utils.StringUtil;
import java.util.Arrays;
import java.util.List;
@@ -63,8 +64,8 @@ public class TopLevelLocationPreferenceController extends BasePreferenceControll
@VisibleForTesting
void setLocationAppCount(int numApps) {
- sSummary = mContext.getResources().getQuantityString(
- R.plurals.location_settings_summary_location_on, numApps, numApps);
+ sSummary = StringUtil.getIcuPluralsString(mContext, numApps,
+ R.string.location_settings_summary_location_on);
refreshSummary(mPreference);
}
diff --git a/src/com/android/settings/network/tether/TetherSettings.java b/src/com/android/settings/network/tether/TetherSettings.java
index a674722e837..118d407941d 100644
--- a/src/com/android/settings/network/tether/TetherSettings.java
+++ b/src/com/android/settings/network/tether/TetherSettings.java
@@ -77,7 +77,7 @@ import java.util.concurrent.atomic.AtomicReference;
*/
@SearchIndexable
public class TetherSettings extends RestrictedSettingsFragment
- implements DataSaverBackend.Listener {
+ implements DataSaverBackend.Listener, TetheringManager.TetheringEventCallback {
@VisibleForTesting
static final String KEY_TETHER_PREFS_SCREEN = "tether_prefs_screen";
@@ -111,7 +111,6 @@ public class TetherSettings extends RestrictedSettingsFragment
private OnStartTetheringCallback mStartTetheringCallback;
private ConnectivityManager mCm;
private EthernetManager mEm;
- private TetheringEventCallback mTetheringEventCallback;
private EthernetListener mEthernetListener;
private final HashSet mAvailableInterfaces = new HashSet<>();
@@ -133,6 +132,7 @@ public class TetherSettings extends RestrictedSettingsFragment
Context mContext;
@VisibleForTesting
TetheringManager mTm;
+ private TetheringHelper mTetheringHelper;
@Override
public int getMetricsCategory() {
@@ -148,6 +148,8 @@ public class TetherSettings extends RestrictedSettingsFragment
super.onAttach(context);
mWifiTetherPreferenceController =
new WifiTetherPreferenceController(context, getSettingsLifecycle());
+ mTetheringHelper = TetheringHelper.getInstance(context, this /* TetheringEventCallback */,
+ getSettingsLifecycle());
}
@Override
@@ -186,7 +188,7 @@ public class TetherSettings extends RestrictedSettingsFragment
mDataSaverBackend.addListener(this);
mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- mTm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE);
+ mTm = mTetheringHelper.getTetheringManager();
// Some devices do not have available EthernetManager. In that case getSystemService will
// return null.
mEm = mContext.getSystemService(EthernetManager.class);
@@ -364,15 +366,14 @@ public class TetherSettings extends RestrictedSettingsFragment
mStartTetheringCallback = new OnStartTetheringCallback(this);
- mTetheringEventCallback = new TetheringEventCallback(this);
- mTm.registerTetheringEventCallback(r -> mHandler.post(r), mTetheringEventCallback);
mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
registerReceiver();
mEthernetListener = new EthernetListener(this);
if (mEm != null) {
- mEm.addInterfaceStateListener(r -> mHandler.post(r), mEthernetListener);
+ mEm.addInterfaceStateListener(mContext.getApplicationContext().getMainExecutor(),
+ mEthernetListener);
}
updateUsbState();
@@ -387,13 +388,11 @@ public class TetherSettings extends RestrictedSettingsFragment
return;
}
getActivity().unregisterReceiver(mTetherChangeReceiver);
- mTm.unregisterTetheringEventCallback(mTetheringEventCallback);
if (mEm != null) {
mEm.removeInterfaceStateListener(mEthernetListener);
}
mTetherChangeReceiver = null;
mStartTetheringCallback = null;
- mTetheringEventCallback = null;
}
@VisibleForTesting
@@ -688,25 +687,8 @@ public class TetherSettings extends RestrictedSettingsFragment
}
}
- private static final class TetheringEventCallback implements
- TetheringManager.TetheringEventCallback {
- final WeakReference mTetherSettings;
-
- TetheringEventCallback(TetherSettings settings) {
- mTetherSettings = new WeakReference<>(settings);
- }
-
- @Override
- public void onTetheredInterfacesChanged(List interfaces) {
- final TetherSettings tetherSettings = mTetherSettings.get();
- if (tetherSettings == null) {
- return;
- }
- tetherSettings.onTetheredInterfacesChanged(interfaces);
- }
- }
-
- void onTetheredInterfacesChanged(List interfaces) {
+ @Override
+ public void onTetheredInterfacesChanged(List interfaces) {
Log.d(TAG, "onTetheredInterfacesChanged() interfaces : " + interfaces.toString());
final String[] tethered = interfaces.toArray(new String[interfaces.size()]);
updateUsbState(tethered);
diff --git a/src/com/android/settings/network/tether/TetheringHelper.java b/src/com/android/settings/network/tether/TetheringHelper.java
new file mode 100644
index 00000000000..da559047f5d
--- /dev/null
+++ b/src/com/android/settings/network/tether/TetheringHelper.java
@@ -0,0 +1,157 @@
+/*
+ * 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.tether;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.net.TetheringManager;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Helper class for TetheringManager.
+ */
+public class TetheringHelper implements DefaultLifecycleObserver {
+ private static final String TAG = "TetheringHelper";
+
+ private static final Object sInstanceLock = new Object();
+ @TestApi
+ @GuardedBy("sInstanceLock")
+ private static Map sTestInstances;
+
+ protected static Context sAppContext;
+ protected static TetheringManager sTetheringManager;
+ protected static TetheringEventCallback sTetheringEventCallback = new TetheringEventCallback();
+ protected static ArrayList sEventCallbacks =
+ new ArrayList<>();
+
+ protected TetheringManager.TetheringEventCallback mCallback;
+
+ /**
+ * Static method to create a singleton class for TetheringHelper.
+ *
+ * @param context The Context this is associated with.
+ * @return an instance of {@link TetheringHelper} object.
+ */
+ @NonNull
+ public static TetheringHelper getInstance(@NonNull Context context,
+ @NonNull TetheringManager.TetheringEventCallback callback,
+ @NonNull Lifecycle lifecycle) {
+ synchronized (sInstanceLock) {
+ if (sTestInstances != null && sTestInstances.containsKey(context)) {
+ TetheringHelper testInstance = sTestInstances.get(context);
+ Log.w(TAG, "The context owner use a test instance:" + testInstance);
+ return testInstance;
+ }
+
+ if (sAppContext == null) {
+ sAppContext = context.getApplicationContext();
+ sTetheringManager = sAppContext.getSystemService(TetheringManager.class);
+ }
+ TetheringHelper helper = new TetheringHelper();
+ helper.mCallback = callback;
+ lifecycle.addObserver(helper);
+ return helper;
+ }
+ }
+
+ /**
+ * A convenience method to set pre-prepared instance or mock class for testing.
+ *
+ * @param context The Context this is associated with.
+ * @param instance of {@link TetheringHelper} object.
+ * @hide
+ */
+ @TestApi
+ @VisibleForTesting
+ public static void setTestInstance(@NonNull Context context, TetheringHelper instance) {
+ synchronized (sInstanceLock) {
+ if (sTestInstances == null) sTestInstances = new ConcurrentHashMap<>();
+ Log.w(TAG, "Set a test instance by context:" + context);
+ sTestInstances.put(context, instance);
+ }
+ }
+
+ /**
+ * The constructor can only be accessed from static method inside the class itself, this is
+ * to avoid creating a class by adding a private constructor.
+ */
+ private TetheringHelper() {
+ // Do nothing.
+ }
+
+ /**
+ * Returns the TetheringManager If the system service is successfully obtained.
+ */
+ public TetheringManager getTetheringManager() {
+ return sTetheringManager;
+ }
+
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner) {
+ if (sEventCallbacks.contains(mCallback)) {
+ Log.w(TAG, "The callback already contains, don't register callback:" + mCallback);
+ return;
+ }
+ sEventCallbacks.add(mCallback);
+ if (sEventCallbacks.size() == 1) {
+ sTetheringManager.registerTetheringEventCallback(sAppContext.getMainExecutor(),
+ sTetheringEventCallback);
+ }
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner) {
+ if (!sEventCallbacks.remove(mCallback)) {
+ Log.w(TAG, "The callback does not contain, don't unregister callback:" + mCallback);
+ return;
+ }
+ if (sEventCallbacks.size() == 0) {
+ sTetheringManager.unregisterTetheringEventCallback(sTetheringEventCallback);
+ }
+ }
+
+ @Override
+ public void onDestroy(@NonNull LifecycleOwner owner) {
+ mCallback = null;
+ }
+
+ protected static final class TetheringEventCallback implements
+ TetheringManager.TetheringEventCallback {
+ @Override
+ public void onTetheredInterfacesChanged(List interfaces) {
+ for (TetheringManager.TetheringEventCallback callback : sEventCallbacks) {
+ if (callback == null) continue;
+ callback.onTetheredInterfacesChanged(interfaces);
+ }
+ }
+ }
+}
+
+
diff --git a/src/com/android/settings/notification/ConversationListSummaryPreferenceController.java b/src/com/android/settings/notification/ConversationListSummaryPreferenceController.java
index 5d7ea205d0e..176d7489ffd 100644
--- a/src/com/android/settings/notification/ConversationListSummaryPreferenceController.java
+++ b/src/com/android/settings/notification/ConversationListSummaryPreferenceController.java
@@ -20,6 +20,7 @@ import android.content.Context;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.utils.StringUtil;
public class ConversationListSummaryPreferenceController extends BasePreferenceController {
@@ -41,9 +42,8 @@ public class ConversationListSummaryPreferenceController extends BasePreferenceC
if (count == 0) {
return mContext.getText(R.string.priority_conversation_count_zero);
}
- return mContext.getResources().getQuantityString(
- R.plurals.priority_conversation_count,
- count, count);
+ return StringUtil.getIcuPluralsString(mContext, count,
+ R.string.priority_conversation_count);
}
void setBackend(NotificationBackend backend) {
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index aa7d547a92c..c63a294ba74 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -509,11 +509,11 @@ public class NotificationBackend {
context, System.currentTimeMillis() - state.lastSent, true);
} else {
if (state.avgSentDaily > 0) {
- return context.getResources().getQuantityString(R.plurals.notifications_sent_daily,
- state.avgSentDaily, state.avgSentDaily);
+ return StringUtil.getIcuPluralsString(context, state.avgSentDaily,
+ R.string.notifications_sent_daily);
}
- return context.getResources().getQuantityString(R.plurals.notifications_sent_weekly,
- state.avgSentWeekly, state.avgSentWeekly);
+ return StringUtil.getIcuPluralsString(context, state.avgSentWeekly,
+ R.string.notifications_sent_weekly);
}
}
diff --git a/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java b/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java
index 77a692f1488..6a1d4cb1247 100644
--- a/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java
+++ b/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java
@@ -23,6 +23,7 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.utils.StringUtil;
public class DeletedChannelsPreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin {
@@ -59,8 +60,8 @@ public class DeletedChannelsPreferenceController extends NotificationPreferenceC
public void updateState(Preference preference) {
if (mAppRow != null) {
int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
- preference.setTitle(mContext.getResources().getQuantityString(
- R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
+ preference.setTitle(StringUtil.getIcuPluralsString(mContext, deletedChannelCount,
+ R.string.deleted_channels));
}
preference.setSelectable(false);
}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
index 1f83cc72896..021ba6fead8 100644
--- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java
+++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
@@ -60,6 +60,7 @@ import com.android.internal.widget.NotificationExpandButton;
import com.android.settings.R;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.MainSwitchBar;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
@@ -203,16 +204,15 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity {
icon.setImageDrawable(nhp.icon);
TextView count = viewForPackage.findViewById(R.id.count);
- count.setText(getResources().getQuantityString(R.plurals.notification_history_count,
- nhp.notifications.size(), nhp.notifications.size()));
+ count.setText(StringUtil.getIcuPluralsString(this, nhp.notifications.size(),
+ R.string.notification_history_count));
final NotificationHistoryRecyclerView rv =
viewForPackage.findViewById(R.id.notification_list);
rv.setAdapter(new NotificationHistoryAdapter(mNm, rv,
newCount -> {
- count.setText(getResources().getQuantityString(
- R.plurals.notification_history_count,
- newCount, newCount));
+ count.setText(StringUtil.getIcuPluralsString(this, newCount,
+ R.string.notification_history_count));
if (newCount == 0) {
viewForPackage.setVisibility(View.GONE);
}
diff --git a/src/com/android/settings/print/PrintSettingPreferenceController.java b/src/com/android/settings/print/PrintSettingPreferenceController.java
index d4ae3116a0c..2a75e486447 100644
--- a/src/com/android/settings/print/PrintSettingPreferenceController.java
+++ b/src/com/android/settings/print/PrintSettingPreferenceController.java
@@ -34,6 +34,7 @@ import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.utils.StringUtil;
import java.util.List;
@@ -110,17 +111,16 @@ public class PrintSettingPreferenceController extends BasePreferenceController i
}
if (numActivePrintJobs > 0) {
- return mContext.getResources().getQuantityString(
- R.plurals.print_jobs_summary, numActivePrintJobs, numActivePrintJobs);
+ return StringUtil.getIcuPluralsString(mContext, numActivePrintJobs,
+ R.string.print_jobs_summary);
} else {
final List services =
mPrintManager.getPrintServices(PrintManager.ENABLED_SERVICES);
if (services == null || services.isEmpty()) {
return mContext.getText(R.string.off);
} else {
- final int count = services.size();
- return mContext.getResources().getQuantityString(
- R.plurals.print_settings_summary, count, count);
+ return StringUtil.getIcuPluralsString(mContext, services.size(),
+ R.string.print_settings_summary);
}
}
}
diff --git a/src/com/android/settings/regionalpreferences/CalendarTypeController.java b/src/com/android/settings/regionalpreferences/CalendarTypeController.java
new file mode 100644
index 00000000000..ea5919198df
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/CalendarTypeController.java
@@ -0,0 +1,66 @@
+/*
+ * 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.regionalpreferences;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.Locale;
+
+/**
+ * A controller for the entry of Calendar types' page
+ */
+public class CalendarTypeController extends BasePreferenceController {
+ public CalendarTypeController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
+ * Setting should be shown or disabled in Settings. Further, it can be used to produce
+ * appropriate error / warning Slice in the case of unavailability.
+ *
+ * The status is used for the convenience methods: {@link #isAvailable()}, {@link
+ * #isSupported()}
+ *
+ * The inherited class doesn't need to check work profile if android:forWork="true" is set in
+ * preference xml.
+ */
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ String record = Settings.System.getString(
+ mContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
+ String result = "";
+ if (record != null) {
+ result = LocalePreferences.getCalendarType(Locale.forLanguageTag(record), false);
+ }
+
+ if (result.isEmpty()) {
+ result = LocalePreferences.getCalendarType(false);
+ }
+ return result.isEmpty()
+ ? mContext.getString(R.string.default_string_of_regional_preference) : result;
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/FirstDayOfWeekController.java b/src/com/android/settings/regionalpreferences/FirstDayOfWeekController.java
new file mode 100644
index 00000000000..3fb8a1ec3d9
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/FirstDayOfWeekController.java
@@ -0,0 +1,64 @@
+/*
+ * 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.regionalpreferences;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.Locale;
+
+/** A controller for the entry of First Day of Week's page */
+public class FirstDayOfWeekController extends BasePreferenceController {
+ public FirstDayOfWeekController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
+ * Setting should be shown or disabled in Settings. Further, it can be used to produce
+ * appropriate error / warning Slice in the case of unavailability.
+ *
+ * The status is used for the convenience methods: {@link #isAvailable()}, {@link
+ * #isSupported()}
+ *
+ * The inherited class doesn't need to check work profile if android:forWork="true" is set in
+ * preference xml.
+ */
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ String record = Settings.System.getString(
+ mContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
+ String result = "";
+ if (record != null) {
+ result = LocalePreferences.getFirstDayOfWeek(Locale.forLanguageTag(record), false);
+ }
+
+ if (result.isEmpty()) {
+ result = LocalePreferences.getFirstDayOfWeek(false);
+ }
+ return result.isEmpty()
+ ? mContext.getString(R.string.default_string_of_regional_preference) : result;
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/LocalePreferences.java b/src/com/android/settings/regionalpreferences/LocalePreferences.java
new file mode 100644
index 00000000000..d029ac2967e
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/LocalePreferences.java
@@ -0,0 +1,604 @@
+/*
+ * 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.regionalpreferences;
+import android.icu.number.LocalizedNumberFormatter;
+import android.icu.number.NumberFormatter;
+import android.icu.text.DateFormat;
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.util.MeasureUnit;
+import android.os.Build.VERSION;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.StringDef;
+import androidx.core.os.BuildCompat;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+import java.util.Locale.Category;
+
+/**
+ * TODO(b/263861083) This is a temp file and will replace it to Androidx version.
+ * Provides friendly APIs to get the user's locale preferences. The data can refer to
+ * external/cldr/common/main/en.xml.
+ */
+public final class LocalePreferences {
+ private static final String TAG = LocalePreferences.class.getSimpleName();
+
+ /** APIs to get the user's preference of the hour cycle. */
+ public static class HourCycle {
+ private static final String U_EXTENSION_OF_HOUR_CYCLE = "hc";
+
+ /** 12 Hour System (0-11) */
+ public static final String H11 = "h11";
+ /** 12 Hour System (1-12) */
+ public static final String H12 = "h12";
+ /** 24 Hour System (0-23) */
+ public static final String H23 = "h23";
+ /** 24 Hour System (1-24) */
+ public static final String H24 = "h24";
+ /** Default hour cycle for the locale */
+ public static final String DEFAULT = "";
+
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ H11,
+ H12,
+ H23,
+ H24,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HourCycleTypes {
+ }
+
+ private HourCycle() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the hour cycle which is from
+ * {@link Locale#getDefault(Locale.Category)}. The returned result is resolved and
+ * bases on the {@code Locale#getDefault(Locale.Category)}. E.g. "h23"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle() {
+ return getHourCycle(true);
+ }
+
+ /**
+ * Return the hour cycle setting of the inputted {@link Locale}. The returned result is resolved
+ * and bases on the inputted {@code Locale}.
+ * E.g. "h23"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle(@NonNull Locale locale) {
+ return getHourCycle(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the hour cycle which is from
+ * {@link Locale#getDefault(Locale.Category)}. E.g. "h23"
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains hour cycle subtag,
+ * this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain hour cycle subtag
+ * and the resolved argument is true, this function tries to find the default
+ * hour cycle for the {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain hour cycle subtag
+ * and the resolved argument is false, this function returns empty string
+ * i.e. HourCycle.Default.
+ * @return {@link HourCycle.HourCycleTypes} If the malformed hour cycle format was specified
+ * in the hour cycle subtag, e.g. en-US-u-hc-h32, this function returns empty string
+ * i.e. HourCycle.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle(
+ boolean resolved) {
+ return getHourCycle(Api33Impl.getDefaultLocale(), resolved);
+ }
+
+ /**
+ * Return the hour cycle setting of the inputted {@link Locale}. E.g. "en-US-u-hc-h23".
+ *
+ * @param locale The {@code Locale} to get the hour cycle.
+ * @param resolved If the given {@code Locale} contains hour cycle subtag, this argument is
+ * ignored. If the given {@code Locale} doesn't contain hour cycle subtag and
+ * the resolved argument is true, this function tries to find the default
+ * hour cycle for the given {@code Locale}. If the given {@code Locale} doesn't
+ * contain hour cycle subtag and the resolved argument is false, this function
+ * return empty string i.e. HourCycle.Default.
+ * @return {@link HourCycle.HourCycleTypes} If the malformed hour cycle format was specified
+ * in the hour cycle subtag, e.g. en-US-u-hc-h32, this function returns empty string
+ * i.e. HourCycle.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle(@NonNull Locale locale, boolean resolved) {
+ if (!BuildCompat.isAtLeastT()) {
+ throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT);
+ }
+ return Api33Impl.getHourCycle(locale, resolved);
+ }
+
+ /** APIs to get the user's preference of Calendar. */
+ public static class CalendarType {
+ private static final String U_EXTENSION_OF_CALENDAR = "ca";
+ /** Chinese Calendar */
+ public static final String CHINESE = "chinese";
+ /** Dangi Calendar (Korea Calendar) */
+ public static final String DANGI = "dangi";
+ /** Gregorian Calendar */
+ public static final String GREGORIAN = "gregorian";
+ /** Hebrew Calendar */
+ public static final String HEBREW = "hebrew";
+ /** Indian National Calendar */
+ public static final String INDIAN = "indian";
+ /** Islamic Calendar */
+ public static final String ISLAMIC = "islamic";
+ /** Islamic Calendar (tabular, civil epoch) */
+ public static final String ISLAMIC_CIVIL = "islamic-civil";
+ /** Islamic Calendar (Saudi Arabia, sighting) */
+ public static final String ISLAMIC_RGSA = "islamic-rgsa";
+ /** Islamic Calendar (tabular, astronomical epoch) */
+ public static final String ISLAMIC_TBLA = "islamic-tbla";
+ /** Islamic Calendar (Umm al-Qura) */
+ public static final String ISLAMIC_UMALQURA = "islamic-umalqura";
+ /** Persian Calendar */
+ public static final String PERSIAN = "persian";
+ /** Default calendar for the locale */
+ public static final String DEFAULT = "";
+
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ CHINESE,
+ DANGI,
+ GREGORIAN,
+ HEBREW,
+ INDIAN,
+ ISLAMIC,
+ ISLAMIC_CIVIL,
+ ISLAMIC_RGSA,
+ ISLAMIC_TBLA,
+ ISLAMIC_UMALQURA,
+ PERSIAN,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CalendarTypes {
+ }
+
+ private CalendarType() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the calendar type which is from {@link
+ * Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on
+ * the {@code Locale#getDefault(Locale.Category)} settings. E.g. "chinese"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType() {
+ return getCalendarType(true);
+ }
+
+ /**
+ * Return the calendar type of the inputted {@link Locale}. The returned result is resolved and
+ * bases on the inputted {@link Locale} settings.
+ * E.g. "chinese"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType(@NonNull Locale locale) {
+ return getCalendarType(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the calendar type which is from {@link
+ * Locale#getDefault(Locale.Category)}. E.g. "chinese"
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains calendar type
+ * subtag, this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain calendar type
+ * subtag and the resolved argument is true, this function tries to find
+ * the default calendar type for the
+ * {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain calendar type
+ * subtag and the resolved argument is false, this function returns empty string
+ * i.e. CalendarTypes.Default.
+ * @return {@link CalendarType.CalendarTypes} If the malformed calendar type format was
+ * specified in the calendar type subtag, e.g. en-US-u-ca-calendar, this function returns
+ * empty string i.e. CalendarTypes.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType(boolean resolved) {
+ return getCalendarType(Api33Impl.getDefaultLocale(), resolved);
+ }
+
+ /**
+ * Return the calendar type of the inputted {@link Locale}. E.g. "chinese"
+ *
+ * @param locale The {@link Locale} to get the calendar type.
+ * @param resolved If the given {@code Locale} contains calendar type subtag, this argument is
+ * ignored. If the given {@code Locale} doesn't contain calendar type subtag and
+ * the resolved argument is true, this function tries to find the default
+ * calendar type for the given {@code Locale}. If the given {@code Locale}
+ * doesn't contain calendar type subtag and the resolved argument is false, this
+ * function return empty string i.e. CalendarTypes.Default.
+ * @return {@link CalendarType.CalendarTypes} If the malformed calendar type format was
+ * specified in the calendar type subtag, e.g. en-US-u-ca-calendar, this function returns
+ * empty string i.e. CalendarTypes.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType(@NonNull Locale locale, boolean resolved) {
+ if (!BuildCompat.isAtLeastT()) {
+ throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT);
+ }
+ return Api33Impl.getCalendarType(locale, resolved);
+ }
+
+ /** APIs to get the user's preference of temperature unit. */
+ public static class TemperatureUnit {
+ private static final String U_EXTENSION_OF_TEMPERATURE_UNIT = "mu";
+ /** Celsius */
+ public static final String CELSIUS = "celsius";
+ /** Fahrenheit */
+ public static final String FAHRENHEIT = "fahrenheit";
+ /** Kelvin */
+ public static final String KELVIN = "kelvin";
+ /** Default Temperature for the locale */
+ public static final String DEFAULT = "";
+
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ CELSIUS,
+ FAHRENHEIT,
+ KELVIN,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TemperatureUnits {
+ }
+
+ private TemperatureUnit() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the temperature unit which is from {@link
+ * Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on the
+ * {@code Locale#getDefault(Locale.Category)} settings. E.g. "fahrenheit"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit() {
+ return getTemperatureUnit(true);
+ }
+
+ /**
+ * Return the temperature unit of the inputted {@link Locale}. The returned result is resolved
+ * and bases on the inputted {@code Locale} settings. E.g. "fahrenheit"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit(
+ @NonNull Locale locale) {
+ return getTemperatureUnit(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the temperature unit which is from {@link
+ * Locale#getDefault(Locale.Category)}. E.g. "fahrenheit"
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains temperature unit
+ * subtag, this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain temperature unit
+ * subtag and the resolved argument is true, this function tries to find
+ * the default temperature unit for the
+ * {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain temperature unit
+ * subtag and the resolved argument is false, this function returns empty string
+ * i.e. TemperatureUnits.Default.
+ * @return {@link TemperatureUnit.TemperatureUnits} If the malformed temperature unit format was
+ * specified in the temperature unit subtag, e.g. en-US-u-mu-temperature, this function returns
+ * empty string i.e. TemperatureUnits.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit(boolean resolved) {
+ return getTemperatureUnit(Api33Impl.getDefaultLocale(), resolved);
+ }
+
+ /**
+ * Return the temperature unit of the inputted {@link Locale}. E.g. "fahrenheit"
+ *
+ * @param locale The {@link Locale} to get the temperature unit.
+ * @param resolved If the given {@code Locale} contains temperature unit subtag, this argument
+ * is ignored. If the given {@code Locale} doesn't contain temperature unit
+ * subtag and the resolved argument is true, this function tries to find
+ * the default temperature unit for the given {@code Locale}. If the given
+ * {@code Locale} doesn't contain temperature unit subtag and the resolved
+ * argument is false, this function return empty string
+ * i.e. TemperatureUnits.Default.
+ * @return {@link TemperatureUnit.TemperatureUnits} If the malformed temperature unit format was
+ * specified in the temperature unit subtag, e.g. en-US-u-mu-temperature, this function returns
+ * empty string i.e. TemperatureUnits.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit(@NonNull Locale locale, boolean resolved) {
+ if (!BuildCompat.isAtLeastT()) {
+ throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT);
+ }
+ return Api33Impl.getTemperatureUnit(locale, resolved);
+ }
+
+ /** APIs to get the user's preference of the first day of week. */
+ public static class FirstDayOfWeek {
+ private static final String U_EXTENSION_OF_FIRST_DAY_OF_WEEK = "fw";
+ /** Sunday */
+ public static final String SUNDAY = "sun";
+ /** Monday */
+ public static final String MONDAY = "mon";
+ /** Tuesday */
+ public static final String TUESDAY = "tue";
+ /** Wednesday */
+ public static final String WEDNESDAY = "wed";
+ /** Thursday */
+ public static final String THURSDAY = "thu";
+ /** Friday */
+ public static final String FRIDAY = "fri";
+ /** Saturday */
+ public static final String SATURDAY = "sat";
+ /** Default first day of week for the locale */
+ public static final String DEFAULT = "";
+
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ SUNDAY,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY,
+ SATURDAY,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Days {
+ }
+
+ private FirstDayOfWeek() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the first day of week which is from
+ * {@link Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on the
+ * {@code Locale#getDefault(Locale.Category)} settings. E.g. "sun"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @FirstDayOfWeek.Days
+ public static String getFirstDayOfWeek() {
+ return getFirstDayOfWeek(true);
+ }
+
+ /**
+ * Return the first day of week of the inputted {@link Locale}. The returned result is resolved
+ * and bases on the inputted {@code Locale} settings.
+ * E.g. "sun"
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ public static @FirstDayOfWeek.Days String getFirstDayOfWeek(@NonNull Locale locale) {
+ return getFirstDayOfWeek(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the first day of week which is from {@link
+ * Locale#getDefault(Locale.Category)}. E.g. "sun"
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains first day of week
+ * subtag, this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain first day of week
+ * subtag and the resolved argument is true, this function tries to find
+ * the default first day of week for the
+ * {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain first day of week
+ * subtag and the resolved argument is false, this function returns empty string
+ * i.e. Days.Default.
+ * @return {@link FirstDayOfWeek.Days} If the malformed first day of week format was specified
+ * in the first day of week subtag, e.g. en-US-u-fw-days, this function returns empty string
+ * i.e. Days.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @FirstDayOfWeek.Days
+ public static String getFirstDayOfWeek(boolean resolved) {
+ return getFirstDayOfWeek(Api33Impl.getDefaultLocale(), resolved);
+ }
+
+ /**
+ * Return the first day of week of the inputted {@link Locale}. E.g. "sun"
+ *
+ * @param locale The {@link Locale} to get the first day of week.
+ * @param resolved If the given {@code Locale} contains first day of week subtag, this argument
+ * is ignored. If the given {@code Locale} doesn't contain first day of week
+ * subtag and the resolved argument is true, this function tries to find
+ * the default first day of week for the given {@code Locale}. If the given
+ * {@code Locale} doesn't contain first day of week subtag and the resolved
+ * argument is false, this function return empty string i.e. Days.Default.
+ * @return {@link FirstDayOfWeek.Days} If the malformed first day of week format was
+ * specified in the first day of week subtag, e.g. en-US-u-fw-days, this function returns
+ * empty string i.e. Days.Default.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @FirstDayOfWeek.Days
+ public static String getFirstDayOfWeek(
+ @NonNull Locale locale, boolean resolved) {
+ if (!BuildCompat.isAtLeastT()) {
+ throw new IllegalArgumentException("not a valid extension: " + VERSION.SDK_INT);
+ }
+
+ return Api33Impl.getFirstDayOfWeek(locale, resolved);
+ }
+
+ private static class Api33Impl {
+ @DoNotInline
+ @HourCycle.HourCycleTypes
+ static String getHourCycle(@NonNull Locale locale,
+ boolean resolved) {
+ String hc = locale.getUnicodeLocaleType(HourCycle.U_EXTENSION_OF_HOUR_CYCLE);
+ if (hc != null) {
+ return hc;
+ }
+ if (!resolved) {
+ return HourCycle.DEFAULT;
+ }
+
+ return getHourCycleType(
+ DateTimePatternGenerator.getInstance(locale).getDefaultHourCycle());
+
+ }
+
+ @DoNotInline
+ @CalendarType.CalendarTypes
+ static String getCalendarType(@NonNull Locale locale, boolean resolved) {
+ String ca = locale.getUnicodeLocaleType(CalendarType.U_EXTENSION_OF_CALENDAR);
+ if (ca != null) {
+ return ca;
+ }
+ if (!resolved) {
+ return CalendarType.DEFAULT;
+ }
+
+ return android.icu.util.Calendar.getInstance(locale).getType();
+ }
+
+ @DoNotInline
+ @TemperatureUnit.TemperatureUnits
+ static String getTemperatureUnit(@NonNull Locale locale, boolean resolved) {
+ String mu =
+ locale.getUnicodeLocaleType(TemperatureUnit.U_EXTENSION_OF_TEMPERATURE_UNIT);
+ if (mu != null) {
+ if (mu.contains("fahrenhe")) {
+ mu = TemperatureUnit.FAHRENHEIT;
+ }
+ return mu;
+ }
+ if (!resolved) {
+ return TemperatureUnit.DEFAULT;
+ }
+
+ return getResolvedTemperatureUnit(locale);
+ }
+
+ @DoNotInline
+ @FirstDayOfWeek.Days
+ static String getFirstDayOfWeek(@NonNull Locale locale, boolean resolved) {
+ String mu =
+ locale.getUnicodeLocaleType(FirstDayOfWeek.U_EXTENSION_OF_FIRST_DAY_OF_WEEK);
+ if (mu != null) {
+ return mu;
+ }
+ if (!resolved) {
+ return FirstDayOfWeek.DEFAULT;
+ }
+ // TODO(b/262294472) Use {@code android.icu.util.Calendar} instead of
+ // {@code java.util.Calendar}.
+ return getStringOfFirstDayOfWeek(
+ java.util.Calendar.getInstance(locale).getFirstDayOfWeek());
+ }
+
+ @DoNotInline
+ static Locale getDefaultLocale() {
+ return Locale.getDefault(Category.FORMAT);
+ }
+
+ private static String getStringOfFirstDayOfWeek(int fw) {
+ String[] arrDays = {
+ FirstDayOfWeek.SUNDAY,
+ FirstDayOfWeek.MONDAY,
+ FirstDayOfWeek.TUESDAY,
+ FirstDayOfWeek.WEDNESDAY,
+ FirstDayOfWeek.THURSDAY,
+ FirstDayOfWeek.FRIDAY,
+ FirstDayOfWeek.SATURDAY};
+
+ return fw >= 1 && fw <= 7 ? arrDays[fw - 1] : FirstDayOfWeek.DEFAULT;
+ }
+
+ @HourCycle.HourCycleTypes
+ private static String getHourCycleType(
+ DateFormat.HourCycle hourCycle) {
+ switch (hourCycle) {
+ case HOUR_CYCLE_11:
+ return HourCycle.H11;
+ case HOUR_CYCLE_12:
+ return HourCycle.H12;
+ case HOUR_CYCLE_23:
+ return HourCycle.H23;
+ case HOUR_CYCLE_24:
+ return HourCycle.H24;
+ default:
+ return HourCycle.DEFAULT;
+ }
+ }
+
+ @TemperatureUnit.TemperatureUnits
+ private static String getResolvedTemperatureUnit(@NonNull Locale locale) {
+ LocalizedNumberFormatter nf = NumberFormatter.with()
+ .usage("temperature")
+ .unit(MeasureUnit.CELSIUS)
+ .locale(locale);
+ return nf.format(1).getOutputUnit().getIdentifier();
+ }
+
+ private Api33Impl() {
+ }
+ }
+
+ private LocalePreferences() {
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/NumberingSystemController.java b/src/com/android/settings/regionalpreferences/NumberingSystemController.java
new file mode 100644
index 00000000000..e951fc24751
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/NumberingSystemController.java
@@ -0,0 +1,76 @@
+/*
+ * 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.regionalpreferences;
+
+import android.content.Context;
+import android.icu.text.NumberingSystem;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.Locale;
+
+/** A controller for the entry of Numbering System's page */
+public class NumberingSystemController extends BasePreferenceController {
+ private static final String UNICODE_EXTENSION_NUMBERING_SYSTEM = "nu";
+
+ public NumberingSystemController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
+ * Setting should be shown or disabled in Settings. Further, it can be used to produce
+ * appropriate error / warning Slice in the case of unavailability.
+ *
+ * The status is used for the convenience methods: {@link #isAvailable()}, {@link
+ * #isSupported()}
+ *
+ * The inherited class doesn't need to check work profile if android:forWork="true" is set in
+ * preference xml.
+ */
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ String record = Settings.System.getString(
+ mContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
+ String result = "";
+ if (!TextUtils.isEmpty(record)) {
+ result = Locale.forLanguageTag(record)
+ .getUnicodeLocaleType(UNICODE_EXTENSION_NUMBERING_SYSTEM);
+ }
+
+ if (TextUtils.isEmpty(result)) {
+ result = Locale.getDefault(Locale.Category.FORMAT)
+ .getUnicodeLocaleType(UNICODE_EXTENSION_NUMBERING_SYSTEM);
+ if (TextUtils.isEmpty(result)) {
+ return mContext.getString(R.string.default_string_of_regional_preference);
+ }
+ }
+
+ Locale locale = new Locale.Builder()
+ .setUnicodeLocaleKeyword(UNICODE_EXTENSION_NUMBERING_SYSTEM, result)
+ .build();
+ return NumberingSystem.getInstance(locale).getName();
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferencesController.java b/src/com/android/settings/regionalpreferences/RegionalPreferencesController.java
new file mode 100644
index 00000000000..9f8d3c0b388
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/RegionalPreferencesController.java
@@ -0,0 +1,48 @@
+/*
+ * 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.regionalpreferences;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import com.android.settings.core.BasePreferenceController;
+
+/** A controller for the entry of Regional preferences */
+public class RegionalPreferencesController extends BasePreferenceController {
+ // This is a feature flag and will be removed after feature completed.
+ static final String FEATURE_PROPERTY = "i18n-feature-locale-preference";
+ public RegionalPreferencesController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
+ * Setting should be shown or disabled in Settings. Further, it can be used to produce
+ * appropriate error / warning Slice in the case of unavailability.
+ *
+ * The status is used for the convenience methods: {@link #isAvailable()}, {@link
+ * #isSupported()}
+ *
+ * The inherited class doesn't need to check work profile if android:forWork="true" is set in
+ * preference xml.
+ */
+ @Override
+ public int getAvailabilityStatus() {
+ return SystemProperties.getBoolean(FEATURE_PROPERTY, false)
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferencesEntriesFragment.java b/src/com/android/settings/regionalpreferences/RegionalPreferencesEntriesFragment.java
new file mode 100644
index 00000000000..08961dc110c
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/RegionalPreferencesEntriesFragment.java
@@ -0,0 +1,57 @@
+/*
+ * 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.regionalpreferences;
+
+import android.app.settings.SettingsEnums;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/** Provides entries of each regional preferences */
+@SearchIndexable
+public class RegionalPreferencesEntriesFragment extends DashboardFragment {
+ private static final String TAG = RegionalPreferencesEntriesFragment.class.getSimpleName();
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ getActivity().setTitle(R.string.regional_preferences_title);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.REGIONAL_PREFERENCE;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.regional_preference_main_page;
+ }
+
+ /**
+ * Get the tag string for logging.
+ */
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.regional_preference_main_page);
+}
diff --git a/src/com/android/settings/regionalpreferences/TemperatureUnitController.java b/src/com/android/settings/regionalpreferences/TemperatureUnitController.java
new file mode 100644
index 00000000000..b13b6c4a7a1
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/TemperatureUnitController.java
@@ -0,0 +1,64 @@
+/*
+ * 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.regionalpreferences;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.Locale;
+
+/** A controller for the entry of Temperature units' page */
+public class TemperatureUnitController extends BasePreferenceController {
+ public TemperatureUnitController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
+ * Setting should be shown or disabled in Settings. Further, it can be used to produce
+ * appropriate error / warning Slice in the case of unavailability.
+ *
+ * The status is used for the convenience methods: {@link #isAvailable()}, {@link
+ * #isSupported()}
+ *
+ * The inherited class doesn't need to check work profile if android:forWork="true" is set in
+ * preference xml.
+ */
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ String record = Settings.System.getString(
+ mContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
+ String result = "";
+ if (record != null) {
+ result = LocalePreferences.getTemperatureUnit(Locale.forLanguageTag(record), false);
+ }
+
+ if (result.isEmpty()) {
+ result = LocalePreferences.getTemperatureUnit(false);
+ }
+ return result.isEmpty()
+ ? mContext.getString(R.string.default_string_of_regional_preference) : result;
+ }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppAdapter.java b/src/com/android/settings/security/CredentialManagementAppAdapter.java
index 3f4eb2a4a2c..567cffda156 100644
--- a/src/com/android/settings/security/CredentialManagementAppAdapter.java
+++ b/src/com/android/settings/security/CredentialManagementAppAdapter.java
@@ -34,6 +34,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
+import com.android.settingslib.utils.StringUtil;
import java.util.ArrayList;
import java.util.List;
@@ -193,8 +194,8 @@ public class CredentialManagementAppAdapter extends RecyclerView.Adapter urisToAliases) {
- return mContext.getResources().getQuantityString(R.plurals.number_of_urls,
- urisToAliases.size(), urisToAliases.size());
+ return StringUtil.getIcuPluralsString(mContext, urisToAliases.size(),
+ R.string.number_of_urls);
}
}
diff --git a/src/com/android/settings/spa/notification/AppNotificationRepository.kt b/src/com/android/settings/spa/notification/AppNotificationRepository.kt
index fe8babb56e7..8dc4f20e591 100644
--- a/src/com/android/settings/spa/notification/AppNotificationRepository.kt
+++ b/src/com/android/settings/spa/notification/AppNotificationRepository.kt
@@ -177,12 +177,14 @@ class AppNotificationRepository(
fun calculateFrequencySummary(sentCount: Int): String {
val dailyFrequency = (sentCount.toFloat() / DAYS_TO_CHECK).roundToInt()
return if (dailyFrequency > 0) {
- context.resources.getQuantityString(
- R.plurals.notifications_sent_daily, dailyFrequency, dailyFrequency
+ context.formatString(
+ R.string.notifications_sent_daily,
+ "count" to dailyFrequency,
)
} else {
- context.resources.getQuantityString(
- R.plurals.notifications_sent_weekly, sentCount, sentCount
+ context.formatString(
+ R.string.notifications_sent_weekly,
+ "count" to sentCount,
)
}
}
diff --git a/src/com/android/settings/wifi/WifiEntryPreference.java b/src/com/android/settings/wifi/WifiEntryPreference.java
index 5187ee3119c..5b448870016 100644
--- a/src/com/android/settings/wifi/WifiEntryPreference.java
+++ b/src/com/android/settings/wifi/WifiEntryPreference.java
@@ -82,7 +82,6 @@ public class WifiEntryPreference extends RestrictedPreference implements
super(context);
setLayoutResource(R.layout.preference_access_point);
- setWidgetLayoutResource(R.layout.access_point_friction_widget);
mFrictionSld = getFrictionStateListDrawable();
mWifiEntry = wifiEntry;
mWifiEntry.setListener(this);
@@ -280,6 +279,11 @@ public class WifiEntryPreference extends RestrictedPreference implements
notifyChanged();
}
+ @Override
+ protected int getSecondTargetResId() {
+ return R.layout.access_point_friction_widget;
+ }
+
@Override
public void onClick(View view) {
if (view.getId() == R.id.icon_button) {
diff --git a/tests/robotests/src/com/android/settings/accessibility/AutoclickPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AutoclickPreferenceControllerTest.java
index fd6bedc36d4..106b617a166 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AutoclickPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AutoclickPreferenceControllerTest.java
@@ -72,8 +72,8 @@ public class AutoclickPreferenceControllerTest {
assertThat(mController.getSummary().toString())
.isEqualTo(AutoclickUtils.getAutoclickDelaySummary(
- mContext.getResources(),
- R.plurals.accessibilty_autoclick_preference_subtitle_medium_delay,
+ mContext,
+ R.string.accessibilty_autoclick_preference_subtitle_medium_delay,
AUTOCLICK_DELAY_DEFAULT).toString());
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AutoclickUtilsTest.java b/tests/robotests/src/com/android/settings/accessibility/AutoclickUtilsTest.java
index 46b1e2b1b7b..a87c03f7e42 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AutoclickUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AutoclickUtilsTest.java
@@ -40,7 +40,7 @@ public final class AutoclickUtilsTest {
@Test
public void getAutoclickDelaySummary_minDelay_shouldReturnOnSummary() {
final CharSequence summary = AutoclickUtils.getAutoclickDelaySummary(
- mContext.getResources(), R.plurals.accessibilty_autoclick_delay_unit_second,
+ mContext, R.string.accessibilty_autoclick_delay_unit_second,
MIN_AUTOCLICK_DELAY_MS);
assertThat(summary.toString()).isEqualTo("0.2 seconds");
}
@@ -48,7 +48,7 @@ public final class AutoclickUtilsTest {
@Test
public void getAutoclickDelaySummary_maxDelay_shouldReturnOnSummary() {
final CharSequence summary = AutoclickUtils.getAutoclickDelaySummary(
- mContext.getResources(), R.plurals.accessibilty_autoclick_delay_unit_second,
+ mContext, R.string.accessibilty_autoclick_delay_unit_second,
MAX_AUTOCLICK_DELAY_MS);
assertThat(summary.toString()).isEqualTo("1 second");
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceControllerTest.java
index c0e27b61264..a9cac87cebc 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceControllerTest.java
@@ -46,6 +46,7 @@ import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Ignore;
@@ -140,8 +141,8 @@ public class FingerprintStatusPreferenceControllerTest {
mController.updateState(mPreference);
- assertThat(mPreference.getSummary()).isEqualTo(mContext.getResources().getQuantityString(
- R.plurals.security_settings_fingerprint_preference_summary, 1, 1));
+ assertThat(mPreference.getSummary()).isEqualTo(StringUtil.getIcuPluralsString(mContext, 1,
+ R.string.security_settings_fingerprint_preference_summary));
assertThat(mPreference.isVisible()).isTrue();
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
index 57af4126540..501279c47f2 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
@@ -159,7 +159,9 @@ public class BluetoothDeviceDetailsFragmentTest {
public void getTitle_inputDeviceTitle() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES,
true);
- doReturn(mock(InputDevice.class)).when(mFragment).getInputDevice(mContext);
+ InputDevice inputDevice = mock(InputDevice.class);
+ doReturn(true).when(inputDevice).supportsSource(InputDevice.SOURCE_STYLUS);
+ doReturn(inputDevice).when(mFragment).getInputDevice(mContext);
mFragment.onAttach(mContext);
mFragment.setTitleForInputDevice();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
index f02eec2cb9e..bb700776532 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
@@ -35,6 +35,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.utils.StringUtil;
import org.junit.After;
import org.junit.Before;
@@ -148,8 +149,8 @@ public class RestrictAppTipTest {
@Test
public void getSummary_oneAppHandled_showHandledSummary() {
assertThat(mHandledBatteryTip.getSummary(mContext).toString())
- .isEqualTo(mContext.getResources().getQuantityString(
- R.plurals.battery_tip_restrict_handled_summary, 1));
+ .isEqualTo(StringUtil.getIcuPluralsString(mContext, 1,
+ R.string.battery_tip_restrict_handled_summary));
}
@Test
@@ -157,8 +158,8 @@ public class RestrictAppTipTest {
mUsageAppList.add(new AppInfo.Builder().build());
mHandledBatteryTip = new RestrictAppTip(BatteryTip.StateType.HANDLED, mUsageAppList);
assertThat(mHandledBatteryTip.getSummary(mContext))
- .isEqualTo(mContext.getResources().getQuantityString(
- R.plurals.battery_tip_restrict_handled_summary, 2));
+ .isEqualTo(StringUtil.getIcuPluralsString(mContext, 2,
+ R.string.battery_tip_restrict_handled_summary));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/location/TopLevelLocationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/TopLevelLocationPreferenceControllerTest.java
index db809f380a0..0801586dc7a 100644
--- a/tests/robotests/src/com/android/settings/location/TopLevelLocationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/TopLevelLocationPreferenceControllerTest.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.location.LocationManager;
import com.android.settings.R;
+import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Test;
@@ -67,9 +68,8 @@ public class TopLevelLocationPreferenceControllerTest {
// Turn on location again and check if the previous summary text is still cached.
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
assertThat(mController.getSummary()).isEqualTo(
- mContext.getResources().getQuantityString(
- R.plurals.location_settings_summary_location_on, locationAppCount,
- locationAppCount));
+ StringUtil.getIcuPluralsString(mContext, locationAppCount,
+ R.string.location_settings_summary_location_on));
}
@Test
@@ -78,9 +78,8 @@ public class TopLevelLocationPreferenceControllerTest {
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
mController.setLocationAppCount(locationAppCount);
assertThat(mController.getSummary()).isEqualTo(
- mContext.getResources().getQuantityString(
- R.plurals.location_settings_summary_location_on,
- locationAppCount, locationAppCount));
+ StringUtil.getIcuPluralsString(mContext, locationAppCount,
+ R.string.location_settings_summary_location_on));
}
@Test
@@ -89,8 +88,7 @@ public class TopLevelLocationPreferenceControllerTest {
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
mController.setLocationAppCount(locationAppCount);
assertThat(mController.getSummary()).isEqualTo(
- mContext.getResources().getQuantityString(
- R.plurals.location_settings_summary_location_on,
- locationAppCount, locationAppCount));
+ StringUtil.getIcuPluralsString(mContext, locationAppCount,
+ R.string.location_settings_summary_location_on));
}
}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/network/tether/TetheringHelperTest.java b/tests/robotests/src/com/android/settings/network/tether/TetheringHelperTest.java
new file mode 100644
index 00000000000..5b9ace8ef7a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/tether/TetheringHelperTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.tether;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.TetheringManager;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class TetheringHelperTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Spy
+ Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ Lifecycle mLifecycle;
+ @Mock
+ LifecycleOwner mLifecycleOwner;
+ @Mock
+ TetheringManager mTetheringManager;
+ @Mock
+ TetheringManager.TetheringEventCallback mTetheringEventCallback;
+ @Mock
+ List mInterfaces;
+
+ TetheringHelper mHelper;
+
+ @Before
+ public void setUp() {
+ mHelper = TetheringHelper.getInstance(mContext, mTetheringEventCallback, mLifecycle);
+ mHelper.sTetheringManager = mTetheringManager;
+ }
+
+ @Test
+ public void constructor_propertiesShouldBeReady() {
+ assertThat(mHelper.sAppContext).isNotNull();
+ assertThat(mHelper.sTetheringManager).isNotNull();
+ assertThat(mHelper.mCallback).isNotNull();
+ verify(mLifecycle).addObserver(any());
+ }
+
+ @Test
+ public void getTetheringManager_isNotNull() {
+ assertThat(mHelper.getTetheringManager()).isNotNull();
+ }
+
+ @Test
+ public void onStart_eventCallbacksClear_addAndRegisterCallback() {
+ mHelper.sEventCallbacks.clear();
+
+ mHelper.onStart(mLifecycleOwner);
+
+ assertThat(mHelper.sEventCallbacks.contains(mTetheringEventCallback)).isTrue();
+ assertThat(mHelper.sEventCallbacks.size()).isEqualTo(1);
+ verify(mTetheringManager).registerTetheringEventCallback(any(Executor.class),
+ eq(TetheringHelper.sTetheringEventCallback));
+ }
+
+ @Test
+ public void onStart_eventCallbacksContains_doNotRegisterCallback() {
+ mHelper.sEventCallbacks.clear();
+ mHelper.sEventCallbacks.add(mTetheringEventCallback);
+
+ mHelper.onStart(mLifecycleOwner);
+
+ verify(mTetheringManager, never()).registerTetheringEventCallback(any(Executor.class),
+ eq(TetheringHelper.sTetheringEventCallback));
+ }
+
+ @Test
+ public void onStop_eventCallbacksContains_removeAndUnregisterCallback() {
+ mHelper.sEventCallbacks.clear();
+ mHelper.sEventCallbacks.add(mTetheringEventCallback);
+
+ mHelper.onStop(mLifecycleOwner);
+
+ assertThat(mHelper.sEventCallbacks.contains(mTetheringEventCallback)).isFalse();
+ assertThat(mHelper.sEventCallbacks.size()).isEqualTo(0);
+ verify(mTetheringManager).unregisterTetheringEventCallback(
+ eq(TetheringHelper.sTetheringEventCallback));
+ }
+
+ @Test
+ public void onStop_eventCallbacksClear_doNotUnregisterCallback() {
+ mHelper.sEventCallbacks.clear();
+
+ mHelper.onStop(mLifecycleOwner);
+
+ assertThat(mHelper.sEventCallbacks.contains(mTetheringEventCallback)).isFalse();
+ assertThat(mHelper.sEventCallbacks.size()).isEqualTo(0);
+ verify(mTetheringManager, never()).unregisterTetheringEventCallback(
+ eq(TetheringHelper.sTetheringEventCallback));
+ }
+
+ @Test
+ public void onTetheredInterfacesChanged_eventCallbacksContains_doCallback() {
+ mHelper.sEventCallbacks.clear();
+ mHelper.sEventCallbacks.add(mTetheringEventCallback);
+
+ mHelper.sTetheringEventCallback.onTetheredInterfacesChanged(mInterfaces);
+
+ verify(mTetheringEventCallback).onTetheredInterfacesChanged(eq(mInterfaces));
+ }
+
+ @Test
+ public void onTetheredInterfacesChanged_eventCallbacksClear_doNotCallback() {
+ mHelper.sEventCallbacks.clear();
+
+ mHelper.sTetheringEventCallback.onTetheredInterfacesChanged(mInterfaces);
+
+ verify(mTetheringEventCallback, never()).onTetheredInterfacesChanged(eq(mInterfaces));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/print/PrintSettingsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/print/PrintSettingsPreferenceControllerTest.java
index c782d9b23f9..9cecf4cbc2b 100644
--- a/tests/robotests/src/com/android/settings/print/PrintSettingsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/print/PrintSettingsPreferenceControllerTest.java
@@ -43,6 +43,7 @@ import androidx.lifecycle.LifecycleOwner;
import com.android.settings.R;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Test;
@@ -119,8 +120,8 @@ public class PrintSettingsPreferenceControllerTest {
mController.updateState(mPreference);
assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getResources()
- .getQuantityString(R.plurals.print_jobs_summary, 1, 1));
+ .isEqualTo(StringUtil.getIcuPluralsString(mContext, 1,
+ R.string.print_jobs_summary));
}
@Test
@@ -135,8 +136,8 @@ public class PrintSettingsPreferenceControllerTest {
mController.updateState(mPreference);
assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getResources()
- .getQuantityString(R.plurals.print_settings_summary, 2, 2));
+ .isEqualTo(StringUtil.getIcuPluralsString(mContext, 2,
+ R.string.print_settings_summary));
// No service
when(mPrintManager.getPrintServices(PrintManager.ENABLED_SERVICES)).thenReturn(null);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java
index a5d63dbd34d..a60b5313730 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java
@@ -80,6 +80,8 @@ public class WifiEntryPreferenceTest {
private static final String MOCK_SUMMARY = "summary";
private static final String FAKE_URI_STRING = "fakeuri";
+ WifiEntryPreference mPref;
+
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
@@ -105,6 +107,8 @@ public class WifiEntryPreferenceTest {
.thenReturn(mMockShowXDrawable3);
when(mMockIconInjector.getIcon(true /* showX */, 4))
.thenReturn(mMockShowXDrawable4);
+
+ mPref = new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
}
@Test
@@ -128,52 +132,46 @@ public class WifiEntryPreferenceTest {
@Test
public void titleChanged_refresh_shouldUpdateTitle() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final String updatedTitle = "updated title";
when(mMockWifiEntry.getTitle()).thenReturn(updatedTitle);
- pref.refresh();
+ mPref.refresh();
- assertThat(pref.getTitle()).isEqualTo(updatedTitle);
+ assertThat(mPref.getTitle().toString()).isEqualTo(updatedTitle);
}
@Test
public void summaryChanged_refresh_shouldUpdateSummary() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final String updatedSummary = "updated summary";
when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(updatedSummary);
- pref.refresh();
+ mPref.refresh();
- assertThat(pref.getSummary()).isEqualTo(updatedSummary);
+ assertThat(mPref.getSummary().toString()).isEqualTo(updatedSummary);
}
@Test
public void levelChanged_refresh_shouldUpdateLevelIcon() {
final List iconList = new ArrayList<>();
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
when(mMockWifiEntry.getLevel()).thenReturn(0);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(1);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(2);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(3);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(4);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(-1);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
assertThat(iconList).containsExactly(mMockDrawable0, mMockDrawable1,
mMockDrawable2, mMockDrawable3, mMockDrawable4, null);
@@ -183,27 +181,25 @@ public class WifiEntryPreferenceTest {
public void levelChanged_showXWifiRefresh_shouldUpdateLevelIcon() {
final List iconList = new ArrayList<>();
when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
when(mMockWifiEntry.getLevel()).thenReturn(0);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(1);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(2);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(3);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(4);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
when(mMockWifiEntry.getLevel()).thenReturn(-1);
- pref.refresh();
- iconList.add(pref.getIcon());
+ mPref.refresh();
+ iconList.add(mPref.getIcon());
assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
@@ -212,14 +208,12 @@ public class WifiEntryPreferenceTest {
@Test
public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
+ final View view = inflater.inflate(mPref.getLayoutResource(), new LinearLayout(mContext),
false);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
- pref.onBindViewHolder(holder);
+ mPref.onBindViewHolder(holder);
assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -227,14 +221,12 @@ public class WifiEntryPreferenceTest {
@Test
public void helpButton_whenGetHelpUriStringNotNull_shouldSetCorrectContentDescription() {
when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
+ final View view = inflater.inflate(mPref.getLayoutResource(), new LinearLayout(mContext),
false);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
- pref.onBindViewHolder(holder);
+ mPref.onBindViewHolder(holder);
assertThat(view.findViewById(R.id.icon_button).getContentDescription()).isEqualTo(
mContext.getString(R.string.help_label));
@@ -243,25 +235,25 @@ public class WifiEntryPreferenceTest {
@Test
public void subscriptionEntry_shouldSetImageButtonGone() {
when(mMockWifiEntry.isSubscription()).thenReturn(true);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
+ final View view = inflater.inflate(mPref.getLayoutResource(), new LinearLayout(mContext),
false);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
- pref.onBindViewHolder(holder);
+ mPref.onBindViewHolder(holder);
assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.GONE);
}
@Test
- public void updateIcon_ShouldSetTintListForDrawable() {
- WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- pref.updateIcon(false /* showX */, 4 /* level */);
+ public void updateIcon_shouldSetTintListForDrawable() {
+ mPref.updateIcon(false /* showX */, 4 /* level */);
verify(mMockDrawable4).setTintList(any());
}
+
+ @Test
+ public void getSecondTargetResId_shouldNotReturnZero() {
+ assertThat(mPref.getSecondTargetResId()).isNotEqualTo(0);
+ }
}
diff --git a/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
index 10f11101ddf..706aedad074 100644
--- a/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
@@ -41,6 +41,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.Settings;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Test;
@@ -244,11 +245,10 @@ public class CombinedBiometricStatusUtilsTest {
public void getSummary_whenNoFaceEnrolled_whenMultipleFingerprints_returnsFingerprints() {
final int enrolledFingerprintsCount = 2;
final int stringResId = ResourcesUtils.getResourcesId(
- ApplicationProvider.getApplicationContext(), "plurals",
+ ApplicationProvider.getApplicationContext(), "string",
"security_settings_fingerprint_preference_summary");
- final String summary = mApplicationContext.getResources().getQuantityString(
- stringResId, enrolledFingerprintsCount /* quantity */,
- enrolledFingerprintsCount /* formatArgs */);
+ final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
+ enrolledFingerprintsCount, stringResId);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
@@ -261,11 +261,10 @@ public class CombinedBiometricStatusUtilsTest {
public void getSummary_whenNoFaceEnrolled_whenSingleFingerprints_returnsFingerprints() {
final int enrolledFingerprintsCount = 1;
final int stringResId = ResourcesUtils.getResourcesId(
- ApplicationProvider.getApplicationContext(), "plurals",
+ ApplicationProvider.getApplicationContext(), "string",
"security_settings_fingerprint_preference_summary");
- final String summary = mApplicationContext.getResources().getQuantityString(
- stringResId, enrolledFingerprintsCount /* quantity */,
- enrolledFingerprintsCount /* formatArgs */);
+ final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
+ enrolledFingerprintsCount, stringResId);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
index 69e5e2f485b..450adc60bb8 100644
--- a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
@@ -37,6 +37,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Test;
@@ -165,11 +166,10 @@ public class FingerprintStatusUtilsTest {
public void getSummary_whenEnrolled_returnsSummary() {
final int enrolledFingerprintsCount = 2;
final int stringResId = ResourcesUtils.getResourcesId(
- ApplicationProvider.getApplicationContext(), "plurals",
+ ApplicationProvider.getApplicationContext(), "string",
"security_settings_fingerprint_preference_summary");
- final String summary = mApplicationContext.getResources().getQuantityString(
- stringResId, enrolledFingerprintsCount /* quantity */,
- enrolledFingerprintsCount /* formatArgs */);
+ final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
+ enrolledFingerprintsCount, stringResId);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
when(mFingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/CalendarTypeControllerTest.java b/tests/unit/src/com/android/settings/regionalpreferences/CalendarTypeControllerTest.java
new file mode 100644
index 00000000000..7e1ec94af60
--- /dev/null
+++ b/tests/unit/src/com/android/settings/regionalpreferences/CalendarTypeControllerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.regionalpreferences;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Locale;
+
+public class CalendarTypeControllerTest {
+ private Context mApplicationContext;
+ private CalendarTypeController mController;
+ private String mCacheProviderContent = "";
+ private Locale mCacheLocale;
+
+ @Before
+ public void setUp() throws Exception {
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ mController = new CalendarTypeController(mApplicationContext, "key");
+ mCacheProviderContent = Settings.System.getString(
+ mApplicationContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
+ mCacheLocale = Locale.getDefault(Locale.Category.FORMAT);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ RegionalPreferenceUtils.setSettingsProviderContent(
+ mApplicationContext, mCacheProviderContent);
+ Locale.setDefault(mCacheLocale);
+ }
+
+ @Test
+ public void getSummary_hasProviderValue_resultIsChinese() {
+ RegionalPreferenceUtils.setSettingsProviderContent(
+ mApplicationContext, "und-u-ca-chinese");
+
+ CharSequence type = mController.getSummary();
+
+ assertEquals(LocalePreferences.CalendarType.CHINESE, type.toString());
+ }
+
+ @Test
+ public void getSummary_hasProviderValue_resultIsDangi() {
+ RegionalPreferenceUtils.setSettingsProviderContent(
+ mApplicationContext, "und-u-ca-dangi");
+
+ CharSequence type = mController.getSummary();
+
+ assertEquals(LocalePreferences.CalendarType.DANGI, type.toString());
+ }
+
+ @Test
+ public void getSummary_noProviderValueButHasDefaultLocaleWithSubtag_resultIsSat() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "");
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese"));
+
+ CharSequence type = mController.getSummary();
+
+ assertEquals(LocalePreferences.CalendarType.CHINESE, type.toString());
+ }
+
+ @Test
+ public void getSummary_noProviderValueAndDefaultLocaleWithoutSubtag_resultIsEmpty() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "");
+ Locale.setDefault(Locale.forLanguageTag("en-US"));
+
+ CharSequence type = mController.getSummary();
+
+ assertEquals(ResourcesUtils.getResourcesString(
+ mApplicationContext, "default_string_of_regional_preference"), type.toString());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/FirstDayOfWeekControllerTest.java b/tests/unit/src/com/android/settings/regionalpreferences/FirstDayOfWeekControllerTest.java
new file mode 100644
index 00000000000..b6582421362
--- /dev/null
+++ b/tests/unit/src/com/android/settings/regionalpreferences/FirstDayOfWeekControllerTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.regionalpreferences;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Locale;
+
+public class FirstDayOfWeekControllerTest {
+ private Context mApplicationContext;
+ private FirstDayOfWeekController mController;
+ private String mCacheProviderContent = "";
+ private Locale mCacheLocale;
+
+ @Before
+ public void setUp() throws Exception {
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ mController = new FirstDayOfWeekController(mApplicationContext, "key");
+ mCacheProviderContent = Settings.System.getString(
+ mApplicationContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
+ mCacheLocale = Locale.getDefault(Locale.Category.FORMAT);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ RegionalPreferenceUtils.setSettingsProviderContent(
+ mApplicationContext, mCacheProviderContent);
+ Locale.setDefault(mCacheLocale);
+ }
+
+ @Test
+ public void getSummary_hasProviderValue_resultIsWed() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "und-u-fw-wed");
+
+ CharSequence day = mController.getSummary();
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.WEDNESDAY, day.toString());
+ }
+
+ @Test
+ public void getSummary_hasProviderValue_resultIsSat() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "und-u-fw-sat");
+
+ CharSequence day = mController.getSummary();
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.SATURDAY, day.toString());
+ }
+
+ @Test
+ public void getSummary_noProviderValueButHasDefaultLocaleWithSubtag_resultIsSat() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "");
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-fw-sat"));
+
+ CharSequence day = mController.getSummary();
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.SATURDAY, day.toString());
+ }
+
+ @Test
+ public void getSummary_noProviderValueAndDefaultLocaleWithoutSubtag_resultIsEmpty() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "");
+ Locale.setDefault(Locale.forLanguageTag("en-US"));
+
+ CharSequence day = mController.getSummary();
+
+ assertEquals(ResourcesUtils.getResourcesString(
+ mApplicationContext, "default_string_of_regional_preference"), day.toString());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferenceUtils.java b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferenceUtils.java
new file mode 100644
index 00000000000..709413c7523
--- /dev/null
+++ b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferenceUtils.java
@@ -0,0 +1,29 @@
+/*
+ * 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.regionalpreferences;
+
+import android.content.Context;
+import android.provider.Settings;
+
+/** Utils for each regional preference unit test. */
+public final class RegionalPreferenceUtils {
+ /** Set language tag to Settings Provider */
+ public static void setSettingsProviderContent(Context context, String languageTag) {
+ Settings.System.putString(context.getContentResolver(),
+ Settings.System.LOCALE_PREFERENCES, languageTag);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesControllerTest.java b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesControllerTest.java
new file mode 100644
index 00000000000..966c46ccb98
--- /dev/null
+++ b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesControllerTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.regionalpreferences;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RegionalPreferencesControllerTest {
+ private boolean mCacheProperty = false;
+ private Context mApplicationContext;
+ private RegionalPreferencesController mController;
+
+ @Before
+ public void setUp() throws Exception {
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ mCacheProperty =
+ SystemProperties.getBoolean(RegionalPreferencesController.FEATURE_PROPERTY, false);
+ mController = new RegionalPreferencesController(mApplicationContext, "key");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ setProp(mCacheProperty);
+ }
+
+ @Test
+ public void getAvailabilityStatus_systemPropertyIstrue_available() throws Exception {
+ setProp(true);
+
+ int result = mController.getAvailabilityStatus();
+
+ assertEquals(AVAILABLE, result);
+ }
+
+ @Test
+ public void getAvailabilityStatus_systemPropertyIstrue_unavailable() throws Exception {
+ setProp(false);
+
+ int result = mController.getAvailabilityStatus();
+
+ assertEquals(CONDITIONALLY_UNAVAILABLE, result);
+ }
+
+ private static void setProp(boolean isEnabled) throws Exception {
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ uiAutomation.executeShellCommand(
+ "setprop " + RegionalPreferencesController.FEATURE_PROPERTY + " " + isEnabled);
+
+ for (int i = 0; i < 3; i++) {
+ Thread.sleep(500);
+ if (SystemProperties.getBoolean(
+ RegionalPreferencesController.FEATURE_PROPERTY, false) == isEnabled) {
+ break;
+ }
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesEntriesFragmentTest.java b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesEntriesFragmentTest.java
new file mode 100644
index 00000000000..f572816966f
--- /dev/null
+++ b/tests/unit/src/com/android/settings/regionalpreferences/RegionalPreferencesEntriesFragmentTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.regionalpreferences;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.settings.SettingsEnums;
+import android.os.Looper;
+
+import androidx.test.annotation.UiThreadTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class RegionalPreferencesEntriesFragmentTest {
+ private RegionalPreferencesEntriesFragment mFragment;
+
+ @Before
+ @UiThreadTest
+ public void setUp() throws Exception {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ mFragment = new RegionalPreferencesEntriesFragment();
+ }
+
+ @Test
+ @UiThreadTest
+ public void getMetricsCategory_returnRegionalPreference() {
+ assertEquals(SettingsEnums.REGIONAL_PREFERENCE, mFragment.getMetricsCategory());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/TemperatureUnitControllerTest.java b/tests/unit/src/com/android/settings/regionalpreferences/TemperatureUnitControllerTest.java
new file mode 100644
index 00000000000..4f473ad604d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/regionalpreferences/TemperatureUnitControllerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.regionalpreferences;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Locale;
+
+public class TemperatureUnitControllerTest {
+ private Context mApplicationContext;
+ private TemperatureUnitController mController;
+ private String mCacheProviderContent = "";
+ private Locale mCacheLocale;
+
+ @Before
+ public void setUp() throws Exception {
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ mController = new TemperatureUnitController(mApplicationContext, "key");
+ mCacheProviderContent = Settings.System.getString(
+ mApplicationContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
+ mCacheLocale = Locale.getDefault(Locale.Category.FORMAT);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ RegionalPreferenceUtils.setSettingsProviderContent(
+ mApplicationContext, mCacheProviderContent);
+ Locale.setDefault(mCacheLocale);
+ }
+
+ @Test
+ public void getSummary_hasProviderValue_resultIsCelsius() {
+ RegionalPreferenceUtils.setSettingsProviderContent(
+ mApplicationContext, "und-u-mu-celsius");
+
+ CharSequence unit = mController.getSummary();
+
+ assertEquals(LocalePreferences.TemperatureUnit.CELSIUS, unit.toString());
+ }
+
+ @Test
+ public void getSummary_hasProviderValue_resultIsFahrenheit() {
+ RegionalPreferenceUtils.setSettingsProviderContent(
+ mApplicationContext, "und-u-mu-fahrenhe");
+
+ CharSequence unit = mController.getSummary();
+
+ assertEquals(LocalePreferences.TemperatureUnit.FAHRENHEIT, unit.toString());
+ }
+
+ @Test
+ public void getSummary_noProviderValueButHasDefaultLocaleWithSubtag_resultIsFahrenheit() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "");
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-mu-fahrenhe"));
+
+ CharSequence unit = mController.getSummary();
+
+ assertEquals(LocalePreferences.TemperatureUnit.FAHRENHEIT, unit.toString());
+ }
+
+ @Test
+ public void getSummary_noProviderValueAndDefaultLocaleWithoutSubtag_resultIsEmpty() {
+ RegionalPreferenceUtils.setSettingsProviderContent(mApplicationContext, "");
+ Locale.setDefault(Locale.forLanguageTag("en-US"));
+
+ CharSequence unit = mController.getSummary();
+
+ assertEquals(ResourcesUtils.getResourcesString(
+ mApplicationContext, "default_string_of_regional_preference"), unit.toString());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
index 07c1571f987..9e22e8d0b35 100644
--- a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
@@ -49,6 +49,7 @@ import com.android.settings.Settings;
import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
import com.android.settings.biometrics.fingerprint.FingerprintSettings;
import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.utils.StringUtil;
import org.junit.After;
import org.junit.Before;
@@ -568,13 +569,12 @@ public class BiometricsSafetySourceTest {
private void assertSafetySourceDisabledDataSetWithPluralSummary(String expectedTitleResName,
String expectedSummaryResName, int expectedSummaryQuantity) {
final int stringResId = ResourcesUtils.getResourcesId(
- ApplicationProvider.getApplicationContext(), "plurals",
+ ApplicationProvider.getApplicationContext(), "string",
expectedSummaryResName);
assertSafetySourceDisabledDataSet(
ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName),
- mApplicationContext.getResources().getQuantityString(stringResId,
- expectedSummaryQuantity /* quantity */,
- expectedSummaryQuantity /* formatArgs */)
+ StringUtil.getIcuPluralsString(mApplicationContext,
+ expectedSummaryQuantity, stringResId)
);
}
@@ -582,13 +582,12 @@ public class BiometricsSafetySourceTest {
String expectedSummaryResName, int expectedSummaryQuantity,
String expectedSettingsClassName) {
final int stringResId = ResourcesUtils.getResourcesId(
- ApplicationProvider.getApplicationContext(), "plurals",
+ ApplicationProvider.getApplicationContext(), "string",
expectedSummaryResName);
assertSafetySourceEnabledDataSet(
ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName),
- mApplicationContext.getResources().getQuantityString(stringResId,
- expectedSummaryQuantity /* quantity */,
- expectedSummaryQuantity /* formatArgs */),
+ StringUtil.getIcuPluralsString(mApplicationContext,
+ expectedSummaryQuantity, stringResId),
expectedSettingsClassName
);
}