Snap for 13120928 from 6191772de7 to 25Q2-release

Change-Id: I0b03f8283054b80ff5cf36c3de16a7b81e61c149
This commit is contained in:
Android Build Coastguard Worker
2025-02-25 16:24:45 -08:00
48 changed files with 1712 additions and 281 deletions

View File

@@ -12587,12 +12587,20 @@
<string name="category_name_how_it_works">How it works</string>
<!-- Title for satellite connection guide [CHAR_LIMIT=NONE] -->
<string name="title_satellite_connection_guide">When you don\u2019t have a mobile network</string>
<!-- Title for satellite connection guide for NTN manual connection type. [CHAR_LIMIT=NONE] -->
<string name="title_satellite_connection_guide_for_manual_type">Text a phone number</string>
<!-- Summary for satellite connection guide [CHAR_LIMIT=NONE] -->
<string name="summary_satellite_connection_guide">Your phone will auto-connect to a satellite. For the best connection, keep a clear view of the sky.</string>
<!-- Summary for satellite connection guide for NTN manual connection type. [CHAR_LIMIT=NONE] -->
<string name="summary_satellite_connection_guide_for_manual_type">If you don\u2019t have a mobile network, you\u2019ll see an option to use satellite messaging.</string>
<!-- Title for satellite supported service [CHAR_LIMIT=NONE] -->
<string name="title_supported_service">After your phone connects to a satellite</string>
<!-- Title for satellite supported service for NTN manual connection type. [CHAR_LIMIT=NONE] -->
<string name="title_supported_service_for_manual_type">Follow steps to connect to the satellite</string>
<!-- Summary for satellite supported service [CHAR_LIMIT=NONE] -->
<string name="summary_supported_service">You can text anyone, including emergency services. Your phone will reconnect to a mobile network when available.</string>
<!-- Summary for satellite supported service for NTN manual connection type. [CHAR_LIMIT=NONE] -->
<string name="summary_supported_service_for_manual_type">After your phone is connected, you can text anyone, including emergency services.</string>
<!-- learn more text - more about satellite messaging [CHAR_LIMIT=NONE] -->
<string name="satellite_setting_summary_more_information"><xliff:g id="subject" example="satellite messaging">%1$s</xliff:g> may take longer and is available only in some areas. Weather and certain structures may affect your satellite connection. Calling by satellite isn\u2019t available. Emergency calls may still connect.\n\nIt may take some time for account changes to show in Settings. Contact <xliff:g id="carrier_name" example="T-Mobile">%2$s</xliff:g> for details.</string>
<!-- learn more text - more about satellite messaging without emergency messaging support. [CHAR_LIMIT=NONE] -->

View File

@@ -56,4 +56,27 @@
android:key="recs"
android:title="@*android:string/recs_notification_channel_label"
settings:controller="com.android.settings.notification.BundleTypePreferenceController"/>
<PreferenceCategory
android:key="notification_bundle_excluded_apps_list"
android:title="@string/notification_excluded_apps"
settings:controller="com.android.settings.notification.AdjustmentExcludedAppsPreferenceController">
<com.android.settingslib.widget.TopIntroPreference
android:key="excluded_description"
android:title="@string/notification_bundle_excluded_description"/>
<!-- apps are added here -->
<Preference
android:key="notification_bundle_manage_apps"
android:order="1000"
android:icon="@drawable/ic_chevron_right_24dp"
android:title="@string/notification_summarization_manage_excluded_apps"
settings:controller="com.android.settings.notification.BundleManageAppsPreferenceController"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
<extra
android:name="classname"
android:value="com.android.settings.Settings$NotificationExcludeClassificationActivity"/>
</Preference>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -114,11 +114,10 @@
android:summary="@string/oem_unlock_enable_summary"
settings:useAdditionalSummary="true" />
<SwitchPreference
<SwitchPreferenceCompat
android:key="enable_16k_pages"
android:title="@string/enable_16k_pages"
android:summary="@string/enable_16k_pages_summary"
settings:useAdditionalSummary="true" />
android:summary="@string/enable_16k_pages_summary"/>
<Preference
android:key="running_apps"

View File

@@ -27,7 +27,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:clickable="false" />
android:clickable="false"
android:focusable="false"/>
<LinearLayout
android:layout_width="0dp"

View File

@@ -36,4 +36,29 @@
android:key="global_pref"
android:title="@string/notification_summarization_main_control_title"
settings:controller="com.android.settings.notification.SummarizationGlobalPreferenceController" />
<PreferenceCategory
android:key="notification_summarization_excluded_apps_list"
android:title="@string/notification_excluded_apps"
settings:controller="com.android.settings.notification.AdjustmentExcludedAppsPreferenceController">
<com.android.settingslib.widget.TopIntroPreference
android:key="excluded_description"
android:title="@string/notification_summarization_excluded_description"/>
<!-- apps are added here -->
<Preference
android:key="notification_summarization_manage_apps"
android:order="1000"
android:icon="@drawable/ic_chevron_right_24dp"
android:title="@string/notification_summarization_manage_excluded_apps"
settings:controller="com.android.settings.notification.SummarizationManageAppsPreferenceController"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
<extra
android:name="classname"
android:value="com.android.settings.Settings$NotificationExcludeSummarizationActivity"/>
</Preference>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -29,7 +29,6 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.network.GlobalSettingsChangeListener;
import com.android.settings.network.ProxySubscriptionManager;
import com.android.settings.overlay.FeatureFactory;
@@ -162,19 +161,13 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
if (context == null || telephonyManager == null) {
return false;
}
if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
try {
if (telephonyManager.getEmergencyCallbackMode()) {
return true;
}
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
// Ignore exception, device is not in ECM mode.
}
} else {
try {
if (telephonyManager.getEmergencyCallbackMode()) {
return true;
}
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
// Ignore exception, device is not in ECM mode.
}
final List<SubscriptionInfo> subInfoList =
ProxySubscriptionManager.getInstance(context).getActiveSubscriptionsInfo();
@@ -185,18 +178,12 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
final TelephonyManager telephonyManagerForSubId =
telephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
if (telephonyManagerForSubId != null) {
if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) {
try {
if (telephonyManagerForSubId.getEmergencyCallbackMode()) {
return true;
}
} else {
try {
if (telephonyManagerForSubId.getEmergencyCallbackMode()) {
return true;
}
} catch (UnsupportedOperationException e) {
// Ignore exception, device is not in ECM mode.
}
} catch (UnsupportedOperationException e) {
// Ignore exception, device is not in ECM mode.
}
}
}

View File

