Snap for 13097162 from 69c4f2dff0 to 25Q2-release

Change-Id: I331aa7b1240b0756b576e77f9ca1b0ae15161ade
This commit is contained in:
Android Build Coastguard Worker
2025-02-20 16:17:53 -08:00
36 changed files with 856 additions and 206 deletions

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal" android:autoMirrored="true">
<path android:fillColor="@android:color/white" android:pathData="M560,720L320,480L560,240L616,296L432,480L616,664L560,720Z"/>
</vector>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal" android:autoMirrored="true">
<path android:fillColor="@android:color/white" android:pathData="M504,480L320,296L376,240L616,480L376,720L320,664L504,480Z" />
</vector>

View File

@@ -38,45 +38,20 @@
android:text="@string/screen_zoom_preview_title"
style="@style/AccessibilityTextReadingPreviewTitle" />
<LinearLayout
<com.android.settings.accessibility.TextReadingPreviewPager
android:id="@+id/preview_pager"
android:layout_width="match_parent"
android:layout_height="217dp"
android:contentDescription="@string/preview_pager_content_description"
android:nestedScrollingEnabled="true" />
<com.android.settings.widget.DotsPageIndicator
android:id="@+id/page_indicator"
style="@style/PreviewPagerPageIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.android.settings.accessibility.TextReadingPreviewPager
android:id="@+id/preview_pager"
android:layout_width="wrap_content"
android:layout_height="217dp"
android:contentDescription="@string/preview_pager_content_description"
android:nestedScrollingEnabled="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal">
<ImageButton
android:id="@+id/preview_left_button"
android:src="@drawable/keyboard_arrow_left"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
style="?android:attr/borderlessButtonStyle" />
<com.android.settings.widget.DotsPageIndicator
android:id="@+id/page_indicator"
style="@style/PreviewPagerPageIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="6dp"
android:visibility="gone" />
<ImageButton
android:id="@+id/preview_right_button"
android:src="@drawable/keyboard_arrow_right"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
</LinearLayout>
android:layout_gravity="center_horizontal"
android:padding="6dp"
android:visibility="gone" />
</LinearLayout>
</FrameLayout>

View File

@@ -78,11 +78,6 @@
<!-- Content description for preview pager. [CHAR LIMIT=NONE] -->
<string name="preview_pager_content_description">Preview</string>
<!-- Previous button for preview pager. [CHAR LIMIT=NONE] -->
<string name="preview_pager_previous_button">Previous preview</string>
<!-- Next button for preview pager. [CHAR LIMIT=NONE] -->
<string name="preview_pager_next_button">Next preview</string>
<!-- Content description for qrcode image. [CHAR LIMIT=none]-->
<string name="qr_code_content_description">QR code</string>
@@ -1954,7 +1949,7 @@
<string name="lockpattern_tutorial_continue_label">Next</string>
<!-- Title of preference to manage device admin apps, which are used by IT admins to manage devices -->
<string name="manage_device_admin">Device admin apps</string>
<string name="manage_device_admin">Device admin</string>
<!-- Summary of preference to manage device admin apps, informing the user that currently no device admin apps are installed and active -->
<string name="number_of_device_admins_none">No active apps</string>
@@ -6933,7 +6928,7 @@
<!-- Label for button to deactivate and uninstall the device admin app [CHAR_LIMIT=50]-->
<string name="remove_and_uninstall_device_admin">Deactivate &amp; uninstall</string>
<!-- Label for screen showing to select device admin apps -->
<string name="select_device_admin_msg">Device admin apps</string>
<string name="select_device_admin_msg">Device admin</string>
<!-- Message when there are no available device admin apps to display -->
<string name="no_device_admins">No device admin apps available</string>
@@ -9337,7 +9332,7 @@
<!-- Title for Polite Notifications setting [CHAR LIMIT=45]-->
<string name="notification_polite_title">Notification cooldown</string>
<string name="notification_polite_main_control_title">Use notification cooldown</string>
<string name="notification_polite_description">When you receive many notifications within a short time, your device will lower its volume and minimize alerts for up to 2 minutes. Calls, alarms, and priority conversations are not affected.
<string name="notification_polite_description">When you receive many notifications within a short time, your device will lower its volume and minimize alerts for up to 1 minute. Calls, alarms, and priority conversations are not affected.
\n\nNotifications received during the cooldown can be found by pulling down from the top of the screen.</string>
<string name="notification_polite_work">Apply to work profiles</string>
<string name="notification_polite_work_summary">Apply to work profile apps</string>

View File

@@ -199,7 +199,7 @@
<!-- Homepage should follow device default design, the values is same as device default theme.-->
<item name="android:navigationBarColor">@android:color/white</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerLow</item>
<item name="android:colorBackground">@android:color/system_surface_container_light</item>
</style>
<style name="Theme.Settings.Home.NoAnimation">

View File

@@ -14,6 +14,7 @@
limitations under the License.
-->
<!-- LINT.IfChange -->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
@@ -40,16 +41,16 @@
settings:controller="com.android.settings.accessibility.ColorInversionPreferenceController"/>
<!-- DarkModePreference is searchable in the Display & Touch setting.
Therefore, we set searchable = false here to avoid duplicate search results. -->
With catalyst, we're reusing the same preference. Will let the SettingsSearch
to determine how to resolve multi-entry on same preference. -->
<com.android.settings.display.darkmode.DarkModePreference
android:key="dark_ui_mode_accessibility"
android:key="dark_ui_mode"
android:icon="@drawable/ic_dark_ui"
android:title="@string/dark_ui_mode"
android:fragment="com.android.settings.display.darkmode.DarkModeSettingsFragment"
android:widgetLayout="@null"
settings:widgetLayout="@null"
settings:controller="com.android.settings.display.DarkUIPreferenceController"
settings:searchable="false"/>
settings:controller="com.android.settings.display.DarkUIPreferenceController" />
<SwitchPreferenceCompat
android:icon="@drawable/ic_accessibility_animation"
@@ -64,3 +65,4 @@
android:title="@string/experimental_category_title">
</PreferenceCategory>
</PreferenceScreen>
<!-- LINT.ThenChange(/src/com/android/settings/accessibility/ColorAndMotionScreen.kt:ui_hierarchy) -->

