From 9855997b3c20c466e94532394a36eeaede673c0a Mon Sep 17 00:00:00 2001 From: menghanli Date: Fri, 4 Feb 2022 13:25:58 +0800 Subject: [PATCH 1/4] Show quick setting tooltips after shortcut tutorial dialog dismiss - ToggleAccessibilityServicePreferenceFragment for unbundle accessibility service - LaunchAccessibilityActivityPreferenceFragment for unbundle accessibility shortcut-activity Bug: 210356011 Test: make RunSettingsRoboTests ROBOTEST_FILTER=AccessibilityGestureNavigationTutorialTest Change-Id: I6f9e7e206ae7642d91e658ceb18b9e7ce4244a22 --- ...ccessibilityGestureNavigationTutorial.java | 9 +++- ...cessibilityActivityPreferenceFragment.java | 27 +++++++++++ ...ccessibilityServicePreferenceFragment.java | 48 +++++++++++-------- ...sibilityGestureNavigationTutorialTest.java | 45 +++++++++++++++-- 4 files changed, 105 insertions(+), 24 deletions(-) diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java index 0ca16cb99b0..773c9878e9d 100644 --- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java +++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java @@ -46,6 +46,7 @@ import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RawRes; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; @@ -125,9 +126,15 @@ public final class AccessibilityGestureNavigationTutorial { } static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) { + return createAccessibilityTutorialDialog(context, shortcutTypes, mOnClickListener); + } + + static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes, + @Nullable DialogInterface.OnClickListener negativeButtonListener) { return new AlertDialog.Builder(context) .setView(createShortcutNavigationContentView(context, shortcutTypes)) - .setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener) + .setNegativeButton(R.string.accessibility_tutorial_dialog_button, + negativeButtonListener) .create(); } diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index 00f280440a9..3a3011c54c4 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -20,9 +20,11 @@ import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logA import android.accessibilityservice.AccessibilityShortcutInfo; import android.app.ActivityOptions; +import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.net.Uri; @@ -124,6 +126,20 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature return null; } + @Override + public Dialog onCreateDialog(int dialogId) { + switch (dialogId) { + case AccessibilityDialogUtils.DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: + final Dialog dialog = AccessibilityGestureNavigationTutorial + .createAccessibilityTutorialDialog(getPrefContext(), + getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked); + dialog.setCanceledOnTouchOutside(false); + return dialog; + default: + return super.onCreateDialog(dialogId); + } + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Do not call super. We don't want to see the "Help & feedback" option on this page so as @@ -208,4 +224,15 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature return settingsIntent; } + + /** + * This method will be invoked when a button in the tutorial dialog is clicked. + * + * @param dialog The dialog that received the click + * @param which The button that was clicked + */ + private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) { + dialog.dismiss(); + showQuickSettingsTooltipIfNeeded(); + } } diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index a1c98cdc0c4..ce9cf1599c9 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -168,9 +168,9 @@ public class ToggleAccessibilityServicePreferenceFragment extends @Override public Dialog onCreateDialog(int dialogId) { + final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); switch (dialogId) { - case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: { - final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: if (info == null) { return null; } @@ -178,10 +178,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends .createCapabilitiesDialog(getPrefContext(), info, this::onDialogButtonFromEnableToggleClicked, this::onDialogButtonFromUninstallClicked); - break; - } - case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: { - final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + return mWarningDialog; + case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: if (info == null) { return null; } @@ -189,10 +187,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends .createCapabilitiesDialog(getPrefContext(), info, this::onDialogButtonFromShortcutToggleClicked, this::onDialogButtonFromUninstallClicked); - break; - } - case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: { - final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + return mWarningDialog; + case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: if (info == null) { return null; } @@ -200,23 +196,24 @@ public class ToggleAccessibilityServicePreferenceFragment extends .createCapabilitiesDialog(getPrefContext(), info, this::onDialogButtonFromShortcutClicked, this::onDialogButtonFromUninstallClicked); - break; - } - case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: { - final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + return mWarningDialog; + case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: if (info == null) { return null; } mWarningDialog = AccessibilityServiceWarning .createDisableDialog(getPrefContext(), info, this::onDialogButtonFromDisableToggleClicked); - break; - } - default: { - mWarningDialog = super.onCreateDialog(dialogId); - } + return mWarningDialog; + case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: + final Dialog dialog = AccessibilityGestureNavigationTutorial + .createAccessibilityTutorialDialog(getPrefContext(), + getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked); + dialog.setCanceledOnTouchOutside(false); + return dialog; + default: + return super.onCreateDialog(dialogId); } - return mWarningDialog; } @Override @@ -488,6 +485,17 @@ public class ToggleAccessibilityServicePreferenceFragment extends mWarningDialog.dismiss(); } + /** + * This method will be invoked when a button in the tutorial dialog is clicked. + * + * @param dialog The dialog that received the click + * @param which The button that was clicked + */ + private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) { + dialog.dismiss(); + showQuickSettingsTooltipIfNeeded(); + } + void onDialogButtonFromShortcutClicked(View view) { final int viewId = view.getId(); if (viewId == R.id.permission_enable_allow_button) { diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java index cf0ce96bf60..6efdcf7a7e2 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java @@ -22,26 +22,41 @@ import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutT import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.verify; + import android.content.Context; +import android.content.DialogInterface; import androidx.appcompat.app.AlertDialog; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; /** Tests for {@link AccessibilityGestureNavigationTutorial}. */ @RunWith(RobolectricTestRunner.class) public final class AccessibilityGestureNavigationTutorialTest { - private Context mContext; + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock + private DialogInterface.OnClickListener mMockOnClickListener; + + private final Context mContext = ApplicationProvider.getApplicationContext(); private int mShortcutTypes; @Before public void setUp() { - mContext = RuntimeEnvironment.application; + mContext.setTheme(R.style.Theme_AppCompat); mShortcutTypes = /* initial */ 0; } @@ -86,4 +101,28 @@ public final class AccessibilityGestureNavigationTutorialTest { mShortcutTypes)).hasSize(/* expectedSize= */ 2); assertThat(alertDialog).isNotNull(); } + + @Test + public void performClickOnNegativeButton_turnOnSoftwareShortcut_dismiss() { + mShortcutTypes |= UserShortcutType.SOFTWARE; + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + alertDialog.show(); + + alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick(); + + assertThat(alertDialog.isShowing()).isFalse(); + } + + @Test + public void performClickOnNegativeButton_turnOnSoftwareShortcut_callOnClickListener() { + mShortcutTypes |= UserShortcutType.SOFTWARE; + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes, mMockOnClickListener); + alertDialog.show(); + + alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick(); + + verify(mMockOnClickListener).onClick(alertDialog, DialogInterface.BUTTON_NEGATIVE); + } } From 0577a068b82a94d8c7f34078d783171d560b8756 Mon Sep 17 00:00:00 2001 From: menghanli Date: Tue, 25 Jan 2022 06:45:40 +0800 Subject: [PATCH 2/4] Show quick setting tooltips after shortcut edit dialog dismiss - ToggleAccessibilityServicePreferenceFragment for unbundle accessibility service - LaunchAccessibilityActivityPreferenceFragment for unbundle accessibility shortcut-activity Bug: 210356011 Test: Manual testing Change-Id: I3bbaa6c0e5a4359e696c776a300520c4572b31f7 --- ...unchAccessibilityActivityPreferenceFragment.java | 7 +++++++ ...oggleAccessibilityServicePreferenceFragment.java | 6 ++++++ .../ToggleFeaturePreferenceFragment.java | 13 +++++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index 3a3011c54c4..8e2ed48826a 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -235,4 +235,11 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature dialog.dismiss(); showQuickSettingsTooltipIfNeeded(); } + + + @Override + protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) { + super.callOnAlertDialogCheckboxClicked(dialog, which); + showQuickSettingsTooltipIfNeeded(getShortcutTypeCheckBoxValue()); + } } diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index ce9cf1599c9..0e0eedae02b 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -496,6 +496,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends showQuickSettingsTooltipIfNeeded(); } + @Override + protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) { + super.callOnAlertDialogCheckboxClicked(dialog, which); + showQuickSettingsTooltipIfNeeded(getShortcutTypeCheckBoxValue()); + } + void onDialogButtonFromShortcutClicked(View view) { final int viewId = view.getId(); if (viewId == R.id.permission_enable_allow_button) { diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 0cc1f1c1662..d36764b61c2 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -798,6 +798,19 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut); } + /** + * Shows the quick settings tooltip if the quick settings service and the shortcut are assigned. + * The tooltip only shows once. + * + * @param shortcutType The shortcut type. + */ + protected void showQuickSettingsTooltipIfNeeded(@UserShortcutType int shortcutType) { + if (shortcutType == AccessibilityUtil.UserShortcutType.EMPTY) { + return; + } + showQuickSettingsTooltipIfNeeded(); + } + /** * Shows the quick settings tooltip if the quick settings service is assigned. The tooltip only * shows once. From 0d6909dc0f6f804d19eadbc92f9104a033aa8905 Mon Sep 17 00:00:00 2001 From: menghanli Date: Tue, 25 Jan 2022 06:32:26 +0800 Subject: [PATCH 3/4] Support accessibility settings load title from quick settings panel - ToggleAccessibilityServicePreferenceFragment for unbundle accessibility service - LaunchAccessibilityActivityPreferenceFragment for unbundle accessibility shortcut-activity Bug: 210356011 Test: make RunSettingsRoboTests ROBOTEST_FILTER=LaunchAccessibilityActivityPreferenceFragmentTest ToggleAccessibilityServicePreferenceFragmentTest Change-Id: I78dcb8dcb2778819ec4bb130a041211598f6d9c --- ...cessibilityActivityPreferenceFragment.java | 6 +- ...ccessibilityServicePreferenceFragment.java | 6 +- .../ToggleFeaturePreferenceFragment.java | 19 +++ ...ibilityActivityPreferenceFragmentTest.java | 145 ++++++++++++++++++ ...sibilityServicePreferenceFragmentTest.java | 145 ++++++++++++++++++ 5 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragmentTest.java create mode 100644 tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index 8e2ed48826a..afd334e199e 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -123,7 +123,11 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature @Override CharSequence getTileName() { - return null; + final ComponentName componentName = getTileComponentName(); + if (componentName == null) { + return null; + } + return loadTileLabel(getPrefContext(), componentName); } @Override diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 0e0eedae02b..d4d2f4cbce1 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -245,7 +245,11 @@ public class ToggleAccessibilityServicePreferenceFragment extends @Override CharSequence getTileName() { - return null; + final ComponentName componentName = getTileComponentName(); + if (componentName == null) { + return null; + } + return loadTileLabel(getPrefContext(), componentName); } @Override diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index d36764b61c2..08e9b88878a 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -25,7 +25,9 @@ import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.graphics.drawable.Drawable; import android.icu.text.CaseMap; import android.net.Uri; @@ -33,6 +35,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; +import android.service.quicksettings.TileService; import android.text.Html; import android.text.TextUtils; import android.view.LayoutInflater; @@ -843,4 +846,20 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference tileComponentName); mNeedsQSTooltipReshow = false; } + + /** Returns user visible name of the tile by given {@link ComponentName}. */ + protected CharSequence loadTileLabel(Context context, ComponentName componentName) { + final PackageManager packageManager = context.getPackageManager(); + final Intent queryIntent = new Intent(TileService.ACTION_QS_TILE); + final List resolveInfos = + packageManager.queryIntentServices(queryIntent, PackageManager.GET_META_DATA); + for (ResolveInfo info : resolveInfos) { + final ServiceInfo serviceInfo = info.serviceInfo; + if (TextUtils.equals(componentName.getPackageName(), serviceInfo.packageName) + && TextUtils.equals(componentName.getClassName(), serviceInfo.name)) { + return serviceInfo.loadLabel(packageManager); + } + } + return null; + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragmentTest.java new file mode 100644 index 00000000000..0c656b37604 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragmentTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 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.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.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.service.quicksettings.TileService; + +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; + +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.Shadows; +import org.robolectric.shadows.ShadowPackageManager; + +import java.util.Arrays; + +/** Tests for {@link LaunchAccessibilityActivityPreferenceFragment} */ +@RunWith(RobolectricTestRunner.class) +public class LaunchAccessibilityActivityPreferenceFragmentTest { + + private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example"; + private static final String PLACEHOLDER_TILE_CLASS_NAME = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; + private static final String PLACEHOLDER_TILE_CLASS_NAME2 = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder2"; + private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName( + PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME); + private static final String PLACEHOLDER_TILE_NAME = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; + private static final String PLACEHOLDER_TILE_NAME2 = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder2"; + + private TestLaunchAccessibilityActivityPreferenceFragment 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 TestLaunchAccessibilityActivityPreferenceFragment()); + when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager); + when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext); + when(mFragment.getContext()).thenReturn(mContext); + mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null)); + when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager); + doReturn(mScreen).when(mFragment).getPreferenceScreen(); + } + + @Test + public void getTileName_noTileServiceAssigned_noMatchString() { + final CharSequence tileName = mFragment.getTileName(); + assertThat(tileName.toString()).isEqualTo(""); + } + + @Test + public void getTileName_hasOneTileService_haveMatchString() { + final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE); + final ResolveInfo info = new ResolveInfo(); + info.serviceInfo = new FakeServiceInfo(); + info.serviceInfo.packageName = PLACEHOLDER_PACKAGE_NAME; + info.serviceInfo.name = PLACEHOLDER_TILE_CLASS_NAME; + final ShadowPackageManager shadowPackageManager = + Shadows.shadowOf(mContext.getPackageManager()); + shadowPackageManager.setResolveInfosForIntent(tileProbe, Arrays.asList(info)); + + final CharSequence tileName = mFragment.getTileName(); + assertThat(tileName.toString()).isEqualTo(PLACEHOLDER_TILE_NAME); + } + + @Test + public void getTileName_hasTwoTileServices_haveMatchString() { + final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE); + final ResolveInfo info = new ResolveInfo(); + info.serviceInfo = new FakeServiceInfo(); + info.serviceInfo.packageName = PLACEHOLDER_PACKAGE_NAME; + info.serviceInfo.name = PLACEHOLDER_TILE_CLASS_NAME; + final ResolveInfo info2 = new ResolveInfo(); + info2.serviceInfo = new FakeServiceInfo2(); + info2.serviceInfo.packageName = PLACEHOLDER_PACKAGE_NAME; + info2.serviceInfo.name = PLACEHOLDER_TILE_CLASS_NAME2; + final ShadowPackageManager shadowPackageManager = + Shadows.shadowOf(mContext.getPackageManager()); + shadowPackageManager.setResolveInfosForIntent(tileProbe, Arrays.asList(info, info2)); + + final CharSequence tileName = mFragment.getTileName(); + assertThat(tileName.toString()).isEqualTo(PLACEHOLDER_TILE_NAME); + } + + private static class FakeServiceInfo extends ServiceInfo { + public String loadLabel(PackageManager mgr) { + return PLACEHOLDER_TILE_NAME; + } + } + + private static class FakeServiceInfo2 extends ServiceInfo { + public String loadLabel(PackageManager mgr) { + return PLACEHOLDER_TILE_NAME2; + } + } + + private static class TestLaunchAccessibilityActivityPreferenceFragment + extends LaunchAccessibilityActivityPreferenceFragment { + + @Override + protected ComponentName getTileComponentName() { + return PLACEHOLDER_TILE_COMPONENT_NAME; + } + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java new file mode 100644 index 00000000000..db5b83c841d --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 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.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.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.service.quicksettings.TileService; + +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; + +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.Shadows; +import org.robolectric.shadows.ShadowPackageManager; + +import java.util.Arrays; + +/** Tests for {@link ToggleAccessibilityServicePreferenceFragment} */ +@RunWith(RobolectricTestRunner.class) +public class ToggleAccessibilityServicePreferenceFragmentTest { + + private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example"; + private static final String PLACEHOLDER_TILE_CLASS_NAME = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; + private static final String PLACEHOLDER_TILE_CLASS_NAME2 = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder2"; + private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName( + PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME); + private static final String PLACEHOLDER_TILE_NAME = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; + private static final String PLACEHOLDER_TILE_NAME2 = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder2"; + + private TestToggleAccessibilityServicePreferenceFragment 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 TestToggleAccessibilityServicePreferenceFragment()); + when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager); + when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext); + when(mFragment.getContext()).thenReturn(mContext); + mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null)); + when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager); + doReturn(mScreen).when(mFragment).getPreferenceScreen(); + } + + @Test + public void getTileName_noTileServiceAssigned_noMatchString() { + final CharSequence tileName = mFragment.getTileName(); + assertThat(tileName.toString()).isEqualTo(""); + } + + @Test + public void getTileName_hasOneTileService_haveMatchString() { + final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE); + final ResolveInfo info = new ResolveInfo(); + info.serviceInfo = new FakeServiceInfo(); + info.serviceInfo.packageName = PLACEHOLDER_PACKAGE_NAME; + info.serviceInfo.name = PLACEHOLDER_TILE_CLASS_NAME; + final ShadowPackageManager shadowPackageManager = + Shadows.shadowOf(mContext.getPackageManager()); + shadowPackageManager.setResolveInfosForIntent(tileProbe, Arrays.asList(info)); + + final CharSequence tileName = mFragment.getTileName(); + assertThat(tileName.toString()).isEqualTo(PLACEHOLDER_TILE_NAME); + } + + @Test + public void getTileName_hasTwoTileServices_haveMatchString() { + final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE); + final ResolveInfo info = new ResolveInfo(); + info.serviceInfo = new FakeServiceInfo(); + info.serviceInfo.packageName = PLACEHOLDER_PACKAGE_NAME; + info.serviceInfo.name = PLACEHOLDER_TILE_CLASS_NAME; + final ResolveInfo info2 = new ResolveInfo(); + info2.serviceInfo = new FakeServiceInfo2(); + info2.serviceInfo.packageName = PLACEHOLDER_PACKAGE_NAME; + info2.serviceInfo.name = PLACEHOLDER_TILE_CLASS_NAME2; + final ShadowPackageManager shadowPackageManager = + Shadows.shadowOf(mContext.getPackageManager()); + shadowPackageManager.setResolveInfosForIntent(tileProbe, Arrays.asList(info, info2)); + + final CharSequence tileName = mFragment.getTileName(); + assertThat(tileName.toString()).isEqualTo(PLACEHOLDER_TILE_NAME); + } + + private static class FakeServiceInfo extends ServiceInfo { + public String loadLabel(PackageManager mgr) { + return PLACEHOLDER_TILE_NAME; + } + } + + private static class FakeServiceInfo2 extends ServiceInfo { + public String loadLabel(PackageManager mgr) { + return PLACEHOLDER_TILE_NAME2; + } + } + + private static class TestToggleAccessibilityServicePreferenceFragment + extends ToggleAccessibilityServicePreferenceFragment { + + @Override + protected ComponentName getTileComponentName() { + return PLACEHOLDER_TILE_COMPONENT_NAME; + } + } +} From 61479cde86f22f399e8ca09ff1506f2e7c3ed672 Mon Sep 17 00:00:00 2001 From: menghanli Date: Thu, 20 Jan 2022 21:59:22 +0800 Subject: [PATCH 4/4] Integrate new accessibility tileService attr into settings - Accessibilty service: AccessibilityService_tileService - Accesibility shortcut: AccessibilityShortcutTarget_tileService Bug: 216614718 Test: manual test Change-Id: I24baff13300dfc9a242fcfffe0a4888575ca0c9f --- .../AccessibilityDetailsSettingsFragment.java | 7 ++++ .../accessibility/AccessibilitySettings.java | 41 ++++++++++++++++++- ...cessibilityActivityPreferenceFragment.java | 10 ++++- ...ccessibilityServicePreferenceFragment.java | 9 +++- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java index f9b537b16c3..5fc4cd21797 100644 --- a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java @@ -221,6 +221,13 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment { extras.putString(AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME, new ComponentName(packageName, settingsClassName).flattenToString()); } + + final String tileServiceClassName = info.getTileServiceClassName(); + if (!TextUtils.isEmpty(tileServiceClassName)) { + extras.putString(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME, + new ComponentName(packageName, tileServiceClassName).flattenToString()); + } + extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName); extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, info.getAnimatedImageRes()); diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index b712b9d37bc..e834640e678 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -97,6 +97,7 @@ public class AccessibilitySettings extends DashboardFragment { static final String EXTRA_SETTINGS_TITLE = "settings_title"; static final String EXTRA_COMPONENT_NAME = "component_name"; static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name"; + static final String EXTRA_TILE_SERVICE_COMPONENT_NAME = "tile_service_component_name"; static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource"; static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw"; static final String EXTRA_ANIMATED_IMAGE_RES = "animated_image_res"; @@ -573,11 +574,13 @@ public class AccessibilitySettings extends DashboardFragment { serviceEnabled); final String htmlDescription = info.loadHtmlDescription(mPm); final String settingsClassName = info.getSettingsActivityName(); + final String tileServiceClassName = info.getTileServiceClassName(); putBasicExtras(preference, prefKey, title, description, imageRes, htmlDescription, componentName); putServiceExtras(preference, resolveInfo, serviceEnabled); putSettingsExtras(preference, packageName, settingsClassName); + putTileServiceExtras(preference, packageName, tileServiceClassName); preferenceList.add(preference); } @@ -631,10 +634,13 @@ public class AccessibilitySettings extends DashboardFragment { final int imageRes = info.getAnimatedImageRes(); final String htmlDescription = info.loadHtmlDescription(mPm); final String settingsClassName = info.getSettingsActivityName(); + final String tileServiceClassName = info.getTileServiceClassName(); putBasicExtras(preference, prefKey, title, description, imageRes, htmlDescription, componentName); putSettingsExtras(preference, componentName.getPackageName(), settingsClassName); + putTileServiceExtras(preference, componentName.getPackageName(), + tileServiceClassName); preferenceList.add(preference); } @@ -730,7 +736,11 @@ public class AccessibilitySettings extends DashboardFragment { /** * Puts the service extras into {@link RestrictedPreference}'s getExtras(). * - * Called by {@link AccessibilityServiceInfo} for now. + *

Note: Called by {@link AccessibilityServiceInfo}.

+ * + * @param preference The preference we are configuring. + * @param resolveInfo The service resolve info. + * @param serviceEnabled Whether the accessibility service is enabled. */ private void putServiceExtras(RestrictedPreference preference, ResolveInfo resolveInfo, Boolean serviceEnabled) { @@ -743,7 +753,12 @@ public class AccessibilitySettings extends DashboardFragment { /** * Puts the settings extras into {@link RestrictedPreference}'s getExtras(). * - * Called when settings UI is needed. + *

Note: Called when settings UI is needed.

+ * + * @param preference The preference we are configuring. + * @param packageName Package of accessibility feature. + * @param settingsClassName The component name of an activity that allows the user to modify + * the settings for this accessibility feature. */ private void putSettingsExtras(RestrictedPreference preference, String packageName, String settingsClassName) { @@ -756,5 +771,27 @@ public class AccessibilitySettings extends DashboardFragment { new ComponentName(packageName, settingsClassName).flattenToString()); } } + + /** + * Puts the information about a particular application + * {@link android.service.quicksettings.TileService} into {@link RestrictedPreference}'s + * getExtras(). + * + *

Note: Called when a tooltip of + * {@link android.service.quicksettings.TileService} is needed.

+ * + * @param preference The preference we are configuring. + * @param packageName Package of accessibility feature. + * @param tileServiceClassName The component name of tileService is associated with this + * accessibility feature. + */ + private void putTileServiceExtras(RestrictedPreference preference, String packageName, + String tileServiceClassName) { + final Bundle extras = preference.getExtras(); + if (!TextUtils.isEmpty(tileServiceClassName)) { + extras.putString(EXTRA_TILE_SERVICE_COMPONENT_NAME, + new ComponentName(packageName, tileServiceClassName).flattenToString()); + } + } } } diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index afd334e199e..ce8db21ecb2 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -53,6 +53,7 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature private static final String TAG = "LaunchA11yActivity"; private static final String EMPTY_STRING = ""; protected static final String KEY_LAUNCH_PREFERENCE = "launch_preference"; + private ComponentName mTileComponentName; @Override public int getMetricsCategory() { @@ -108,6 +109,13 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature AccessibilitySettings.EXTRA_SETTINGS_TITLE); mSettingsIntent = TextUtils.isEmpty(settingsTitle) ? null : getSettingsIntent(arguments); mSettingsTitle = (mSettingsIntent == null) ? null : settingsTitle; + + // Tile service. + if (arguments.containsKey(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME)) { + final String tileServiceComponentName = arguments.getString( + AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME); + mTileComponentName = ComponentName.unflattenFromString(tileServiceComponentName); + } } @Override @@ -118,7 +126,7 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature @Override ComponentName getTileComponentName() { - return null; + return mTileComponentName; } @Override diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index d4d2f4cbce1..d7a506a21df 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -65,6 +65,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends private static final String EMPTY_STRING = ""; private Dialog mWarningDialog; + private ComponentName mTileComponentName; private BroadcastReceiver mPackageRemovedReceiver; private boolean mDisabledStateLogged = false; private long mStartTimeMillsForLogging = 0; @@ -240,7 +241,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends @Override ComponentName getTileComponentName() { - return null; + return mTileComponentName; } @Override @@ -387,6 +388,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel( getPackageManager()); + if (arguments.containsKey(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME)) { + final String tileServiceComponentName = arguments.getString( + AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME); + mTileComponentName = ComponentName.unflattenFromString(tileServiceComponentName); + } + mStartTimeMillsForLogging = arguments.getLong(AccessibilitySettings.EXTRA_TIME_FOR_LOGGING); }