From 78c093b0b30964e24f6b4b731154bd4fc2a21511 Mon Sep 17 00:00:00 2001 From: menghanli Date: Mon, 7 Jun 2021 13:38:50 +0800 Subject: [PATCH 01/16] Move DialogEumn into AccessibilityDialogUtils Bug: 190022774 Test: Manual testing Change-Id: I391089b675b415c26bbacd028caf1d1ab617e707 --- ...ils.java => AccessibilityDialogUtils.java} | 54 ++++++++++++++++- ...MagnificationModePreferenceController.java | 8 +-- ...ccessibilityServicePreferenceFragment.java | 1 + .../ToggleFeaturePreferenceFragment.java | 58 ++----------------- ...ScreenMagnificationPreferenceFragment.java | 5 +- ...ificationModePreferenceControllerTest.java | 2 +- .../ToggleFeaturePreferenceFragmentTest.java | 8 +-- ...enMagnificationPreferenceFragmentTest.java | 8 +-- 8 files changed, 73 insertions(+), 71 deletions(-) rename src/com/android/settings/accessibility/{AccessibilityEditDialogUtils.java => AccessibilityDialogUtils.java} (91%) diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java similarity index 91% rename from src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java rename to src/com/android/settings/accessibility/AccessibilityDialogUtils.java index c1ac604c94a..ffe5d6db59b 100644 --- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java +++ b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java @@ -62,7 +62,57 @@ import java.util.List; /** * Utility class for creating the edit dialog. */ -public class AccessibilityEditDialogUtils { +public class AccessibilityDialogUtils { + + /** Denotes the dialog emuns for show dialog. */ + @Retention(RetentionPolicy.SOURCE) + public @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; + } /** * 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/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..4c4757a0e0b 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; diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 7c189436b60..c247a193509 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; @@ -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/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..471e83d2a1c 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; @@ -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..a5811e26dbf 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; @@ -209,7 +209,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 +226,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 +247,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(); From cb40bd29477d7e7c8df4836f05e6bafc7674fb83 Mon Sep 17 00:00:00 2001 From: "Wesley.CW Wang" Date: Mon, 7 Jun 2021 14:59:13 +0800 Subject: [PATCH 02/16] Fix AppBatteryPreferenceControllerTest test fail Bug: 190187197 Bug: 190239610 Test: make RunSettingsRoboTests Change-Id: I113eb738003531c67acd72aa8f597e9c34ca2cc9 --- .../appinfo/AppBatteryPreferenceControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 101823e6021386e9c62316e69f35436e2051884a Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Mon, 7 Jun 2021 16:18:02 +0800 Subject: [PATCH 03/16] Update the theme to fix background color problem The action bar in the some setting pages has different background colors when the action bar is in collapsed mode. These pages are using the default settings theme that doesn't include edge-to-edge setting. This can be fixed by assigning SubSettings theme to this activity. Fix: 189189879 Fix: 188110157 Test: visual verified Change-Id: Ie8d9b4c950e6a8ab5a7e361f94b8587d854b3b70 --- AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) 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"> From c534a58163f944471ae397e838ef70cb49122305 Mon Sep 17 00:00:00 2001 From: Matt Casey Date: Fri, 4 Jun 2021 10:09:34 -0400 Subject: [PATCH 04/16] Update system settings to honor device defaults for assistant invocation Bug: 190186067 Change-Id: Ie3b46a79ee73feb677ce2e6e549864a1919a662c --- .../GestureNavigationSettingsAssistController.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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; } From b1bcedc0553c1cdae397455c13556fe9695b985c Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Thu, 3 Jun 2021 20:47:06 +0800 Subject: [PATCH 05/16] Fixing the animation of drawable couldn't play automatically for the banner. Root cause: Only supported AnimatedImageDrawable before. Solution: Support all classes which implement Animatable Bug: 190032215 Test: atest AnimatedImagePreferenceTest Change-Id: Iba18bfab9a46fd02f642d66a3bb9b9ce513c4456 --- .../AnimatedImagePreference.java | 46 ++++++++++++++++--- .../AnimatedImagePreferenceTest.java | 38 ++++++++++++--- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/com/android/settings/accessibility/AnimatedImagePreference.java b/src/com/android/settings/accessibility/AnimatedImagePreference.java index 2ca13f33fe2..69c0d136cbd 100644 --- a/src/com/android/settings/accessibility/AnimatedImagePreference.java +++ b/src/com/android/settings/accessibility/AnimatedImagePreference.java @@ -17,7 +17,9 @@ 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.widget.ImageView; @@ -36,6 +38,14 @@ public class AnimatedImagePreference extends Preference { 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); @@ -51,12 +61,10 @@ public class AnimatedImagePreference extends Preference { } if (mImageUri != null) { - imageView.setImageURI(mImageUri); + resetAnimation(imageView.getDrawable()); - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AnimatedImageDrawable) { - ((AnimatedImageDrawable) drawable).start(); - } + imageView.setImageURI(mImageUri); + startAnimation(imageView.getDrawable()); } if (mMaxHeight > -1) { @@ -87,4 +95,30 @@ public class AnimatedImagePreference extends Preference { notifyChanged(); } } + + 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 resetAnimation(Drawable drawable) { + if (!(drawable instanceof Animatable)) { + return; + } + + if (drawable instanceof Animatable2) { + ((Animatable2) drawable).clearAnimationCallbacks(); + } + + ((Animatable) drawable).stop(); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java index c3dd7b5af33..b8ab432d2e9 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java @@ -19,12 +19,15 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; 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.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; @@ -37,7 +40,6 @@ import com.android.settings.R; 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; @@ -54,9 +56,6 @@ public class AnimatedImagePreferenceTest { @Spy private ImageView mImageView; - @Mock - private AnimatedImageDrawable mAnimatedImageDrawable; - @Before public void init() { MockitoAnnotations.initMocks(this); @@ -72,14 +71,39 @@ public class AnimatedImagePreferenceTest { } @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 From f70d574278d55853b294fb9624b559ab4d2567d0 Mon Sep 17 00:00:00 2001 From: Curtis Belmonte Date: Mon, 7 Jun 2021 16:10:28 -0700 Subject: [PATCH 06/16] Fix color and asset name for large face enroll icon Don't use extracted color for the large face enroll icon asset shown on the education and confirmation screens. Also update the name of this asset to be more accurate and less confusing. Test: Manual Bug: 187845699 Change-Id: I1ac1d523f79757caa8aaac6b04b059c2c40fc0de --- color-check-baseline.xml | 2 +- ..._introduction.xml => face_enroll_icon_large.xml} | 13 +++++++------ res/layout/face_enroll_education.xml | 2 +- res/layout/face_enroll_finish.xml | 2 +- res/values-night/colors.xml | 2 +- res/values/colors.xml | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) rename res/drawable/{face_enroll_introduction.xml => face_enroll_icon_large.xml} (86%) 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/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/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 From 11c4b2c51ef2c6e215cc5838bf640868580ea59d Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Tue, 8 Jun 2021 16:55:51 +0800 Subject: [PATCH 07/16] Remove the large space when searching region list Collapse the tool bar when we're doing search for a better user experience. And we don't allow user can expand the tool bar at this moment since it will see a large space at this moment. Fix: 181741353 Test: Open the page and don't see a huge space now. Change-Id: Iec5ebe9b22a2aad15dc8cf90113a64c358ee447e --- .../datetime/timezone/BaseTimeZonePicker.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) 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; From fab609f8921d45ad8533a701daf50549fba3b5dc Mon Sep 17 00:00:00 2001 From: Stanley Wang Date: Tue, 8 Jun 2021 20:34:55 +0800 Subject: [PATCH 08/16] Fix the problem of the TalkBack function can not be turned on. - Check the states of the switch and the accessibility service. If the states are different, update the state of accessibility service. Fix: 190309601 Test: robotest and test the TalkBack manually. Change-Id: Id8c28223b1958087e7cdc481348487376afa2203 --- ...ccessibilityServicePreferenceFragment.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 7902ec6c092..346b6ef3e51 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -49,7 +49,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 +231,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 +317,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends } private void handleConfirmServiceEnabled(boolean confirmed) { - mToggleServiceSwitchPreference.setChecked(confirmed); getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed); onPreferenceToggled(mPreferenceKey, confirmed); } @@ -338,8 +340,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 +535,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 +548,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; } From e771cbb1aa4972c22211f4d6285b0f9592ed435c Mon Sep 17 00:00:00 2001 From: menghanli Date: Mon, 7 Jun 2021 13:52:13 +0800 Subject: [PATCH 09/16] Add AccessibilityShortcutPreferenceFragment to share non-accesibility tools to add shortcut Bug: 190022774 Test: make RunSettingsRoboTests -j52 ROBOTEST_FILTER=AccessibilityShortcutPreferenceFragmentTest Change-Id: I1cae8fbed059ba7c309126e2dff46adfb48cffd8 --- ...cessibilityShortcutPreferenceFragment.java | 366 ++++++++++++++++++ .../ToggleFeaturePreferenceFragment.java | 2 +- ...ibilityShortcutPreferenceFragmentTest.java | 222 +++++++++++ .../ToggleFeaturePreferenceFragmentTest.java | 2 +- ...enMagnificationPreferenceFragmentTest.java | 1 + 5 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java create mode 100644 tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java 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/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index c247a193509..1002a8491ca 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -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); } 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/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index 471e83d2a1c..c47a7935de5 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -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, diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java index a5811e26dbf..9aa0e03f39e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java @@ -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, From 8c29ff0a27e2c71641ef13ce05c4b077d81b4806 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Wed, 9 Jun 2021 00:35:53 +0800 Subject: [PATCH 10/16] [Provider Model] Fix the panel non responsive issue Bug: 187340277 Test: manual test atest -c ProviderModelSliceTest Change-Id: Ie27a5049b2cab05b7c7936395d0db85c3c594d96 --- .../settings/network/ProviderModelSlice.java | 13 +++++++++++++ .../settings/network/ProviderModelSliceTest.java | 10 ++++++++++ 2 files changed, 23 insertions(+) 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/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() { From ad973331eaaa9a4e3157e80f310be88a5b94903f Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Mon, 7 Jun 2021 21:27:21 +0800 Subject: [PATCH 11/16] Move Power menu from Gesture to System Settings - Add icon for Power menu - Remove from Gesture Settings - Add to System Settings Bug: 168176408 Test: manual Change-Id: Ie7d5530d7e4e32a6db5ce43717131018944bbe28 --- res/drawable/ic_power_settings.xml | 26 ++++++++++++++++++++++++++ res/xml/gestures.xml | 6 ------ res/xml/system_dashboard_fragment.xml | 8 ++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 res/drawable/ic_power_settings.xml 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/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"/> + + Date: Tue, 2 Feb 2021 12:55:03 +0800 Subject: [PATCH 12/16] UK emergency alert app rename Bug: 179052608 Test: Build Change-Id: Ic9d4e1138113e3c90abb27ba0a72d2ed07528c1e (cherry picked from commit f7e3d570236f64992b7e34db37a4634c16a878a6) Merged-in: Ic9d4e1138113e3c90abb27ba0a72d2ed07528c1e --- res/values-mcc234/strings.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 res/values-mcc234/strings.xml 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 From b458c1d4ab4f1fc2e314c9ed463d4438f7a6ea56 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 9 Jun 2021 12:04:31 +0800 Subject: [PATCH 13/16] Update homepage summary of Accessibility Fixes: 190015037 Test: robotests & visual Change-Id: Iab001452ae240aee3d3d1ebe9d58c96bda66a50d --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 2a75186e4b7d5b8d3f7f2b8fd43ac26c0500e1b7 Mon Sep 17 00:00:00 2001 From: ykhung Date: Wed, 9 Jun 2021 11:41:33 +0800 Subject: [PATCH 14/16] Resolve locale not update issues in the chart view - read locale from configuration rather than Locale.getDefault - refine 12-24 format to align the current status bar short style - resolve locale change not update chart percentage label - extend timestamp label in the chart graph from 4 to 5 labels Bug: 190150515 Bug: 190422902 Bug: 190226837 Test: make SettingsRoboTests Change-Id: I5347964900123a6d112dbc37c2af87eb7d73f1d2 --- .../BatteryChartPreferenceController.java | 12 ++--- .../settings/fuelgauge/BatteryChartView.java | 40 +++++++------- .../settings/fuelgauge/BatteryHistEntry.java | 3 +- .../settings/fuelgauge/ConvertUtils.java | 23 ++++++-- .../BatteryChartPreferenceControllerTest.java | 12 +++-- .../fuelgauge/BatteryChartViewTest.java | 10 ++-- .../settings/fuelgauge/ConvertUtilsTest.java | 54 ++++++++++++++----- 7 files changed, 104 insertions(+), 50 deletions(-) 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/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( From ff0c06f20aab1c11d64e8a145021dded86a975eb Mon Sep 17 00:00:00 2001 From: Zoey Chen Date: Wed, 9 Jun 2021 14:23:04 +0800 Subject: [PATCH 15/16] [Settings] Fix test failure of WFC group Bug: 190569916 Test: atest NetworkProviderWifiCallingGroupTest Change-Id: Ie520f3709eb0751150fdbda1f14e878ba21e22c4 --- .../network/telephony/NetworkProviderWifiCallingGroup.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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(); From 3ce3a93e711ca796a971ad1f6418efc46a2e7abc Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Thu, 3 Jun 2021 23:46:25 +0800 Subject: [PATCH 16/16] Support the lottie image file for the banner in Accessibility Settings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Action: Currently, the ImageView component couldn’t handle the lottie image from raw resource folder, so temporarily using the LottieAnimationView from settingsLib to support lottie image for the banner of Accessibility settings. Bug: 186065724 Bug: 179451422 Test: atest AnimatedImagePreferenceTest Change-Id: I99fb2ed26085c73bc262c58001de8dec3078e1d0 --- res/layout/preference_animated_image.xml | 10 +++ .../AnimatedImagePreference.java | 63 ++++++++++++++++++- .../AnimatedImagePreferenceTest.java | 33 ++++++++-- 3 files changed, 99 insertions(+), 7 deletions(-) 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/src/com/android/settings/accessibility/AnimatedImagePreference.java b/src/com/android/settings/accessibility/AnimatedImagePreference.java index 69c0d136cbd..c707e5cc0a7 100644 --- a/src/com/android/settings/accessibility/AnimatedImagePreference.java +++ b/src/com/android/settings/accessibility/AnimatedImagePreference.java @@ -22,6 +22,9 @@ 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; @@ -29,12 +32,20 @@ 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; @@ -56,19 +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) { - resetAnimation(imageView.getDrawable()); + resetAnimations(imageView, lottieView); + hideAllChildViews(holder.itemView); imageView.setImageURI(mImageUri); - startAnimation(imageView.getDrawable()); + 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); } } @@ -96,6 +115,22 @@ public class AnimatedImagePreference extends Preference { } } + 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; @@ -110,6 +145,12 @@ public class AnimatedImagePreference extends Preference { ((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; @@ -121,4 +162,20 @@ public class AnimatedImagePreference extends Preference { ((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/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java index b8ab432d2e9..4bce0bb264b 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AnimatedImagePreferenceTest.java @@ -18,12 +18,15 @@ 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; @@ -37,6 +40,8 @@ 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; @@ -45,9 +50,12 @@ 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; @@ -60,13 +68,12 @@ public class AnimatedImagePreferenceTest { 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(); } @@ -126,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)); + } }