diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c4bf977f0b8..0a40c89d84b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -182,6 +182,7 @@ @@ -3713,6 +3714,7 @@ android:name="Settings$GestureNavigationSettingsActivity" android:label="@string/gesture_settings_activity_title" android:exported="true" + android:theme="@style/Theme.SubSettings" android:enabled="true"> diff --git a/color-check-baseline.xml b/color-check-baseline.xml index 48a204f2cd2..b9f5009ba20 100644 --- a/color-check-baseline.xml +++ b/color-check-baseline.xml @@ -2424,7 +2424,7 @@ errorLine1=" android:fillColor="@color/face_intro_outline"" errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/res/drawable/face_enroll_introduction.xml b/res/drawable/face_enroll_icon_large.xml similarity index 86% rename from res/drawable/face_enroll_introduction.xml rename to res/drawable/face_enroll_icon_large.xml index 217b13c2ff2..578a4a9b741 100644 --- a/res/drawable/face_enroll_introduction.xml +++ b/res/drawable/face_enroll_icon_large.xml @@ -19,26 +19,27 @@ android:width="300dp" android:height="300dp" android:viewportWidth="300" - android:viewportHeight="300"> + android:viewportHeight="300" + android:tint="@color/face_enroll_icon_color"> \ No newline at end of file diff --git a/res/drawable/ic_power_settings.xml b/res/drawable/ic_power_settings.xml new file mode 100644 index 00000000000..b56e3328474 --- /dev/null +++ b/res/drawable/ic_power_settings.xml @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/res/layout/face_enroll_education.xml b/res/layout/face_enroll_education.xml index 01494790c97..a8455da9507 100644 --- a/res/layout/face_enroll_education.xml +++ b/res/layout/face_enroll_education.xml @@ -63,7 +63,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="invisible" - android:background="@drawable/face_enroll_introduction"/> + android:background="@drawable/face_enroll_icon_large"/> diff --git a/res/layout/face_enroll_finish.xml b/res/layout/face_enroll_finish.xml index d3c04796599..30a6957e66c 100644 --- a/res/layout/face_enroll_finish.xml +++ b/res/layout/face_enroll_finish.xml @@ -47,7 +47,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@null" - android:src="@drawable/face_enroll_introduction" /> + android:src="@drawable/face_enroll_icon_large" /> diff --git a/res/layout/preference_animated_image.xml b/res/layout/preference_animated_image.xml index e7d9b52518a..0ab8adf275d 100644 --- a/res/layout/preference_animated_image.xml +++ b/res/layout/preference_animated_image.xml @@ -31,4 +31,14 @@ android:focusable="false" android:clickable="false" android:adjustViewBounds="true"/> + + \ No newline at end of file diff --git a/res/values-mcc234/strings.xml b/res/values-mcc234/strings.xml new file mode 100644 index 00000000000..f7d0d33d0b4 --- /dev/null +++ b/res/values-mcc234/strings.xml @@ -0,0 +1,18 @@ + + + + Emergency alerts + \ No newline at end of file diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml index 3cc23c323f5..f262104eb8e 100644 --- a/res/values-night/colors.xml +++ b/res/values-night/colors.xml @@ -27,7 +27,7 @@ #AECBFA #5F6368 #202124 - ?android:attr/colorAccent + @android:color/black @color/palette_list_dark_mode_color_red diff --git a/res/values/colors.xml b/res/values/colors.xml index edef6ef257b..0b26c136b82 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -152,7 +152,7 @@ ?androidprv:attr/colorSurfaceHighlight - #ffdadce0 + #42a5f5 #4182ef diff --git a/res/values/strings.xml b/res/values/strings.xml index 05d2b4f5f6d..b99dfc71675 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5179,7 +5179,7 @@ Accessibility settings - Screen readers, interaction controls + Display, interaction, audio Vision Settings diff --git a/res/xml/gestures.xml b/res/xml/gestures.xml index f18d8a55a0b..1f4895eff76 100644 --- a/res/xml/gestures.xml +++ b/res/xml/gestures.xml @@ -93,10 +93,4 @@ android:title="@string/gesture_prevent_ringing_screen_title" android:fragment="com.android.settings.gestures.PreventRingingGestureSettings" settings:controller="com.android.settings.gestures.PreventRingingParentPreferenceController" /> - - diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml index 9228ddd5f5a..67377753f33 100644 --- a/res/xml/system_dashboard_fragment.xml +++ b/res/xml/system_dashboard_fragment.xml @@ -43,6 +43,14 @@ android:fragment="com.android.settings.datetime.DateTimeSettings" settings:controller="com.android.settings.datetime.DateTimePreferenceController"/> + + Accessibility > Any toggle service > Shortcut > Settings. */ + int EDIT_SHORTCUT = 1; + + /** OPEN: Settings > Accessibility > Magnification > Shortcut > Settings. */ + int MAGNIFICATION_EDIT_SHORTCUT = 1001; + + /** + * OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to + * enable service. + */ + int ENABLE_WARNING_FROM_TOGGLE = 1002; + + /** OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox. */ + int ENABLE_WARNING_FROM_SHORTCUT = 1003; + + /** + * OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox + * toggle. + */ + int ENABLE_WARNING_FROM_SHORTCUT_TOGGLE = 1004; + + /** + * OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to + * disable service. + */ + int DISABLE_WARNING_FROM_TOGGLE = 1005; + + /** + * OPEN: Settings > Accessibility > Magnification > Toggle user service in button + * navigation. + */ + int ACCESSIBILITY_BUTTON_TUTORIAL = 1006; + + /** + * OPEN: Settings > Accessibility > Magnification > Toggle user service in gesture + * navigation. + */ + int GESTURE_NAVIGATION_TUTORIAL = 1007; + + /** + * OPEN: Settings > Accessibility > Downloaded toggle service > Toggle user service > Show + * launch tutorial. + */ + int LAUNCH_ACCESSIBILITY_TUTORIAL = 1008; + } /** * IntDef enum for dialog type that indicates different dialog for user to choose the shortcut @@ -77,7 +127,7 @@ public class AccessibilityEditDialogUtils { DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT, }) - public @interface DialogType { + public @interface DialogType { int EDIT_SHORTCUT_GENERIC = 0; int EDIT_SHORTCUT_GENERIC_SUW = 1; int EDIT_SHORTCUT_MAGNIFICATION = 2; diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java new file mode 100644 index 00000000000..709e1657b7c --- /dev/null +++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java @@ -0,0 +1,366 @@ +/* + * 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.accessibility; + +import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; + +import android.app.Dialog; +import android.app.settings.SettingsEnums; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.icu.text.CaseMap; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.provider.Settings; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; +import android.widget.CheckBox; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.utils.LocaleUtils; + +import com.google.android.setupcompat.util.WizardManagerHelper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Base class for accessibility fragments shortcut functions and dialog management. + */ +public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPreferenceFragment + implements ShortcutPreference.OnClickCallback { + private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference"; + protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type"; + protected static final int NOT_SET = -1; + // Save user's shortcutType value when savedInstance has value (e.g. device rotated). + protected int mSavedCheckBoxValue = NOT_SET; + + protected ShortcutPreference mShortcutPreference; + private AccessibilityManager.TouchExplorationStateChangeListener + mTouchExplorationStateChangeListener; + private SettingsContentObserver mSettingsContentObserver; + private CheckBox mSoftwareTypeCheckBox; + private CheckBox mHardwareTypeCheckBox; + + /** Returns the accessibility component name. */ + protected abstract ComponentName getComponentName(); + + /** Returns the accessibility feature name. */ + protected abstract CharSequence getLabelName(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Restore the user shortcut type. + if (savedInstanceState != null && savedInstanceState.containsKey( + KEY_SAVED_USER_SHORTCUT_TYPE)) { + mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE, NOT_SET); + } + + final int resId = getPreferenceScreenResId(); + if (resId <= 0) { + final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen( + getPrefContext()); + setPreferenceScreen(preferenceScreen); + } + + final List shortcutFeatureKeys = new ArrayList<>(); + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + mSettingsContentObserver = new SettingsContentObserver(new Handler(), shortcutFeatureKeys) { + @Override + public void onChange(boolean selfChange, Uri uri) { + updateShortcutPreferenceData(); + updateShortcutPreference(); + } + }; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mShortcutPreference = new ShortcutPreference(getPrefContext(), /* attrs= */ null); + mShortcutPreference.setPersistent(false); + mShortcutPreference.setKey(getShortcutPreferenceKey()); + mShortcutPreference.setOnClickCallback(this); + + final CharSequence title = getString(R.string.accessibility_shortcut_title, getLabelName()); + mShortcutPreference.setTitle(title); + getPreferenceScreen().addPreference(mShortcutPreference); + + mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> { + removeDialog(DialogEnums.EDIT_SHORTCUT); + mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); + }; + + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + final AccessibilityManager am = getPrefContext().getSystemService( + AccessibilityManager.class); + am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener); + mSettingsContentObserver.register(getContentResolver()); + updateShortcutPreferenceData(); + updateShortcutPreference(); + } + + @Override + public void onPause() { + final AccessibilityManager am = getPrefContext().getSystemService( + AccessibilityManager.class); + am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener); + mSettingsContentObserver.unregister(getContentResolver()); + super.onPause(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + final int value = getShortcutTypeCheckBoxValue(); + if (value != NOT_SET) { + outState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, value); + } + super.onSaveInstanceState(outState); + } + + @Override + public Dialog onCreateDialog(int dialogId) { + switch (dialogId) { + case DialogEnums.EDIT_SHORTCUT: + final CharSequence dialogTitle = getPrefContext().getString( + R.string.accessibility_shortcut_title, getLabelName()); + final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent()) + ? AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC_SUW : + AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC; + final Dialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( + getPrefContext(), dialogType, dialogTitle, + this::callOnAlertDialogCheckboxClicked); + setupEditShortcutDialog(dialog); + return dialog; + default: + throw new IllegalArgumentException("Unsupported dialogId " + dialogId); + } + } + + @Override + public int getDialogMetricsCategory(int dialogId) { + switch (dialogId) { + case DialogEnums.EDIT_SHORTCUT: + return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT; + default: + return SettingsEnums.ACTION_UNKNOWN; + } + } + + @Override + public void onSettingsClicked(ShortcutPreference preference) { + showDialog(DialogEnums.EDIT_SHORTCUT); + } + + @Override + public void onToggleClicked(ShortcutPreference preference) { + if (getComponentName() == null) { + return; + } + + final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(), + getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE); + if (preference.isChecked()) { + AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, + getComponentName()); + showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); + } else { + AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, + getComponentName()); + } + mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); + } + + protected String getShortcutPreferenceKey() { + return KEY_SHORTCUT_PREFERENCE; + } + + @VisibleForTesting + void setupEditShortcutDialog(Dialog dialog) { + final View dialogSoftwareView = dialog.findViewById(R.id.software_shortcut); + mSoftwareTypeCheckBox = dialogSoftwareView.findViewById(R.id.checkbox); + setDialogTextAreaClickListener(dialogSoftwareView, mSoftwareTypeCheckBox); + + final View dialogHardwareView = dialog.findViewById(R.id.hardware_shortcut); + mHardwareTypeCheckBox = dialogHardwareView.findViewById(R.id.checkbox); + setDialogTextAreaClickListener(dialogHardwareView, mHardwareTypeCheckBox); + + updateEditShortcutDialogCheckBox(); + } + + /** + * Returns accumulated {@link AccessibilityUtil.UserShortcutType} checkbox value or + * {@code NOT_SET} if checkboxes did not exist. + */ + protected int getShortcutTypeCheckBoxValue() { + if (mSoftwareTypeCheckBox == null || mHardwareTypeCheckBox == null) { + return NOT_SET; + } + + int value = AccessibilityUtil.UserShortcutType.EMPTY; + if (mSoftwareTypeCheckBox.isChecked()) { + value |= AccessibilityUtil.UserShortcutType.SOFTWARE; + } + if (mHardwareTypeCheckBox.isChecked()) { + value |= AccessibilityUtil.UserShortcutType.HARDWARE; + } + return value; + } + + + /** + * This method will be invoked when a button in the edit shortcut dialog is clicked. + * + * @param dialog The dialog that received the click + * @param which The button that was clicked + */ + protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) { + if (getComponentName() == null) { + return; + } + + final int value = getShortcutTypeCheckBoxValue(); + + saveNonEmptyUserShortcutType(value); + AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, getComponentName()); + AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, getComponentName()); + mShortcutPreference.setChecked(value != AccessibilityUtil.UserShortcutType.EMPTY); + mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); + } + + @VisibleForTesting + void saveNonEmptyUserShortcutType(int type) { + if (type == AccessibilityUtil.UserShortcutType.EMPTY) { + return; + } + + final PreferredShortcut shortcut = new PreferredShortcut( + getComponentName().flattenToString(), type); + PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut); + } + + private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) { + final View dialogTextArea = dialogView.findViewById(R.id.container); + dialogTextArea.setOnClickListener(v -> checkBox.toggle()); + } + + protected CharSequence getShortcutTypeSummary(Context context) { + if (!mShortcutPreference.isSettingsEditable()) { + return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware); + } + + if (!mShortcutPreference.isChecked()) { + return context.getText(R.string.switch_off_text); + } + + final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context, + getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE); + + final List list = new ArrayList<>(); + final CharSequence softwareTitle = context.getText( + R.string.accessibility_shortcut_edit_summary_software); + + if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.SOFTWARE)) { + list.add(softwareTitle); + } + if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.HARDWARE)) { + final CharSequence hardwareTitle = context.getText( + R.string.accessibility_shortcut_hardware_keyword); + list.add(hardwareTitle); + } + + // Show software shortcut if first time to use. + if (list.isEmpty()) { + list.add(softwareTitle); + } + + return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */ + null, LocaleUtils.getConcatenatedString(list)); + } + + private void updateEditShortcutDialogCheckBox() { + // If it is during onConfigChanged process then restore the value, or get the saved value + // when shortcutPreference is checked. + int value = restoreOnConfigChangedValue(); + if (value == NOT_SET) { + final int lastNonEmptyUserShortcutType = PreferredShortcuts.retrieveUserShortcutType( + getPrefContext(), getComponentName().flattenToString(), + AccessibilityUtil.UserShortcutType.SOFTWARE); + value = mShortcutPreference.isChecked() ? lastNonEmptyUserShortcutType + : AccessibilityUtil.UserShortcutType.EMPTY; + } + + mSoftwareTypeCheckBox.setChecked( + hasShortcutType(value, AccessibilityUtil.UserShortcutType.SOFTWARE)); + mHardwareTypeCheckBox.setChecked( + hasShortcutType(value, AccessibilityUtil.UserShortcutType.HARDWARE)); + } + + private int restoreOnConfigChangedValue() { + final int savedValue = mSavedCheckBoxValue; + mSavedCheckBoxValue = NOT_SET; + return savedValue; + } + + private boolean hasShortcutType(int value, @AccessibilityUtil.UserShortcutType int type) { + return (value & type) == type; + } + + protected void updateShortcutPreferenceData() { + if (getComponentName() == null) { + return; + } + + final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings( + getPrefContext(), getComponentName()); + if (shortcutTypes != AccessibilityUtil.UserShortcutType.EMPTY) { + final PreferredShortcut shortcut = new PreferredShortcut( + getComponentName().flattenToString(), shortcutTypes); + PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut); + } + } + + protected void updateShortcutPreference() { + if (getComponentName() == null) { + return; + } + + final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(), + getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE); + mShortcutPreference.setChecked( + AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes, + getComponentName())); + mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); + } +} diff --git a/src/com/android/settings/accessibility/AnimatedImagePreference.java b/src/com/android/settings/accessibility/AnimatedImagePreference.java index 2ca13f33fe2..c707e5cc0a7 100644 --- a/src/com/android/settings/accessibility/AnimatedImagePreference.java +++ b/src/com/android/settings/accessibility/AnimatedImagePreference.java @@ -17,9 +17,14 @@ package com.android.settings.accessibility; import android.content.Context; -import android.graphics.drawable.AnimatedImageDrawable; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Animatable2; +import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import androidx.preference.Preference; @@ -27,15 +32,31 @@ import androidx.preference.PreferenceViewHolder; import com.android.settings.R; +import com.airbnb.lottie.LottieAnimationView; +import com.airbnb.lottie.LottieDrawable; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Objects; + /** * A custom {@link ImageView} preference for showing animated or static image, such as animated webp and static png. */ public class AnimatedImagePreference extends Preference { + private static final String TAG = "AnimatedImagePreference"; private Uri mImageUri; private int mMaxHeight = -1; + private final Animatable2.AnimationCallback mAnimationCallback = + new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + ((Animatable2) drawable).start(); + } + }; + AnimatedImagePreference(Context context) { super(context); setLayoutResource(R.layout.preference_animated_image); @@ -46,21 +67,27 @@ public class AnimatedImagePreference extends Preference { super.onBindViewHolder(holder); final ImageView imageView = holder.itemView.findViewById(R.id.animated_img); - if (imageView == null) { + final LottieAnimationView lottieView = holder.itemView.findViewById(R.id.lottie_view); + if (imageView == null || lottieView == null) { return; } if (mImageUri != null) { - imageView.setImageURI(mImageUri); + resetAnimations(imageView, lottieView); + hideAllChildViews(holder.itemView); - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AnimatedImageDrawable) { - ((AnimatedImageDrawable) drawable).start(); + imageView.setImageURI(mImageUri); + if (imageView.getDrawable() != null) { + startAnimationWith(imageView); + } else { + // The lottie image from the raw folder also returns null. + startLottieAnimationWith(lottieView); } } if (mMaxHeight > -1) { imageView.setMaxHeight(mMaxHeight); + lottieView.setMaxHeight(mMaxHeight); } } @@ -87,4 +114,68 @@ public class AnimatedImagePreference extends Preference { notifyChanged(); } } + + private void startAnimationWith(ImageView imageView) { + startAnimation(imageView.getDrawable()); + + imageView.setVisibility(View.VISIBLE); + } + + private void startLottieAnimationWith(LottieAnimationView lottieView) { + final InputStream inputStream = getInputStreamFromUri(mImageUri); + Objects.requireNonNull(inputStream, "Invalid resource."); + lottieView.setAnimation(inputStream, /* cacheKey= */ null); + lottieView.setRepeatCount(LottieDrawable.INFINITE); + lottieView.playAnimation(); + + lottieView.setVisibility(View.VISIBLE); + } + + private void startAnimation(Drawable drawable) { + if (!(drawable instanceof Animatable)) { + return; + } + + if (drawable instanceof Animatable2) { + ((Animatable2) drawable).registerAnimationCallback(mAnimationCallback); + } else if (drawable instanceof AnimationDrawable) { + ((AnimationDrawable) drawable).setOneShot(false); + } + + ((Animatable) drawable).start(); + } + + private void resetAnimations(ImageView imageView, LottieAnimationView lottieView) { + resetAnimation(imageView.getDrawable()); + + lottieView.cancelAnimation(); + } + + private void resetAnimation(Drawable drawable) { + if (!(drawable instanceof Animatable)) { + return; + } + + if (drawable instanceof Animatable2) { + ((Animatable2) drawable).clearAnimationCallbacks(); + } + + ((Animatable) drawable).stop(); + } + + private InputStream getInputStreamFromUri(Uri uri) { + try { + return getContext().getContentResolver().openInputStream(uri); + } catch (FileNotFoundException e) { + Log.w(TAG, "Cannot find content uri: " + uri, e); + return null; + } + } + + private void hideAllChildViews(View itemView) { + final ViewGroup viewGroup = (ViewGroup) itemView; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + viewGroup.getChildAt(i).setVisibility(View.GONE); + } + } } diff --git a/src/com/android/settings/accessibility/MagnificationModePreferenceController.java b/src/com/android/settings/accessibility/MagnificationModePreferenceController.java index ef858345cea..711065602be 100644 --- a/src/com/android/settings/accessibility/MagnificationModePreferenceController.java +++ b/src/com/android/settings/accessibility/MagnificationModePreferenceController.java @@ -17,7 +17,7 @@ package com.android.settings.accessibility; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; -import static com.android.settings.accessibility.AccessibilityEditDialogUtils.CustomButton; +import static com.android.settings.accessibility.AccessibilityDialogUtils.CustomButton; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; @@ -166,7 +166,7 @@ public class MagnificationModePreferenceController extends BasePreferenceControl } private Dialog createMagnificationModeDialog() { - mMagnificationModesListView = AccessibilityEditDialogUtils.createSingleChoiceListView( + mMagnificationModesListView = AccessibilityDialogUtils.createSingleChoiceListView( mContext, mModeInfos, this::onMagnificationModeSelected); final View headerView = LayoutInflater.from(mContext).inflate( @@ -179,7 +179,7 @@ public class MagnificationModePreferenceController extends BasePreferenceControl final CharSequence title = mContext.getString( R.string.accessibility_magnification_mode_dialog_title); - return AccessibilityEditDialogUtils.createCustomDialog(mContext, title, + return AccessibilityDialogUtils.createCustomDialog(mContext, title, mMagnificationModesListView, this::onMagnificationModeDialogPositiveButtonClicked); } @@ -235,7 +235,7 @@ public class MagnificationModePreferenceController extends BasePreferenceControl } private Dialog createMagnificationShortCutConfirmDialog() { - return AccessibilityEditDialogUtils.createMagnificationSwitchShortcutDialog(mContext, + return AccessibilityDialogUtils.createMagnificationSwitchShortcutDialog(mContext, this::onSwitchShortcutDialogButtonClicked); } diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 7902ec6c092..9d948583fd4 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -16,6 +16,7 @@ package com.android.settings.accessibility; +import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled; import static com.android.settings.accessibility.PreferredShortcuts.retrieveUserShortcutType; @@ -49,7 +50,6 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Switch; import androidx.annotation.Nullable; -import androidx.preference.Preference; import com.android.internal.widget.LockPatternUtils; import com.android.settings.R; @@ -232,14 +232,18 @@ public class ToggleAccessibilityServicePreferenceFragment extends @Override protected void updateSwitchBarToggleSwitch() { - final boolean checked = AccessibilityUtils.getEnabledServicesFromSettings(getPrefContext()) - .contains(mComponentName); + final boolean checked = isAccessibilityServiceEnabled(); if (mToggleServiceSwitchPreference.isChecked() == checked) { return; } mToggleServiceSwitchPreference.setChecked(checked); } + private boolean isAccessibilityServiceEnabled() { + return AccessibilityUtils.getEnabledServicesFromSettings(getPrefContext()) + .contains(mComponentName); + } + /** * Return whether the device is encrypted with legacy full disk encryption. Newer devices * should be using File Based Encryption. @@ -314,7 +318,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends } private void handleConfirmServiceEnabled(boolean confirmed) { - mToggleServiceSwitchPreference.setChecked(confirmed); getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed); onPreferenceToggled(mPreferenceKey, confirmed); } @@ -338,8 +341,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends @Override public void onSwitchChanged(Switch switchView, boolean isChecked) { - if (isChecked != mToggleServiceSwitchPreference.isChecked()) { - onPreferenceClick(mToggleServiceSwitchPreference); + if (isChecked != isAccessibilityServiceEnabled()) { + onPreferenceClick(isChecked); } } @@ -533,14 +536,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends mDialog.dismiss(); } - private boolean onPreferenceClick(Preference preference) { - boolean checked = ((SettingsMainSwitchPreference) preference).isChecked(); - if (checked) { - mToggleServiceSwitchPreference.setChecked(true); - getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, - /* enableService */ true); - showDialog(DialogEnums.DISABLE_WARNING_FROM_TOGGLE); - } else { + private boolean onPreferenceClick(boolean isChecked) { + if (isChecked) { mToggleServiceSwitchPreference.setChecked(false); getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, /* disableService */ false); @@ -552,6 +549,11 @@ public class ToggleAccessibilityServicePreferenceFragment extends showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } } + } else { + mToggleServiceSwitchPreference.setChecked(true); + getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, + /* enableService */ true); + showDialog(DialogEnums.DISABLE_WARNING_FROM_TOGGLE); } return true; } diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 7c189436b60..1002a8491ca 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -16,6 +16,8 @@ package com.android.settings.accessibility; +import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; + import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.ComponentName; @@ -50,7 +52,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType; +import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType; import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; import com.android.settings.utils.LocaleUtils; import com.android.settings.widget.SettingsMainSwitchBar; @@ -61,8 +63,6 @@ import com.android.settingslib.widget.OnMainSwitchChangeListener; import com.google.android.setupcompat.util.WizardManagerHelper; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -136,7 +136,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference setupDefaultShortcutIfNecessary(getPrefContext()); final int resId = getPreferenceScreenResId(); if (resId <= 0) { - PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen( + final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen( getPrefContext()); setPreferenceScreen(preferenceScreen); } @@ -228,7 +228,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference R.string.accessibility_shortcut_title, mPackageName); final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent()) ? DialogType.EDIT_SHORTCUT_GENERIC_SUW : DialogType.EDIT_SHORTCUT_GENERIC; - dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + dialog = AccessibilityDialogUtils.showEditShortcutDialog( getPrefContext(), dialogType, dialogTitle, this::callOnAlertDialogCheckboxClicked); setupEditShortcutDialog(dialog); @@ -256,56 +256,6 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference } } - /** Denotes the dialog emuns for show dialog */ - @Retention(RetentionPolicy.SOURCE) - protected @interface DialogEnums { - - /** OPEN: Settings > Accessibility > Any toggle service > Shortcut > Settings. */ - int EDIT_SHORTCUT = 1; - - /** OPEN: Settings > Accessibility > Magnification > Shortcut > Settings. */ - int MAGNIFICATION_EDIT_SHORTCUT = 1001; - - /** - * OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to - * enable service. - */ - int ENABLE_WARNING_FROM_TOGGLE = 1002; - - /** OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox. */ - int ENABLE_WARNING_FROM_SHORTCUT = 1003; - - /** - * OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox - * toggle. - */ - int ENABLE_WARNING_FROM_SHORTCUT_TOGGLE = 1004; - - /** - * OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to - * disable service. - */ - int DISABLE_WARNING_FROM_TOGGLE = 1005; - - /** - * OPEN: Settings > Accessibility > Magnification > Toggle user service in button - * navigation. - */ - int ACCESSIBILITY_BUTTON_TUTORIAL = 1006; - - /** - * OPEN: Settings > Accessibility > Magnification > Toggle user service in gesture - * navigation. - */ - int GESTURE_NAVIGATION_TUTORIAL = 1007; - - /** - * OPEN: Settings > Accessibility > Downloaded toggle service > Toggle user service > Show - * launch tutorial. - */ - int LAUNCH_ACCESSIBILITY_TUTORIAL = 1008; - } - @Override public int getMetricsCategory() { return SettingsEnums.ACCESSIBILITY_SERVICE; diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index 57fd7ea9918..ca33a964725 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -17,6 +17,7 @@ package com.android.settings.accessibility; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; +import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; @@ -44,7 +45,7 @@ import androidx.preference.PreferenceCategory; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.DialogCreatable; import com.android.settings.R; -import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType; +import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType; import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; import com.android.settings.utils.LocaleUtils; @@ -134,7 +135,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent()) ? DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW : DialogType.EDIT_SHORTCUT_MAGNIFICATION; - dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(getPrefContext(), + dialog = AccessibilityDialogUtils.showEditShortcutDialog(getPrefContext(), dialogType, dialogTitle, this::callOnAlertDialogCheckboxClicked); setupMagnificationEditShortcutDialog(dialog); return dialog; diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java index dca597f16dc..6032abd9b17 100644 --- a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java +++ b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java @@ -27,6 +27,7 @@ import android.widget.LinearLayout; import android.widget.SearchView; import android.widget.TextView; +import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -35,6 +36,8 @@ import com.android.settings.core.InstrumentedFragment; import com.android.settings.datetime.timezone.model.TimeZoneData; import com.android.settings.datetime.timezone.model.TimeZoneDataLoader; +import com.google.android.material.appbar.AppBarLayout; + import java.util.Locale; /** @@ -43,12 +46,15 @@ import java.util.Locale; * The search matches the prefix of words in the search text. */ public abstract class BaseTimeZonePicker extends InstrumentedFragment - implements SearchView.OnQueryTextListener { + implements SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener { public static final String EXTRA_RESULT_REGION_ID = "com.android.settings.datetime.timezone.result_region_id"; public static final String EXTRA_RESULT_TIME_ZONE_ID = "com.android.settings.datetime.timezone.result_time_zone_id"; + + protected AppBarLayout mAppBarLayout; + private final int mTitleResId; private final int mSearchHintResId; private final boolean mSearchEnabled; @@ -88,6 +94,7 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, /* reverseLayout */ false)); mRecyclerView.setAdapter(mAdapter); + mAppBarLayout = getActivity().findViewById(R.id.app_bar); // Initialize TimeZoneDataLoader only when mRecyclerView is ready to avoid race // during onDateLoaderReady callback. @@ -121,6 +128,7 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment inflater.inflate(R.menu.time_zone_base_search_menu, menu); final MenuItem searchMenuItem = menu.findItem(R.id.time_zone_search_menu); + searchMenuItem.setOnActionExpandListener(this); mSearchView = (SearchView) searchMenuItem.getActionView(); mSearchView.setQueryHint(getText(mSearchHintResId)); @@ -148,6 +156,21 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment } } + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + // To prevent a large space on tool bar. + mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/); + // To prevent user can expand the collpasing tool bar view. + ViewCompat.setNestedScrollingEnabled(mRecyclerView, false); + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + ViewCompat.setNestedScrollingEnabled(mRecyclerView, true); + return true; + } + @Override public boolean onQueryTextSubmit(String query) { return false; diff --git a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java index ffbd2d90e4e..3c9cbaa8e89 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java @@ -272,7 +272,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll final Map entryMap = batteryHistoryMap.get(timestamp); if (entryMap == null || entryMap.isEmpty()) { Log.e(TAG, "abnormal entry list in the timestamp:" - + ConvertUtils.utcToLocalTime(timestamp)); + + ConvertUtils.utcToLocalTime(mPrefContext, timestamp)); continue; } // Averages the battery level in each time slot to avoid corner conditions. @@ -287,7 +287,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll Log.d(TAG, String.format( "setBatteryHistoryMap() size=%d\nkeys=%s\nlevels=%s", batteryHistoryMap.size(), - utcToLocalTime(mBatteryHistoryKeys), + utcToLocalTime(mPrefContext, mBatteryHistoryKeys), Arrays.toString(mBatteryHistoryLevels))); // Loads item icon and label in the background. @@ -496,9 +496,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll if (mTrapezoidIndex < 0) { return null; } - final String fromHour = ConvertUtils.utcToLocalTimeHour( + final String fromHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, mBatteryHistoryKeys[mTrapezoidIndex * 2], mIs24HourFormat); - final String toHour = ConvertUtils.utcToLocalTimeHour( + final String toHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, mBatteryHistoryKeys[(mTrapezoidIndex + 1) * 2], mIs24HourFormat); return String.format("%s - %s", fromHour, toHour); } @@ -584,11 +584,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference)); } - private static String utcToLocalTime(long[] timestamps) { + private static String utcToLocalTime(Context context, long[] timestamps) { final StringBuilder builder = new StringBuilder(); for (int index = 0; index < timestamps.length; index++) { builder.append(String.format("%s| ", - ConvertUtils.utcToLocalTime(timestamps[index]))); + ConvertUtils.utcToLocalTime(context, timestamps[index]))); } return builder.toString(); } diff --git a/src/com/android/settings/fuelgauge/BatteryChartView.java b/src/com/android/settings/fuelgauge/BatteryChartView.java index ed6417704f7..b721f14be71 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/BatteryChartView.java @@ -56,14 +56,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private static final List ACCESSIBILITY_SERVICE_NAMES = Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); - // For drawing the percentage information. - private static final String[] PERCENTAGES = new String[] { - formatPercentage(/*percentage=*/ 100, /*round=*/ true), - formatPercentage(/*percentage=*/ 50, /*round=*/ true), - formatPercentage(/*percentage=*/ 0, /*round=*/ true)}; - private static final int DEFAULT_TRAPEZOID_COUNT = 12; - private static final int DEFAULT_TIMESTAMP_COUNT = 4; + private static final int DEFAULT_TIMESTAMP_COUNT = 5; private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5"); private static final long UPDATE_STATE_DELAYED_TIME = 500L; @@ -82,6 +76,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private float mTrapezoidVOffset; private float mTrapezoidHOffset; private boolean mIsSlotsClickabled; + private String[] mPercentages = getPercentages(); @VisibleForTesting int mSelectedIndex; @VisibleForTesting String[] mTimestamps; @@ -96,7 +91,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick new Rect[] {new Rect(), new Rect(), new Rect()}; // For drawing the timestamp information. private final Rect[] mTimestampsBounds = - new Rect[] {new Rect(), new Rect(), new Rect(), new Rect()}; + new Rect[] {new Rect(), new Rect(), new Rect(), new Rect(), new Rect()}; @VisibleForTesting Handler mHandler = new Handler(); @@ -107,6 +102,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private Paint mTextPaint; private Paint mDividerPaint; private Paint mTrapezoidPaint; + @VisibleForTesting Paint mTrapezoidCurvePaint = null; private TrapezoidSlot[] mTrapezoidSlots; @@ -201,12 +197,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick if (mTimestamps == null) { mTimestamps = new String[DEFAULT_TIMESTAMP_COUNT]; } - final long timeSlotOffset = DateUtils.HOUR_IN_MILLIS * 8; + final long timeSlotOffset = DateUtils.HOUR_IN_MILLIS * 6; final boolean is24HourFormat = DateFormat.is24HourFormat(getContext()); for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { mTimestamps[index] = ConvertUtils.utcToLocalTimeHour( - latestTimestamp - (3 - index) * timeSlotOffset, + getContext(), + latestTimestamp - (4 - index) * timeSlotOffset, is24HourFormat); } requestLayout(); @@ -217,9 +214,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Measures text bounds and updates indent configuration. if (mTextPaint != null) { - for (int index = 0; index < PERCENTAGES.length; index++) { + for (int index = 0; index < mPercentages.length; index++) { mTextPaint.getTextBounds( - PERCENTAGES[index], 0, PERCENTAGES[index].length(), + mPercentages[index], 0, mPercentages[index].length(), mPercentageBounds[index]); } // Updates the indent configurations. @@ -396,7 +393,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private void drawPercentage(Canvas canvas, int index, float offsetY) { if (mTextPaint != null) { canvas.drawText( - PERCENTAGES[index], + mPercentages[index], getWidth() - mPercentageBounds[index].width() - mPercentageBounds[index].left, offsetY + mPercentageBounds[index].height() *.5f, mTextPaint); @@ -429,7 +426,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick final float baselineX = mDividerWidth * .5f; final float offsetX = mDividerWidth + unitWidth; for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { - xOffsets[index] = baselineX + index * offsetX * 4; + xOffsets[index] = baselineX + index * offsetX * 3; } drawTimestamp(canvas, xOffsets); } @@ -443,11 +440,11 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick getTimestampY(0), mTextPaint); // Draws the last timestamp info. canvas.drawText( - mTimestamps[3], - xOffsets[3] - mTimestampsBounds[3].width() - mTimestampsBounds[3].left, - getTimestampY(3), mTextPaint); + mTimestamps[4], + xOffsets[4] - mTimestampsBounds[4].width() - mTimestampsBounds[4].left, + getTimestampY(4), mTextPaint); // Draws the rest of timestamp info since it is located in the center. - for (int index = 1; index <= 2; index++) { + for (int index = 1; index <= 3; index++) { canvas.drawText( mTimestamps[index], xOffsets[index] - @@ -544,6 +541,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick && mLevels[trapezoidIndex + 1] != 0; } + private static String[] getPercentages() { + return new String[] { + formatPercentage(/*percentage=*/ 100, /*round=*/ true), + formatPercentage(/*percentage=*/ 50, /*round=*/ true), + formatPercentage(/*percentage=*/ 0, /*round=*/ true)}; + } + @VisibleForTesting static boolean isAccessibilityEnabled(Context context) { final AccessibilityManager accessibilityManager = diff --git a/src/com/android/settings/fuelgauge/BatteryHistEntry.java b/src/com/android/settings/fuelgauge/BatteryHistEntry.java index d83d8149691..4c8ecee6c8e 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryHistEntry.java @@ -184,7 +184,8 @@ public class BatteryHistEntry { @Override public String toString() { - final String recordAtDateTime = ConvertUtils.utcToLocalTime(mTimestamp); + final String recordAtDateTime = + ConvertUtils.utcToLocalTime(/*context=*/ null, mTimestamp); final StringBuilder builder = new StringBuilder() .append("\nBatteryHistEntry{") .append(String.format("\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b", diff --git a/src/com/android/settings/fuelgauge/ConvertUtils.java b/src/com/android/settings/fuelgauge/ConvertUtils.java index 19805648f3a..01f510eca6d 100644 --- a/src/com/android/settings/fuelgauge/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/ConvertUtils.java @@ -17,6 +17,7 @@ import android.annotation.IntDef; import android.content.ContentValues; import android.content.Context; import android.os.BatteryUsageStats; +import android.os.LocaleList; import android.os.UserHandle; import android.text.format.DateUtils; import android.util.Log; @@ -133,8 +134,8 @@ public final class ConvertUtils { } /** Converts UTC timestamp to human readable local time string. */ - public static String utcToLocalTime(long timestamp) { - final Locale currentLocale = Locale.getDefault(); + public static String utcToLocalTime(Context context, long timestamp) { + final Locale currentLocale = getLocale(context); final String currentZoneId = TimeZone.getDefault().getID(); if (!currentZoneId.equals(sZoneId) || !currentLocale.equals(sLocale) @@ -148,8 +149,9 @@ public final class ConvertUtils { } /** Converts UTC timestamp to local time hour data. */ - public static String utcToLocalTimeHour(long timestamp, boolean is24HourFormat) { - final Locale currentLocale = Locale.getDefault(); + public static String utcToLocalTimeHour( + Context context, long timestamp, boolean is24HourFormat) { + final Locale currentLocale = getLocale(context); final String currentZoneId = TimeZone.getDefault().getID(); if (!currentZoneId.equals(sZoneIdForHour) || !currentLocale.equals(sLocaleForHour) @@ -159,7 +161,7 @@ public final class ConvertUtils { sZoneIdForHour = currentZoneId; sIs24HourFormat = is24HourFormat; sSimpleDateFormatForHour = new SimpleDateFormat( - sIs24HourFormat ? "HH" : "h aa", currentLocale); + sIs24HourFormat ? "HH" : "h", currentLocale); } return sSimpleDateFormatForHour.format(new Date(timestamp)) .toLowerCase(currentLocale); @@ -356,4 +358,15 @@ public final class ConvertUtils { ? entry3 : null; } } + + @VisibleForTesting + static Locale getLocale(Context context) { + if (context == null) { + return Locale.getDefault(); + } + final LocaleList locales = + context.getResources().getConfiguration().getLocales(); + return locales != null && !locales.isEmpty() ? locales.get(0) + : Locale.getDefault(); + } } diff --git a/src/com/android/settings/gestures/GestureNavigationSettingsAssistController.java b/src/com/android/settings/gestures/GestureNavigationSettingsAssistController.java index 5c495382f2a..95fd9f1fd61 100644 --- a/src/com/android/settings/gestures/GestureNavigationSettingsAssistController.java +++ b/src/com/android/settings/gestures/GestureNavigationSettingsAssistController.java @@ -26,18 +26,16 @@ import com.android.settings.core.TogglePreferenceController; */ public class GestureNavigationSettingsAssistController extends TogglePreferenceController { - // This value is based on SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java - // behaviour. We assume that the gestures are enabled by default. - private static final int ASSIST_TOUCH_GESTURE_DEFAULT_VALUE = 1; - public GestureNavigationSettingsAssistController(Context context, String key) { super(context, key); } @Override public boolean isChecked() { + boolean onByDefault = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_assistTouchGestureEnabledDefault); return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, ASSIST_TOUCH_GESTURE_DEFAULT_VALUE) + Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, onByDefault ? 1 : 0) == 1; } diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java index b778a05a96c..cfaef53ac60 100644 --- a/src/com/android/settings/network/ProviderModelSlice.java +++ b/src/com/android/settings/network/ProviderModelSlice.java @@ -46,6 +46,7 @@ import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.network.telephony.NetworkProviderWorker; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SliceBackgroundWorker; +import com.android.settings.slices.SliceBroadcastReceiver; import com.android.settings.slices.SliceBuilderUtils; import com.android.settings.wifi.WifiUtils; import com.android.settings.wifi.slice.WifiSlice; @@ -163,6 +164,18 @@ public class ProviderModelSlice extends WifiSlice { return listBuilder.build(); } + @Override + public PendingIntent getBroadcastIntent(Context context) { + final Intent intent = new Intent(getUri().toString()) + // The FLAG_RECEIVER_FOREGROUND flag is necessary to avoid the intent delay of + // the first sending after the device restarts + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) + .setData(getUri()) + .setClass(context, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + } + /** * Update the current carrier's mobile data status. */ diff --git a/src/com/android/settings/network/telephony/NetworkProviderWifiCallingGroup.java b/src/com/android/settings/network/telephony/NetworkProviderWifiCallingGroup.java index 3423ad8e3f6..b4ab2a0c535 100644 --- a/src/com/android/settings/network/telephony/NetworkProviderWifiCallingGroup.java +++ b/src/com/android/settings/network/telephony/NetworkProviderWifiCallingGroup.java @@ -47,6 +47,7 @@ import com.android.settings.network.ims.WifiCallingQueryImsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -86,7 +87,8 @@ public class NetworkProviderWifiCallingGroup extends } private void setSubscriptionInfoList(Context context) { - mSubInfoListForWfc = SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager); + mSubInfoListForWfc = new ArrayList<>( + SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager)); if (mSubInfoListForWfc != null) { mSubInfoListForWfc.removeIf(info -> { final int subId = info.getSubscriptionId(); diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java new file mode 100644 index 00000000000..104f33553c8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java @@ -0,0 +1,222 @@ +/* + * 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.accessibility; + +import static com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE; + +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.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.provider.Settings; + +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.testutils.shadow.ShadowFragment; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** Tests for {@link AccessibilityShortcutPreferenceFragment} */ +@RunWith(RobolectricTestRunner.class) +public class AccessibilityShortcutPreferenceFragmentTest { + + private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example"; + private static final String PLACEHOLDER_CLASS_NAME = PLACEHOLDER_PACKAGE_NAME + ".placeholder"; + private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName( + PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME); + private static final String PLACEHOLDER_DIALOG_TITLE = "title"; + + private static final String SOFTWARE_SHORTCUT_KEY = + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; + private static final String HARDWARE_SHORTCUT_KEY = + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; + + private TestAccessibilityShortcutPreferenceFragment mFragment; + private PreferenceScreen mScreen; + private Context mContext = ApplicationProvider.getApplicationContext(); + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private PreferenceManager mPreferenceManager; + + @Before + public void setUpTestFragment() { + MockitoAnnotations.initMocks(this); + + mFragment = spy(new TestAccessibilityShortcutPreferenceFragment()); + when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager); + when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext); + when(mFragment.getContext()).thenReturn(mContext); + mScreen = spy(new PreferenceScreen(mContext, null)); + when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager); + doReturn(mScreen).when(mFragment).getPreferenceScreen(); + } + + @Test + public void updateShortcutPreferenceData_assignDefaultValueToVariable() { + mFragment.updateShortcutPreferenceData(); + + final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, + mFragment.getComponentName().flattenToString(), + AccessibilityUtil.UserShortcutType.SOFTWARE); + // Compare to default UserShortcutType + assertThat(expectedType).isEqualTo(AccessibilityUtil.UserShortcutType.SOFTWARE); + } + + @Test + public void updateShortcutPreferenceData_hasValueInSettings_assignToVariable() { + putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString()); + putStringIntoSettings(HARDWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString()); + + mFragment.updateShortcutPreferenceData(); + + final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, + mFragment.getComponentName().flattenToString(), + AccessibilityUtil.UserShortcutType.SOFTWARE); + assertThat(expectedType).isEqualTo(AccessibilityUtil.UserShortcutType.SOFTWARE + | AccessibilityUtil.UserShortcutType.HARDWARE); + } + + @Test + public void updateShortcutPreferenceData_hasValueInSharedPreference_assignToVariable() { + final PreferredShortcut hardwareShortcut = new PreferredShortcut( + PLACEHOLDER_COMPONENT_NAME.flattenToString(), + AccessibilityUtil.UserShortcutType.HARDWARE); + + putUserShortcutTypeIntoSharedPreference(mContext, hardwareShortcut); + mFragment.updateShortcutPreferenceData(); + + final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, + mFragment.getComponentName().flattenToString(), + AccessibilityUtil.UserShortcutType.SOFTWARE); + assertThat(expectedType).isEqualTo(AccessibilityUtil.UserShortcutType.HARDWARE); + } + + @Test + public void setupEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() { + mContext.setTheme(R.style.Theme_AppCompat); + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( + mContext, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC, + PLACEHOLDER_DIALOG_TITLE, + this::callEmptyOnClicked); + final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ + null); + mFragment.mShortcutPreference = shortcutPreference; + + mFragment.mShortcutPreference.setChecked(false); + mFragment.setupEditShortcutDialog(dialog); + + final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue(); + assertThat(checkboxValue).isEqualTo(AccessibilityUtil.UserShortcutType.EMPTY); + } + + @Test + public void setupEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() { + mContext.setTheme(R.style.Theme_AppCompat); + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( + mContext, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC, + PLACEHOLDER_DIALOG_TITLE, + this::callEmptyOnClicked); + final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ + null); + final PreferredShortcut hardwareShortcut = new PreferredShortcut( + PLACEHOLDER_COMPONENT_NAME.flattenToString(), + AccessibilityUtil.UserShortcutType.HARDWARE); + mFragment.mShortcutPreference = shortcutPreference; + + PreferredShortcuts.saveUserShortcutType(mContext, hardwareShortcut); + mFragment.mShortcutPreference.setChecked(true); + mFragment.setupEditShortcutDialog(dialog); + + final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue(); + assertThat(checkboxValue).isEqualTo(AccessibilityUtil.UserShortcutType.HARDWARE); + } + + @Test + @Config(shadows = ShadowFragment.class) + public void restoreValueFromSavedInstanceState_assignToVariable() { + mContext.setTheme(R.style.Theme_AppCompat); + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( + mContext, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC, + PLACEHOLDER_DIALOG_TITLE, + this::callEmptyOnClicked); + final Bundle savedInstanceState = new Bundle(); + final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ + null); + mFragment.mShortcutPreference = shortcutPreference; + + savedInstanceState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, + AccessibilityUtil.UserShortcutType.SOFTWARE + | AccessibilityUtil.UserShortcutType.HARDWARE); + mFragment.onCreate(savedInstanceState); + mFragment.setupEditShortcutDialog(dialog); + final int value = mFragment.getShortcutTypeCheckBoxValue(); + mFragment.saveNonEmptyUserShortcutType(value); + + final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, + mFragment.getComponentName().flattenToString(), + AccessibilityUtil.UserShortcutType.SOFTWARE); + assertThat(expectedType).isEqualTo( + AccessibilityUtil.UserShortcutType.SOFTWARE + | AccessibilityUtil.UserShortcutType.HARDWARE); + } + + private void callEmptyOnClicked(DialogInterface dialog, int which) {} + + private void putStringIntoSettings(String key, String componentName) { + Settings.Secure.putString(mContext.getContentResolver(), key, componentName); + } + + private void putUserShortcutTypeIntoSharedPreference(Context context, + PreferredShortcut shortcut) { + PreferredShortcuts.saveUserShortcutType(context, shortcut); + } + + public static class TestAccessibilityShortcutPreferenceFragment + extends AccessibilityShortcutPreferenceFragment { + @Override + protected ComponentName getComponentName() { + return PLACEHOLDER_COMPONENT_NAME; + } + + @Override + protected CharSequence getLabelName() { + return PLACEHOLDER_PACKAGE_NAME; + } + + @Override + public int getMetricsCategory() { + return 0; + } + }; +} diff --git a/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java index c3dd7b5af33..4bce0bb264b 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java @@ -18,13 +18,19 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +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 android.content.ContentResolver; import android.content.Context; import android.graphics.drawable.AnimatedImageDrawable; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.AnimationDrawable; import android.net.Uri; import android.view.LayoutInflater; import android.view.View; @@ -34,18 +40,22 @@ import androidx.preference.PreferenceViewHolder; import com.android.settings.R; +import com.airbnb.lottie.LottieAnimationView; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.io.InputStream; + /** Tests for {@link AnimatedImagePreference}. */ @RunWith(RobolectricTestRunner.class) public class AnimatedImagePreferenceTest { + private final Context mContext = RuntimeEnvironment.application; private Uri mImageUri; private View mRootView; private PreferenceViewHolder mViewHolder; @@ -54,32 +64,53 @@ public class AnimatedImagePreferenceTest { @Spy private ImageView mImageView; - @Mock - private AnimatedImageDrawable mAnimatedImageDrawable; - @Before public void init() { MockitoAnnotations.initMocks(this); - final Context context = RuntimeEnvironment.application; - final LayoutInflater inflater = LayoutInflater.from(context); + final LayoutInflater inflater = LayoutInflater.from(mContext); mRootView = spy(inflater.inflate(R.layout.preference_animated_image, /* root= */ null)); mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView)); - mImageView = spy(new ImageView(context)); + mImageView = spy(new ImageView(mContext)); - mAnimatedImagePreference = new AnimatedImagePreference(context); + mAnimatedImagePreference = new AnimatedImagePreference(mContext); mImageUri = new Uri.Builder().build(); } @Test - public void readImageUri_animatedImage_startAnimation() { + public void playAnimation_animatedImageDrawable_success() { + final AnimatedImageDrawable drawable = mock(AnimatedImageDrawable.class); doReturn(mImageView).when(mRootView).findViewById(R.id.animated_img); - doReturn(mAnimatedImageDrawable).when(mImageView).getDrawable(); + doReturn(drawable).when(mImageView).getDrawable(); mAnimatedImagePreference.setImageUri(mImageUri); mAnimatedImagePreference.onBindViewHolder(mViewHolder); - verify(mAnimatedImageDrawable).start(); + verify(drawable).start(); + } + + @Test + public void playAnimation_animatedVectorDrawable_success() { + final AnimatedVectorDrawable drawable = mock(AnimatedVectorDrawable.class); + doReturn(mImageView).when(mRootView).findViewById(R.id.animated_img); + doReturn(drawable).when(mImageView).getDrawable(); + + mAnimatedImagePreference.setImageUri(mImageUri); + mAnimatedImagePreference.onBindViewHolder(mViewHolder); + + verify(drawable).start(); + } + + @Test + public void playAnimation_animationDrawable_success() { + final AnimationDrawable drawable = mock(AnimationDrawable.class); + doReturn(mImageView).when(mRootView).findViewById(R.id.animated_img); + doReturn(drawable).when(mImageView).getDrawable(); + + mAnimatedImagePreference.setImageUri(mImageUri); + mAnimatedImagePreference.onBindViewHolder(mViewHolder); + + verify(drawable).start(); } @Test @@ -102,4 +133,22 @@ public class AnimatedImagePreferenceTest { assertThat(mImageView.getMaxHeight()).isEqualTo(maxHeight); } + + @Test + public void setImageUriAndRebindViewHolder_lottieImageFromRawFolder_setAnimation() { + final int fakeLottieResId = 111111; + final Uri lottieImageUri = + new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(mContext.getPackageName()) + .appendPath(String.valueOf(fakeLottieResId)) + .build(); + final LottieAnimationView lottieView = spy(new LottieAnimationView(mContext)); + doReturn(mImageView).when(mRootView).findViewById(R.id.animated_img); + doReturn(lottieView).when(mRootView).findViewById(R.id.lottie_view); + + mAnimatedImagePreference.setImageUri(lottieImageUri); + mAnimatedImagePreference.onBindViewHolder(mViewHolder); + + verify(lottieView).setAnimation(any(InputStream.class), eq(null)); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationModePreferenceControllerTest.java index d4b7ea038fe..e3940e7165b 100644 --- a/tests/robotests/src/com/android/settings/accessibility/MagnificationModePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationModePreferenceControllerTest.java @@ -16,7 +16,7 @@ package com.android.settings.accessibility; -import static com.android.settings.accessibility.AccessibilityEditDialogUtils.CustomButton; +import static com.android.settings.accessibility.AccessibilityDialogUtils.CustomButton; import static com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode; import static com.android.settings.accessibility.MagnificationModePreferenceController.MagnificationModeInfo; import static com.android.settings.accessibility.MagnificationPreferenceFragment.ON; diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index 4f4185cb81c..c47a7935de5 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -43,7 +43,7 @@ import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; -import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType; +import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType; import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; import com.android.settings.testutils.shadow.ShadowFragment; @@ -118,9 +118,9 @@ public class ToggleFeaturePreferenceFragmentTest { @Test public void updateShortcutPreferenceData_hasValueInSettings_assignToVariable() { mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME; - putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString()); putStringIntoSettings(HARDWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString()); + mFragment.updateShortcutPreferenceData(); final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, @@ -145,7 +145,7 @@ public class ToggleFeaturePreferenceFragmentTest { @Test public void setupEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() { mContext.setTheme(R.style.Theme_AppCompat); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked); final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ @@ -163,7 +163,7 @@ public class ToggleFeaturePreferenceFragmentTest { @Test public void setupEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() { mContext.setTheme(R.style.Theme_AppCompat); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked); final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ @@ -185,7 +185,7 @@ public class ToggleFeaturePreferenceFragmentTest { @Config(shadows = ShadowFragment.class) public void restoreValueFromSavedInstanceState_assignToVariable() { mContext.setTheme(R.style.Theme_AppCompat); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked); final Bundle savedInstanceState = new Bundle(); diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java index 43f691e2005..9aa0e03f39e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java @@ -53,7 +53,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.DialogCreatable; import com.android.settings.R; -import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType; +import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -186,6 +186,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { public void updateShortcutPreferenceData_hasValueInSettings_assignToVariable() { putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, MAGNIFICATION_CONTROLLER_NAME); setMagnificationTripleTapEnabled(/* enabled= */ true); + mFragment.updateShortcutPreferenceData(); final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, @@ -209,7 +210,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { @Test public void setupMagnificationEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() { mContext.setTheme(R.style.Theme_AppCompat); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( mContext, DialogType.EDIT_SHORTCUT_MAGNIFICATION, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked); final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ @@ -226,7 +227,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { @Test public void setupMagnificationEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() { mContext.setTheme(R.style.Theme_AppCompat); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( mContext, DialogType.EDIT_SHORTCUT_MAGNIFICATION, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked); final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ @@ -247,7 +248,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { @Config(shadows = ShadowFragment.class) public void restoreValueFromSavedInstanceState_assignToVariable() { mContext.setTheme(R.style.Theme_AppCompat); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( mContext, DialogType.EDIT_SHORTCUT_MAGNIFICATION, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked); final Bundle savedInstanceState = new Bundle(); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java index 73e9bed3a61..21159475a1a 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java @@ -134,7 +134,7 @@ public class AppBatteryPreferenceControllerTest { mController.updateBattery(); assertThat(mBatteryPreference.getSummary()) - .isEqualTo("No battery use since last full charge"); + .isEqualTo("No battery use for past 24 hours"); } @Test @@ -147,7 +147,7 @@ public class AppBatteryPreferenceControllerTest { mController.updateBattery(); - assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use since last full charge"); + assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use for past 24 hours"); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java index 7894c3f0c60..ef76eeeaa67 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java @@ -35,6 +35,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.LocaleList; import android.text.format.DateUtils; import android.util.Pair; @@ -100,6 +101,8 @@ public final class BatteryChartPreferenceControllerTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; mContext = spy(RuntimeEnvironment.application); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.mPrefContext = mContext; mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup; @@ -573,14 +576,12 @@ public final class BatteryChartPreferenceControllerTest { // Verifies the title in the preference group. verify(mBatteryChartPreferenceController.mAppListPrefGroup) .setTitle(captor.capture()); - assertThat(captor.getValue()) - .isEqualTo("App usage for 4 pm - 7 am"); + assertThat(captor.getValue()).isEqualTo("App usage for 4 - 7"); // Verifies the title in the expandable divider. captor = ArgumentCaptor.forClass(String.class); verify(mBatteryChartPreferenceController.mExpandDividerPreference) .setTitle(captor.capture()); - assertThat(captor.getValue()) - .isEqualTo("System usage for 4 pm - 7 am"); + assertThat(captor.getValue()).isEqualTo("System usage for 4 - 7"); } @Test @@ -716,7 +717,8 @@ public final class BatteryChartPreferenceControllerTest { private void setUpBatteryHistoryKeys() { mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[] {1619196786769L, 0L, 1619247636826L}; - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ false); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); // Simulates the locale in GMT. ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone("GMT")); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java index 3f94456f8e1..ec77f4c3b02 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; +import android.os.LocaleList; import android.view.accessibility.AccessibilityManager; import com.android.settings.testutils.FakeFeatureFactory; @@ -41,6 +42,7 @@ import org.robolectric.RuntimeEnvironment; import java.util.Arrays; import java.util.ArrayList; +import java.util.Locale; import java.util.TimeZone; @RunWith(RobolectricTestRunner.class) @@ -60,6 +62,8 @@ public final class BatteryChartViewTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider; mContext = spy(RuntimeEnvironment.application); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); mBatteryChartView = new BatteryChartView(mContext); doReturn(mockAccessibilityManager).when(mContext) .getSystemService(AccessibilityManager.class); @@ -234,11 +238,11 @@ public final class BatteryChartViewTest { final long timestamp = 1619196786769L; ConvertUtils.sSimpleDateFormatForHour = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ false); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); - final String[] expectedTimestamps = - new String[] {"9 am", "5 pm", "1 am", "9 am"}; + final String[] expectedTimestamps = new String[] {"00", "06", "12", "18", "00"}; mBatteryChartView.setLatestTimestamp(timestamp); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java index efabe44ed0d..67a60af4c74 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java @@ -24,6 +24,7 @@ import android.content.ContentValues; import android.content.Context; import android.os.BatteryManager; import android.os.BatteryUsageStats; +import android.os.LocaleList; import android.os.UserHandle; import com.android.settings.testutils.FakeFeatureFactory; @@ -315,6 +316,7 @@ public final class ConvertUtilsTest { .isEqualTo(entry.mConsumePower * ratio); } + @Test public void testUtcToLocalTime_returnExpectedResult() { ConvertUtils.sZoneId = null; ConvertUtils.sLocale = null; @@ -322,48 +324,76 @@ public final class ConvertUtilsTest { final String expectedZoneId = "America/Los_Angeles"; ConvertUtils.sSimpleDateFormat = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTime(/*timestamp=*/ 0); + ConvertUtils.utcToLocalTime(mContext, /*timestamp=*/ 0); ConvertUtils.sSimpleDateFormat .setTimeZone(TimeZone.getTimeZone(expectedZoneId)); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); - assertThat(ConvertUtils.utcToLocalTime(timestamp)) - .isEqualTo("Apr 23,2021 09:53:06"); + assertThat(ConvertUtils.utcToLocalTime(mContext, timestamp)) + .isEqualTo("Apr 24,2021 00:53:06"); assertThat(ConvertUtils.sZoneId).isNotEqualTo(expectedZoneId); - assertThat(ConvertUtils.sLocale).isEqualTo(Locale.getDefault()); + assertThat(ConvertUtils.sLocale).isEqualTo(new Locale("en_US")); } + @Test public void testUtcToLocalTimeHour_12HourFormat_returnExpectedResult() { ConvertUtils.sZoneIdForHour = null; ConvertUtils.sLocaleForHour = null; - final long timestamp = 1619196786769L; + final long timestamp = 1619000086769L; final String expectedZoneId = "America/Los_Angeles"; ConvertUtils.sSimpleDateFormatForHour = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ false); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone(expectedZoneId)); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); assertThat(ConvertUtils.utcToLocalTimeHour( - timestamp, /*is24HourFormat=*/ false)).isEqualTo("9 am"); + mContext, timestamp, /*is24HourFormat=*/ false)).isEqualTo("6"); assertThat(ConvertUtils.sZoneIdForHour).isNotEqualTo(expectedZoneId); - assertThat(ConvertUtils.sLocaleForHour).isEqualTo(Locale.getDefault()); + assertThat(ConvertUtils.sLocaleForHour).isEqualTo(new Locale("en_US")); } + @Test public void testUtcToLocalTimeHour_24HourFormat_returnExpectedResult() { ConvertUtils.sZoneIdForHour = null; ConvertUtils.sLocaleForHour = null; - final long timestamp = 1619196786769L; + final long timestamp = 1619000086769L; final String expectedZoneId = "America/Los_Angeles"; ConvertUtils.sSimpleDateFormatForHour = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ true); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone(expectedZoneId)); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); assertThat(ConvertUtils.utcToLocalTimeHour( - timestamp, /*is24HourFormat=*/ true)).isEqualTo("09"); + mContext, timestamp, /*is24HourFormat=*/ true)).isEqualTo("18"); assertThat(ConvertUtils.sZoneIdForHour).isNotEqualTo(expectedZoneId); - assertThat(ConvertUtils.sLocaleForHour).isEqualTo(Locale.getDefault()); + assertThat(ConvertUtils.sLocaleForHour).isEqualTo(new Locale("en_US")); + } + + @Test + public void getLocale_nullContext_returnDefaultLocale() { + assertThat(ConvertUtils.getLocale(/*context=*/ null)) + .isEqualTo(Locale.getDefault()); + } + + @Test + public void getLocale_nullLocaleList_returnDefaultLocale() { + mContext.getResources().getConfiguration().setLocales(null); + assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault()); + } + + @Test + public void getLocale_emptyLocaleList_returnDefaultLocale() { + mContext.getResources().getConfiguration().setLocales(new LocaleList()); + assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault()); } private static BatteryHistEntry createBatteryHistEntry( diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java index fb17e34a9bc..a96e7cf21b4 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java @@ -142,6 +142,16 @@ public class ProviderModelSliceTest { mockBuilder(); } + @Test + @UiThreadTest + public void getBroadcastIntent_shouldHaveFlagReceiverForeground() { + final PendingIntent pendingIntent = mMockProviderModelSlice.getBroadcastIntent(mContext); + + final int flags = pendingIntent.getIntent().getFlags(); + assertThat(flags & Intent.FLAG_RECEIVER_FOREGROUND) + .isEqualTo(Intent.FLAG_RECEIVER_FOREGROUND); + } + @Test @UiThreadTest public void getSlice_noWifiAndHasCarrierNoData_oneCarrier() {