View File

@@ -34,7 +34,7 @@
<PreferenceCategory
android:key="apps_group"
android:layout="@layout/preference_category_no_label"
android:title="@string/app_list_memory_use"
settings:controller="com.android.settings.datausage.DataUsageListAppsController" />
</PreferenceScreen>

View File

@@ -63,15 +63,17 @@ public class ColorAndMotionFragment extends DashboardFragment {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
initializeAllPreferences();
updateSystemPreferences();
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
if (!isCatalystEnabled()) {
initializeAllPreferences();
updateSystemPreferences();
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
mSettingsContentObserver.registerKeysToObserverCallback(mShortcutFeatureKeys,
key -> updatePreferencesState());
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
mSettingsContentObserver.registerKeysToObserverCallback(mShortcutFeatureKeys,
key -> updatePreferencesState());
}
}
private void updatePreferencesState() {
@@ -84,15 +86,17 @@ public class ColorAndMotionFragment extends DashboardFragment {
@Override
public void onStart() {
super.onStart();
mSettingsContentObserver.register(getContentResolver());
if (!isCatalystEnabled()) {
mSettingsContentObserver.register(getContentResolver());
}
}
@Override
public void onStop() {
super.onStop();
mSettingsContentObserver.unregister(getContentResolver());
if (!isCatalystEnabled()) {
mSettingsContentObserver.unregister(getContentResolver());
}
}
@Override
@@ -116,9 +120,11 @@ public class ColorAndMotionFragment extends DashboardFragment {
/**
* Updates preferences related to system configurations.
*/
// LINT.IfChange(ui_hierarchy)
private void updateSystemPreferences() {
final PreferenceCategory experimentalCategory = getPreferenceScreen().findPreference(
CATEGORY_EXPERIMENTAL);
if (ColorDisplayManager.isColorTransformAccelerated(getContext())) {
getPreferenceScreen().removePreference(experimentalCategory);
} else {
@@ -130,6 +136,7 @@ public class ColorAndMotionFragment extends DashboardFragment {
experimentalCategory.addPreference(mToggleDisableAnimationsPreference);
}
}
// LINT.ThenChange(/src/com/android/settings/accessibility/ColorAndMotionScreen.kt:ui_hierarchy)
@Nullable
@Override

View File

@@ -17,10 +17,15 @@
package com.android.settings.accessibility
import android.content.Context
import android.hardware.display.ColorDisplayManager
import com.android.settings.R
import com.android.settings.Settings.ColorAndMotionActivity
import com.android.settings.display.darkmode.DarkModeScreen
import com.android.settings.flags.Flags
import com.android.settings.utils.makeLaunchIntent
import com.android.settingslib.metadata.PreferenceCategory
import com.android.settingslib.metadata.PreferenceGroup
import com.android.settingslib.metadata.PreferenceHierarchy
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.preferenceHierarchy
@@ -39,14 +44,34 @@ class ColorAndMotionScreen : PreferenceScreenCreator {
override fun isFlagEnabled(context: Context) = Flags.catalystAccessibilityColorAndMotion()
override fun hasCompleteHierarchy(): Boolean = false
override fun hasCompleteHierarchy(): Boolean = true
override fun fragmentClass() = ColorAndMotionFragment::class.java
override fun getPreferenceHierarchy(context: Context) =
preferenceHierarchy(context, this) {
+RemoveAnimationsPreference()
override fun getPreferenceHierarchy(context: Context): PreferenceHierarchy {
// LINT.IfChange(ui_hierarchy)
if (ColorDisplayManager.isColorTransformAccelerated(context)) {
return preferenceHierarchy(context, this) {
+DaltonizerPreference()
+ColorInversionPreference()
+DarkModeScreen.KEY
+RemoveAnimationsPreference()
}
} else {
return preferenceHierarchy(context, this) {
+ColorInversionPreference()
+DarkModeScreen.KEY
+PreferenceCategory(
"experimental_category",
R.string.experimental_category_title
) += {
+DaltonizerPreference()
+RemoveAnimationsPreference()
}
}
}
// LINT.ThenChange(/res/xml/accessibility_color_and_motion.xml, /src/com/android/settings/accessibility/ColorAndMotionFragment.java:ui_hierarchy)
}
override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?) =
makeLaunchIntent(context, ColorAndMotionActivity::class.java, metadata?.key)

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.accessibility
import android.app.settings.SettingsEnums
import android.content.Context
import android.provider.Settings
import com.android.settings.R
import com.android.settings.core.SubSettingLauncher
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceSummaryProvider
class ColorInversionPreference : PreferenceMetadata, PreferenceSummaryProvider,
PreferenceLifecycleProvider {
override val key: String
get() = PREFERENCE_KEY
override val title: Int
get() = R.string.accessibility_display_inversion_preference_title
override val icon: Int
get() = R.drawable.ic_color_inversion
override val keywords: Int
get() = R.string.keywords_color_inversion
private var mSettingsKeyedObserver: KeyedObserver<String>? = null
override fun intent(context: Context) =
SubSettingLauncher(context)
.setDestination(ToggleColorInversionPreferenceFragment::class.java.name)
.setSourceMetricsCategory(SettingsEnums.ACCESSIBILITY_COLOR_AND_MOTION)
.toIntent()
override fun getSummary(context: Context): CharSequence? {
return AccessibilityUtil.getSummary(
context,
SETTING_KEY,
R.string.color_inversion_state_on, R.string.color_inversion_state_off
)
}
override fun onStart(context: PreferenceLifecycleContext) {
val observer =
KeyedObserver<String> { _, _ -> context.notifyPreferenceChange(PREFERENCE_KEY) }
mSettingsKeyedObserver = observer
val storage = SettingsSecureStore.get(context)
storage.addObserver(SETTING_KEY, observer, HandlerExecutor.main)
}
override fun onStop(context: PreferenceLifecycleContext) {
mSettingsKeyedObserver?.let {
val storage = SettingsSecureStore.get(context)
storage.removeObserver(SETTING_KEY, it)
mSettingsKeyedObserver = null
}
}
companion object {
const val PREFERENCE_KEY = "toggle_inversion_preference"
const val SETTING_KEY = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED
}
}

View File

@@ -22,6 +22,7 @@ import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
// LINT.IfChange
/** Controller that shows the color inversion summary. */
public class ColorInversionPreferenceController extends BasePreferenceController {
@@ -45,3 +46,4 @@ public class ColorInversionPreferenceController extends BasePreferenceController
return AVAILABLE;
}
}
// LINT.ThenChange(/src/com/android/settings/accessibility/ColorInversionPreference.kt)

View File

@@ -0,0 +1,82 @@
/*
* 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.accessibility
import android.app.settings.SettingsEnums
import android.content.Context
import android.provider.Settings
import com.android.settings.R
import com.android.settings.core.SubSettingLauncher
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceSummaryProvider
class DaltonizerPreference : PreferenceMetadata, PreferenceSummaryProvider,
PreferenceLifecycleProvider {
override val key: String
get() = PREFERENCE_KEY
override val title: Int
get() = com.android.settingslib.R.string.accessibility_display_daltonizer_preference_title
override val icon: Int
get() = R.drawable.ic_daltonizer
override val keywords: Int
get() = R.string.keywords_color_correction
private var mSettingsKeyedObserver: KeyedObserver<String>? = null
override fun intent(context: Context) =
SubSettingLauncher(context)
.setDestination(ToggleDaltonizerPreferenceFragment::class.java.name)
.setSourceMetricsCategory(SettingsEnums.ACCESSIBILITY_COLOR_AND_MOTION)
.toIntent()
override fun getSummary(context: Context): CharSequence? {
return AccessibilityUtil.getSummary(
context,
SETTING_KEY,
R.string.daltonizer_state_on, R.string.daltonizer_state_off
)
}
override fun onStart(context: PreferenceLifecycleContext) {
val observer =
KeyedObserver<String> { _, _ -> context.notifyPreferenceChange(PREFERENCE_KEY) }
mSettingsKeyedObserver = observer
val storage = SettingsSecureStore.get(context)
storage.addObserver(SETTING_KEY, observer, HandlerExecutor.main)
}
override fun onStop(context: PreferenceLifecycleContext) {
mSettingsKeyedObserver?.let {
val storage = SettingsSecureStore.get(context)
storage.removeObserver(SETTING_KEY, it)
mSettingsKeyedObserver = null
}
}
companion object {
const val PREFERENCE_KEY = "daltonizer_preference"
const val SETTING_KEY = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED
}
}

View File

@@ -22,6 +22,7 @@ import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
// LINT.IfChange
/** Controller that shows and updates the color correction summary. */
public class DaltonizerPreferenceController extends BasePreferenceController {
private static final String DALTONIZER_ENABLED =
@@ -44,3 +45,4 @@ public class DaltonizerPreferenceController extends BasePreferenceController {
R.string.daltonizer_state_on, R.string.daltonizer_state_off);
}
}
// LINT.ThenChange(/src/com/android/settings/accessibility/DaltonizerPreference.kt)

View File

@@ -22,7 +22,6 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import androidx.preference.Preference;
@@ -100,23 +99,6 @@ public class TextReadingPreviewPreference extends Preference {
(DotsPageIndicator) holder.findViewById(R.id.page_indicator);
updateAdapterIfNeeded(viewPager, pageIndicator, mPreviewAdapter);
updatePagerAndIndicator(viewPager, pageIndicator);
viewPager.setClipToOutline(true);
int layoutDirection =
getContext().getResources().getConfiguration().getLayoutDirection();
int previousId = (layoutDirection == View.LAYOUT_DIRECTION_RTL)
? R.id.preview_right_button : R.id.preview_left_button;
int nextId = (layoutDirection == View.LAYOUT_DIRECTION_RTL)
? R.id.preview_left_button : R.id.preview_right_button;
final ImageButton previousButton = previewLayout.findViewById(previousId);
final ImageButton nextButton = previewLayout.findViewById(nextId);
previousButton.setOnClickListener((view) -> setCurrentItem(getCurrentItem() - 1));
previousButton.setContentDescription(getContext().getString(
R.string.preview_pager_previous_button));
nextButton.setOnClickListener((view) -> setCurrentItem(getCurrentItem() + 1));
previousButton.setContentDescription(getContext().getString(
R.string.preview_pager_next_button));
}
@Override
@@ -176,9 +158,7 @@ public class TextReadingPreviewPreference extends Preference {
Preconditions.checkNotNull(mPreviewAdapter,
"Preview adapter is null, you should init the preview adapter first");
if (currentItem < 0 || currentItem >= mPreviewAdapter.getCount()) {
return;
} else if (currentItem != mCurrentItem) {
if (currentItem != mCurrentItem) {
mCurrentItem = currentItem;
notifyChanged();
}

View File

@@ -308,7 +308,10 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
getActivity().setTitle(title);
}
protected void onProcessArguments(Bundle arguments) {
protected void onProcessArguments(@Nullable Bundle arguments) {
if (arguments == null) {
return;
}
// Key.
mPreferenceKey = arguments.getString(AccessibilitySettings.EXTRA_PREFERENCE_KEY);
@@ -432,7 +435,8 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
private void initToggleServiceSwitchPreference() {
mToggleServiceSwitchPreference = new SettingsMainSwitchPreference(getPrefContext());
mToggleServiceSwitchPreference.setKey(getUseServicePreferenceKey());
if (getArguments().containsKey(AccessibilitySettings.EXTRA_CHECKED)) {
if (getArguments() != null
&& getArguments().containsKey(AccessibilitySettings.EXTRA_CHECKED)) {
final boolean enabled = getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED);
mToggleServiceSwitchPreference.setChecked(enabled);
}

View File

@@ -155,7 +155,8 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final boolean isExpressiveStyle = BiometricUtils.isExpressiveStyle(getBaseContext());
final boolean isExpressiveStyle = BiometricUtils.isExpressiveStyle(
getBaseContext(), false);
if (shouldShowSplitScreenDialog()) {
BiometricsSplitScreenDialog

View File

@@ -535,11 +535,13 @@ public class BiometricUtils {
/**
* Check if device is using Expressive Style theme.
* @param context that for applying Expressive Style
* @param isSettingsPreference Apply Expressive style on Settings Preference or not.
* @return true if device using Expressive Style theme, otherwise false.
*/
public static boolean isExpressiveStyle(@NonNull Context context) {
return SettingsThemeHelper.isExpressiveTheme(context)
|| ThemeHelper.shouldApplyGlifExpressiveStyle(context);
public static boolean isExpressiveStyle(@NonNull Context context,
boolean isSettingsPreference) {
return isSettingsPreference ? SettingsThemeHelper.isExpressiveTheme(context) :
ThemeHelper.shouldApplyGlifExpressiveStyle(context);
}
private static String capitalize(final String input) {

View File

@@ -128,6 +128,16 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
mIllustrationLottie.setAnimation(R.raw.face_education_lottie);
mIllustrationLottie.setVisibility(View.VISIBLE);
mIllustrationLottie.playAnimation();
mIllustrationLottie.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mIllustrationLottie.isAnimating()) {
mIllustrationLottie.pauseAnimation();
} else {
mIllustrationLottie.resumeAnimation();
}
}
});
}
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);

View File

@@ -37,6 +37,7 @@ import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.AmbientVolumeUi;
import com.android.settingslib.widget.SliderPreference;
@@ -58,6 +59,10 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV
private static final int ORDER_AMBIENT_VOLUME_CONTROL_UNIFIED = 0;
private static final int ORDER_AMBIENT_VOLUME_CONTROL_SEPARATED = 1;
private static final String METRIC_KEY_AMBIENT_SLIDER = "ambient_slider";
private static final String METRIC_KEY_AMBIENT_MUTE = "ambient_mute";
private static final String METRIC_KEY_AMBIENT_EXPAND = "ambient_expand";
@Nullable
private AmbientVolumeUiListener mListener;
@Nullable
@@ -71,12 +76,17 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV
private final BiMap<Integer, SliderPreference> mSideToSliderMap = HashBiMap.create();
private int mVolumeLevel = AMBIENT_VOLUME_LEVEL_DEFAULT;
private int mMetricsCategory;
private final OnPreferenceChangeListener mPreferenceChangeListener =
(slider, v) -> {
if (slider instanceof SliderPreference && v instanceof final Integer value) {
final Integer side = mSideToSliderMap.inverse().get(slider);
if (mListener != null && side != null) {
mListener.onSliderValueChange(side, value);
if (side != null) {
logMetrics(METRIC_KEY_AMBIENT_SLIDER, side);
if (mListener != null) {
mListener.onSliderValueChange(side, value);
}
}
return true;
}
@@ -106,6 +116,7 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV
return;
}
setMuted(!mMuted);
logMetrics(METRIC_KEY_AMBIENT_MUTE, mMuted ? 1 : 0);
if (mListener != null) {
mListener.onAmbientVolumeIconClick();
}
@@ -115,6 +126,7 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV
mExpandIcon = holder.itemView.requireViewById(R.id.expand_icon);
mExpandIcon.setOnClickListener(v -> {
setExpanded(!mExpanded);
logMetrics(METRIC_KEY_AMBIENT_EXPAND, mExpanded ? 1 : 0);
if (mListener != null) {
mListener.onExpandIconClick();
}
@@ -249,6 +261,15 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV
updateVolumeLevel();
}
/** Sets the metrics category. */
public void setMetricsCategory(int category) {
mMetricsCategory = category;
}
private int getMetricsCategory() {
return mMetricsCategory;
}
private void updateVolumeLevel() {
int leftLevel, rightLevel;
if (mExpanded) {
@@ -329,4 +350,9 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV
Map<Integer, SliderPreference> getSliders() {
return mSideToSliderMap;
}
private void logMetrics(String key, int value) {
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().changed(
getMetricsCategory(), key, value);
}
}

View File

@@ -32,6 +32,7 @@ import com.android.settingslib.bluetooth.AmbientVolumeUiController;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -83,6 +84,9 @@ public class BluetoothDetailsAmbientVolumePreferenceController extends Bluetooth
mPreference = new AmbientVolumePreference(deviceControls.getContext());
mPreference.setKey(KEY_AMBIENT_VOLUME);
mPreference.setOrder(ORDER_AMBIENT_VOLUME);
if (mFragment instanceof Instrumentable) {
mPreference.setMetricsCategory(((Instrumentable) mFragment).getMetricsCategory());
}
deviceControls.addPreference(mPreference);
mAmbientUiController = new AmbientVolumeUiController(mContext, mBluetoothManager,

View File

@@ -21,10 +21,13 @@ import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -33,11 +36,10 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
public class RebootConfirmationDialogFragment extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
private static final String TAG = "FreeformPrefRebootDlg";
@VisibleForTesting
static final String TAG = "DevOptionRebootDlg";
private final int mMessageId;
private final int mCancelButtonId;
private final RebootConfirmationDialogHost mHost;
private RebootConfirmationDialogViewModel mViewModel;
/** Show an instance of this dialog. */
public static void show(Fragment fragment, int messageId, RebootConfirmationDialogHost host) {
@@ -50,55 +52,60 @@ public class RebootConfirmationDialogFragment extends InstrumentedDialogFragment
int messageId,
int cancelButtonId,
RebootConfirmationDialogHost host) {
final FragmentManager manager = fragment.getActivity().getSupportFragmentManager();
final FragmentManager manager = fragment.requireActivity().getSupportFragmentManager();
if (manager.findFragmentByTag(TAG) == null) {
final RebootConfirmationDialogFragment dialog =
new RebootConfirmationDialogFragment(messageId, cancelButtonId, host);
new RebootConfirmationDialogFragment();
RebootConfirmationDialogViewModel mViewModel = new ViewModelProvider(
fragment.requireActivity()).get(
RebootConfirmationDialogViewModel.class);
mViewModel.setMessageId(messageId);
mViewModel.setCancelButtonId(cancelButtonId);
mViewModel.setHost(host);
dialog.show(manager, TAG);
}
}
@VisibleForTesting
RebootConfirmationDialogFragment(
int messageId, int cancelButtonId, RebootConfirmationDialogHost host) {
mMessageId = messageId;
mCancelButtonId = cancelButtonId;
mHost = host;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.REBOOT_CONFIRMATION_DIALOG;
}
@Override
public Dialog onCreateDialog(Bundle savedInstances) {
return new AlertDialog.Builder(getActivity())
.setMessage(mMessageId)
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewModel = new ViewModelProvider(requireActivity()).get(
RebootConfirmationDialogViewModel.class);
}
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstances) {
int messageId = mViewModel.getMessageId();
int cancelButtonId = mViewModel.getCancelButtonId();
return new AlertDialog.Builder(requireActivity())
.setMessage(messageId)
.setPositiveButton(R.string.reboot_dialog_reboot_now, this)
.setNegativeButton(mCancelButtonId, this)
.setNegativeButton(cancelButtonId, this)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
RebootConfirmationDialogHost host = mViewModel.getHost();
if (host == null) return;
if (which == DialogInterface.BUTTON_POSITIVE) {
mHost.onRebootConfirmed(getContext());
host.onRebootConfirmed(requireContext());
} else {
mHost.onRebootCancelled();
host.onRebootCancelled();
}
}
@Override
public void onDismiss(DialogInterface dialog) {
public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog);
mHost.onRebootDialogDismissed();
}
@Override
public void onPause() {
dismiss();
super.onPause();
RebootConfirmationDialogHost host = mViewModel.getHost();
if (host != null) {
host.onRebootDialogDismissed();
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.development;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModel;
/**
* {@link ViewModel} for the reboot confirmation dialog.
*
* This class holds the data necessary to display a confirmation dialog for reboots.
*/
public class RebootConfirmationDialogViewModel extends ViewModel {
@Nullable
private RebootConfirmationDialogHost mHost = null;
private int mMessageId;
private int mCancelButtonId;
@Nullable
public RebootConfirmationDialogHost getHost() {
return mHost;
}
public void setHost(RebootConfirmationDialogHost mHost) {
this.mHost = mHost;
}
public int getMessageId() {
return mMessageId;
}
public void setMessageId(int mMessageId) {
this.mMessageId = mMessageId;
}
public int getCancelButtonId() {
return mCancelButtonId;
}
public void setCancelButtonId(int mCancelButtonId) {
this.mCancelButtonId = mCancelButtonId;
}
}

View File

@@ -49,7 +49,7 @@ public class BatterySaverScheduleSeekBarController
implements OnPreferenceChangeListener, OnSeekBarChangeListener {
public static final int MAX_SEEKBAR_VALUE = 15;
public static final int MIN_SEEKBAR_VALUE = 2;
public static final int MIN_SEEKBAR_VALUE = 4;
public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar";
private static final int LEVEL_UNIT_SCALE = 5;

View File

@@ -59,7 +59,7 @@ public class NetworkDashboardFragment extends DashboardFragment implements
public void onAttach(Context context) {
super.onAttach(context);
if (isCatalystEnabled()) {
if (!isCatalystEnabled()) {
use(AirplaneModePreferenceController.class).setFragment(this);
}
use(NetworkProviderCallsSmsController.class).init(this);
@@ -104,7 +104,7 @@ public class NetworkDashboardFragment extends DashboardFragment implements
switch (requestCode) {
case AirplaneModePreferenceController.REQUEST_CODE_EXIT_ECM:
if (isCatalystEnabled()) {
if (!isCatalystEnabled()) {
use(AirplaneModePreferenceController.class)
.onActivityResult(requestCode, resultCode, data);
}

View File

@@ -131,6 +131,7 @@ public class PoliteNotifWorkProfileToggleController extends TogglePreferenceCont
public void updateState(@Nullable Preference preference) {
if (preference == null) return;
preference.setVisible(isAvailable());
super.updateState(preference);
}
private boolean isCoolDownEnabledForPrimary() {

View File

@@ -0,0 +1,115 @@
/*
* 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.accessibility
import android.app.settings.SettingsEnums
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.SettingsActivity
import com.android.settings.SubSettings
import com.android.settings.accessibility.ColorInversionPreference.Companion.PREFERENCE_KEY
import com.android.settings.accessibility.ColorInversionPreference.Companion.SETTING_KEY
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.robolectric.shadows.ShadowLooper
@RunWith(AndroidJUnit4::class)
class ColorInversionPreferenceTest {
private val mockLifecycleContext = mock<PreferenceLifecycleContext>()
private val appContext: Context = ApplicationProvider.getApplicationContext()
private val colorInversionPreference = ColorInversionPreference()
@Before
fun setUp() {
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.OFF)
}
@Test
fun getIntent_returnColorInversionScreenIntent() {
val intent = colorInversionPreference.intent(appContext)
assertThat(intent).isNotNull()
assertThat(intent!!.action).isEqualTo(Intent.ACTION_MAIN)
assertThat(intent.component).isEqualTo(ComponentName(appContext, SubSettings::class.java))
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
ToggleColorInversionPreferenceFragment::class.java.name
)
assertThat(
intent.getIntExtra(
MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
0
)
).isEqualTo(
SettingsEnums.ACCESSIBILITY_COLOR_AND_MOTION
)
}
@Test
fun onStart_settingChanges_notifyPrefChange() {
colorInversionPreference.onStart(mockLifecycleContext)
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.ON)
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
verify(mockLifecycleContext, times(1)).notifyPreferenceChange(PREFERENCE_KEY)
}
@Test
fun onStop_settingChanges_doNotNotifyPrefChange() {
colorInversionPreference.onStop(mockLifecycleContext)
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.ON)
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
verify(mockLifecycleContext, never()).notifyPreferenceChange(any())
}
@Test
fun getSummary_colorInversionOn_verifySummary() {
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.ON)
assertThat(colorInversionPreference.getPreferenceSummary(appContext)).isEqualTo(
appContext.getText(
R.string.color_inversion_state_on
)
)
}
@Test
fun getSummary_colorInversionOff_verifySummary() {
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.OFF)
assertThat(colorInversionPreference.getPreferenceSummary(appContext)).isEqualTo(
appContext.getText(
R.string.color_inversion_state_off
)
)
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.accessibility
import android.app.settings.SettingsEnums
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.SettingsActivity
import com.android.settings.SubSettings
import com.android.settings.accessibility.DaltonizerPreference.Companion.PREFERENCE_KEY
import com.android.settings.accessibility.DaltonizerPreference.Companion.SETTING_KEY
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider
import com.android.settingslib.datastore.SettingsSecureStore
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.robolectric.shadows.ShadowLooper
@RunWith(AndroidJUnit4::class)
class DaltonizerPreferenceTest {
private val mockLifecycleContext = mock<PreferenceLifecycleContext>()
private val appContext: Context = ApplicationProvider.getApplicationContext()
private val daltonizerPreference = DaltonizerPreference()
@Before
fun setUp() {
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.OFF)
}
@Test
fun getIntent_returnDaltonizerScreenIntent() {
val intent = daltonizerPreference.intent(appContext)
assertThat(intent).isNotNull()
assertThat(intent!!.action).isEqualTo(Intent.ACTION_MAIN)
assertThat(intent.component).isEqualTo(ComponentName(appContext, SubSettings::class.java))
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
ToggleDaltonizerPreferenceFragment::class.java.name
)
assertThat(
intent.getIntExtra(
MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
0
)
).isEqualTo(
SettingsEnums.ACCESSIBILITY_COLOR_AND_MOTION
)
}
@Test
fun onStart_settingChanges_notifyPrefChange() {
daltonizerPreference.onStart(mockLifecycleContext)
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.ON)
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
verify(mockLifecycleContext, times(1)).notifyPreferenceChange(PREFERENCE_KEY)
}
@Test
fun onStop_settingChanges_doNotNotifyPrefChange() {
daltonizerPreference.onStop(mockLifecycleContext)
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.ON)
ShadowLooper.runUiThreadTasksIncludingDelayedTasks()
verify(mockLifecycleContext, never()).notifyPreferenceChange(any())
}
@Test
fun getSummary_daltonizerOn_verifySummary() {
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.ON)
assertThat(daltonizerPreference.getPreferenceSummary(appContext)).isEqualTo(
appContext.getText(
R.string.daltonizer_state_on
)
)
}
@Test
fun getSummary_daltonizerOff_verifySummary() {
SettingsSecureStore.get(appContext).setInt(SETTING_KEY, AccessibilityUtil.State.OFF)
assertThat(daltonizerPreference.getPreferenceSummary(appContext)).isEqualTo(
appContext.getText(
R.string.daltonizer_state_off
)
)
}
}

View File

@@ -16,7 +16,22 @@
package com.android.settings.development;
import static android.os.Looper.getMainLooper;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import android.content.DialogInterface;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import com.android.settings.R;
@@ -24,38 +39,124 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.androidx.fragment.FragmentController;
import org.robolectric.shadows.ShadowDialog;
@RunWith(RobolectricTestRunner.class)
public class RebootConfirmationDialogFragmentTest {
private RebootConfirmationDialogFragment mFragment;
@Mock
RebootConfirmationDialogHost mRebootConfirmationDialogHost;
private RebootConfirmationDialogHost mHost;
private FragmentActivity mActivity;
private Fragment mFragment;
private FragmentManager mFragmentManager;
private RebootConfirmationDialogViewModel mViewModel;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
RebootConfirmationDialogFragment dialogFragment =
FragmentController.setupFragment(
new RebootConfirmationDialogFragment(
R.string.reboot_dialog_override_desktop_mode,
R.string.reboot_dialog_reboot_later,
mRebootConfirmationDialogHost),
FragmentActivity.class,
0 /* containerViewId= */, null /* bundle= */);
mFragment = Mockito.spy(dialogFragment);
mActivity = Robolectric.buildActivity(FragmentActivity.class).create().get();
mFragmentManager = mActivity.getSupportFragmentManager();
mFragment = new Fragment();
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.add(mFragment, "testFragment");
transaction.commit();
mFragmentManager.executePendingTransactions();
}
@Test
public void onPause_shouldDismissDialog() {
mFragment.onPause();
public void show_shouldCreateAndShowDialog() {
RebootConfirmationDialogFragment.show(mFragment,
R.string.reboot_dialog_override_desktop_mode, mHost);
shadowOf(getMainLooper()).idle();
Mockito.verify(mFragment).dismiss();
RebootConfirmationDialogFragment dialogFragment =
(RebootConfirmationDialogFragment) mFragmentManager.findFragmentByTag(
RebootConfirmationDialogFragment.TAG);
assertThat(dialogFragment).isNotNull();
assertThat(dialogFragment.getShowsDialog()).isTrue();
}
@Test
public void show_shouldStoreViewModel() {
RebootConfirmationDialogFragment.show(mFragment,
R.string.reboot_dialog_override_desktop_mode, R.string.reboot_dialog_reboot_later,
mHost);
shadowOf(getMainLooper()).idle();
mViewModel = new ViewModelProvider(mActivity).get(RebootConfirmationDialogViewModel.class);
assertThat(mViewModel.getHost()).isEqualTo(mHost);
assertThat(mViewModel.getMessageId()).isEqualTo(
R.string.reboot_dialog_override_desktop_mode);
assertThat(mViewModel.getCancelButtonId()).isEqualTo(R.string.reboot_dialog_reboot_later);
}
@Test
public void onCreateDialog_shouldCreateAlertDialogFromViewModel() {
RebootConfirmationDialogFragment dialogFragment = new RebootConfirmationDialogFragment();
dialogFragment.show(mFragmentManager, RebootConfirmationDialogFragment.TAG);
shadowOf(getMainLooper()).idle();
// Set up ViewModel
mViewModel = new ViewModelProvider(mActivity).get(RebootConfirmationDialogViewModel.class);
mViewModel.setMessageId(R.string.reboot_dialog_override_desktop_mode);
mViewModel.setCancelButtonId(R.string.reboot_dialog_reboot_later);
mViewModel.setHost(mHost);
dialogFragment.onCreateDialog(null).show();
shadowOf(getMainLooper()).idle();
AlertDialog alertDialog = (AlertDialog) ShadowDialog.getLatestDialog();
TextView messageView = alertDialog.findViewById(android.R.id.message);
assertThat(messageView.getText().toString()).isEqualTo(
mActivity.getString(R.string.reboot_dialog_override_desktop_mode));
assertThat(alertDialog.getButton(
DialogInterface.BUTTON_POSITIVE).getText().toString()).isEqualTo(
mActivity.getString(R.string.reboot_dialog_reboot_now));
assertThat(alertDialog.getButton(
DialogInterface.BUTTON_NEGATIVE).getText().toString()).isEqualTo(
mActivity.getString(R.string.reboot_dialog_reboot_later));
}
@Test
public void onClick_positiveButton_shouldCallRebootConfirmed() {
RebootConfirmationDialogFragment dialogFragment = showDialog();
AlertDialog alertDialog = (AlertDialog) ShadowDialog.getLatestDialog();
dialogFragment.onClick(alertDialog, DialogInterface.BUTTON_POSITIVE);
verify(mHost).onRebootConfirmed(mActivity);
}
@Test
public void onClick_negativeButton_shouldCallRebootCancelled() {
RebootConfirmationDialogFragment dialogFragment = showDialog();
AlertDialog alertDialog = (AlertDialog) ShadowDialog.getLatestDialog();
dialogFragment.onClick(alertDialog, DialogInterface.BUTTON_NEGATIVE);
verify(mHost).onRebootCancelled();
}
@Test
public void onDismiss_shouldCallRebootDialogDismissed() {
RebootConfirmationDialogFragment dialogFragment = showDialog();
dialogFragment.onDismiss(null);
verify(mHost).onRebootDialogDismissed();
}
private RebootConfirmationDialogFragment showDialog() {
RebootConfirmationDialogFragment dialogFragment = new RebootConfirmationDialogFragment();
dialogFragment.show(mFragmentManager, RebootConfirmationDialogFragment.TAG);
shadowOf(getMainLooper()).idle();
mViewModel = new ViewModelProvider(mActivity).get(RebootConfirmationDialogViewModel.class);
mViewModel.setMessageId(R.string.reboot_dialog_override_desktop_mode);
mViewModel.setCancelButtonId(R.string.reboot_dialog_reboot_later);
mViewModel.setHost(mHost);
dialogFragment.onCreateDialog(null).show();
shadowOf(getMainLooper()).idle();
return dialogFragment;
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.development;
import static com.google.common.truth.Truth.assertThat;
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;
@RunWith(RobolectricTestRunner.class)
public class RebootConfirmationDialogViewModelTest {
@Mock
private RebootConfirmationDialogHost mHost;
private RebootConfirmationDialogViewModel mViewModel;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mViewModel = new RebootConfirmationDialogViewModel();
}
@Test
public void getHost_returnsSetHost() {
mViewModel.setHost(mHost);
assertThat(mViewModel.getHost()).isEqualTo(mHost);
}
@Test
public void getMessageId_returnsSetMessageId() {
mViewModel.setMessageId(123);
assertThat(mViewModel.getMessageId()).isEqualTo(123);
}
@Test
public void getCancelButtonId_returnsSetCancelButtonId() {
mViewModel.setCancelButtonId(456);
assertThat(mViewModel.getCancelButtonId()).isEqualTo(456);
}
}

View File

@@ -54,6 +54,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowSystemProperties;
@@ -74,8 +75,6 @@ public class DesktopExperiencePreferenceControllerTest {
@Mock
private DevelopmentSettingsDashboardFragment mFragment;
@Mock
private FragmentActivity mActivity;
@Mock
private FragmentManager mFragmentManager;
@Mock
private FragmentTransaction mTransaction;
@@ -88,10 +87,12 @@ public class DesktopExperiencePreferenceControllerTest {
public void setup() {
MockitoAnnotations.initMocks(this);
FragmentActivity activity = spy(Robolectric.buildActivity(
FragmentActivity.class).create().get());
mContext = spy(ApplicationProvider.getApplicationContext());
doReturn(mTransaction).when(mFragmentManager).beginTransaction();
doReturn(mFragmentManager).when(mActivity).getSupportFragmentManager();
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mFragmentManager).when(activity).getSupportFragmentManager();
doReturn(activity).when(mFragment).requireActivity();
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);

View File

@@ -54,6 +54,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowSystemProperties;
@@ -76,8 +77,6 @@ public class DesktopModePreferenceControllerTest {
@Mock
private DevelopmentSettingsDashboardFragment mFragment;
@Mock
private FragmentActivity mActivity;
@Mock
private FragmentManager mFragmentManager;
@Mock
private FragmentTransaction mTransaction;
@@ -90,10 +89,13 @@ public class DesktopModePreferenceControllerTest {
public void setup() {
MockitoAnnotations.initMocks(this);
FragmentActivity activity = spy(Robolectric.buildActivity(
FragmentActivity.class).create().get());
mContext = spy(ApplicationProvider.getApplicationContext());
doReturn(mTransaction).when(mFragmentManager).beginTransaction();
doReturn(mFragmentManager).when(mActivity).getSupportFragmentManager();
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mFragmentManager).when(activity).getSupportFragmentManager();
doReturn(activity).when(mFragment).requireActivity();
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);

View File

@@ -54,6 +54,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@@ -76,8 +77,6 @@ public class DesktopModeSecondaryDisplayPreferenceControllerTest {
@Mock
private DevelopmentSettingsDashboardFragment mFragment;
@Mock
private FragmentActivity mActivity;
@Mock
private FragmentManager mFragmentManager;
@Mock
private FragmentTransaction mTransaction;
@@ -89,12 +88,14 @@ public class DesktopModeSecondaryDisplayPreferenceControllerTest {
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
FragmentActivity activity = spy(Robolectric.buildActivity(
FragmentActivity.class).create().get());
mContext = spy(RuntimeEnvironment.application);
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);
doReturn(mTransaction).when(mFragmentManager).beginTransaction();
doReturn(mFragmentManager).when(mActivity).getSupportFragmentManager();
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mFragmentManager).when(activity).getSupportFragmentManager();
doReturn(activity).when(mFragment).requireActivity();
mController = new DesktopModeSecondaryDisplayPreferenceController(mContext, mFragment);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);

View File

@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,6 +54,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@@ -78,8 +80,6 @@ public class FreeformWindowsPreferenceControllerTest {
@Mock
private DevelopmentSettingsDashboardFragment mFragment;
@Mock
private FragmentActivity mActivity;
@Mock
private FragmentManager mFragmentManager;
@Mock
private FragmentTransaction mTransaction;
@@ -89,9 +89,11 @@ public class FreeformWindowsPreferenceControllerTest {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
FragmentActivity activity = spy(Robolectric.buildActivity(
FragmentActivity.class).create().get());
doReturn(mTransaction).when(mFragmentManager).beginTransaction();
doReturn(mFragmentManager).when(mActivity).getSupportFragmentManager();
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mFragmentManager).when(activity).getSupportFragmentManager();
doReturn(activity).when(mFragment).requireActivity();
doReturn(true).when(mResources).getBoolean(R.bool.config_isDesktopModeSupported);
doReturn(true).when(mResources).getBoolean(R.bool.config_canInternalDisplayHostDesktops);
mController = new FreeformWindowsPreferenceController(mContext, mFragment);

View File

@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +46,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@@ -59,7 +61,6 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest {
@Mock private PreferenceScreen mScreen;
@Mock private SwitchPreference mPreference;
@Mock private DevelopmentSettingsDashboardFragment mFragment;
@Mock private FragmentActivity mActivity;
@Mock private FragmentManager mFragmentManager;
@Mock private FragmentTransaction mTransaction;
@@ -70,11 +71,13 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FragmentActivity activity = spy(Robolectric.buildActivity(
FragmentActivity.class).create().get());
mContext = RuntimeEnvironment.application;
ShadowSystemProperties.override(PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION, "true");
doReturn(mTransaction).when(mFragmentManager).beginTransaction();
doReturn(mFragmentManager).when(mActivity).getSupportFragmentManager();
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mFragmentManager).when(activity).getSupportFragmentManager();
doReturn(activity).when(mFragment).requireActivity();
mController = new GraphicsDriverEnableAngleAsSystemDriverController(mContext, mFragment);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);

View File

@@ -95,7 +95,7 @@ public class BatterySaverScheduleSeekBarControllerTest {
@Test
public void updateSeekBar_percentageMode_hasCorrectProperties() {
final CharSequence expectedTitle = "10%";
final CharSequence expectedTitle = "20%";
Settings.Global.putInt(
mResolver,
Global.AUTOMATIC_POWER_SAVE_MODE,

View File

@@ -15,10 +15,19 @@
*/
package com.android.settings.network;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.settings.flags.Flags.FLAG_CATALYST;
import static com.android.settings.network.AirplaneModePreferenceController.REQUEST_CODE_EXIT_ECM;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
import android.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.SearchIndexableResource;
import androidx.test.core.app.ApplicationProvider;
@@ -28,21 +37,29 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settingslib.drawer.CategoryKey;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class NetworkDashboardFragmentTest {
private Context mContext;
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@Spy
private Context mContext = ApplicationProvider.getApplicationContext();
private NetworkDashboardFragment mFragment;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
instrumentation.runOnMainSync(() -> {
@@ -65,4 +82,13 @@ public class NetworkDashboardFragmentTest {
assertThat(indexRes).hasSize(1);
assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
}
@Test
public void onActivityResult_catalystIsEnabled_doNotCrash() {
mSetFlagsRule.enableFlags(FLAG_CATALYST);
NetworkDashboardFragment spyFragment = spy(mFragment);
when(spyFragment.getContext()).thenReturn(mContext);
spyFragment.onActivityResult(REQUEST_CODE_EXIT_ECM, 0, null);
}
}