@@ -358,6 +358,8 @@ public class Settings extends SettingsActivity {
public static class AppBubbleNotificationSettingsActivity extends SettingsActivity { /* empty */ }
public static class NotificationAssistantSettingsActivity extends SettingsActivity{ /* empty */ }
public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
public static class NotificationExcludeSummarizationActivity extends SettingsActivity { /* empty */ }
public static class NotificationExcludeClassificationActivity extends SettingsActivity { /* empty */ }
/** Activity to manage Cloned Apps page */
public static class ClonedAppsListActivity extends SettingsActivity { /* empty */ }
/** Activity to manage Aspect Ratio app list page */

View File

@@ -19,6 +19,8 @@ package com.android.settings.accessibility;
import android.content.Context;
import android.content.res.Resources;
import androidx.annotation.NonNull;
import com.android.settingslib.display.DisplayDensityUtils;
import java.util.Arrays;
@@ -28,13 +30,17 @@ import java.util.stream.Collectors;
/**
* Data class for storing the configurations related to the display size.
*/
class DisplaySizeData extends PreviewSizeData<Integer> {
public class DisplaySizeData extends PreviewSizeData<Integer> {
private final DisplayDensityUtils mDensity;
DisplaySizeData(Context context) {
this(context, new DisplayDensityUtils(context));
}
public DisplaySizeData(@NonNull Context context, @NonNull DisplayDensityUtils util) {
super(context);
mDensity = new DisplayDensityUtils(getContext());
mDensity = util;
final int initialIndex = mDensity.getCurrentIndex();
if (initialIndex < 0) {
// Failed to obtain default density, which means we failed to
@@ -54,7 +60,7 @@ class DisplaySizeData extends PreviewSizeData<Integer> {
}
@Override
void commit(int currentProgress) {
public void commit(int currentProgress) {
final int densityDpi = getValues().get(currentProgress);
if (densityDpi == getDefaultValue()) {
mDensity.clearForcedDisplayDensity();

View File

@@ -22,7 +22,6 @@ import android.provider.Settings;
import android.telephony.TelephonyManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.overlay.FeatureFactory;
@@ -51,17 +50,12 @@ public class HearingAidCompatibilityPreferenceController extends TogglePreferenc
@Override
public int getAvailabilityStatus() {
if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
try {
return mTelephonyManager.isHearingAidCompatibilitySupported() ? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
return UNSUPPORTED_ON_DEVICE;
}
} else {
try {
return mTelephonyManager.isHearingAidCompatibilitySupported() ? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
return UNSUPPORTED_ON_DEVICE;
}
}

View File

@@ -40,7 +40,8 @@ abstract class PreviewSizeData<T extends Number> {
return mContext;
}
List<T> getValues() {
@NonNull
public List<T> getValues() {
return mValues;
}
@@ -56,7 +57,7 @@ abstract class PreviewSizeData<T extends Number> {
mDefaultValue = defaultValue;
}
int getInitialIndex() {
public int getInitialIndex() {
return mInitialIndex;
}

View File

@@ -270,6 +270,8 @@ public class ManageApplications extends InstrumentedFragment
public static final int LIST_TYPE_NFC_TAG_APPS = 18;
public static final int LIST_TYPE_TURN_SCREEN_ON = 19;
public static final int LIST_TYPE_USER_ASPECT_RATIO_APPS = 20;
public static final int LIST_TYPE_NOTIFICATION_EXCLUDE_SUMMARIZATION = 21;
public static final int LIST_TYPE_NOTIFICATION_EXCLUDE_CLASSIFICATION = 22;
// List types that should show instant apps.
public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(

View File

@@ -31,6 +31,8 @@ import com.android.settings.Settings.ManageExternalSourcesActivity
import com.android.settings.Settings.ManageExternalStorageActivity
import com.android.settings.Settings.MediaManagementAppsActivity
import com.android.settings.Settings.NotificationAppListActivity
import com.android.settings.Settings.NotificationExcludeClassificationActivity
import com.android.settings.Settings.NotificationExcludeSummarizationActivity
import com.android.settings.Settings.NotificationReviewPermissionsActivity
import com.android.settings.Settings.OverlaySettingsActivity
import com.android.settings.Settings.StorageUseActivity
@@ -44,6 +46,8 @@ import com.android.settings.applications.manageapplications.ManageApplications.L
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USER_ASPECT_RATIO_APPS
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_BATTERY_OPTIMIZATION
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_CLONED_APPS
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION_EXCLUDE_CLASSIFICATION
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION_EXCLUDE_SUMMARIZATION
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_GAMES
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_HIGH_POWER
import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_LONG_BACKGROUND_TASKS
@@ -99,6 +103,9 @@ object ManageApplicationsUtil {
ChangeNfcTagAppsActivity::class to LIST_TYPE_NFC_TAG_APPS,
TurnScreenOnSettingsActivity::class to LIST_TYPE_TURN_SCREEN_ON,
UserAspectRatioAppListActivity::class to LIST_TYPE_USER_ASPECT_RATIO_APPS,
NotificationExcludeSummarizationActivity::class to LIST_TYPE_NOTIFICATION_EXCLUDE_SUMMARIZATION,
NotificationExcludeClassificationActivity::class to LIST_TYPE_NOTIFICATION_EXCLUDE_CLASSIFICATION,
)
@JvmField
@@ -117,7 +124,7 @@ object ManageApplicationsUtil {
LIST_TYPE_MEDIA_MANAGEMENT_APPS -> MediaManagementAppsAppListProvider.getAppListRoute()
LIST_TYPE_ALARMS_AND_REMINDERS -> AlarmsAndRemindersAppListProvider.getAppListRoute()
LIST_TYPE_WIFI_ACCESS -> WifiControlAppListProvider.getAppListRoute()
LIST_TYPE_NOTIFICATION -> AppListNotificationsPageProvider.name
LIST_TYPE_NOTIFICATION -> AppListNotificationsPageProvider.AllApps.name
LIST_TYPE_APPS_LOCALE -> AppLanguagesPageProvider.name
LIST_TYPE_MAIN -> AllAppListPageProvider.name
LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
@@ -128,6 +135,8 @@ object ManageApplicationsUtil {
//LIST_TYPE_STORAGE -> StorageAppListPageProvider.Apps.name
//LIST_TYPE_GAMES -> StorageAppListPageProvider.Games.name
LIST_TYPE_BATTERY_OPTIMIZATION -> BatteryOptimizationModeAppListPageProvider.name
LIST_TYPE_NOTIFICATION_EXCLUDE_SUMMARIZATION -> AppListNotificationsPageProvider.ExcludeSummarization.name
LIST_TYPE_NOTIFICATION_EXCLUDE_CLASSIFICATION -> AppListNotificationsPageProvider.ExcludeClassification.name
else -> null
}
}

View File

@@ -32,8 +32,11 @@ import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.Choreographer;
import android.view.Display;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import android.window.DesktopExperienceFlags;
@@ -48,10 +51,12 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragmentBase;
import com.android.settings.accessibility.AccessibilitySeekBarPreference;
import com.android.settings.accessibility.DisplaySizeData;
import com.android.settings.accessibility.TextReadingPreferenceFragment;
import com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.DisplayListener;
import com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.Injector;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.display.DisplayDensityUtils;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.IllustrationPreference;
import com.android.settingslib.widget.MainSwitchPreference;
@@ -368,7 +373,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@NonNull
private AccessibilitySeekBarPreference reuseSizePreference(Context context,
PrefRefresh refresh) {
PrefRefresh refresh, int displayId) {
AccessibilitySeekBarPreference pref =
refresh.findUnusedPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
if (pref == null) {
@@ -378,11 +383,27 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
pref.setIconEnd(R.drawable.ic_add_24dp);
pref.setIconEndContentDescription(R.string.screen_zoom_make_larger_desc);
PrefBasics.EXTERNAL_DISPLAY_SIZE.apply(pref);
setStateForDisplaySizePreference(context, displayId, pref);
}
refresh.addPreference(pref);
return pref;
}
private void setStateForDisplaySizePreference(Context context, int displayId,
AccessibilitySeekBarPreference preference) {
var displaySizeData = new DisplaySizeData(context,
new DisplayDensityUtils(context, (info) -> info.displayId == displayId));
ExternalDisplaySizePreferenceStateHandler seekBarChangeHandler =
new ExternalDisplaySizePreferenceStateHandler(
displaySizeData, preference);
preference.setMax(displaySizeData.getValues().size() - 1);
preference.setProgress(displaySizeData.getInitialIndex());
preference.setContinuousUpdates(false);
preference.setOnSeekBarChangeListener(seekBarChangeHandler);
}
private void restoreState(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
return;
@@ -409,7 +430,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
showTextWhenNoDisplaysToShow(screen, context);
} else if (displaysToShow.size() == 1
&& ((displayId == INVALID_DISPLAY && !mPreviouslyShownListOfDisplays)
|| displaysToShow.get(0).getDisplayId() == displayId)) {
|| displaysToShow.get(0).getDisplayId() == displayId)) {
showDisplaySettings(displaysToShow.get(0), screen, context);
if (displayId == INVALID_DISPLAY && isTopologyPaneEnabled(mInjector)) {
// Only show the topology pane if the user did not arrive via the displays list.
@@ -514,7 +535,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
}
}
if (isDisplaySizeSettingEnabled(mInjector)) {
addSizePreference(context, refresh);
addSizePreference(context, refresh, display.getDisplayId());
}
}
@@ -532,7 +553,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
}
private void showDisplaysList(@NonNull List<Display> displaysToShow,
@NonNull PrefRefresh screen, @NonNull Context context) {
@NonNull PrefRefresh screen, @NonNull Context context) {
maybeAddV2Components(context, screen);
int order = PrefBasics.BUILTIN_DISPLAY_LIST.order;
for (var display : displaysToShow) {
@@ -685,8 +706,8 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
pref.setEnabled(isResolutionSettingEnabled(mInjector));
}
private void addSizePreference(final Context context, PrefRefresh refresh) {
var pref = reuseSizePreference(context, refresh);
private void addSizePreference(final Context context, PrefRefresh refresh, int displayId) {
var pref = reuseSizePreference(context, refresh, displayId);
pref.setSummary(EXTERNAL_DISPLAY_SIZE_SUMMARY_RESOURCE);
pref.setOnPreferenceClickListener(
(Preference p) -> {
@@ -735,6 +756,54 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
}
}
private static class ExternalDisplaySizePreferenceStateHandler
implements SeekBar.OnSeekBarChangeListener {
private static final long MIN_COMMIT_INTERVAL_MS = 800;
private static final long CHANGE_BY_BUTTON_DELAY_MS = 300;
private final DisplaySizeData mDisplaySizeData;
private int mLastDisplayProgress;
private long mLastCommitTime;
private final AccessibilitySeekBarPreference mPreference;
ExternalDisplaySizePreferenceStateHandler(DisplaySizeData displaySizeData,
AccessibilitySeekBarPreference preference) {
mDisplaySizeData = displaySizeData;
mPreference = preference;
}
final Choreographer.FrameCallback mCommit = this::tryCommitDisplaySizeConfig;
private void tryCommitDisplaySizeConfig(long unusedFrameTimeNanos) {
final int displayProgress = mPreference.getProgress();
if (displayProgress != mLastDisplayProgress) {
mDisplaySizeData.commit(displayProgress);
mLastDisplayProgress = displayProgress;
}
mLastCommitTime = SystemClock.elapsedRealtime();
}
private void postCommitDelayed() {
var commitDelayMs = CHANGE_BY_BUTTON_DELAY_MS;
if (SystemClock.elapsedRealtime() - mLastCommitTime < MIN_COMMIT_INTERVAL_MS) {
commitDelayMs += MIN_COMMIT_INTERVAL_MS;
}
final Choreographer choreographer = Choreographer.getInstance();
choreographer.removeFrameCallback(mCommit);
choreographer.postFrameCallbackDelayed(mCommit, commitDelayMs);
}
@Override
public void onProgressChanged(@NonNull SeekBar seekBar, int i, boolean b) {
postCommitDelayed();
}
@Override
public void onStartTrackingTouch(@NonNull SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(@NonNull SeekBar seekBar) {}
}
@VisibleForTesting
class DisplayPreference extends TwoTargetPreference
implements Preference.OnPreferenceClickListener {

View File

@@ -39,7 +39,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import androidx.preference.SwitchPreferenceCompat;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
@@ -139,7 +139,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
Settings.Global.ENABLE_16K_PAGES,
defaultOptionValue /* default */);
((SwitchPreference) mPreference).setChecked(optionValue == ENABLE_16K_PAGE_SIZE);
((SwitchPreferenceCompat) mPreference).setChecked(optionValue == ENABLE_16K_PAGE_SIZE);
}
@Override
@@ -150,7 +150,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
mContext.getContentResolver(),
Settings.Global.ENABLE_16K_PAGES,
ENABLE_4K_PAGE_SIZE);
((SwitchPreference) mPreference).setChecked(false);
((SwitchPreferenceCompat) mPreference).setChecked(false);
}
@Override

View File

@@ -14,15 +14,39 @@
package com.android.settings.display;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
public class ColorModePreferenceController extends BasePreferenceController {
public class ColorModePreferenceController extends BasePreferenceController
implements LifecycleObserver {
public ColorModePreferenceController(Context context, String key) {
private Preference mPreference;
private final ContentObserver mContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, @Nullable Uri uri) {
if (mPreference != null) {
updateState(mPreference);
}
}
};
public ColorModePreferenceController(@NonNull Context context, @NonNull String key) {
super(context, key);
}
@@ -34,13 +58,43 @@ public class ColorModePreferenceController extends BasePreferenceController {
AVAILABLE : DISABLED_FOR_USER;
}
@Override
public CharSequence getSummary() {
return ColorModeUtils.getColorModeMapping(mContext.getResources()).get(getColorMode());
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.DISPLAY_COLOR_MODE),
/* notifyForDescendants= */ false,
mContentObserver);
}
@VisibleForTesting
public int getColorMode() {
return mContext.getSystemService(ColorDisplayManager.class).getColorMode();
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
}
@Override
public CharSequence getSummary() {
return getColorModeName();
}
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
if (mPreference != null) {
updateState(mPreference);
}
}
@Override
public void updateState(@Nullable Preference preference) {
if (preference == null) {
return;
}
super.updateState(preference);
preference.setSummary(getSummary());
}
@NonNull
private String getColorModeName() {
return ColorModeUtils.getActiveColorModeName(mContext);
}
}

View File

@@ -213,8 +213,7 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment {
final Map<Integer, String> colorModesToSummaries =
ColorModeUtils.getColorModeMapping(mResources);
final List<ColorModeCandidateInfo> candidates = new ArrayList<>();
for (int colorMode : mResources.getIntArray(
com.android.internal.R.array.config_availableColorModes)) {
for (int colorMode : ColorModeUtils.getAvailableColorModes(getContext())) {
candidates.add(new ColorModeCandidateInfo(
colorModesToSummaries.get(colorMode),
getKeyForColorMode(colorMode),
@@ -390,8 +389,8 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment {
@Override
protected boolean isPageSearchEnabled(Context context) {
final int[] availableColorModes = context.getResources().getIntArray(
com.android.internal.R.array.config_availableColorModes);
final int[] availableColorModes =
ColorModeUtils.getAvailableColorModes(context);
return availableColorModes != null && availableColorModes.length > 0
&& !ColorDisplayManager.areAccessibilityTransformsEnabled(context);
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display;
import static android.hardware.display.ColorDisplayManager.COLOR_MODE_AUTOMATIC;
import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED;
import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL;
import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED;
import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MAX;
import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MIN;
import android.content.res.Resources;
import android.util.ArrayMap;
import com.android.settings.R;
import java.util.Map;
final class ColorModeUtils {
private ColorModeUtils() {
// Do not instantiate.
}
static Map<Integer, String> getColorModeMapping(Resources resources) {
final String[] colorModeOptionsStrings = resources.getStringArray(
R.array.config_color_mode_options_strings);
final int[] colorModeOptionsValues = resources.getIntArray(
R.array.config_color_mode_options_values);
if (colorModeOptionsStrings.length != colorModeOptionsValues.length) {
throw new RuntimeException("Color mode options of unequal length");
}
final Map<Integer, String> colorModesToSummaries = new ArrayMap<>();
for (int i = 0; i < colorModeOptionsValues.length; i++) {
final int colorMode = colorModeOptionsValues[i];
if (colorMode == COLOR_MODE_NATURAL
|| colorMode == COLOR_MODE_BOOSTED
|| colorMode == COLOR_MODE_SATURATED
|| colorMode == COLOR_MODE_AUTOMATIC
|| (colorMode >= VENDOR_COLOR_MODE_RANGE_MIN
&& colorMode <= VENDOR_COLOR_MODE_RANGE_MAX)) {
colorModesToSummaries.put(colorMode, colorModeOptionsStrings[i]);
}
}
return colorModesToSummaries;
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2025 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.display
import android.content.Context
import android.content.res.Resources
import android.hardware.display.ColorDisplayManager
import android.hardware.display.ColorDisplayManager.*
import android.util.Log
import com.android.settings.R
object ColorModeUtils {
private val TAG = "ColorModeUtils"
@JvmStatic
fun getColorModeMapping(resources: Resources): Map<Int, String> {
val colorModeOptionsStrings = resources.getStringArray(
R.array.config_color_mode_options_strings
)
val colorModeOptionsValues = resources.getIntArray(
R.array.config_color_mode_options_values
)
if (colorModeOptionsStrings.size!= colorModeOptionsValues.size) {
throw RuntimeException("Color mode options of unequal length")
}
val colorModesToSummaries = colorModeOptionsValues.zip(colorModeOptionsStrings).toMap().filterKeys { colorMode ->
colorMode == COLOR_MODE_NATURAL ||
colorMode == COLOR_MODE_BOOSTED ||
colorMode == COLOR_MODE_SATURATED ||
colorMode == COLOR_MODE_AUTOMATIC ||
(colorMode >= VENDOR_COLOR_MODE_RANGE_MIN &&
colorMode <= VENDOR_COLOR_MODE_RANGE_MAX)
}
return colorModesToSummaries
}
@JvmStatic
fun getColorMode(context: Context): Int =
context.getSystemService(ColorDisplayManager::class.java).colorMode
@JvmStatic
fun getActiveColorModeName(context: Context): String =
getColorModeMapping(context.resources)[getColorMode(context)] ?: ""
@JvmStatic
fun getAvailableColorModes(context: Context): IntArray =
context.getResources().getIntArray(com.android.internal.R.array.config_availableColorModes)
}

View File

@@ -28,6 +28,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -164,7 +165,8 @@ public abstract class LocalePickerBaseListPreferenceController extends
return getSortedLocaleList(searchItem);
}
private void setupPreference(List<LocaleStore.LocaleInfo> localeInfoList,
@VisibleForTesting
void setupPreference(List<LocaleStore.LocaleInfo> localeInfoList,
Map<String, Preference> existingPreferences) {
Log.d(TAG, "setupPreference: isNumberingMode = " + isNumberingMode());
if (isNumberingMode() && getPreferenceCategoryKey().contains(KEY_SUPPORTED)) {
@@ -255,7 +257,8 @@ public abstract class LocalePickerBaseListPreferenceController extends
return localeInfos;
}
private void switchFragment(LocaleStore.LocaleInfo localeInfo) {
@VisibleForTesting
void switchFragment(LocaleStore.LocaleInfo localeInfo) {
boolean shouldShowLocaleEditor = shouldShowLocaleEditor(localeInfo);
if (shouldShowLocaleEditor) {
List<LocaleStore.LocaleInfo> feedItemList = getUserLocaleList();
@@ -281,7 +284,6 @@ public abstract class LocalePickerBaseListPreferenceController extends
.setArguments(extra)
.launch();
}
((Activity) mContext).finish();
}
public void setFragmentManager(@NonNull FragmentManager fragmentManager) {
@@ -295,7 +297,8 @@ public abstract class LocalePickerBaseListPreferenceController extends
}
}
private boolean shouldShowLocaleEditor(LocaleStore.LocaleInfo localeInfo) {
@VisibleForTesting
boolean shouldShowLocaleEditor(LocaleStore.LocaleInfo localeInfo) {
boolean isSystemLocale = localeInfo.isSystemLocale();
boolean isRegionLocale = localeInfo.getParent() != null;
boolean mayHaveDifferentNumberingSystem = localeInfo.hasNumberingSystems();

View File

@@ -162,8 +162,8 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr
@Override
public int getAvailabilityStatus(int subId) {
if (mTelephonyManager == null) {
Log.w(LOG_TAG, "Telephony manager not yet initialized");
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
Log.w(LOG_TAG, "getAvailabilityStatus: Telephony manager not yet initialized");
return CONDITIONALLY_UNAVAILABLE;
}
boolean visible =
SubscriptionManager.isUsableSubscriptionId(subId)
@@ -189,6 +189,10 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr
return false;
}
if (mTelephonyManager == null) {
Log.w(LOG_TAG, "isChecked: Telephony manager not yet initialized");
return false;
}
long currentlyAllowedNetworkTypes = mTelephonyManager.getAllowedNetworkTypesForReason(
mTelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
return (currentlyAllowedNetworkTypes & BITMASK_2G) != 0;
@@ -214,6 +218,12 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr
if (!SubscriptionManager.isUsableSubscriptionId(mSubId)) {
return false;
}
if (mTelephonyManager == null) {
Log.w(LOG_TAG, "setChecked: Telephony manager not yet initialized");
return false;
}
long currentlyAllowedNetworkTypes = mTelephonyManager.getAllowedNetworkTypesForReason(
mTelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
boolean enabled = (currentlyAllowedNetworkTypes & BITMASK_2G) != 0;

View File

@@ -911,15 +911,11 @@ public class EnabledNetworkModePreferenceController extends
// assign current call state so that it helps to show correct preference state even
// before first onCallStateChanged() by initial registration.
if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
try {
mCallState = mTelephonyManager.getCallState(subId);
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
mCallState = TelephonyManager.CALL_STATE_IDLE;
}
} else {
try {
mCallState = mTelephonyManager.getCallState(subId);
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
mCallState = TelephonyManager.CALL_STATE_IDLE;
}
mTelephonyManager.registerTelephonyCallback(
mContext.getMainExecutor(), mTelephonyCallback);

View File

@@ -33,7 +33,6 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.R;
import com.android.settings.network.ims.VolteQueryImsState;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -233,15 +232,11 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc
}
// assign current call state so that it helps to show correct preference state even
// before first onCallStateChanged() by initial registration.
if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
try {
mCallState = mTelephonyManager.getCallState(subId);
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
mCallState = TelephonyManager.CALL_STATE_IDLE;
}
} else {
try {
mCallState = mTelephonyManager.getCallState(subId);
} catch (UnsupportedOperationException e) {
// Device doesn't support FEATURE_TELEPHONY_CALLING
mCallState = TelephonyManager.CALL_STATE_IDLE;
}
mTelephonyManager.registerTelephonyCallback(
mContext.getMainExecutor(), mTelephonyCallback);

View File

@@ -70,6 +70,9 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
private static final String PREF_KEY_YOUR_SATELLITE_DATA_PLAN = "key_your_satellite_data_plan";
private static final String PREF_KEY_CATEGORY_ABOUT_SATELLITE = "key_category_about_satellite";
private static final String KEY_FOOTER_PREFERENCE = "satellite_setting_extra_info_footer_pref";
private static final String KEY_SATELLITE_CONNECTION_GUIDE = "key_satellite_connection_guide";
private static final String KEY_SUPPORTED_SERVICE = "key_supported_service";
static final String SUB_ID = "sub_id";
static final String EXTRA_IS_SERVICE_DATA_TYPE = "is_service_data_type";
@@ -227,6 +230,15 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
category.setEnabled(false);
category.setShouldDisableView(true);
}
if (!isCarrierRoamingNtnConnectedTypeManual()) {
return;
}
Preference connectionGuide = findPreference(KEY_SATELLITE_CONNECTION_GUIDE);
connectionGuide.setTitle(R.string.title_satellite_connection_guide_for_manual_type);
connectionGuide.setSummary(R.string.summary_satellite_connection_guide_for_manual_type);
Preference supportedService = findPreference(KEY_SUPPORTED_SERVICE);
supportedService.setTitle(R.string.title_supported_service_for_manual_type);
supportedService.setSummary(R.string.summary_supported_service_for_manual_type);
}
private void updateFooterContent() {

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2025 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.notification;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static android.service.notification.Adjustment.KEY_TYPE;
import android.app.Flags;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.service.notification.Adjustment;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.utils.ThreadUtils;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List;
/**
* Adds a preference to the PreferenceCategory for every app excluded from an adjustment key
*/
public class AdjustmentExcludedAppsPreferenceController extends BasePreferenceController
implements PreferenceControllerMixin {
@NonNull private NotificationBackend mBackend;
@Nullable String mAdjustmentKey;
@Nullable @VisibleForTesting ApplicationsState mApplicationsState;
@VisibleForTesting PreferenceCategory mPreferenceCategory;
@VisibleForTesting Context mPrefContext;
private ApplicationsState.Session mAppSession;
public AdjustmentExcludedAppsPreferenceController(@NonNull Context context,
@NonNull String preferenceKey) {
super(context, preferenceKey);
mBackend = new NotificationBackend();
}
protected void onAttach(@Nullable ApplicationsState appState, @Nullable Fragment host,
@NonNull NotificationBackend helperBackend, @Adjustment.Keys String adjustment) {
mApplicationsState = appState;
mBackend = helperBackend;
mAdjustmentKey = adjustment;
if (mApplicationsState != null && host != null) {
mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
}
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
mPreferenceCategory = screen.findPreference(getPreferenceKey());
mPrefContext = screen.getContext();
updateAppList();
super.displayPreference(screen);
}
@Override
public int getAvailabilityStatus() {
if (!(Flags.nmSummarization() || Flags.nmSummarizationUi()
|| Flags.notificationClassificationUi())) {
return CONDITIONALLY_UNAVAILABLE;
}
if (KEY_SUMMARIZATION.equals(mAdjustmentKey)
&& mBackend.isNotificationSummarizationSupported()) {
return AVAILABLE;
}
if (KEY_TYPE.equals(mAdjustmentKey) && mBackend.isNotificationBundlingSupported()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
/**
* Call this method to trigger the app list to refresh.
*/
public void updateAppList() {
if (mAppSession == null) {
return;
}
ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures()
&& android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
? ApplicationsState.FILTER_ENABLED_NOT_QUIET
: ApplicationsState.FILTER_ALL_ENABLED;
mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR);
}
// Set the icon for the given preference to the entry icon from cache if available, or look
// it up.
private void updateIcon(Preference pref, ApplicationsState.AppEntry entry) {
synchronized (entry) {
final Drawable cachedIcon = AppUtils.getIconFromCache(entry);
if (cachedIcon != null && entry.mounted) {
pref.setIcon(cachedIcon);
} else {
ListenableFuture unused = ThreadUtils.postOnBackgroundThread(() -> {
final Drawable icon = AppUtils.getIcon(mPrefContext, entry);
if (icon != null) {
ThreadUtils.postOnMainThread(() -> pref.setIcon(icon));
}
});
}
}
}
@VisibleForTesting
void updateAppList(List<ApplicationsState.AppEntry> apps) {
if (mPreferenceCategory == null || apps == null) {
return;
}
List<String> excludedApps = List.of(mBackend.getAdjustmentDeniedPackages(mAdjustmentKey));
for (ApplicationsState.AppEntry app : apps) {
String pkg = app.info.packageName;
final String key = getKey(pkg, app.info.uid);
boolean doesAppPassCriteria = false;
if (excludedApps.contains(pkg)) {
doesAppPassCriteria = true;
}
Preference pref = mPreferenceCategory.findPreference(key);
if (pref == null) {
if (doesAppPassCriteria) {
// does not exist but should
pref = new Preference(mPrefContext);
pref.setKey(key);
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label));
updateIcon(pref, app);
mPreferenceCategory.addPreference(pref);
}
} else if (!doesAppPassCriteria) {
// exists but shouldn't anymore
mPreferenceCategory.removePreference(pref);
}
}
}
/**
* Create a unique key to identify an AppPreference
*/
static String getKey(String pkg, int uid) {
return "all|" + pkg + "|" + uid;
}
private final ApplicationsState.Callbacks mAppSessionCallbacks =
new ApplicationsState.Callbacks() {
@Override
public void onRunningStateChanged(boolean running) {
}
@Override
public void onPackageListChanged() {
}
@Override
public void onRebuildComplete(@NonNull ArrayList<ApplicationsState.AppEntry> apps) {
updateAppList(apps);
}
@Override
public void onPackageIconChanged() {
}
@Override
public void onPackageSizeChanged(@NonNull String packageName) {
}
@Override
public void onAllSizesComputed() { }
@Override
public void onLauncherInfoChanged() {
}
@Override
public void onLoadEntriesCompleted() {
updateAppList();
}
};
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2025 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.notification;
import android.app.Flags;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.settings.core.BasePreferenceController;
public class BundleManageAppsPreferenceController extends
BasePreferenceController {
NotificationBackend mBackend;
public BundleManageAppsPreferenceController(@NonNull Context context,
@NonNull String preferenceKey) {
super(context, preferenceKey);
mBackend = new NotificationBackend();
}
@Override
public int getAvailabilityStatus() {
if (Flags.notificationClassificationUi() && mBackend.isNotificationBundlingSupported()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -16,6 +16,11 @@
package com.android.settings.notification;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static android.service.notification.Adjustment.KEY_TYPE;
import android.app.Activity;
import android.app.Application;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.app.Flags;
@@ -25,6 +30,7 @@ import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.search.SearchIndexable;
import org.jetbrains.annotations.NotNull;
@@ -49,6 +55,26 @@ public class BundlePreferenceFragment extends DashboardFragment {
return "BundlePreferenceFragment";
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (use(AdjustmentExcludedAppsPreferenceController.class) != null) {
final Activity activity = getActivity();
Application app = null;
ApplicationsState appState = null;
if (activity != null) {
app = activity.getApplication();
} else {
app = null;
}
if (app != null) {
appState = ApplicationsState.getInstance(app);
}
use(AdjustmentExcludedAppsPreferenceController.class).onAttach(
appState, this, new NotificationBackend(), KEY_TYPE);
}
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.bundle_notifications_settings) {

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2025 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.notification;
import android.app.Flags;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.settings.core.BasePreferenceController;
public class SummarizationManageAppsPreferenceController extends
BasePreferenceController {
NotificationBackend mBackend;
public SummarizationManageAppsPreferenceController(@NonNull Context context,
@NonNull String preferenceKey) {
super(context, preferenceKey);
mBackend = new NotificationBackend();
}
@Override
public int getAvailabilityStatus() {
if ((Flags.nmSummarization() || Flags.nmSummarizationUi())
&& mBackend.isNotificationSummarizationSupported()) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -16,13 +16,19 @@
package com.android.settings.notification;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import android.app.Activity;
import android.app.Application;
import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.app.HeaderPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.search.SearchIndexable;
/**
@@ -45,6 +51,26 @@ public class SummarizationPreferenceFragment extends DashboardFragment {
return "SummarizationPreferenceFragment";
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (use(AdjustmentExcludedAppsPreferenceController.class) != null) {
final Activity activity = getActivity();
Application app = null;
ApplicationsState appState = null;
if (activity != null) {
app = activity.getApplication();
} else {
app = null;
}
if (app != null) {
appState = ApplicationsState.getInstance(app);
}
use(AdjustmentExcludedAppsPreferenceController.class).onAttach(
appState, this, new NotificationBackend(), KEY_SUMMARIZATION);
}
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.summarization_notifications_settings) {

View File

@@ -21,6 +21,7 @@ import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -126,6 +127,14 @@ public class ContentProtectionTogglePreferenceController extends TogglePreferenc
&& mContentProtectionPolicy
!= DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY) {
mSwitchBar.setDisabledByAdmin(mEnforcedAdmin);
return;
}
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager != null
&& userManager.isGuestUser()
&& mSwitchBar != null) {
mSwitchBar.setEnabled(false);
}
}

View File

@@ -16,12 +16,19 @@
package com.android.settings.security
import android.content.Context
import android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
import android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS
import com.android.settings.R
import com.android.settings.Settings.LockScreenSettingsActivity
import com.android.settings.display.AmbientDisplayAlwaysOnPreference
import com.android.settings.flags.Flags
import com.android.settings.notification.LockScreenNotificationPreferenceController
import com.android.settings.utils.makeLaunchIntent
import com.android.settingslib.datastore.AbstractKeyedDataObservable
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.PreferenceChangeReason
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.ProvidePreferenceScreen
@@ -29,7 +36,12 @@ import com.android.settingslib.metadata.preferenceHierarchy
import com.android.settingslib.preference.PreferenceScreenCreator
@ProvidePreferenceScreen(LockScreenPreferenceScreen.KEY)
open class LockScreenPreferenceScreen : PreferenceScreenCreator, PreferenceSummaryProvider {
open class LockScreenPreferenceScreen(private val context: Context) :
AbstractKeyedDataObservable<String>(), PreferenceScreenCreator, PreferenceSummaryProvider {
private val observer =
KeyedObserver<String> { _, _ -> notifyChange(KEY, PreferenceChangeReason.STATE) }
override val key: String
get() = KEY
@@ -39,6 +51,20 @@ open class LockScreenPreferenceScreen : PreferenceScreenCreator, PreferenceSumma
override val keywords: Int
get() = R.string.keywords_ambient_display_screen
override fun onFirstObserverAdded() {
val store = SettingsSecureStore.get(context)
val executor = HandlerExecutor.main
// update summary when lock screen notification settings are changed
store.addObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS, observer, executor)
store.addObserver(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, observer, executor)
}
override fun onLastObserverRemoved() {
val store = SettingsSecureStore.get(context)
store.removeObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS, observer)
store.removeObserver(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, observer)
}
override fun getSummary(context: Context): CharSequence? =
context.getString(LockScreenNotificationPreferenceController.getSummaryResource(context))

View File

@@ -45,7 +45,6 @@ import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.spa.app.specialaccess.WriteSystemPreferencesAppListProvider
import com.android.settings.spa.app.storage.StorageAppListPageProvider
import com.android.settings.spa.core.instrumentation.SpaLogMetricsProvider
import com.android.settings.spa.core.instrumentation.SpaLogProvider
import com.android.settings.spa.development.UsageStatsPageProvider
import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider
import com.android.settings.spa.home.HomePageProvider
@@ -107,7 +106,9 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
AppInfoSettingsProvider,
SpecialAppAccessPageProvider,
NotificationMainPageProvider,
AppListNotificationsPageProvider,
AppListNotificationsPageProvider.AllApps,
AppListNotificationsPageProvider.ExcludeClassification,
AppListNotificationsPageProvider.ExcludeSummarization,
SystemMainPageProvider,
LanguageAndInputPageProvider,
AppLanguagesPageProvider,

View File

@@ -17,34 +17,72 @@
package com.android.settings.spa.notification
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
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.rememberContext
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.template.app.AppList
import com.android.settingslib.spaprivileged.template.app.AppListInput
import com.android.settingslib.spaprivileged.template.app.AppListPage
object AppListNotificationsPageProvider : SettingsPageProvider {
override val name = "AppListNotifications"
sealed class AppListNotificationsPageProvider(private val type: ListType) : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
AppListPage(
title = stringResource(R.string.app_notifications_title),
listModel = rememberContext(::AppNotificationsListModel),
)
NotificationsAppListPage(type)
}
@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 = { summary }
override val onClick = navigator(name)
})
object AllApps : AppListNotificationsPageProvider(ListType.Apps) {
override val name = "AppListNotifications"
@Composable
fun EntryItem() {
val summary = stringResource(R.string.app_notification_field_summary)
Preference(object : PreferenceModel {
override val title = stringResource(ListType.Apps.titleResource)
override val summary = { summary }
override val onClick = navigator(name)
})
}
}
object ExcludeSummarization : AppListNotificationsPageProvider(ListType.ExcludeSummarization) {
override val name = "NotificationsExcludeSummarization"
}
object ExcludeClassification : AppListNotificationsPageProvider(ListType.ExcludeClassification) {
override val name = "NotificationsExcludeClassification"
}
}
@Composable
fun NotificationsAppListPage(
type: ListType,
appList: @Composable AppListInput<AppNotificationsRecord>.() -> Unit = { AppList() }
) {
val context = LocalContext.current
AppListPage(
title = stringResource(type.titleResource),
listModel = remember(context) { AppNotificationsListModel(context, type) },
appList = appList,
)
}
sealed class ListType(
@StringRes val titleResource: Int
) {
object Apps : ListType(
titleResource = R.string.app_notifications_title,
)
object ExcludeSummarization : ListType(
titleResource = R.string.notification_summarization_manage_excluded_apps_title,
)
object ExcludeClassification : ListType(
titleResource = R.string.notification_bundle_manage_excluded_apps_title,
)
}

View File

@@ -17,12 +17,16 @@
package com.android.settings.spa.notification
import android.content.pm.ApplicationInfo
import android.service.notification.Adjustment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.android.settings.spa.app.storage.StorageAppListModel
import com.android.settings.spa.app.storage.StorageType
class AppNotificationController(
private val repository: AppNotificationRepository,
private val app: ApplicationInfo,
private val listType: ListType,
) {
val isEnabled: LiveData<Boolean>
get() = _isEnabled
@@ -47,4 +51,62 @@ class AppNotificationController(
postValue(it)
}
}
val isAllowed: LiveData<Boolean>
get() = _isAllowed
fun getAllowed() = _isAllowed.get()
fun setAllowed(enabled: Boolean) {
when (listType) {
ListType.ExcludeSummarization -> {
if (repository.setAdjustmentSupportedForPackage(
app, Adjustment.KEY_SUMMARIZATION, enabled)) {
_isAllowed.postValue(enabled)
}
}
ListType.ExcludeClassification -> {
if (repository.setAdjustmentSupportedForPackage(
app, Adjustment.KEY_TYPE, enabled)) {
_isAllowed.postValue(enabled)
}
}
else -> {}
}
}
private val _isAllowed = object : MutableLiveData<Boolean>() {
override fun onActive() {
when (listType) {
ListType.ExcludeSummarization -> {
postValue(repository.isAdjustmentSupportedForPackage(
app, Adjustment.KEY_SUMMARIZATION))
}
ListType.ExcludeClassification -> {
postValue(repository.isAdjustmentSupportedForPackage(
app, Adjustment.KEY_TYPE))
}
else -> {}
}
}
override fun onInactive() {
}
fun get(): Boolean = when (listType) {
ListType.ExcludeSummarization -> {
value ?: repository.isAdjustmentSupportedForPackage(
app, Adjustment.KEY_SUMMARIZATION).also {
postValue(it)
}
}
ListType.ExcludeClassification -> {
value ?: repository.isAdjustmentSupportedForPackage(
app, Adjustment.KEY_TYPE).also {
postValue(it)
}
}
else -> false
}
}
}

View File

@@ -126,6 +126,20 @@ class AppNotificationRepository(
}
}
fun isAdjustmentSupportedForPackage(app: ApplicationInfo, key: String): Boolean =
notificationManager.isAdjustmentSupportedForPackage(key, app.packageName)
fun setAdjustmentSupportedForPackage(app: ApplicationInfo, key: String, enabled: Boolean):
Boolean {
return try {
notificationManager.setAdjustmentSupportedForPackage(key, app.packageName, enabled)
true
} catch (e: Exception) {
Log.w(TAG, "Error calling INotificationManager", e)
false
}
}
fun isUserUnlocked(user: Int): Boolean {
return try {
userManager.isUserUnlocked(user)

View File

@@ -36,6 +36,7 @@ import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.userId
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
import com.android.settingslib.spaprivileged.template.app.AppListSwitchItem
import com.android.settingslib.spaprivileged.template.app.AppListTwoTargetSwitchItem
import com.android.settingslib.utils.StringUtil
import kotlinx.coroutines.Dispatchers
@@ -52,6 +53,7 @@ data class AppNotificationsRecord(
class AppNotificationsListModel(
private val context: Context,
private val listType: ListType
) : AppListModel<AppNotificationsRecord> {
private val repository = AppNotificationRepository(context)
private val now = System.currentTimeMillis()
@@ -64,7 +66,7 @@ class AppNotificationsListModel(
AppNotificationsRecord(
app = app,
sentState = usageEvents[app.packageName],
controller = AppNotificationController(repository, app),
controller = AppNotificationController(repository, app, listType),
)
}
}
@@ -129,17 +131,35 @@ class AppNotificationsListModel(
@Composable
override fun AppListItemModel<AppNotificationsRecord>.AppItem() {
val changeable by produceState(initialValue = false) {
withContext(Dispatchers.Default) {
value = repository.isChangeable(record.app)
when (listType) {
ListType.ExcludeSummarization -> {
AppListSwitchItem(
checked = record.controller.isAllowed.observeAsCallback(),
changeable = { true },
onCheckedChange = record.controller::setAllowed,
)
}
ListType.ExcludeClassification -> {
AppListSwitchItem(
checked = record.controller.isAllowed.observeAsCallback(),
changeable = { true },
onCheckedChange = record.controller::setAllowed,
)
}
else -> {
val changeable by produceState(initialValue = false) {
withContext(Dispatchers.Default) {
value = repository.isChangeable(record.app)
}
}
AppListTwoTargetSwitchItem(
onClick = { navigateToAppNotificationSettings(app = record.app) },
checked = record.controller.isEnabled.observeAsCallback(),
changeable = { changeable },
onCheckedChange = record.controller::setEnabled,
)
}
}
AppListTwoTargetSwitchItem(
onClick = { navigateToAppNotificationSettings(app = record.app) },
checked = record.controller.isEnabled.observeAsCallback(),
changeable = { changeable },
onCheckedChange = record.controller::setEnabled,
)
}
private fun navigateToAppNotificationSettings(app: ApplicationInfo) {

View File

@@ -41,7 +41,7 @@ object NotificationMainPageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = getTitle(arguments)) {
AppListNotificationsPageProvider.EntryItem()
AppListNotificationsPageProvider.AllApps.EntryItem()
}
}

View File

@@ -111,7 +111,7 @@ public class FactoryResetPreferenceController extends BasePreferenceController {
return prepareFactoryResetWizardRequest;
}
}
return prepareFactoryResetWizardRequest;
return null;
}
Log.i(TAG, "Unable to resolve a Factory Reset Handler Activity");
return null;

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2025 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.display
import android.content.ContentResolver
import android.content.Context
import android.database.ContentObserver
import android.hardware.display.ColorDisplayManager
import android.provider.Settings
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import androidx.preference.PreferenceManager
import androidx.test.core.app.ApplicationProvider
import com.android.settingslib.testutils.shadow.ShadowColorDisplayManager
import com.android.settings.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.shadow.api.Shadow
import org.robolectric.shadows.ShadowContentResolver
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowColorDisplayManager::class, ShadowContentResolver::class])
class ColorModePreferenceControllerTest {
private lateinit var context: Context
private lateinit var preference: Preference
private lateinit var controller: ColorModePreferenceController
private lateinit var shadowColorDisplayManager: ShadowColorDisplayManager
private lateinit var shadowContentResolver: ShadowContentResolver
@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
controller = ColorModePreferenceController(context, "test")
preference = Preference(context)
val preferenceManager = PreferenceManager(context)
val preferenceScreen = preferenceManager.createPreferenceScreen(context)
preference.setKey(controller.getPreferenceKey());
preferenceScreen.addPreference(preference)
shadowColorDisplayManager = Shadow.extract(
context.getSystemService(ColorDisplayManager::class.java))
val contentResolver = context.getContentResolver();
shadowContentResolver = Shadow.extract(contentResolver)
controller.displayPreference(preferenceScreen)
}
@Test
fun updateState_colorModeAutomatic_shouldSetSummaryToAutomatic() {
shadowColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC)
controller.updateState(preference)
val automaticColorModeName = context.getString(R.string.color_mode_option_automatic)
assertThat(preference.summary.toString()).isEqualTo(automaticColorModeName)
}
@Test
fun updateState_colorModeSaturated_shouldSetSummaryToSaturated() {
shadowColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED)
controller.updateState(preference)
val saturatedColorModeName = context.getString(R.string.color_mode_option_saturated)
assertThat(preference.summary.toString()).isEqualTo(saturatedColorModeName)
}
@Test
fun updateState_colorModeBoosted_shouldSetSummaryToBoosted() {
shadowColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED)
controller.updateState(preference)
val boostedColorModeName = context.getString(R.string.color_mode_option_boosted)
assertThat(preference.summary.toString()).isEqualTo(boostedColorModeName)
}
@Test
fun updateState_colorModeNatural_shouldSetSummaryToNatural() {
shadowColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL)
controller.updateState(preference)
val naturalColorModeName = context.getString(R.string.color_mode_option_natural)
assertThat(preference.summary.toString()).isEqualTo(naturalColorModeName)
}
@Test
fun onResume_verifyRegisterColorModeObserver() {
controller.onResume()
assertThat(shadowContentResolver.getContentObservers(
Settings.System.getUriFor(Settings.System.DISPLAY_COLOR_MODE)))
.hasSize(1)
}
@Test
fun onPause_verifyUnregisterColorModeObserver() {
controller.onResume()
controller.onPause()
assertThat(shadowContentResolver.getContentObservers(
Settings.System.getUriFor(Settings.System.DISPLAY_COLOR_MODE)))
.isEmpty()
}
@Test
fun contentObserver_onChange_updatesPreferenceSummary() {
controller.onResume()
assertThat(shadowContentResolver.getContentObservers(
Settings.System.getUriFor(Settings.System.DISPLAY_COLOR_MODE)))
.hasSize(1)
shadowColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL)
triggerOnChangeListener()
assertThat(preference.summary).isEqualTo(context.getString(R.string.color_mode_option_natural))
shadowColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC)
triggerOnChangeListener()
assertThat(preference.summary).isEqualTo(context.getString(R.string.color_mode_option_automatic))
}
private fun triggerOnChangeListener() {
shadowContentResolver.getContentObservers(
Settings.System.getUriFor(Settings.System.DISPLAY_COLOR_MODE))
.forEach {it.onChange(false, null)};
}
}

View File

@@ -0,0 +1,181 @@
/**
* Copyright (C) 2025 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.localepicker;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.os.LocaleList;
import android.os.Looper;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import com.android.internal.app.LocaleStore;
import com.android.settings.testutils.shadow.ShadowActivityManager;
import com.android.settings.testutils.shadow.ShadowFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
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.Shadows;
import org.robolectric.shadows.ShadowTelephonyManager;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowFragment.class,
ShadowActivityManager.class,
})
public class SystemLocaleSuggestedListPreferenceControllerTest {
private static final String KEY_CATEGORY_SYSTEM_SUGGESTED_LIST =
"system_language_suggested_category";
private static final String KEY_SUGGESTED = "system_locale_suggested_list";
private Context mContext;
private PreferenceCategory mPreferenceCategory;
private PreferenceScreen mPreferenceScreen;
private SystemLocaleSuggestedListPreferenceController mController;
private List<LocaleStore.LocaleInfo> mLocaleList;
private Map<String, Preference> mPreferences = new ArrayMap<>();
@Mock
private PreferenceManager mPreferenceManager;
@Mock
private IActivityManager mActivityService;
@Mock
private LocaleStore.LocaleInfo mSuggestedLocaleInfo_1;
@Mock
private LocaleStore.LocaleInfo mSuggestedLocaleInfo_2;
@Mock
private FragmentManager mFragmentManager;
@Mock
private FragmentTransaction mFragmentTransaction;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
if (Looper.myLooper() == null) {
Looper.prepare();
}
ShadowActivityManager.setService(mActivityService);
final Configuration config = new Configuration();
setUpLocaleConditions();
config.setLocales(new LocaleList(mSuggestedLocaleInfo_1.getLocale(),
mSuggestedLocaleInfo_2.getLocale()));
when(mActivityService.getConfiguration()).thenReturn(config);
ShadowTelephonyManager shadowTelephonyManager =
Shadows.shadowOf(mContext.getSystemService(TelephonyManager.class));
shadowTelephonyManager.setSimCountryIso("us");
shadowTelephonyManager.setNetworkCountryIso("us");
when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
mPreferenceScreen = spy(new PreferenceScreen(mContext, null));
mPreferenceCategory = spy(new PreferenceCategory(mContext, null));
when(mPreferenceScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
when(mPreferenceCategory.getPreferenceManager()).thenReturn(mPreferenceManager);
mPreferenceManager = new PreferenceManager(mContext);
mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
mPreferenceCategory.setKey(KEY_CATEGORY_SYSTEM_SUGGESTED_LIST);
mPreferenceScreen.addPreference(mPreferenceCategory);
mController = new SystemLocaleSuggestedListPreferenceController(mContext, KEY_SUGGESTED);
}
private void setUpLocaleConditions() {
mLocaleList = new ArrayList<>();
when(mSuggestedLocaleInfo_1.getFullNameNative()).thenReturn("English");
when(mSuggestedLocaleInfo_1.getLocale()).thenReturn(
LocaleList.forLanguageTags("en-US").get(0));
mLocaleList.add(mSuggestedLocaleInfo_1);
when(mSuggestedLocaleInfo_2.getFullNameNative()).thenReturn("Español (Estados Unidos)");
when(mSuggestedLocaleInfo_2.getLocale()).thenReturn(
LocaleList.forLanguageTags("es-US").get(0));
mLocaleList.add(mSuggestedLocaleInfo_2);
}
@Test
public void displayPreference_hasSuggestedPreference_categoryIsVisible() {
mController.displayPreference(mPreferenceScreen);
mController.setupPreference(mLocaleList, mPreferences);
assertTrue(mPreferenceCategory.isVisible());
assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
}
@Test
public void displayPreference_noSuggestedPreference_categoryIsGone() {
mLocaleList.clear();
mController.displayPreference(mPreferenceScreen);
mController.setupPreference(mLocaleList, mPreferences);
assertFalse(mPreferenceCategory.isVisible());
assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
}
@Test
public void switchFragment_shouldShowLocaleEditor() {
when(mSuggestedLocaleInfo_1.isSuggested()).thenReturn(true);
mController.shouldShowLocaleEditor(mSuggestedLocaleInfo_1);
mController.switchFragment(mSuggestedLocaleInfo_1);
verify(mFragmentTransaction, never()).add(any(LocaleListEditor.class),
anyString());
}
@Test
public void switchFragment_shouldShowRegionNumberingPicker() {
Context activityContext = mock(Context.class);
mController = new SystemLocaleSuggestedListPreferenceController(activityContext,
KEY_SUGGESTED);
when(mSuggestedLocaleInfo_1.isSuggested()).thenReturn(false);
when(mSuggestedLocaleInfo_1.isSystemLocale()).thenReturn(false);
when(mSuggestedLocaleInfo_1.getParent()).thenReturn(null);
mController.shouldShowLocaleEditor(mSuggestedLocaleInfo_1);
mController.switchFragment(mSuggestedLocaleInfo_1);
verify(mFragmentTransaction, never()).add(any(RegionAndNumberingSystemPickerFragment.class),
anyString());
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2025 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.notification;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.Flags;
import android.app.INotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@EnableFlags({Flags.FLAG_NM_SUMMARIZATION_UI, Flags.FLAG_NM_SUMMARIZATION,
Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
public class AdjustmentExcludedAppsPreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
private NotificationBackend mBackend;
@Mock
private ApplicationsState mApplicationState;
private AdjustmentExcludedAppsPreferenceController mController;
private Context mContext;
@Mock
INotificationManager mInm;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new AdjustmentExcludedAppsPreferenceController(mContext, "key");
mController.onAttach(null, mock(Fragment.class), mBackend, KEY_SUMMARIZATION);
PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
mController.mPreferenceCategory = new PreferenceCategory(mContext);
screen.addPreference(mController.mPreferenceCategory);
mController.mApplicationsState = mApplicationState;
mController.mPrefContext = mContext;
}
@Test
public void testIsAvailable() {
when(mBackend.isNotificationBundlingSupported()).thenReturn(true);
when(mBackend.isNotificationSummarizationSupported()).thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception {
when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_SUMMARIZATION));
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void testUpdateAppList() throws Exception {
when(mBackend.getAdjustmentDeniedPackages(KEY_SUMMARIZATION)).thenReturn(
new String[] {"cannot", "cannot2"});
// GIVEN there are four apps, and two have KEY_SUMMARIZATION off
ApplicationsState.AppEntry canSummarize =
mock(ApplicationsState.AppEntry.class);
canSummarize.info = new ApplicationInfo();
canSummarize.info.packageName = "canSummarize";
canSummarize.info.uid = 0;
ApplicationsState.AppEntry canSummarize2 = mock(ApplicationsState.AppEntry.class);
canSummarize2.info = new ApplicationInfo();
canSummarize2.info.packageName = "canSummarizeTwo";
canSummarize2.info.uid = 0;
ApplicationsState.AppEntry cannot =
mock(ApplicationsState.AppEntry.class);
cannot.info = new ApplicationInfo();
cannot.info.packageName = "cannot";
cannot.info.uid = 0;
ApplicationsState.AppEntry cannot2 =
mock(ApplicationsState.AppEntry.class);
cannot2.info = new ApplicationInfo();
cannot2.info.packageName = "cannot2";
cannot2.info.uid = 0;
List<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(canSummarize);
appEntries.add(canSummarize2);
appEntries.add(cannot);
appEntries.add(cannot2);
// WHEN the controller updates the app list with the app entries
mController.updateAppList(appEntries);
// THEN only the 'cannot' entries make it to the app list
assertThat(mController.mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
assertThat((Preference) mController.mPreferenceCategory.findPreference(
AdjustmentExcludedAppsPreferenceController.getKey(
cannot.info.packageName,cannot.info.uid))).isNotNull();
assertThat((Preference) mController.mPreferenceCategory.findPreference(
AdjustmentExcludedAppsPreferenceController.getKey(
cannot2.info.packageName,cannot2.info.uid))).isNotNull();
}
@Test
public void testUpdateAppList_nullApps() {
mController.updateAppList(null);
assertThat(mController.mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2025 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.notification;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.app.Flags;
import android.app.INotificationManager;
import android.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.Adjustment;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class BundleManageAppsPreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PREFERENCE_KEY = "preference_key";
private Context mContext;
BundleManageAppsPreferenceController mController;
@Mock
INotificationManager mInm;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
mController = new BundleManageAppsPreferenceController(mContext, PREFERENCE_KEY);
mController.mBackend.setNm(mInm);
}
@Test
public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() {
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception {
when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(Adjustment.KEY_TYPE));
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() {
mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
assertThat(mController.isAvailable()).isFalse();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2025 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.notification;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.app.Flags;
import android.app.INotificationManager;
import android.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class SummarizationManageAppsPreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PREFERENCE_KEY = "preference_key";
private Context mContext;
SummarizationManageAppsPreferenceController mController;
@Mock
INotificationManager mInm;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mSetFlagsRule.enableFlags(Flags.FLAG_NM_SUMMARIZATION, Flags.FLAG_NM_SUMMARIZATION_UI);
mController = new SummarizationManageAppsPreferenceController(mContext, PREFERENCE_KEY);
mController.mBackend.setNm(mInm);
}
@Test
public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() {
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception {
when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_SUMMARIZATION));
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() {
mSetFlagsRule.disableFlags(Flags.FLAG_NM_SUMMARIZATION);
mSetFlagsRule.disableFlags(Flags.FLAG_NM_SUMMARIZATION_UI);
assertThat(mController.isAvailable()).isFalse();
}
}

View File

@@ -24,8 +24,11 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
@@ -38,6 +41,7 @@ import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.RestrictedLockUtils;
@@ -53,7 +57,8 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUtils.class})
@Config(shadows = {ShadowUtils.class,
ShadowUserManager.class})
public class ContentProtectionTogglePreferenceControllerTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -62,7 +67,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock private PreferenceScreen mMockPreferenceScreen;
@Mock private PreferenceScreen mMockPreferenceScreen;
@Mock private SettingsMainSwitchPreference mMockSwitchPreference;
@@ -74,9 +79,13 @@ public class ContentProtectionTogglePreferenceControllerTest {
private TestContentProtectionTogglePreferenceController mController;
private int mSettingBackupValue;
private ShadowUserManager mShadowUserManager;
@Before
public void setUp() {
mShadowUserManager = ShadowUserManager.getShadow();
mShadowUserManager.setGuestUser(false);
mController = new TestContentProtectionTogglePreferenceController();
SettingsMainSwitchPreference switchPreference = new SettingsMainSwitchPreference(mContext);
when(mMockPreferenceScreen.findPreference(mController.getPreferenceKey()))
@@ -225,6 +234,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference, never()).setDisabledByAdmin(any());
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
@@ -237,6 +247,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference).setDisabledByAdmin(mEnforcedAdmin);
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
@@ -249,6 +260,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference, never()).setDisabledByAdmin(any());
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
@@ -261,6 +273,30 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference, never()).setDisabledByAdmin(any());
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
public void updateState_flagEnabled_noEnforcedAdmin_guestUser_switchBarDisabled() {
mShadowUserManager.setGuestUser(true);
mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
mContentProtectionPolicy = DevicePolicyManager.CONTENT_PROTECTION_ENABLED;
setupForUpdateState();
mController.updateState(mMockSwitchPreference);
verify(mMockSwitchPreference).setEnabled(false);
}
@Test
public void updateState_flagEnabled_noEnforcedAdmin_nonGuestUser_switchBarEnabled() {
mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
mContentProtectionPolicy = DevicePolicyManager.CONTENT_PROTECTION_ENABLED;
setupForUpdateState();
mController.updateState(mMockSwitchPreference);
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
@@ -273,6 +309,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference, never()).setDisabledByAdmin(any());
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
@@ -286,6 +323,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference).setDisabledByAdmin(mEnforcedAdmin);
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
@@ -299,6 +337,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference).setDisabledByAdmin(mEnforcedAdmin);
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test
@@ -312,6 +351,7 @@ public class ContentProtectionTogglePreferenceControllerTest {
assertThat(mController.mCounterGetEnforcedAdmin).isEqualTo(1);
verify(mMockSwitchPreference, never()).setDisabledByAdmin(any());
verify(mMockSwitchPreference, never()).setEnabled(false);
}
@Test

View File

@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.Manifest;
@@ -157,4 +158,21 @@ public class FactoryResetPreferenceControllerTest {
assertThat(intentArgumentCaptor.getValue().getAction())
.isEqualTo(FactoryResetPreferenceController.ACTION_PREPARE_FACTORY_RESET);
}
@Test
@RequiresFlagsEnabled(com.android.settings.factory_reset.Flags.FLAG_ENABLE_FACTORY_RESET_WIZARD)
public void handlePreference_factoryResetWizardEnabled_noExistingFrwApp()
throws PackageManager.NameNotFoundException {
PackageInfo info = new PackageInfo();
info.requestedPermissions =
new String[] {Manifest.permission.PREPARE_FACTORY_RESET};
info.requestedPermissionsFlags = new int[] {0};
when(mPackageManager.getPackageInfo(anyString(), anyInt()))
.thenReturn(info);
assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue();
verify(mPackageManager).getPackageInfo(eq(FACTORY_RESET_APP_PACKAGE),
eq(PackageManager.GET_PERMISSIONS));
verifyNoMoreInteractions(mFactoryResetLauncher);
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.ColorDisplayManager;
import androidx.preference.Preference;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ColorModePreferenceControllerTest {
private Preference mPreference;
private ColorModePreferenceController mController;
@Before
public void setup() {
final Context context = spy(ApplicationProvider.getApplicationContext());
mController = spy(new ColorModePreferenceController(context, "test"));
mPreference = new Preference(context);
final Resources res = spy(context.getResources());
when(res.getIntArray(com.android.internal.R.array.config_availableColorModes)).thenReturn(
new int[]{
ColorDisplayManager.COLOR_MODE_NATURAL,
ColorDisplayManager.COLOR_MODE_BOOSTED,
ColorDisplayManager.COLOR_MODE_SATURATED,
ColorDisplayManager.COLOR_MODE_AUTOMATIC
});
doReturn(res).when(context).getResources();
}
@Test
@UiThreadTest
public void updateState_colorModeAutomatic_shouldSetSummaryToAutomatic() {
doReturn(ColorDisplayManager.COLOR_MODE_AUTOMATIC).when(mController).getColorMode();
mController.updateState(mPreference);
assertThat(mPreference.getSummary()).isEqualTo("Adaptive");
}
@Test
@UiThreadTest
public void updateState_colorModeSaturated_shouldSetSummaryToSaturated() {
doReturn(ColorDisplayManager.COLOR_MODE_SATURATED).when(mController).getColorMode();
mController.updateState(mPreference);
assertThat(mPreference.getSummary()).isEqualTo("Saturated");
}
@Test
public void updateState_colorModeBoosted_shouldSetSummaryToBoosted() {
doReturn(ColorDisplayManager.COLOR_MODE_BOOSTED).when(mController).getColorMode();
mController.updateState(mPreference);
assertThat(mPreference.getSummary()).isEqualTo("Boosted");
}
@Test
public void updateState_colorModeNatural_shouldSetSummaryToNatural() {
doReturn(ColorDisplayManager.COLOR_MODE_NATURAL).when(mController).getColorMode();
mController.updateState(mPreference);
assertThat(mPreference.getSummary()).isEqualTo("Natural");
}
}

View File

@@ -76,6 +76,7 @@ public class ColorModePreferenceFragmentTest {
});
doReturn(res).when(mContext).getResources();
mFragment.onAttach(mContext);
doReturn(mContext).when(mFragment).getContext();
final List<? extends CandidateInfo> candidates = mFragment.getCandidates();
@@ -99,6 +100,7 @@ public class ColorModePreferenceFragmentTest {
});
doReturn(res).when(mContext).getResources();
mFragment.onAttach(mContext);
doReturn(mContext).when(mFragment).getContext();
List<? extends CandidateInfo> candidates = mFragment.getCandidates();
@@ -116,6 +118,7 @@ public class ColorModePreferenceFragmentTest {
});
doReturn(res).when(mContext).getResources();
mFragment.onAttach(mContext);
doReturn(mContext).when(mFragment).getContext();
List<? extends CandidateInfo> candidates = mFragment.getCandidates();
@@ -138,6 +141,7 @@ public class ColorModePreferenceFragmentTest {
});
doReturn(res).when(mContext).getResources();
mFragment.onAttach(mContext);
doReturn(mContext).when(mFragment).getContext();
List<? extends CandidateInfo> candidates = mFragment.getCandidates();

View File

@@ -83,9 +83,11 @@ public class LocaleDialogFragmentTest {
LocaleDialogFragment.LocaleDialogController.DialogContent dialogContent =
controller.getDialogContent();
assertEquals(R.string.button_label_confirmation_of_system_locale_change,
dialogContent.mPositiveButton);
assertEquals(R.string.cancel, dialogContent.mNegativeButton);
assertEquals(ResourcesUtils.getResourcesString(
mContext, "button_label_confirmation_of_system_locale_change"),
mContext.getString(dialogContent.mPositiveButton));
assertEquals(ResourcesUtils.getResourcesString(mContext, "cancel"),
mContext.getString(dialogContent.mNegativeButton));
}
@Test
@@ -98,7 +100,8 @@ public class LocaleDialogFragmentTest {
LocaleDialogFragment.LocaleDialogController.DialogContent dialogContent =
controller.getDialogContent();
assertEquals(R.string.okay, dialogContent.mPositiveButton);
assertEquals(ResourcesUtils.getResourcesString(mContext, "okay"),
mContext.getString(dialogContent.mPositiveButton));
assertTrue(dialogContent.mNegativeButton == 0);
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (C) 2025 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.localepicker;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.os.Looper;
import com.android.internal.app.LocaleStore;
import com.android.internal.app.SystemLocaleCollector;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import java.util.Locale;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class SystemLocaleSuggestedListPreferenceControllerTest {
private static final String KEY_CATEGORY_SYSTEM_SUGGESTED_LIST =
"system_language_suggested_category";
private static final String KEY_SUGGESTED = "system_locale_suggested_list";
private Context mContext;
private PreferenceManager mPreferenceManager;
private PreferenceCategory mPreferenceCategory;
private PreferenceScreen mPreferenceScreen;
private Preference mSuggestedPreference;
private SystemLocaleSuggestedListPreferenceController mController;
private Set<LocaleStore.LocaleInfo> mLocaleList;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
if (Looper.myLooper() == null) {
Looper.prepare();
}
SystemLocaleCollector systemLocaleCollector = new SystemLocaleCollector(mContext, null);
mLocaleList = systemLocaleCollector.getSupportedLocaleList(null, false, false);
mPreferenceManager = new PreferenceManager(mContext);
mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
mPreferenceCategory = new PreferenceCategory(mContext);
mPreferenceCategory.setKey(KEY_CATEGORY_SYSTEM_SUGGESTED_LIST);
mPreferenceScreen.addPreference(mPreferenceCategory);
mController = new SystemLocaleSuggestedListPreferenceController(mContext, KEY_SUGGESTED);
mController.displayPreference(mPreferenceScreen);
}
@Test
public void displayPreference_suggestedLocaleShouldBeInSuggestedCategory() {
int count = 0;
for (LocaleStore.LocaleInfo localeInfo : mLocaleList) {
if (localeInfo.isSuggested()) {
count++;
}
}
assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(count);
}
@Test
public void displayPreference_hasSuggestedPreference_categoryIsVisible() {
int count = 0;
for (LocaleStore.LocaleInfo localeInfo : mLocaleList) {
if (localeInfo.isSuggested()) {
count++;
}
}
if (count > 0) {
assertTrue(mPreferenceCategory.isVisible());
} else {
assertFalse(mPreferenceCategory.isVisible());
}
}
}