Merge "[Audiosharing] Impl audio sharing main switch." into main
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 78fba34..6b7869e 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -10,6 +10,7 @@
"settings_connecteddevice_flag_declarations.aconfig",
"settings_globalintl_flag_declarations.aconfig",
"settings_experience_flag_declarations.aconfig",
+ "settings_notification_flag_declarations.aconfig",
"settings_onboarding_experience_flag_declarations.aconfig",
"settings_telephony_flag_declarations.aconfig",
"settings_biometrics_integration_declarations.aconfig",
diff --git a/aconfig/settings_notification_flag_declarations.aconfig b/aconfig/settings_notification_flag_declarations.aconfig
new file mode 100644
index 0000000..f2bf1c8
--- /dev/null
+++ b/aconfig/settings_notification_flag_declarations.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.settings.flags"
+
+flag {
+ name: "dedupe_dnd_settings_channels"
+ namespace: "systemui"
+ description: "Controls adding group names to channel names in the DND>Apps settings page"
+ bug: "294333850"
+}
+
diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto
index e75ca48..36126a5 100644
--- a/protos/fuelgauge_log.proto
+++ b/protos/fuelgauge_log.proto
@@ -43,6 +43,7 @@
RECHECK_JOB = 3;
FETCH_USAGE_DATA = 4;
INSERT_USAGE_DATA = 5;
+ TIME_UPDATED = 6;
}
optional int64 timestamp = 1;
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a3760c5..02b7a4c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9520,6 +9520,13 @@
<!-- Label for showing apps that can manage external storage[CHAR LIMIT=45] -->
<string name="filter_manage_external_storage">Can access all files</string>
+ <!-- Voice Activation apps settings title [CHAR LIMIT=40] -->
+ <string name="voice_activation_apps_title">Voice activation apps</string>
+ <!-- Label for a setting which controls whether an app can be voice activated [CHAR LIMIT=NONE] -->
+ <string name="permit_voice_activation_apps">Allow voice activation</string>
+ <!-- Description for a setting which controls whether an app can be voice activated [CHAR LIMIT=NONE] -->
+ <string name ="allow_voice_activation_apps_description">Voice activation turns-on approved apps, hands-free, using voice command.\n\nUntill activated, none of these apps can directly access your microphone.Instead, this device uses built-in proteced adaptive sensing to turn-on aprroved apps for you.\n\n<a href="">More about protected adaptive sensing</a></string>
+
<!-- Manage full screen intent permission title [CHAR LIMIT=40] -->
<string name="full_screen_intent_title">Full screen notifications</string>
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index b3f3f7d..3f3d75d 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -100,6 +100,11 @@
settings:controller="com.android.settings.spa.app.specialaccess.UseFullScreenIntentPreferenceController" />
<Preference
+ android:key="voice_activation_apps"
+ android:title="@string/voice_activation_apps_title"
+ settings:controller="com.android.settings.spa.app.specialaccess.VoiceActivationAppsPreferenceController" />
+
+ <Preference
android:key="picture_in_picture"
android:title="@string/picture_in_picture_title"
android:order="-1100"
diff --git a/src/com/android/settings/SettingsActivityUtil.kt b/src/com/android/settings/SettingsActivityUtil.kt
index 65d26de..c23bc18 100644
--- a/src/com/android/settings/SettingsActivityUtil.kt
+++ b/src/com/android/settings/SettingsActivityUtil.kt
@@ -37,6 +37,7 @@
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
+import com.android.settings.spa.app.specialaccess.VoiceActivationAppsListProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.wifi.ChangeWifiStateDetails
@@ -65,6 +66,8 @@
WifiControlAppListProvider.getAppInfoRoutePrefix(),
NfcTagAppsSettingsProvider::class.qualifiedName to
NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
+ VoiceActivationAppsListProvider::class.qualifiedName to
+ VoiceActivationAppsListProvider.getAppInfoRoutePrefix(),
)
@JvmStatic
diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
index 076d37c..02d5c27 100644
--- a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
+++ b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
@@ -28,6 +28,7 @@
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
@@ -44,8 +45,10 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.widget.ActionButtonsPreference;
import java.util.ArrayList;
@@ -104,6 +107,7 @@
Log.e(TAG, "Unable to set user min aspect ratio");
return;
}
+ logActionMetrics(selectedKey, mSelectedKey);
// Only update to selected aspect ratio if nothing goes wrong
mSelectedKey = selectedKey;
updateAllPreferences(mSelectedKey);
@@ -118,8 +122,7 @@
@Override
public int getMetricsCategory() {
- // TODO(b/292566895): add metrics for logging
- return 0;
+ return SettingsEnums.USER_ASPECT_RATIO_APP_INFO_SETTINGS;
}
@Override
@@ -244,6 +247,68 @@
}
}
+ private void logActionMetrics(@NonNull String selectedKey, @NonNull String unselectedKey) {
+ final MetricsFeatureProvider metricsFeatureProvider =
+ FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
+ final int attribution = metricsFeatureProvider.getAttribution(getActivity());
+ metricsFeatureProvider.action(
+ attribution,
+ getUnselectedAspectRatioAction(unselectedKey),
+ getMetricsCategory(),
+ mPackageName,
+ mUserId
+ );
+ metricsFeatureProvider.action(
+ attribution,
+ getSelectedAspectRatioAction(selectedKey),
+ getMetricsCategory(),
+ mPackageName,
+ mUserId
+ );
+ }
+
+ private static int getSelectedAspectRatioAction(@NonNull String selectedKey) {
+ switch (selectedKey) {
+ case KEY_PREF_DEFAULT:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_APP_DEFAULT_SELECTED;
+ case KEY_PREF_FULLSCREEN:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_FULL_SCREEN_SELECTED;
+ case KEY_PREF_HALF_SCREEN:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_HALF_SCREEN_SELECTED;
+ case KEY_PREF_4_3:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_4_3_SELECTED;
+ case KEY_PREF_16_9:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_16_9_SELECTED;
+ case KEY_PREF_3_2:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_3_2_SELECTED;
+ case KEY_PREF_DISPLAY_SIZE:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_DISPLAY_SIZE_SELECTED;
+ default:
+ return SettingsEnums.ACTION_UNKNOWN;
+ }
+ }
+
+ private static int getUnselectedAspectRatioAction(@NonNull String unselectedKey) {
+ switch (unselectedKey) {
+ case KEY_PREF_DEFAULT:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_APP_DEFAULT_UNSELECTED;
+ case KEY_PREF_FULLSCREEN:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_FULL_SCREEN_UNSELECTED;
+ case KEY_PREF_HALF_SCREEN:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_HALF_SCREEN_UNSELECTED;
+ case KEY_PREF_4_3:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_4_3_UNSELECTED;
+ case KEY_PREF_16_9:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_16_9_UNSELECTED;
+ case KEY_PREF_3_2:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_3_2_UNSELECTED;
+ case KEY_PREF_DISPLAY_SIZE:
+ return SettingsEnums.ACTION_USER_ASPECT_RATIO_DISPLAY_SIZE_UNSELECTED;
+ default:
+ return SettingsEnums.ACTION_UNKNOWN;
+ }
+ }
+
@VisibleForTesting
UserAspectRatioManager getAspectRatioManager() {
return mUserAspectRatioManager;
diff --git a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
index c94edc6..1d96688 100644
--- a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
+++ b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
@@ -46,14 +46,14 @@
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.spaprivileged.template.app.AppListItemKt;
+import com.android.settingslib.spaprivileged.template.app.AppListItemModelKt;
import com.android.settingslib.spaprivileged.template.app.AppListPageKt;
import com.android.settingslib.widget.LottieColorUtils;
import com.airbnb.lottie.LottieAnimationView;
/**
- * @deprecated Will be removed, use {@link AppListItemKt} {@link AppListPageKt} instead.
+ * @deprecated Will be removed, use {@link AppListItemModelKt} {@link AppListPageKt} instead.
*/
@Deprecated(forRemoval = true)
public class ApplicationViewHolder extends RecyclerView.ViewHolder {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
index 216ce47..82e987e 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
+++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
@@ -64,9 +64,11 @@
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
+import com.android.settings.spa.app.specialaccess.LongBackgroundTasksAppListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
+import com.android.settings.spa.app.specialaccess.TurnScreenOnAppsAppListProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.spa.app.storage.StorageAppListPageProvider
import com.android.settings.spa.notification.AppListNotificationsPageProvider
@@ -120,6 +122,8 @@
LIST_TYPE_MAIN -> AllAppListPageProvider.name
LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
LIST_TYPE_USER_ASPECT_RATIO_APPS -> UserAspectRatioAppsPageProvider.name
+ LIST_TYPE_LONG_BACKGROUND_TASKS -> LongBackgroundTasksAppListProvider.getAppListRoute()
+ LIST_TYPE_TURN_SCREEN_ON -> TurnScreenOnAppsAppListProvider.getAppListRoute()
// TODO(b/292165031) enable once sorting is supported
//LIST_TYPE_STORAGE -> StorageAppListPageProvider.Apps.name
//LIST_TYPE_GAMES -> StorageAppListPageProvider.Games.name
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
index ebf1543..dc8dfb6 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
@@ -70,8 +70,7 @@
break;
case Intent.ACTION_TIME_CHANGED:
Log.d(TAG, "refresh job and clear all data from action=" + action);
- DatabaseUtils.clearAll(context);
- PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ false);
+ DatabaseUtils.clearDataAfterTimeChangedIfNeeded(context);
break;
default:
Log.w(TAG, "receive unsupported action=" + action);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index 1a226fd..c298afa 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -72,8 +72,6 @@
private static final String TAG = "DataProcessManager";
private static final List<BatteryEventType> POWER_CONNECTION_EVENTS =
List.of(BatteryEventType.POWER_CONNECTED, BatteryEventType.POWER_DISCONNECTED);
- private static final List<BatteryEventType> BATTERY_LEVEL_RECORD_EVENTS =
- List.of(BatteryEventType.FULL_CHARGED, BatteryEventType.EVEN_HOUR);
// For testing only.
@VisibleForTesting
@@ -575,7 +573,7 @@
final List<BatteryEvent> batteryLevelRecordEvents =
DatabaseUtils.getBatteryEvents(
context, Calendar.getInstance(), lastFullChargeTime,
- BATTERY_LEVEL_RECORD_EVENTS);
+ DatabaseUtils.BATTERY_LEVEL_RECORD_EVENTS);
final long startTimestamp = batteryLevelRecordEvents.isEmpty()
? lastFullChargeTime : batteryLevelRecordEvents.get(0).getTimestamp();
final BatteryLevelData batteryLevelData = getPeriodBatteryLevelData(context, handler,
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index e78d25c..d099843 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -53,6 +53,7 @@
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@@ -133,6 +134,9 @@
.authority(AUTHORITY)
.appendPath(BATTERY_USAGE_SLOT_TABLE)
.build();
+ /** A list of level record event types to access battery usage data. */
+ public static final List<BatteryEventType> BATTERY_LEVEL_RECORD_EVENTS =
+ List.of(BatteryEventType.FULL_CHARGED, BatteryEventType.EVEN_HOUR);
// For testing only.
@VisibleForTesting
@@ -408,6 +412,35 @@
});
}
+ /** Clears all data and jobs if current timestamp is out of the range of last recorded job. */
+ public static void clearDataAfterTimeChangedIfNeeded(Context context) {
+ AsyncTask.execute(() -> {
+ try {
+ final List<BatteryEvent> batteryLevelRecordEvents =
+ DatabaseUtils.getBatteryEvents(context, Calendar.getInstance(),
+ getLastFullChargeTime(context), BATTERY_LEVEL_RECORD_EVENTS);
+ final long lastRecordTimestamp = batteryLevelRecordEvents.isEmpty()
+ ? INVALID_TIMESTAMP : batteryLevelRecordEvents.get(0).getTimestamp();
+ final long nextRecordTimestamp =
+ TimestampUtils.getNextEvenHourTimestamp(lastRecordTimestamp);
+ final long currentTime = System.currentTimeMillis();
+ final boolean isOutOfTimeRange = lastRecordTimestamp == INVALID_TIMESTAMP
+ || currentTime < lastRecordTimestamp || currentTime > nextRecordTimestamp;
+ final String logInfo = String.format(Locale.ENGLISH,
+ "clear database = %b, current time = %d, last record time = %d",
+ isOutOfTimeRange, currentTime, lastRecordTimestamp);
+ Log.d(TAG, logInfo);
+ BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo);
+ if (isOutOfTimeRange) {
+ DatabaseUtils.clearAll(context);
+ PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false);
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "refreshDataAndJobIfNeededAfterTimeChanged() failed", e);
+ }
+ });
+ }
+
/** Returns the timestamp for 00:00 6 days before the calendar date. */
public static long getTimestampSixDaysAgo(Calendar calendar) {
Calendar startCalendar =
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
index 0a6de71..91ff4e8 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
@@ -43,7 +43,7 @@
/** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
@Query("SELECT * FROM BatteryEventEntity"
- + " WHERE timestamp > :timestamp AND batteryEventType IN (:batteryEventTypes)"
+ + " WHERE timestamp >= :timestamp AND batteryEventType IN (:batteryEventTypes)"
+ " ORDER BY timestamp DESC")
Cursor getAllAfter(long timestamp, List<Integer> batteryEventTypes);
diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
new file mode 100644
index 0000000..be72e56
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.datasaver;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.NetworkPolicyManager;
+
+import androidx.annotation.VisibleForTesting;
+
+/** A class to dynamically manage per apps {@link NetworkPolicyManager} POLICY_ flags. */
+public final class DynamicDenylistManager {
+
+ private static final String TAG = "DynamicDenylistManager";
+ private static final String PREF_KEY_MANUAL_DENY = "manual_denylist_preference";
+ private static final String PREF_KEY_DYNAMIC_DENY = "dynamic_denylist_preference";
+
+ private final Context mContext;
+ private final NetworkPolicyManager mNetworkPolicyManager;
+
+ private static DynamicDenylistManager sInstance;
+
+ /** @return a DynamicDenylistManager object */
+ public static DynamicDenylistManager getInstance(Context context) {
+ synchronized (DynamicDenylistManager.class) {
+ if (sInstance == null) {
+ sInstance = new DynamicDenylistManager(context);
+ }
+ return sInstance;
+ }
+ }
+
+ DynamicDenylistManager(Context context) {
+ mContext = context.getApplicationContext();
+ mNetworkPolicyManager = NetworkPolicyManager.from(mContext);
+ }
+
+ /** Update the target uid policy in {@link #getManualDenylistPref()}. */
+ public void updateManualDenylist(String uid, int policy) {
+ if (policy != NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND) {
+ getManualDenylistPref().edit().remove(uid).apply();
+ } else {
+ getManualDenylistPref().edit().putInt(uid, policy).apply();
+ }
+ }
+
+ /** Return true if the target uid is in {@link #getManualDenylistPref()}. */
+ public boolean isInManualDenylist(String uid) {
+ return getManualDenylistPref().contains(uid);
+ }
+
+ /** Clear all data in {@link #getManualDenylistPref()} */
+ public void clearManualDenylistPref() {
+ getManualDenylistPref().edit().clear().apply();
+ }
+
+ /** Clear all data in {@link #getDynamicDenylistPref()} */
+ public void clearDynamicDenylistPref() {
+ getDynamicDenylistPref().edit().clear().apply();
+ }
+
+ @VisibleForTesting
+ SharedPreferences getManualDenylistPref() {
+ return mContext.getSharedPreferences(PREF_KEY_MANUAL_DENY, Context.MODE_PRIVATE);
+ }
+
+ @VisibleForTesting
+ SharedPreferences getDynamicDenylistPref() {
+ return mContext.getSharedPreferences(PREF_KEY_DYNAMIC_DENY, Context.MODE_PRIVATE);
+ }
+}
diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java
index 194a08f..6706c6d 100644
--- a/src/com/android/settings/localepicker/AppLocalePickerActivity.java
+++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java
@@ -23,6 +23,7 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
@@ -45,12 +46,18 @@
import com.android.settings.applications.AppLocaleUtil;
import com.android.settings.applications.appinfo.AppLocaleDetails;
import com.android.settings.core.SettingsBaseActivity;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
public class AppLocalePickerActivity extends SettingsBaseActivity
implements LocalePickerWithRegion.LocaleSelectedListener, MenuItem.OnActionExpandListener {
private static final String TAG = AppLocalePickerActivity.class.getSimpleName();
private static final String CHANNEL_ID_SUGGESTION = "suggestion";
private static final String CHANNEL_ID_SUGGESTION_TO_USER = "Locale suggestion";
+ private static final int SIM_LOCALE = 1 << 0;
+ private static final int SYSTEM_LOCALE = 1 << 1;
+ private static final int APP_LOCALE = 1 << 2;
+ private static final int IME_LOCALE = 1 << 3;
static final String EXTRA_APP_LOCALE = "app_locale";
static final String EXTRA_NOTIFICATION_ID = "notification_id";
@@ -59,6 +66,7 @@
private AppLocaleDetails mAppLocaleDetails;
private View mAppLocaleDetailContainer;
private NotificationController mNotificationController;
+ private MetricsFeatureProvider mMetricsFeatureProvider;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -84,6 +92,7 @@
setTitle(R.string.app_locale_picker_title);
getActionBar().setDisplayHomeAsUpEnabled(true);
+ mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
mNotificationController = NotificationController.getInstance(this);
mLocalePickerWithRegion = LocalePickerWithRegion.createLanguagePicker(
@@ -113,6 +122,7 @@
if (localeInfo == null || localeInfo.getLocale() == null || localeInfo.isSystemLocale()) {
setAppDefaultLocale("");
} else {
+ logLocaleSource(localeInfo);
setAppDefaultLocale(localeInfo.getLocale().toLanguageTag());
broadcastAppLocaleChange(localeInfo);
}
@@ -268,4 +278,32 @@
return false;
}
+
+ private void logLocaleSource(LocaleStore.LocaleInfo localeInfo) {
+ if (!localeInfo.isSuggested() || localeInfo.isAppCurrentLocale()) {
+ return;
+ }
+ int localeSource = 0;
+ if (hasSuggestionType(localeInfo,
+ LocaleStore.LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE)) {
+ localeSource |= SYSTEM_LOCALE;
+ }
+ if (hasSuggestionType(localeInfo,
+ LocaleStore.LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE)) {
+ localeSource |= APP_LOCALE;
+ }
+ if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE)) {
+ localeSource |= IME_LOCALE;
+ }
+ if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_SIM)) {
+ localeSource |= SIM_LOCALE;
+ }
+ mMetricsFeatureProvider.action(this,
+ SettingsEnums.ACTION_CHANGE_APP_LANGUAGE_FROM_SUGGESTED, localeSource);
+ }
+
+ private static boolean hasSuggestionType(LocaleStore.LocaleInfo localeInfo,
+ int suggestionType) {
+ return localeInfo.isSuggestionOfType(suggestionType);
+ }
}
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index bfe0749..43fc9cf 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -16,6 +16,7 @@
package com.android.settings.localepicker;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
@@ -38,6 +39,7 @@
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocaleStore;
import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.shortcut.ShortcutsUpdateTask;
import java.text.NumberFormat;
@@ -225,6 +227,12 @@
"Negative position in onItemMove %d -> %d", fromPosition, toPosition));
}
+ if (fromPosition != toPosition) {
+ FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
+ .action(mContext, SettingsEnums.ACTION_REORDER_LANGUAGE,
+ mDragLocale.getLocale().toLanguageTag() + " move to " + toPosition);
+ }
+
notifyItemChanged(fromPosition); // to update the numbers
notifyItemChanged(toPosition);
notifyItemMoved(fromPosition, toPosition);
@@ -263,6 +271,9 @@
for (int i = itemCount - 1; i >= 0; i--) {
localeInfo = mFeedItemList.get(i);
if (localeInfo.getChecked()) {
+ FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
+ .action(mContext, SettingsEnums.ACTION_REMOVE_LANGUAGE,
+ localeInfo.getLocale().toLanguageTag());
mFeedItemList.remove(i);
}
}
diff --git a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
index a639c9d..b962b9e 100644
--- a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
+++ b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.localepicker;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
@@ -24,8 +25,10 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.widget.FooterPreference;
/**
@@ -36,8 +39,11 @@
private static final String KEY_FOOTER_LANGUAGE_PICKER = "footer_languages_picker";
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+
public LocaleHelperPreferenceController(Context context) {
super(context);
+ mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
}
@Override
@@ -72,6 +78,7 @@
mContext.getString(R.string.link_locale_picker_footer_learn_more),
mContext.getClass().getName());
if (intent != null) {
+ mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_LANGUAGES_LEARN_MORE);
mContext.startActivity(intent);
} else {
Log.w(TAG, "HelpIntent is null");
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index bdda549..28f066a 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -224,6 +224,8 @@
localeInfo = mayAppendUnicodeTags(localeInfo, preferencesTags);
mAdapter.addLocale(localeInfo);
updateVisibilityOfRemoveMenu();
+ mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_ADD_LANGUAGE,
+ localeInfo.getLocale().toLanguageTag());
} else if (requestCode == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
localeInfo = mAdapter.getFeedItemList().get(0);
if (resultCode == Activity.RESULT_OK) {
diff --git a/src/com/android/settings/network/MobileNetworkListFragment.java b/src/com/android/settings/network/MobileNetworkListFragment.java
deleted file mode 100644
index 3de05af..0000000
--- a/src/com/android/settings/network/MobileNetworkListFragment.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network;
-
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.os.UserManager;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.network.telephony.MobileNetworkUtils;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.SearchIndexable;
-
-@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
-public class MobileNetworkListFragment extends DashboardFragment {
- private static final String LOG_TAG = "NetworkListFragment";
-
- private static final String KEY_ADD_SIM = "add_sim";
-
- @Override
- public void onResume() {
- super.onResume();
- // Disable the animation of the preference list
- final RecyclerView prefListView = getListView();
- if (prefListView != null) {
- prefListView.setItemAnimator(null);
- }
-
- findPreference(KEY_ADD_SIM).setVisible(MobileNetworkUtils.showEuiccSettings(getContext()));
- }
-
- @Override
- protected int getPreferenceScreenResId() {
- return R.xml.network_provider_sims_list;
- }
-
- @Override
- protected String getLogTag() {
- return LOG_TAG;
- }
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.MOBILE_NETWORK_LIST;
- }
-
- public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.network_provider_sims_list) {
-
- @Override
- protected boolean isPageSearchEnabled(Context context) {
- return SubscriptionUtil.isSimHardwareVisible(context) &&
- context.getSystemService(UserManager.class).isAdminUser();
- }
- };
-}
diff --git a/src/com/android/settings/network/MobileNetworkListFragment.kt b/src/com/android/settings/network/MobileNetworkListFragment.kt
new file mode 100644
index 0000000..5000afd
--- /dev/null
+++ b/src/com/android/settings/network/MobileNetworkListFragment.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.os.Bundle
+import android.provider.Settings
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.SettingsPreferenceFragment
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.network.telephony.MobileNetworkUtils
+import com.android.settings.search.BaseSearchIndexProvider
+import com.android.settings.utils.observeSettingsGlobalBoolean
+import com.android.settingslib.search.SearchIndexable
+import com.android.settingslib.spaprivileged.framework.common.userManager
+
+@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv())
+class MobileNetworkListFragment : DashboardFragment() {
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ observeAirplaneModeAndFinishIfOn()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ // Disable the animation of the preference list
+ listView.itemAnimator = null
+
+ findPreference<Preference>(KEY_ADD_SIM)!!.isVisible =
+ MobileNetworkUtils.showEuiccSettings(context)
+ }
+
+ override fun getPreferenceScreenResId() = R.xml.network_provider_sims_list
+
+ override fun getLogTag() = LOG_TAG
+
+ override fun getMetricsCategory() = SettingsEnums.MOBILE_NETWORK_LIST
+
+ companion object {
+ private const val LOG_TAG = "NetworkListFragment"
+ private const val KEY_ADD_SIM = "add_sim"
+
+ @JvmStatic
+ fun SettingsPreferenceFragment.observeAirplaneModeAndFinishIfOn() {
+ requireContext().observeSettingsGlobalBoolean(
+ name = Settings.Global.AIRPLANE_MODE_ON,
+ lifecycle = viewLifecycleOwner.lifecycle,
+ ) { isAirplaneModeOn: Boolean ->
+ if (isAirplaneModeOn) {
+ finish()
+ }
+ }
+ }
+
+ @JvmField
+ val SEARCH_INDEX_DATA_PROVIDER = SearchIndexProvider()
+
+ @VisibleForTesting
+ class SearchIndexProvider : BaseSearchIndexProvider(R.xml.network_provider_sims_list) {
+ public override fun isPageSearchEnabled(context: Context): Boolean =
+ SubscriptionUtil.isSimHardwareVisible(context) &&
+ context.userManager.isAdminUser
+ }
+ }
+}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index afc1b7e..7e290b8 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -16,6 +16,8 @@
package com.android.settings.network.telephony;
+import static com.android.settings.network.MobileNetworkListFragment.observeAirplaneModeAndFinishIfOn;
+
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -31,7 +33,10 @@
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -327,6 +332,12 @@
}
@Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ observeAirplaneModeAndFinishIfOn(this);
+ }
+
+ @Override
public void onResume() {
super.onResume();
mMobileNetworkRepository.addRegister(this, this, mSubId);
@@ -361,11 +372,6 @@
super.onPause();
}
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
@VisibleForTesting
void onRestoreInstance(Bundle icicle) {
if (icicle != null) {
diff --git a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
index 2830024..200a47b 100644
--- a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
@@ -39,13 +39,18 @@
import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.flags.Flags;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.RestrictedSwitchPreference;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Populates the PreferenceCategory with notification channels associated with the given app.
@@ -61,6 +66,9 @@
private RestrictedSwitchPreference mAllNotificationsToggle;
private PreferenceCategory mPreferenceCategory;
private List<NotificationChannel> mChannels = new ArrayList<>();
+ private Set<String> mDuplicateChannelNames = new HashSet<>();
+ private Map<NotificationChannel, String> mChannelGroupNames =
+ new HashMap<NotificationChannel, String>();
public AppChannelsBypassingDndPreferenceController(
Context context,
@@ -135,10 +143,20 @@
List<NotificationChannel> newChannelList = new ArrayList<>();
List<NotificationChannelGroup> mChannelGroupList = mBackend.getGroups(mAppRow.pkg,
mAppRow.uid).getList();
+ Set<String> allChannelNames = new HashSet<>();
for (NotificationChannelGroup channelGroup : mChannelGroupList) {
for (NotificationChannel channel : channelGroup.getChannels()) {
if (!isConversation(channel)) {
newChannelList.add(channel);
+ if (Flags.dedupeDndSettingsChannels()) {
+ mChannelGroupNames.put(channel, channelGroup.getName().toString());
+ // Check if channel name is unique on this page; if not, save it.
+ if (allChannelNames.contains(channel.getName())) {
+ mDuplicateChannelNames.add(channel.getName().toString());
+ } else {
+ allChannelNames.add(channel.getName().toString());
+ }
+ }
}
}
}
@@ -172,6 +190,17 @@
&& isChannelConfigurable(channel)
&& showNotification(channel));
channelPreference.setTitle(BidiFormatter.getInstance().unicodeWrap(channel.getName()));
+ if (Flags.dedupeDndSettingsChannels()) {
+ // If the channel shares its name with another channel, set group name as summary
+ // to disambiguate in the list.
+ if (mDuplicateChannelNames.contains(channel.getName().toString())
+ && mChannelGroupNames.containsKey(channel)
+ && mChannelGroupNames.get(channel) != null
+ && !mChannelGroupNames.get(channel).isEmpty()) {
+ channelPreference.setSummary(BidiFormatter.getInstance().unicodeWrap(
+ mChannelGroupNames.get(channel)));
+ }
+ }
channelPreference.setChecked(showNotificationInDnd(channel));
channelPreference.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
diff --git a/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java b/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java
index 03a59de..d509d2e 100644
--- a/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java
+++ b/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java
@@ -16,6 +16,7 @@
package com.android.settings.regionalpreferences;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
@@ -57,4 +58,9 @@
protected String[] getUnitValues() {
return mContext.getResources().getStringArray(R.array.first_day_of_week);
}
+
+ @Override
+ protected int getMetricsActionKey() {
+ return SettingsEnums.ACTION_SET_FIRST_DAY_OF_WEEK;
+ }
}
diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
index 2f2bf76..ac0e7ee 100644
--- a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
+++ b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
@@ -22,16 +22,20 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.TickButtonPreference;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/** A base controller for handling all regional preferences controllers. */
public abstract class RegionalPreferenceListBasePreferenceController extends
BasePreferenceController {
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
private PreferenceCategory mPreferenceCategory;
public RegionalPreferenceListBasePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
+ mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
}
@Override
@@ -61,6 +65,8 @@
RegionalPreferencesDataUtils.savePreference(mContext, getExtensionTypes(),
item.equals(RegionalPreferencesDataUtils.DEFAULT_VALUE)
? null : item);
+ mMetricsFeatureProvider.action(mContext, getMetricsActionKey(),
+ getPreferenceTitle(value) + " > " + getPreferenceTitle(item));
return true;
});
pref.setSelected(!value.isEmpty() && item.equals(value));
@@ -90,4 +96,8 @@
protected abstract String getExtensionTypes();
protected abstract String[] getUnitValues();
+
+ protected abstract int getMetricsActionKey();
+
+
}
diff --git a/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java b/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java
index c51ca71..91ab1a2 100644
--- a/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java
+++ b/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java
@@ -16,6 +16,7 @@
package com.android.settings.regionalpreferences;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
@@ -55,4 +56,9 @@
protected String[] getUnitValues() {
return mContext.getResources().getStringArray(R.array.temperature_units);
}
+
+ @Override
+ protected int getMetricsActionKey() {
+ return SettingsEnums.ACTION_SET_TEMPERATURE_UNIT;
+ }
}
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 40cc9a2..6b96460 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -30,12 +30,15 @@
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
+import com.android.settings.spa.app.specialaccess.LongBackgroundTasksAppListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
+import com.android.settings.spa.app.specialaccess.TurnScreenOnAppsAppListProvider
import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
+import com.android.settings.spa.app.specialaccess.VoiceActivationAppsListProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.spa.app.storage.StorageAppListPageProvider
import com.android.settings.spa.core.instrumentation.SpaLogProvider
@@ -66,8 +69,11 @@
PictureInPictureListProvider,
InstallUnknownAppsListProvider,
AlarmsAndRemindersAppListProvider,
+ VoiceActivationAppsListProvider,
WifiControlAppListProvider,
NfcTagAppsSettingsProvider,
+ LongBackgroundTasksAppListProvider,
+ TurnScreenOnAppsAppListProvider,
)
}
diff --git a/src/com/android/settings/spa/about/AboutPhone.kt b/src/com/android/settings/spa/about/AboutPhone.kt
index 7343da0..5f9aa97 100644
--- a/src/com/android/settings/spa/about/AboutPhone.kt
+++ b/src/com/android/settings/spa/about/AboutPhone.kt
@@ -29,7 +29,6 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -56,7 +55,7 @@
val deviceNamePresenter = remember { DeviceNamePresenter(context) }
Preference(object : PreferenceModel {
override val title = stringResource(R.string.about_settings)
- override val summary = deviceNamePresenter.deviceName.toState()
+ override val summary = { deviceNamePresenter.deviceName }
override val onClick = navigator(name)
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.PermDeviceInformation)
diff --git a/src/com/android/settings/spa/about/DeviceName.kt b/src/com/android/settings/spa/about/DeviceName.kt
index c481e32..86a9512 100644
--- a/src/com/android/settings/spa/about/DeviceName.kt
+++ b/src/com/android/settings/spa/about/DeviceName.kt
@@ -24,7 +24,6 @@
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.deviceinfo.DeviceNamePreferenceController
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
import com.android.settingslib.spa.widget.preference.Preference
@@ -48,7 +47,7 @@
Preference(object : PreferenceModel {
override val title =
stringResource(R.string.my_device_info_device_name_preference_title)
- override val summary = deviceNamePresenter.deviceName.toState()
+ override val summary = { deviceNamePresenter.deviceName }
override val onClick = dialogPresenter::open
})
diff --git a/src/com/android/settings/spa/app/AllAppList.kt b/src/com/android/settings/spa/app/AllAppList.kt
index 383a0e8..5b13211 100644
--- a/src/com/android/settings/spa/app/AllAppList.kt
+++ b/src/com/android/settings/spa/app/AllAppList.kt
@@ -21,8 +21,6 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
@@ -116,7 +114,7 @@
option: Int,
recordListFlow: Flow<List<AppRecordWithSize>>,
): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem(
- when (SpinnerItem.values().getOrNull(option)) {
+ when (SpinnerItem.entries.getOrNull(option)) {
SpinnerItem.Enabled -> ({ it.app.enabled && !it.app.isInstantApp })
SpinnerItem.Disabled -> isDisabled
SpinnerItem.Instant -> isInstant
@@ -130,22 +128,20 @@
private val isInstant: (AppRecordWithSize) -> Boolean = { it.app.isInstantApp }
@Composable
- override fun getSummary(option: Int, record: AppRecordWithSize): State<String> {
+ override fun getSummary(option: Int, record: AppRecordWithSize): () -> String {
val storageSummary = record.app.getStorageSummary()
- return remember {
- derivedStateOf {
- storageSummary.value +
- when {
- !record.app.installed && !record.app.isArchived -> {
- System.lineSeparator() + context.getString(R.string.not_installed)
- }
- isDisabled(record) -> {
- System.lineSeparator() +
- context.getString(com.android.settingslib.R.string.disabled)
- }
- else -> ""
- }
+ return {
+ val summaryList = mutableListOf(storageSummary.value)
+ when {
+ !record.app.installed && !record.app.isArchived -> {
+ summaryList += context.getString(R.string.not_installed)
+ }
+
+ isDisabled(record) -> {
+ summaryList += context.getString(com.android.settingslib.R.string.disabled)
+ }
}
+ summaryList.joinToString(separator = System.lineSeparator())
}
}
diff --git a/src/com/android/settings/spa/app/AppsMain.kt b/src/com/android/settings/spa/app/AppsMain.kt
index 2dea9c5..83b3080 100644
--- a/src/com/android/settings/spa/app/AppsMain.kt
+++ b/src/com/android/settings/spa/app/AppsMain.kt
@@ -30,7 +30,6 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -54,10 +53,10 @@
fun buildInjectEntry() =
SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
+ val summary = stringResource(R.string.app_and_notification_dashboard_summary)
Preference(object : PreferenceModel {
override val title = stringResource(R.string.apps_dashboard_title)
- override val summary =
- stringResource(R.string.app_and_notification_dashboard_summary).toState()
+ override val summary = { summary }
override val onClick = navigator(name)
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Apps)
diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
index 61098e8..96884be 100644
--- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
+++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -40,11 +41,12 @@
val presenter = remember { UserAspectRatioAppPresenter(context, app) }
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.aspect_ratio_experimental_title)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
+ override val summary = { summary }
override val onClick = presenter::startActivity
})
}
@@ -75,4 +77,4 @@
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
index deea745..5af29ef 100644
--- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
+++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
@@ -28,7 +28,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -41,15 +41,14 @@
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.util.asyncMap
import com.android.settingslib.spa.framework.util.filterItem
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.illustration.Illustration
import com.android.settingslib.spa.widget.illustration.IllustrationModel
import com.android.settingslib.spa.widget.illustration.ResourceType
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.ui.SettingsBody
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -80,12 +79,14 @@
@Composable
@VisibleForTesting
- fun EntryItem() =
+ fun EntryItem() {
+ val summary = getSummary()
Preference(object : PreferenceModel {
override val title = stringResource(R.string.aspect_ratio_experimental_title)
- override val summary = getSummary().toState()
+ override val summary = { summary }
override val onClick = navigator(name)
})
+ }
@VisibleForTesting
fun buildInjectEntry() = SettingsEntryBuilder
@@ -177,7 +178,7 @@
option: Int,
recordListFlow: Flow<List<UserAspectRatioAppListItemModel>>
): Flow<List<UserAspectRatioAppListItemModel>> = recordListFlow.filterItem(
- when (SpinnerItem.values().getOrNull(option)) {
+ when (SpinnerItem.entries.getOrNull(option)) {
SpinnerItem.Suggested -> ({ it.canDisplay && it.suggested })
SpinnerItem.Overridden -> ({ it.userOverride != USER_MIN_ASPECT_RATIO_UNSET })
else -> ({ it.canDisplay })
@@ -185,13 +186,15 @@
)
@Composable
- override fun getSummary(option: Int, record: UserAspectRatioAppListItemModel) : State<String> =
- remember(record.userOverride) {
+ override fun getSummary(option: Int, record: UserAspectRatioAppListItemModel): () -> String {
+ val summary by remember(record.userOverride) {
flow {
emit(userAspectRatioManager.getUserMinAspectRatioEntry(record.userOverride,
record.app.packageName))
}.flowOn(Dispatchers.IO)
}.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder))
+ return { summary }
+ }
private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try {
packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
diff --git a/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt b/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
index 34272d4..31e068c 100644
--- a/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
@@ -24,6 +24,7 @@
import android.os.Bundle
import android.util.Log
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
@@ -52,11 +53,12 @@
val presenter = remember { AppAllServicesPresenter(context, app, coroutineScope) }
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.app_info_all_services_label)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
+ override val summary = { summary }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
index 7dd78a9..c707b44b 100644
--- a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
@@ -21,7 +21,6 @@
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -91,16 +90,17 @@
}
}
- val enabled = derivedStateOf { batteryDiffEntryState is LoadingState.Done }
+ val enabled = { batteryDiffEntryState is LoadingState.Done }
- val summary = derivedStateOf<String> {
- if (!app.installed) return@derivedStateOf ""
- batteryDiffEntryState.let { batteryDiffEntryState ->
- when (batteryDiffEntryState) {
- is LoadingState.Loading -> context.getString(R.string.summary_placeholder)
- is LoadingState.Done -> batteryDiffEntryState.result.getSummary()
+ val summary = {
+ if (app.installed) {
+ batteryDiffEntryState.let { batteryDiffEntryState ->
+ when (batteryDiffEntryState) {
+ is LoadingState.Loading -> context.getString(R.string.summary_placeholder)
+ is LoadingState.Done -> batteryDiffEntryState.result.getSummary()
+ }
}
- }
+ } else ""
}
private fun BatteryDiffEntry?.getSummary(): String =
@@ -155,7 +155,7 @@
}
private sealed class LoadingState<out T> {
- object Loading : LoadingState<Nothing>()
+ data object Loading : LoadingState<Nothing>()
data class Done<T>(val result: T) : LoadingState<T>()
diff --git a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
index ceb3986..057f911 100644
--- a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
@@ -20,6 +20,7 @@
import android.content.pm.ApplicationInfo
import android.net.NetworkTemplate
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
@@ -34,7 +35,6 @@
import com.android.settings.datausage.lib.INetworkTemplates
import com.android.settings.datausage.lib.NetworkTemplates
import com.android.settings.datausage.lib.NetworkTemplates.getTitleResId
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.hasFlag
@@ -64,16 +64,17 @@
}
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.computing_size),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(
presenter.titleResIdFlow.collectAsStateWithLifecycle(
initialValue = R.string.summary_placeholder,
).value
)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.computing_size),
- )
- override val enabled = presenter.isEnabled().toState()
+ override val summary = { summary }
+ override val enabled = { presenter.isEnabled() }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index ed912c3..3b7f579 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -32,6 +32,7 @@
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavType
import androidx.navigation.navArgument
+import com.android.settings.flags.Flags
import com.android.settings.R
import com.android.settings.applications.AppInfoBase
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
@@ -42,6 +43,7 @@
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
+import com.android.settings.spa.app.specialaccess.VoiceActivationAppsListProvider
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.LifecycleEffect
import com.android.settingslib.spa.framework.compose.navigator
@@ -160,6 +162,9 @@
InstallUnknownAppsListProvider.InfoPageEntryItem(app)
InteractAcrossProfilesDetailsPreference(app)
AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app)
+ if (Flags.enableVoiceActivationAppsInSettings()) {
+ VoiceActivationAppsListProvider.InfoPageEntryItem(app)
+ }
}
Category(title = stringResource(R.string.app_install_details_group_title)) {
diff --git a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
index 5a348f7..62e714a 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
@@ -19,15 +19,16 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.applications.AppStoreUtil
import com.android.settingslib.applications.AppUtils
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.framework.common.asUser
@@ -49,13 +50,14 @@
val presenter = remember { AppInstallerInfoPresenter(context, app, coroutineScope) }
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
+ val enabled by presenter.enabledFlow.collectAsStateWithLifecycle(initialValue = false)
Preference(object : PreferenceModel {
override val title = stringResource(R.string.app_install_details_title)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
- override val enabled =
- presenter.enabledFlow.collectAsStateWithLifecycle(initialValue = false)
+ override val summary = { summary }
+ override val enabled = { enabled }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt b/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt
index 2d6fbb6..3b2aace 100644
--- a/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt
@@ -23,15 +23,16 @@
import android.content.pm.PackageManager.ResolveInfoFlags
import android.net.Uri
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.applications.AppInfoBase
import com.android.settings.applications.AppLocaleUtil
import com.android.settings.applications.appinfo.AppLocaleDetails
import com.android.settings.localepicker.AppLocalePickerActivity
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.userHandle
@@ -46,11 +47,12 @@
val presenter = remember { AppLocalePresenter(context, app) }
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.app_locale_preference_title)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
+ override val summary = { summary }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt b/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt
index 45033e7..28527c1 100644
--- a/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -29,7 +30,6 @@
import com.android.settings.spa.notification.AppNotificationRepository
import com.android.settings.spa.notification.IAppNotificationRepository
import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.installed
@@ -43,17 +43,17 @@
repository: IAppNotificationRepository = rememberContext(::AppNotificationRepository),
) {
val context = LocalContext.current
- val summaryFlow = remember(app) {
+ val summary by remember(app) {
flow {
emit(repository.getNotificationSummary(app))
- }.flowOn(Dispatchers.IO)
- }
+ }.flowOn(Dispatchers.Default)
+ }.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder)
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.notifications_label)
- override val summary = summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder)
- )
- override val enabled = stateOf(app.installed)
+ override val summary = { summary }
+ override val enabled = { app.installed }
override val onClick = { navigateToAppNotificationSettings(context, app) }
})
}
@@ -65,4 +65,4 @@
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt b/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
index 757ddc2..aae9569 100644
--- a/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -28,7 +29,6 @@
import com.android.settings.applications.intentpicker.AppLaunchSettings
import com.android.settings.applications.intentpicker.IntentPickerUtils
import com.android.settingslib.applications.AppUtils
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.framework.common.asUser
@@ -46,12 +46,13 @@
val presenter = remember(app) { AppOpenByDefaultPresenter(context, app) }
if (remember(presenter) { !presenter.isAvailable() }) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.launch_by_default)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
- override val enabled = stateOf(presenter.isEnabled())
+ override val summary = { summary }
+ override val enabled = { presenter.isEnabled() }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt b/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt
index ad666dc..ec1780f 100644
--- a/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt
@@ -22,7 +22,6 @@
import android.content.pm.ApplicationInfo
import android.util.Log
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@@ -52,8 +51,8 @@
model = remember {
object : PreferenceModel {
override val title = context.getString(R.string.permissions_label)
- override val summary = derivedStateOf { summaryState.value.summary }
- override val enabled = derivedStateOf { summaryState.value.enabled }
+ override val summary = { summaryState.value.summary }
+ override val enabled = { summaryState.value.enabled }
override val onClick = { startManagePermissionsActivity(context, app) }
}
},
diff --git a/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt b/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
index e8b1018..2b96454 100644
--- a/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
@@ -19,9 +19,6 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
@@ -47,12 +44,13 @@
}
@Composable
-private fun getSummary(context: Context, app: ApplicationInfo): State<String> {
+private fun getSummary(context: Context, app: ApplicationInfo): () -> String {
val sizeState = app.getStorageSize()
- return remember {
- derivedStateOf {
- val size = sizeState.value
- if (size.isBlank()) return@derivedStateOf context.getString(R.string.computing_size)
+ return {
+ val size = sizeState.value
+ if (size.isBlank()) {
+ context.getString(R.string.computing_size)
+ } else {
val storageType = context.getString(
when (app.hasFlag(ApplicationInfo.FLAG_EXTERNAL_STORAGE)) {
true -> R.string.storage_type_external
diff --git a/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt b/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
index 21b3d73..7ba61dc 100644
--- a/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager.ResolveInfoFlags
import android.provider.Settings
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@@ -29,7 +30,6 @@
import androidx.lifecycle.liveData
import com.android.settings.R
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.hasFlag
@@ -43,12 +43,13 @@
val presenter = remember { AppTimeSpentPresenter(context, app) }
if (!presenter.isAvailable()) return
+ val summary by presenter.summaryLiveData.observeAsState(
+ initial = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.time_spent_in_app_pref_title)
- override val summary = presenter.summaryLiveData.observeAsState(
- initial = stringResource(R.string.summary_placeholder),
- )
- override val enabled = stateOf(presenter.isEnabled())
+ override val summary = { summary }
+ override val enabled = { presenter.isEnabled() }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt b/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt
index 74c0aa4..51f6845 100644
--- a/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt
@@ -22,6 +22,7 @@
import android.content.pm.ApplicationInfo
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@@ -57,11 +58,12 @@
if (remember(presenter) { !presenter.isAvailable() }) return
if (presenter.isVisible().observeAsState().value != true) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(shortcut.titleResId)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
+ override val summary = { summary }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
index f62a3be..77d68b5 100644
--- a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
@@ -31,12 +31,11 @@
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED
import com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS
import com.android.settingslib.spa.framework.compose.OverridableFlow
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.framework.common.appHibernationManager
@@ -44,12 +43,12 @@
import com.android.settingslib.spaprivileged.framework.common.asUser
import com.android.settingslib.spaprivileged.framework.common.permissionControllerManager
import com.android.settingslib.spaprivileged.model.app.userHandle
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
@Composable
fun HibernationSwitchPreference(app: ApplicationInfo) {
@@ -62,7 +61,7 @@
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = context.getString(R.string.unused_apps_switch)
- override val summary = stateOf(context.getString(R.string.unused_apps_switch_summary))
+ override val summary = { context.getString(R.string.unused_apps_switch_summary) }
override val changeable = isEligibleState
override val checked = derivedStateOf {
diff --git a/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt b/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
index 7b9480d..9c3ec97 100644
--- a/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
@@ -32,9 +32,9 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.Utils
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -53,11 +53,12 @@
val presenter = remember { InstantAppDomainsPresenter(context, app) }
var openDialog by rememberSaveable { mutableStateOf(false) }
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.app_launch_supported_domain_urls_title)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
+ override val summary = { summary }
override val onClick = { openDialog = true }
})
diff --git a/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt b/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
index 12f6907..905e057 100644
--- a/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
@@ -19,13 +19,14 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetails
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.framework.common.crossProfileApps
@@ -39,11 +40,12 @@
val presenter = remember { InteractAcrossProfilesDetailsPresenter(context, app) }
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+ val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+ initialValue = stringResource(R.string.summary_placeholder),
+ )
Preference(object : PreferenceModel {
override val title = stringResource(R.string.interact_across_profiles_title)
- override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
- initialValue = stringResource(R.string.summary_placeholder),
- )
+ override val summary = { summary }
override val onClick = presenter::startActivity
})
}
diff --git a/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt b/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt
index 6e0643b..89f473b 100644
--- a/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt
+++ b/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt
@@ -32,6 +32,7 @@
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -88,9 +89,10 @@
@Composable
fun EntryItem() {
if(featureIsDisabled) return
+ val summary by generatePreferenceSummary()
Preference(object : PreferenceModel {
override val title = stringResource(R.string.background_install_title)
- override val summary = generatePreferenceSummary()
+ override val summary = { summary }
override val onClick = navigator(name)
})
}
diff --git a/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksApps.kt b/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksApps.kt
new file mode 100644
index 0000000..3ba9b08
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksApps.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.settings.SettingsEnums
+import android.content.Context
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+
+object LongBackgroundTasksAppListProvider : TogglePermissionAppListProvider {
+ override val permissionType = "LongBackgroundTasksApps"
+ override fun createModel(context: Context) = LongBackgroundTasksAppsListModel(context)
+}
+
+class LongBackgroundTasksAppsListModel(context: Context) : AppOpPermissionListModel(context) {
+ override val pageTitleResId = R.string.long_background_tasks_title
+ override val switchTitleResId = R.string.long_background_tasks_switch_title
+ override val footerResId = R.string.long_background_tasks_footer_title
+ override val appOp = AppOpsManager.OP_RUN_USER_INITIATED_JOBS
+ override val permission = Manifest.permission.RUN_USER_INITIATED_JOBS
+ override val setModeByUid = true
+
+ override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
+ super.setAllowed(record, newAllowed)
+ logPermissionChange(newAllowed)
+ }
+
+ private fun logPermissionChange(newAllowed: Boolean) {
+ featureFactory.metricsFeatureProvider.action(
+ context,
+ SettingsEnums.ACTION_LONG_BACKGROUND_TASKS_TOGGLE,
+ if (newAllowed) 1 else 0
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
index b40e32b..fb05a38 100644
--- a/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
+++ b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
@@ -66,7 +66,10 @@
PictureInPictureListProvider,
InstallUnknownAppsListProvider,
AlarmsAndRemindersAppListProvider,
+ VoiceActivationAppsListProvider,
WifiControlAppListProvider,
+ LongBackgroundTasksAppListProvider,
+ TurnScreenOnAppsAppListProvider,
)
.map { it.buildAppListInjectEntry().setLink(fromPage = owner).build() }
}
diff --git a/src/com/android/settings/spa/app/specialaccess/TurnScreenOnApps.kt b/src/com/android/settings/spa/app/specialaccess/TurnScreenOnApps.kt
new file mode 100644
index 0000000..262acb7
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/TurnScreenOnApps.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.settings.SettingsEnums
+import android.content.Context
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+
+object TurnScreenOnAppsAppListProvider : TogglePermissionAppListProvider {
+ override val permissionType = "TurnScreenOnApps"
+ override fun createModel(context: Context) = TurnScreenOnAppsListModel(context)
+}
+
+class TurnScreenOnAppsListModel(context: Context) : AppOpPermissionListModel(context) {
+ override val pageTitleResId = com.android.settingslib.R.string.turn_screen_on_title
+ override val switchTitleResId = com.android.settingslib.R.string.allow_turn_screen_on
+ override val footerResId = com.android.settingslib.R.string.allow_turn_screen_on_description
+ override val appOp = AppOpsManager.OP_TURN_SCREEN_ON
+ override val permission = Manifest.permission.TURN_SCREEN_ON
+ override val setModeByUid = true
+
+ override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
+ super.setAllowed(record, newAllowed)
+ logPermissionChange(newAllowed)
+ }
+
+ private fun logPermissionChange(newAllowed: Boolean) {
+ featureFactory.metricsFeatureProvider.action(
+ context,
+ SettingsEnums.SETTINGS_MANAGE_TURN_SCREEN_ON,
+ if (newAllowed) 1 else 0
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
new file mode 100644
index 0000000..de5f3b7
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.res.Resources
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+
+
+object VoiceActivationAppsListProvider : TogglePermissionAppListProvider {
+ override val permissionType = "VoiceActivationApps"
+ override fun createModel(context: Context) = VoiceActivationAppsListModel(context)
+}
+
+class VoiceActivationAppsListModel(context: Context) : AppOpPermissionListModel(context) {
+ override val pageTitleResId = R.string.voice_activation_apps_title
+ override val switchTitleResId = R.string.permit_voice_activation_apps
+ override val footerResId = R.string.allow_voice_activation_apps_description
+ override val appOp = AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
+ override val permission = Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO
+ override val setModeByUid = true
+
+ override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
+ super.setAllowed(record, newAllowed)
+ logPermissionChange(newAllowed)
+ }
+
+ private fun logPermissionChange(newAllowed: Boolean) {
+ val category = when {
+ newAllowed -> SettingsEnums.APP_SPECIAL_PERMISSION_RECEIVE_SANDBOX_TRIGGER_AUDIO_ALLOW
+ else -> SettingsEnums.APP_SPECIAL_PERMISSION_RECEIVE_SANDBOX_TRIGGER_AUDIO_DENY
+ }
+ /**
+ * Leave the package string empty as we should not log the package names for the collected
+ * metrics.
+ */
+ FeatureFactory.featureFactory.metricsFeatureProvider.action(context, category, "")
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceController.kt b/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceController.kt
new file mode 100644
index 0000000..27d4b4b
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceController.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.content.Context
+import androidx.preference.Preference
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.flags.Flags
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+
+class VoiceActivationAppsPreferenceController(context: Context, preferenceKey: String) :
+ BasePreferenceController(context, preferenceKey) {
+ override fun getAvailabilityStatus() =
+ if (Flags.enableVoiceActivationAppsInSettings()) AVAILABLE
+ else CONDITIONALLY_UNAVAILABLE
+
+ override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+ if (preference.key == mPreferenceKey) {
+ mContext.startSpaActivity(VoiceActivationAppsListProvider.getAppListRoute())
+ return true
+ }
+ return false
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/storage/StorageAppList.kt b/src/com/android/settings/spa/app/storage/StorageAppList.kt
index 8fc3eb5..c33de33 100644
--- a/src/com/android/settings/spa/app/storage/StorageAppList.kt
+++ b/src/com/android/settings/spa/app/storage/StorageAppList.kt
@@ -22,7 +22,7 @@
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -121,13 +121,9 @@
): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it) }
@Composable
- override fun getSummary(option: Int, record: AppRecordWithSize): State<String> {
- val storageSummary = record.app.getStorageSummary()
- return remember {
- derivedStateOf {
- storageSummary.value
- }
- }
+ override fun getSummary(option: Int, record: AppRecordWithSize): () -> String {
+ val storageSummary by record.app.getStorageSummary()
+ return { storageSummary }
}
@Composable
diff --git a/src/com/android/settings/spa/development/UsageStatsListModel.kt b/src/com/android/settings/spa/development/UsageStatsListModel.kt
index 61c24ac..d27796d 100644
--- a/src/com/android/settings/spa/development/UsageStatsListModel.kt
+++ b/src/com/android/settings/spa/development/UsageStatsListModel.kt
@@ -22,10 +22,8 @@
import android.content.pm.ApplicationInfo
import android.text.format.DateUtils
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import com.android.settings.R
import com.android.settings.spa.development.UsageStatsListModel.SpinnerItem.Companion.toSpinnerItem
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.model.app.AppEntry
import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -55,7 +53,7 @@
}
override fun getSpinnerOptions(recordList: List<UsageStatsAppRecord>): List<SpinnerOption> =
- SpinnerItem.values().map {
+ SpinnerItem.entries.map {
SpinnerOption(
id = it.ordinal,
text = context.getString(it.stringResId),
@@ -77,7 +75,7 @@
}.then(super.getComparator(option))
@Composable
- override fun getSummary(option: Int, record: UsageStatsAppRecord): State<String>? {
+ override fun getSummary(option: Int, record: UsageStatsAppRecord): (() -> String)? {
val usageStats = record.usageStats ?: return null
val lastTimeUsed = DateUtils.formatSameDayTime(
usageStats.lastTimeUsed, now, DateFormat.MEDIUM, DateFormat.MEDIUM
@@ -85,7 +83,7 @@
val lastTimeUsedLine = "${context.getString(R.string.last_time_used_label)}: $lastTimeUsed"
val usageTime = DateUtils.formatElapsedTime(usageStats.totalTimeInForeground / 1000)
val usageTimeLine = "${context.getString(R.string.usage_time_label)}: $usageTime"
- return stateOf("$lastTimeUsedLine\n$usageTimeLine")
+ return { "$lastTimeUsedLine\n$usageTimeLine" }
}
private fun getUsageStats(): Map<String, UsageStats> {
@@ -101,7 +99,7 @@
AppName(R.string.usage_stats_sort_by_app_name);
companion object {
- fun Int.toSpinnerItem(): SpinnerItem = values()[this]
+ fun Int.toSpinnerItem(): SpinnerItem = entries[this]
}
}
}
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
index c6752b9..8f53698 100644
--- a/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
@@ -24,7 +24,6 @@
import androidx.core.os.bundleOf
import com.android.settings.core.SubSettingLauncher
import com.android.settings.development.compat.PlatformCompatDashboard
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.filterItem
import com.android.settingslib.spa.framework.util.mapItem
import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -53,8 +52,9 @@
}
@Composable
- override fun getSummary(option: Int, record: PlatformCompatAppRecord) =
- stateOf(record.app.packageName)
+ override fun getSummary(option: Int, record: PlatformCompatAppRecord): () -> String = {
+ record.app.packageName
+ }
@Composable
override fun AppListItemModel<PlatformCompatAppRecord>.AppItem() {
diff --git a/src/com/android/settings/spa/network/NetworkAndInternet.kt b/src/com/android/settings/spa/network/NetworkAndInternet.kt
index 777133e..f985237 100644
--- a/src/com/android/settings/spa/network/NetworkAndInternet.kt
+++ b/src/com/android/settings/spa/network/NetworkAndInternet.kt
@@ -34,7 +34,6 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -60,9 +59,10 @@
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
+ val summary = stringResource(getSummaryResId())
Preference(object : PreferenceModel {
override val title = stringResource(R.string.network_dashboard_title)
- override val summary = stringResource(getSummaryResId()).toState()
+ override val summary = { summary }
override val onClick = navigator(name)
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Wifi)
diff --git a/src/com/android/settings/spa/notification/AppListNotifications.kt b/src/com/android/settings/spa/notification/AppListNotifications.kt
index c1e5d64..00e4394 100644
--- a/src/com/android/settings/spa/notification/AppListNotifications.kt
+++ b/src/com/android/settings/spa/notification/AppListNotifications.kt
@@ -23,7 +23,6 @@
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.template.app.AppListPage
@@ -41,9 +40,10 @@
@Composable
fun EntryItem() {
+ val summary = stringResource(R.string.app_notification_field_summary)
Preference(object : PreferenceModel {
override val title = stringResource(R.string.app_notifications_title)
- override val summary = stringResource(R.string.app_notification_field_summary).toState()
+ override val summary = { summary }
override val onClick = navigator(name)
})
}
diff --git a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
index 0b9b676..692ffcb 100644
--- a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
+++ b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
@@ -27,7 +27,6 @@
import com.android.settings.applications.AppInfoBase
import com.android.settings.notification.app.AppNotificationSettings
import com.android.settings.spa.notification.SpinnerItem.Companion.toSpinnerItem
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.asyncFilter
import com.android.settingslib.spa.framework.util.asyncForEach
import com.android.settingslib.spa.widget.ui.SpinnerOption
@@ -91,16 +90,17 @@
}.then(super.getComparator(option))
@Composable
- override fun getSummary(option: Int, record: AppNotificationsRecord) = record.sentState?.let {
- when (option.toSpinnerItem()) {
- SpinnerItem.MostRecent -> stateOf(formatLastSent(it.lastSent))
- SpinnerItem.MostFrequent -> stateOf(repository.calculateFrequencySummary(it.sentCount))
- else -> null
+ override fun getSummary(option: Int, record: AppNotificationsRecord): (() -> String)? =
+ record.sentState?.let {
+ when (option.toSpinnerItem()) {
+ SpinnerItem.MostRecent -> ({ formatLastSent(it.lastSent) })
+ SpinnerItem.MostFrequent -> ({ repository.calculateFrequencySummary(it.sentCount) })
+ else -> null
+ }
}
- }
override fun getSpinnerOptions(recordList: List<AppNotificationsRecord>): List<SpinnerOption> =
- SpinnerItem.values().map {
+ SpinnerItem.entries.map {
SpinnerOption(
id = it.ordinal,
text = context.getString(it.stringResId),
@@ -145,6 +145,6 @@
TurnedOff(R.string.filter_notif_blocked_apps);
companion object {
- fun Int.toSpinnerItem(): SpinnerItem = values()[this]
+ fun Int.toSpinnerItem(): SpinnerItem = entries[this]
}
}
diff --git a/src/com/android/settings/spa/notification/NotificationMain.kt b/src/com/android/settings/spa/notification/NotificationMain.kt
index 305f201..b3c7a55 100644
--- a/src/com/android/settings/spa/notification/NotificationMain.kt
+++ b/src/com/android/settings/spa/notification/NotificationMain.kt
@@ -25,13 +25,12 @@
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
+import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.SettingsIcon
-import com.android.settingslib.spa.framework.common.createSettingsPage
object NotificationMainPageProvider : SettingsPageProvider {
override val name = "NotificationMain"
@@ -53,9 +52,10 @@
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
+ val summary = stringResource(R.string.notification_dashboard_summary)
Preference(object : PreferenceModel {
override val title = stringResource(R.string.configure_notification_settings)
- override val summary = stringResource(R.string.notification_dashboard_summary).toState()
+ override val summary = { summary }
override val onClick = navigator(name)
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Notifications)
diff --git a/src/com/android/settings/spa/system/AppLanguages.kt b/src/com/android/settings/spa/system/AppLanguages.kt
index b878aa7..d836a32 100644
--- a/src/com/android/settings/spa/system/AppLanguages.kt
+++ b/src/com/android/settings/spa/system/AppLanguages.kt
@@ -26,7 +26,6 @@
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -52,9 +51,10 @@
@Composable
fun EntryItem() {
+ val summary = stringResource(R.string.app_locale_picker_summary)
Preference(object : PreferenceModel {
override val title = stringResource(R.string.app_locales_picker_menu_title)
- override val summary = stringResource(R.string.app_locale_picker_summary).toState()
+ override val summary = { summary }
override val onClick = navigator(name)
})
}
diff --git a/src/com/android/settings/spa/system/AppLanguagesListModel.kt b/src/com/android/settings/spa/system/AppLanguagesListModel.kt
index 3413ff0..3573e25 100644
--- a/src/com/android/settings/spa/system/AppLanguagesListModel.kt
+++ b/src/com/android/settings/spa/system/AppLanguagesListModel.kt
@@ -23,7 +23,7 @@
import android.net.Uri
import android.os.UserHandle
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -79,12 +79,14 @@
) = recordListFlow.filterItem { it.isAppLocaleSupported }
@Composable
- override fun getSummary(option: Int, record: AppLanguagesRecord): State<String> =
- remember(record.app) {
+ override fun getSummary(option: Int, record: AppLanguagesRecord): () -> String {
+ val summary by remember(record.app) {
flow {
emit(getSummary(record.app))
}.flowOn(Dispatchers.IO)
}.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder))
+ return { summary }
+ }
private fun getSummary(app: ApplicationInfo): String =
AppLocaleDetails.getSummary(context, app).toString()
diff --git a/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt b/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt
index b5cd299..5c1038d 100644
--- a/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt
+++ b/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt
@@ -24,7 +24,6 @@
import com.android.settings.R
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -42,9 +41,10 @@
@Composable
fun EntryItem() {
+ val summary = stringResource(R.string.language_settings)
Preference(object : PreferenceModel {
override val title = stringResource(R.string.language_settings)
- override val summary = stringResource(R.string.language_settings).toState()
+ override val summary = { summary }
override val onClick = navigator(name)
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Language)
diff --git a/src/com/android/settings/spa/system/SystemMain.kt b/src/com/android/settings/spa/system/SystemMain.kt
index 04ae512..c9aa8cc 100644
--- a/src/com/android/settings/spa/system/SystemMain.kt
+++ b/src/com/android/settings/spa/system/SystemMain.kt
@@ -27,7 +27,6 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -53,9 +52,10 @@
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
+ val summary = stringResource(R.string.system_dashboard_summary)
Preference(object : PreferenceModel {
override val title = stringResource(R.string.header_category_system)
- override val summary = stringResource(R.string.system_dashboard_summary).toState()
+ override val summary = { summary }
override val onClick = navigator(name)
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Info)
diff --git a/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt b/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt
new file mode 100644
index 0000000..fdfbdb4
--- /dev/null
+++ b/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.utils
+
+import android.content.ContentResolver
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.provider.Settings
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+fun Context.observeSettingsGlobalBoolean(
+ name: String,
+ lifecycle: Lifecycle,
+ onChange: (newValue: Boolean) -> Unit,
+) {
+ val field by settingsGlobalBoolean(name)
+ val contentObserver = object : ContentObserver(Handler.getMain()) {
+ override fun onChange(selfChange: Boolean) {
+ onChange(field)
+ }
+ }
+ val uri = Settings.Global.getUriFor(name)
+ lifecycle.addObserver(object : DefaultLifecycleObserver {
+ override fun onStart(owner: LifecycleOwner) {
+ contentResolver.registerContentObserver(uri, false, contentObserver)
+ onChange(field)
+ }
+
+ override fun onStop(owner: LifecycleOwner) {
+ contentResolver.unregisterContentObserver(contentObserver)
+ }
+ })
+}
+
+fun Context.settingsGlobalBoolean(name: String): ReadWriteProperty<Any?, Boolean> =
+ SettingsGlobalBooleanDelegate(this, name)
+
+private class SettingsGlobalBooleanDelegate(context: Context, private val name: String) :
+ ReadWriteProperty<Any?, Boolean> {
+
+ private val contentResolver: ContentResolver = context.contentResolver
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean =
+ Settings.Global.getInt(contentResolver, name, 0) != 0
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
+ Settings.Global.putInt(contentResolver, name, if (value) 1 else 0)
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
index d98b0e7..b615163 100644
--- a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
@@ -21,21 +21,27 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.IActivityManager;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.RemoteException;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowActivityManager;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -56,6 +62,7 @@
private RadioWithImagePreference mRadioButtonPref;
private Context mContext;
private UserAspectRatioDetails mFragment;
+ private MetricsFeatureProvider mMetricsFeatureProvider;
@Before
public void setUp() {
@@ -67,6 +74,8 @@
when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager);
ShadowActivityManager.setService(mAm);
mRadioButtonPref = new RadioWithImagePreference(mContext);
+ final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+ mMetricsFeatureProvider = featureFactory.metricsFeatureProvider;
}
@Test
@@ -93,4 +102,31 @@
verify(mUserAspectRatioManager).setUserMinAspectRatio(
any(), anyInt(), anyInt());
}
+
+ @Test
+ public void onRadioButtonClicked_prefChange_logMetrics() throws NullPointerException {
+ // Default was already selected
+ mRadioButtonPref.setKey(KEY_PREF_DEFAULT);
+ mFragment.onRadioButtonClicked(mRadioButtonPref);
+ // Preference changed
+ mRadioButtonPref.setKey(KEY_PREF_3_2);
+ mFragment.onRadioButtonClicked(mRadioButtonPref);
+ InOrder inOrder = inOrder(mMetricsFeatureProvider);
+ // Check the old aspect ratio value is logged as having been unselected
+ inOrder.verify(mMetricsFeatureProvider)
+ .action(
+ eq(SettingsEnums.PAGE_UNKNOWN),
+ eq(SettingsEnums.ACTION_USER_ASPECT_RATIO_APP_DEFAULT_UNSELECTED),
+ eq(SettingsEnums.USER_ASPECT_RATIO_APP_INFO_SETTINGS),
+ any(),
+ anyInt());
+ // Check the new aspect ratio value is logged as having been selected
+ inOrder.verify(mMetricsFeatureProvider)
+ .action(
+ eq(SettingsEnums.PAGE_UNKNOWN),
+ eq(SettingsEnums.ACTION_USER_ASPECT_RATIO_3_2_SELECTED),
+ eq(SettingsEnums.USER_ASPECT_RATIO_APP_INFO_SETTINGS),
+ any(),
+ anyInt());
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java
index 8462867..3c3e3c3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java
@@ -149,4 +149,31 @@
mBatteryEventDao.clearAll();
assertThat(mBatteryEventDao.getAll()).isEmpty();
}
+
+ @Test
+ public void getAllAfter_filterTimestamp_returnExpectedResult() {
+ mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
+ .setTimestamp(100L)
+ .setBatteryEventType(1)
+ .setBatteryLevel(66)
+ .build());
+ mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
+ .setTimestamp(200L)
+ .setBatteryEventType(1)
+ .setBatteryLevel(88)
+ .build());
+
+ final Cursor cursor = mBatteryEventDao.getAllAfter(200L, List.of(1));
+ assertThat(cursor.getCount()).isEqualTo(1);
+ cursor.moveToFirst();
+ assertThat(cursor.getLong(cursor.getColumnIndex(KEY_TIMESTAMP)))
+ .isEqualTo(200L);
+ assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_EVENT_TYPE)))
+ .isEqualTo(1);
+ assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_LEVEL)))
+ .isEqualTo(88);
+
+ mBatteryEventDao.clearAll();
+ assertThat(mBatteryEventDao.getAll()).isEmpty();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
new file mode 100644
index 0000000..cdf1514
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.datasaver;
+
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class DynamicDenylistManagerTest {
+
+ private static final String FAKE_UID_1 = "package_uid_1";
+ private static final String FAKE_UID_2 = "package_uid_2";
+
+ private SharedPreferences mManualDenyListPref;
+ private SharedPreferences mDynamicDenyListPref;
+ private DynamicDenylistManager mDynamicDenylistManager;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application.getApplicationContext();
+ mDynamicDenylistManager = new DynamicDenylistManager(mContext);
+ mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref();
+ mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref();
+ }
+
+ @After
+ public void tearDown() {
+ mDynamicDenylistManager.clearManualDenylistPref();
+ mDynamicDenylistManager.clearDynamicDenylistPref();
+ }
+
+ @Test
+ public void getManualDenylistPref_isEmpty() {
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ }
+
+ @Test
+ public void getDynamicDenylistPref_isEmpty() {
+ assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+ }
+
+ @Test
+ public void getManualDenylistPref_initiated_containsExpectedValue() {
+ mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+ assertThat(mManualDenyListPref.getAll().size()).isEqualTo(1);
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ }
+
+ @Test
+ public void getDynamicDenylistPref_initiated_containsExpectedValue() {
+ mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+ assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+ assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+ }
+
+ @Test
+ public void updateManualDenylist_policyReject_addsUid() {
+ mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+
+ assertThat(mManualDenyListPref.getAll()).hasSize(1);
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ }
+
+ @Test
+ public void updateManualDenylist_policyNone_removesUid() {
+ mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+
+ mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_NONE);
+
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ }
+
+ @Test
+ public void updateManualDenylist_samePolicy_doNothing() {
+ mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+
+ mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+
+ assertThat(mManualDenyListPref.getAll()).hasSize(1);
+ }
+
+ @Test
+ public void isManualDenylist_returnsFalse() {
+ assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+ }
+
+ @Test
+ public void isManualDenylist_incorrectUid_returnsFalse() {
+ mManualDenyListPref.edit().putInt(FAKE_UID_2, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+ assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+ }
+
+ @Test
+ public void isManualDenylist_initiated_returnsTrue() {
+ mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+ assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+ }
+
+ @Test
+ public void clearManualDenylistPref_isEmpty() {
+ mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ assertThat(mManualDenyListPref.getAll()).hasSize(1);
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+
+ mDynamicDenylistManager.clearManualDenylistPref();
+
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ }
+
+ @Test
+ public void clearDynamicDenylistPref_isEmpty() {
+ mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+ assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+
+ mDynamicDenylistManager.clearDynamicDenylistPref();
+
+ assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
index 72b01f8..817df4c 100644
--- a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
+++ b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -27,6 +29,7 @@
import android.app.Activity;
import android.app.ApplicationPackageManager;
import android.app.LocaleConfig;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -51,6 +54,7 @@
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.AppLocaleUtil;
import com.android.settings.flags.Flags;
+import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.After;
import org.junit.Before;
@@ -91,6 +95,7 @@
private static final String EN_US = "en-US";
private static int sUid;
+ private FakeFeatureFactory mFeatureFactory;
private LocaleNotificationDataManager mDataManager;
private AppLocalePickerActivity mActivity;
@@ -117,6 +122,7 @@
when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US"));
ReflectionHelpers.setStaticField(AppLocaleUtil.class, "sLocaleConfig", mLocaleConfig);
sUid = Process.myUid();
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
}
@After
@@ -229,6 +235,37 @@
}
@Test
+ public void onLocaleSelected_logLocaleSource() {
+ ActivityController<TestAppLocalePickerActivity> controller =
+ initActivityController(true);
+ LocaleList.setDefault(LocaleList.forLanguageTags("ja-JP,en-CA,en-US"));
+ Locale locale = new Locale("en", "US");
+ when(mLocaleInfo.getLocale()).thenReturn(locale);
+ when(mLocaleInfo.isSystemLocale()).thenReturn(false);
+ when(mLocaleInfo.isSuggested()).thenReturn(true);
+ when(mLocaleInfo.isSuggestionOfType(LocaleStore.LocaleInfo.SUGGESTION_TYPE_SIM)).thenReturn(
+ true);
+ when(mLocaleInfo.isSuggestionOfType(
+ LocaleStore.LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE)).thenReturn(
+ true);
+ when(mLocaleInfo.isSuggestionOfType(
+ LocaleStore.LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE)).thenReturn(
+ true);
+ when(mLocaleInfo.isSuggestionOfType(
+ LocaleStore.LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE)).thenReturn(
+ true);
+
+ controller.create();
+ AppLocalePickerActivity mActivity = controller.get();
+ mActivity.onLocaleSelected(mLocaleInfo);
+
+ int localeSource = 15; // SIM_LOCALE | SYSTEM_LOCALE |IME_LOCALE|APP_LOCALE
+ verify(mFeatureFactory.metricsFeatureProvider).action(
+ any(), eq(SettingsEnums.ACTION_CHANGE_APP_LANGUAGE_FROM_SUGGESTED),
+ eq(localeSource));
+ }
+
+ @Test
@RequiresFlagsEnabled(Flags.FLAG_LOCALE_NOTIFICATION_ENABLED)
public void onLocaleSelected_evaluateNotification_simpleLocaleUpdate_localeCreatedWithUid()
throws Exception {
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java
deleted file mode 100644
index 2e04ea7..0000000
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.UserManager;
-
-import com.android.settings.R;
-import com.android.settings.search.BaseSearchIndexProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(RobolectricTestRunner.class)
-public class MobileNetworkListFragmentTest {
- @Mock
- private Context mContext;
- @Mock
- private Resources mResources;
- @Mock
- private UserManager mUserManager;
-
- private MobileNetworkListFragment mFragment;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mFragment = new MobileNetworkListFragment();
- }
-
- @Test
- public void isPageSearchEnabled_adminUser_shouldReturnTrue() {
- when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
- when(mUserManager.isAdminUser()).thenReturn(true);
- final BaseSearchIndexProvider provider =
- (BaseSearchIndexProvider) mFragment.SEARCH_INDEX_DATA_PROVIDER;
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true);
-
- final Object obj = ReflectionHelpers.callInstanceMethod(provider, "isPageSearchEnabled",
- ReflectionHelpers.ClassParameter.from(Context.class, mContext));
- final boolean isEnabled = (Boolean) obj;
-
- assertThat(isEnabled).isTrue();
- }
-
- @Test
- public void isPageSearchEnabled_nonAdminUser_shouldReturnFalse() {
- when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
- when(mUserManager.isAdminUser()).thenReturn(false);
- final BaseSearchIndexProvider provider =
- (BaseSearchIndexProvider) mFragment.SEARCH_INDEX_DATA_PROVIDER;
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true);
-
- final Object obj = ReflectionHelpers.callInstanceMethod(provider, "isPageSearchEnabled",
- ReflectionHelpers.ClassParameter.from(Context.class, mContext));
- final boolean isEnabled = (Boolean) obj;
-
- assertThat(isEnabled).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
index 13528b4..053b352 100644
--- a/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
@@ -26,16 +26,19 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.flags.Flags;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.PrimarySwitchPreference;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -51,6 +54,9 @@
@LooperMode(LooperMode.Mode.LEGACY)
public class AppChannelsBypassingDndPreferenceControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private NotificationBackend mBackend;
@@ -150,4 +156,44 @@
}
return new ParceledListSlice<>(Collections.singletonList(group));
}
+
+ @Test
+ public void displayPreference_duplicateChannelName_AddsGroupNameAsSummary() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEDUPE_DND_SETTINGS_CHANNELS);
+ NotificationChannelGroup group1 = new NotificationChannelGroup("group1_id", "Group1");
+ NotificationChannelGroup group2 = new NotificationChannelGroup("group2_id", "Group2");
+
+ group1.addChannel(new NotificationChannel("mail_group1_id", "Mail",
+ NotificationManager.IMPORTANCE_DEFAULT));
+ group1.addChannel(new NotificationChannel("other_group1_id", "Other",
+ NotificationManager.IMPORTANCE_DEFAULT));
+
+ group2.addChannel(new NotificationChannel("music_group2_id", "Music",
+ NotificationManager.IMPORTANCE_DEFAULT));
+ // This channel has the same name as a channel in group1.
+ group2.addChannel(new NotificationChannel("mail_group2_id", "Mail",
+ NotificationManager.IMPORTANCE_DEFAULT));
+
+ ParceledListSlice<NotificationChannelGroup> groups = new ParceledListSlice<>(
+ new ArrayList<NotificationChannelGroup>() {
+ {
+ add(group1);
+ add(group2);
+ }
+ }
+ );
+
+ when(mBackend.getGroups(eq(mAppRow.pkg), eq(mAppRow.uid))).thenReturn(groups);
+ mController.displayPreference(mPreferenceScreen);
+ ShadowApplication.runBackgroundTasks();
+ // Check that we've added the group name as a summary to channels that have identical names.
+ // Channels are also alphabetized.
+ assertThat(mCategory.getPreference(1).getTitle().toString()).isEqualTo("Mail");
+ assertThat(mCategory.getPreference(1).getSummary().toString()).isEqualTo("Group1");
+ assertThat(mCategory.getPreference(2).getTitle().toString()).isEqualTo("Mail");
+ assertThat(mCategory.getPreference(2).getSummary().toString()).isEqualTo("Group2");
+ assertThat(mCategory.getPreference(3).getTitle().toString()).isEqualTo("Music");
+ assertThat(mCategory.getPreference(4).getTitle().toString()).isEqualTo("Other");
+
+ }
}
diff --git a/tests/spa_unit/Android.bp b/tests/spa_unit/Android.bp
index 28a2667..c3e99f7 100644
--- a/tests/spa_unit/Android.bp
+++ b/tests/spa_unit/Android.bp
@@ -34,6 +34,7 @@
"androidx.compose.runtime_runtime",
"androidx.test.ext.junit",
"androidx.test.runner",
+ "flag-junit",
"mockito-target-extended-minus-junit4",
],
jni_libs: [
diff --git a/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt
new file mode 100644
index 0000000..3ba4bac
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.Context
+import android.content.res.Resources
+import android.os.UserManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class MobileNetworkListFragmentTest {
+ private val mockUserManager = mock<UserManager>()
+
+ private val mockResources = mock<Resources>()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { userManager } doReturn mockUserManager
+ on { resources } doReturn mockResources
+ }
+
+ @Test
+ fun isPageSearchEnabled_adminUser_shouldReturnTrue() {
+ mockUserManager.stub {
+ on { isAdminUser } doReturn true
+ }
+ mockResources.stub {
+ on { getBoolean(R.bool.config_show_sim_info) } doReturn true
+ }
+
+ val isEnabled =
+ MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
+
+ assertThat(isEnabled).isTrue()
+ }
+
+ @Test
+ fun isPageSearchEnabled_nonAdminUser_shouldReturnFalse() {
+ mockUserManager.stub {
+ on { isAdminUser } doReturn false
+ }
+ mockResources.stub {
+ on { getBoolean(R.bool.config_show_sim_info) } doReturn true
+ }
+
+ val isEnabled =
+ MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
+
+ assertThat(isEnabled).isFalse()
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt
index 53ed4f0..1a05479 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt
@@ -21,7 +21,6 @@
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.State
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
@@ -146,12 +145,12 @@
fun allAppListModel_getSummary() {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
- lateinit var summaryState: State<String>
+ lateinit var summary: () -> String
composeTestRule.setContent {
- summaryState = listModel.getSummary(option = 0, record = AppRecordWithSize(app = APP))
+ summary = listModel.getSummary(option = 0, record = AppRecordWithSize(app = APP))
}
- assertThat(summaryState.value).isEqualTo(SUMMARY)
+ assertThat(summary()).isEqualTo(SUMMARY)
}
@Test
@@ -163,13 +162,13 @@
enabled = false
}
- lateinit var summaryState: State<String>
+ lateinit var summary: () -> String
composeTestRule.setContent {
- summaryState =
+ summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = disabledApp))
}
- assertThat(summaryState.value).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled")
+ assertThat(summary()).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled")
}
@Test
@@ -179,13 +178,13 @@
packageName = PACKAGE_NAME
}
- lateinit var summaryState: State<String>
+ lateinit var summary: () -> String
composeTestRule.setContent {
- summaryState =
+ summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = notInstalledApp))
}
- assertThat(summaryState.value)
+ assertThat(summary())
.isEqualTo("$SUMMARY${System.lineSeparator()}Not installed for this user")
}
@@ -207,7 +206,7 @@
AppListItemModel(
record = AppRecordWithSize(app = app),
label = LABEL,
- summary = stateOf(SUMMARY),
+ summary = { SUMMARY },
).AppItem()
}
}
@@ -224,13 +223,13 @@
isArchived = true
}
- lateinit var summaryState: State<String>
+ lateinit var summary: () -> String
composeTestRule.setContent {
- summaryState =
+ summary =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = archivedApp))
}
- assertThat(summaryState.value).isEqualTo(SUMMARY)
+ assertThat(summary()).isEqualTo(SUMMARY)
}
private fun getAppListInput(): AppListInput<AppRecordWithSize> {
@@ -252,7 +251,7 @@
AppListItemModel(
record = AppRecordWithSize(app = APP),
label = LABEL,
- summary = stateOf(SUMMARY),
+ summary = { SUMMARY },
).AppItem()
}
}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
index 4f372e2..dfacca8 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
@@ -21,7 +21,6 @@
import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN
import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET
import android.os.Build
-import androidx.compose.runtime.State
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
@@ -29,7 +28,6 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
@@ -132,7 +130,7 @@
AppListItemModel(
record = APP_RECORD_SUGGESTED,
label = LABEL,
- summary = stateOf(SUMMARY)
+ summary = { SUMMARY }
).AppItem()
}
}
@@ -141,23 +139,23 @@
@Test
fun aspectRatioAppListModel_getSummaryDefault() {
- val summaryState = setSummaryState(USER_MIN_ASPECT_RATIO_UNSET)
- assertThat(summaryState.value)
- .isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
+ val summary = getSummary(USER_MIN_ASPECT_RATIO_UNSET)
+
+ assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
}
@Test
fun aspectRatioAppListModel_getSummaryWhenSplitScreen() {
- val summaryState = setSummaryState(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
- assertThat(summaryState.value)
- .isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
+ val summary = getSummary(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
+
+ assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
}
- private fun setSummaryState(userOverride: Int): State<String> {
+ private fun getSummary(userOverride: Int): String {
val listModel = UserAspectRatioAppListModel(context)
- lateinit var summaryState: State<String>
+ lateinit var summary: () -> String
composeTestRule.setContent {
- summaryState = listModel.getSummary(option = 0,
+ summary = listModel.getSummary(option = 0,
record = UserAspectRatioAppListItemModel(
app = APP,
userOverride = userOverride,
@@ -165,7 +163,7 @@
canDisplay = true,
))
}
- return summaryState
+ return summary()
}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
index ccd385f..5c65da1 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
@@ -29,7 +29,6 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
import com.google.common.truth.Truth.assertThat
@@ -244,7 +243,7 @@
app = APP,
dateOfInstall = TEST_FIRST_INSTALL_TIME),
label = TEST_LABEL,
- summary = stateOf(TEST_SUMMARY),
+ summary = { TEST_SUMMARY },
).AppItem()
}
}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksAppsTest.kt
new file mode 100644
index 0000000..579c6c9
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksAppsTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.android.settings.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LongBackgroundTasksAppsTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val listModel = LongBackgroundTasksAppsListModel(context)
+
+ @Test
+ fun modelResourceIdAndProperties() {
+ assertThat(listModel.pageTitleResId).isEqualTo(R.string.long_background_tasks_title)
+ assertThat(listModel.switchTitleResId).isEqualTo(R.string.long_background_tasks_switch_title)
+ assertThat(listModel.footerResId).isEqualTo(R.string.long_background_tasks_footer_title)
+ assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RUN_USER_INITIATED_JOBS)
+ assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_USER_INITIATED_JOBS)
+ assertThat(listModel.setModeByUid).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt
new file mode 100644
index 0000000..54ae6c6
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TurnScreenOnAppsTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val listModel = TurnScreenOnAppsListModel(context)
+
+ @Test
+ fun pageTitleResId() {
+ assertThat(listModel.pageTitleResId).isEqualTo(com.android.settingslib.R.string.turn_screen_on_title)
+ }
+
+ @Test
+ fun switchTitleResId() {
+ assertThat(listModel.switchTitleResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on)
+ }
+
+ @Test
+ fun footerResId() {
+ assertThat(listModel.footerResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on_description)
+ }
+
+ @Test
+ fun appOp() {
+ assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_TURN_SCREEN_ON)
+ }
+
+ @Test
+ fun permission() {
+ assertThat(listModel.permission).isEqualTo(Manifest.permission.TURN_SCREEN_ON)
+ }
+
+ @Test
+ fun setModeByUid() {
+ assertThat(listModel.setModeByUid).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceControllerTest.kt
new file mode 100644
index 0000000..2127497
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceControllerTest.kt
@@ -0,0 +1,65 @@
+package com.android.settings.spa.app.specialaccess
+
+import android.content.Context
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.preference.Preference
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import com.android.settings.flags.Flags
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class VoiceActivationAppsPreferenceControllerTest {
+
+ @get:Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ doNothing().whenever(mock).startActivity(any())
+ }
+
+ private val matchedPreference = Preference(context).apply { key = preferenceKey }
+
+ private val misMatchedPreference = Preference(context).apply { key = testPreferenceKey }
+
+ private val controller = VoiceActivationAppsPreferenceController(context, preferenceKey)
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_VOICE_ACTIVATION_APPS_IN_SETTINGS)
+ fun getAvailabilityStatus_enableVoiceActivationApps_returnAvailable() {
+ assertThat(controller.isAvailable).isTrue()
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_VOICE_ACTIVATION_APPS_IN_SETTINGS)
+ fun getAvailableStatus_disableVoiceActivationApps_returnConditionallyUnavailable() {
+ assertThat(controller.isAvailable).isFalse()
+ }
+
+ @Test
+ fun handlePreferenceTreeClick_keyMatched_returnTrue() {
+ assertThat(controller.handlePreferenceTreeClick(matchedPreference)).isTrue()
+ }
+
+ @Test
+ fun handlePreferenceTreeClick_keyMisMatched_returnFalse() {
+ assertThat(controller.handlePreferenceTreeClick(misMatchedPreference)).isFalse()
+ }
+
+ companion object {
+ private const val preferenceKey: String = "voice_activation_apps"
+ private const val testPreferenceKey: String = "test_key"
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsTest.kt
new file mode 100644
index 0000000..7d636b3
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsTest.kt
@@ -0,0 +1,50 @@
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class VoiceActivationAppsTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val listModel = VoiceActivationAppsListModel(context)
+
+ @Test
+ fun pageTitleResId() {
+ assertThat(listModel.pageTitleResId).isEqualTo(R.string.voice_activation_apps_title)
+ }
+
+ @Test
+ fun switchTitleResId() {
+ assertThat(listModel.switchTitleResId).isEqualTo(R.string.permit_voice_activation_apps)
+ }
+
+ @Test
+ fun footerResId() {
+ assertThat(listModel.footerResId)
+ .isEqualTo(R.string.allow_voice_activation_apps_description)
+ }
+
+ @Test
+ fun appOp() {
+ assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO)
+ }
+
+ @Test
+ fun permission() {
+ assertThat(listModel.permission).isEqualTo(
+ Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO)
+ }
+
+ @Test
+ fun setModeByUid() {
+ assertThat(listModel.setModeByUid).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
index 0cfdc7d..6aee4ce 100644
--- a/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
@@ -20,7 +20,6 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
-import androidx.compose.runtime.State
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -79,20 +78,20 @@
@Test
fun getSummary() = runTest {
- val summaryState = getSummaryState(APP)
+ val summary = getSummary(APP)
- assertThat(summaryState.value).isEqualTo(PACKAGE_NAME)
+ assertThat(summary).isEqualTo(PACKAGE_NAME)
}
- private fun getSummaryState(app: ApplicationInfo): State<String> {
- lateinit var summary: State<String>
+ private fun getSummary(app: ApplicationInfo): String {
+ lateinit var summary: () -> String
composeTestRule.setContent {
summary = listModel.getSummary(
option = 0,
record = PlatformCompatAppRecord(app),
)
}
- return summary
+ return summary()
}
private companion object {
diff --git a/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt b/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt
new file mode 100644
index 0000000..75c3685
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.utils
+
+import android.content.Context
+import android.provider.Settings
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsGlobalBooleanDelegateTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun getValue_setTrue_returnTrue() {
+ Settings.Global.putInt(context.contentResolver, TEST_NAME, 1)
+
+ val value by context.settingsGlobalBoolean(TEST_NAME)
+
+ assertThat(value).isTrue()
+ }
+
+ @Test
+ fun getValue_setFalse_returnFalse() {
+ Settings.Global.putInt(context.contentResolver, TEST_NAME, 0)
+
+ val value by context.settingsGlobalBoolean(TEST_NAME)
+
+ assertThat(value).isFalse()
+ }
+
+ @Test
+ fun setValue_setTrue_returnTrue() {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+
+ value = true
+
+ assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 0)).isEqualTo(1)
+ }
+
+ @Test
+ fun setValue_setFalse_returnFalse() {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+
+ value = false
+
+ assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 1)).isEqualTo(0)
+ }
+
+ @Test
+ fun observeSettingsGlobalBoolean_valueNotChanged() {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+ value = false
+ var newValue: Boolean? = null
+
+ context.observeSettingsGlobalBoolean(TEST_NAME, TestLifecycleOwner().lifecycle) {
+ newValue = it
+ }
+
+ assertThat(newValue).isFalse()
+ }
+
+ @Test
+ fun observeSettingsGlobalBoolean_valueChanged() {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+ value = false
+ var newValue: Boolean? = null
+
+ context.observeSettingsGlobalBoolean(TEST_NAME, TestLifecycleOwner().lifecycle) {
+ newValue = it
+ }
+ value = true
+
+ assertThat(newValue).isFalse()
+ }
+
+ private companion object {
+ const val TEST_NAME = "test_boolean_delegate"
+ }
+}
diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
index 31b8e79..5ac367e 100644
--- a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
@@ -19,12 +19,14 @@
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.verify;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.widget.FooterPreference;
import org.junit.Before;
@@ -37,6 +39,7 @@
public class LocaleHelperPreferenceControllerTest {
private Context mContext;
private LocaleHelperPreferenceController mLocaleHelperPreferenceController;
+ private FakeFeatureFactory mFeatureFactory;
@Mock
private FooterPreference mMockFooterPreference;
@@ -49,11 +52,16 @@
}
mContext = ApplicationProvider.getApplicationContext();
mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(mContext);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
}
@Test
public void updateFooterPreference_setFooterPreference_hasClickAction() {
mLocaleHelperPreferenceController.updateFooterPreference(mMockFooterPreference);
verify(mMockFooterPreference).setLearnMoreText(anyString());
+ mMockFooterPreference.setLearnMoreAction(v -> {
+ verify(mFeatureFactory.metricsFeatureProvider).action(
+ mContext, SettingsEnums.ACTION_LANGUAGES_LEARN_MORE);
+ });
}
}
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java b/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java
index 0a67824..5c42ad9 100644
--- a/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java
+++ b/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.os.LocaleList;
@@ -51,6 +52,7 @@
private NumberingPreferencesFragment mFragment;
private PreferenceScreen mPreferenceScreen;
private LocaleList mCacheLocale;
+ private FakeFeatureFactory mFeatureFactory;
@Before
@UiThreadTest
@@ -59,6 +61,7 @@
Looper.prepare();
}
mApplicationContext = ApplicationProvider.getApplicationContext();
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
mFragment = spy(new NumberingPreferencesFragment());
PreferenceManager preferenceManager = new PreferenceManager(mApplicationContext);
mPreferenceScreen = preferenceManager.createPreferenceScreen(mApplicationContext);
@@ -94,6 +97,10 @@
}
assertTrue(isCallingStartActivity);
+ verify(mFeatureFactory.metricsFeatureProvider).action(
+ mApplicationContext,
+ SettingsEnums.ACTION_CHOOSE_LANGUAGE_FOR_NUMBERS_PREFERENCES,
+ "I_am_the_key");
}
@Test
@@ -114,6 +121,9 @@
mController.handlePreferenceTreeClick(preference);
verify(mFragment).setArguments(any());
+ verify(mFeatureFactory.metricsFeatureProvider).action(
+ mApplicationContext, SettingsEnums.ACTION_SET_NUMBERS_PREFERENCES,
+ "test_key");
}
@Test