From c86e2f347244290d4f982780064ddf08ef6869a4 Mon Sep 17 00:00:00 2001 From: Menghan Li Date: Thu, 13 Feb 2025 09:08:30 +0000 Subject: [PATCH] feat(A11yFeedback): Add feedback entry for Accessibility page This entry point allows users to access in the action bar. Visibility is controlled by the aconfig and FeedbackManager#isAvailable Bug: 393981463 Test: atest AccessibilitySettingsTest Flag: com.android.server.accessibility.enable_low_vision_generic_feedback Change-Id: I8c219b8220b5839121d14959fe526e6200afeecb --- res/values/strings.xml | 2 + .../accessibility/AccessibilitySettings.java | 40 ++++++++++- .../AccessibilitySettingsTest.java | 71 ++++++++++++++++++- 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 39e0dbdf4e8..c6ed89a22f9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5157,6 +5157,8 @@ Help improve by taking a survey No surveys available + + Send feedback Downloaded apps diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index c0341cc5949..57eb4d5fba4 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -30,6 +30,9 @@ import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; @@ -101,6 +104,8 @@ public class AccessibilitySettings extends DashboardFragment implements // presentation. private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000; + static final int MENU_ID_SEND_FEEDBACK = 0; + private final Handler mHandler = new Handler(); private final Runnable mUpdateRunnable = new Runnable() { @@ -143,8 +148,9 @@ public class AccessibilitySettings extends DashboardFragment implements } }; - @VisibleForTesting - AccessibilitySettingsContentObserver mSettingsContentObserver; + private AccessibilitySettingsContentObserver mSettingsContentObserver; + + private FeedbackManager mFeedbackManager; private final Map mCategoryToPrefCategoryMap = new ArrayMap<>(); @@ -245,6 +251,24 @@ public class AccessibilitySettings extends DashboardFragment implements super.onDestroy(); } + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + if (getFeedbackManager().isAvailable()) { + menu.add(Menu.NONE, MENU_ID_SEND_FEEDBACK, Menu.NONE, + getPrefContext().getText(R.string.accessibility_send_feedback_title)); + } + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + if (item.getItemId() == MENU_ID_SEND_FEEDBACK) { + getFeedbackManager().sendFeedback(); + return true; + } + return super.onOptionsItemSelected(item); + } + @Override protected int getPreferenceScreenResId() { return R.xml.accessibility_settings; @@ -255,6 +279,18 @@ public class AccessibilitySettings extends DashboardFragment implements return TAG; } + @VisibleForTesting + void setFeedbackManager(FeedbackManager feedbackManager) { + this.mFeedbackManager = feedbackManager; + } + + private FeedbackManager getFeedbackManager() { + if (mFeedbackManager == null) { + mFeedbackManager = new FeedbackManager(getActivity()); + } + return mFeedbackManager; + } + /** * Returns the summary for the current state of this accessibilityService. * diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index 91d7d91c833..13168658055 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -21,7 +21,11 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -43,6 +47,8 @@ import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; +import android.view.Menu; +import android.view.MenuItem; import android.view.accessibility.AccessibilityManager; import androidx.fragment.app.Fragment; @@ -113,7 +119,8 @@ public class AccessibilitySettingsTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); @Spy private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo( @@ -121,7 +128,13 @@ public class AccessibilitySettingsTest { private ShadowAccessibilityManager mShadowAccessibilityManager; @Mock private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private Menu mMenu; + @Mock + private MenuItem mMenuItem; + private ActivityController mActivityController; + private AccessibilitySettings mFragment; @Before @@ -438,6 +451,62 @@ public class AccessibilitySettingsTest { } + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onCreateOptionsMenu_enableLowVisionGenericFeedback_shouldAddSendFeedbackMenu() { + setupFragment(); + mFragment.setFeedbackManager(new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME)); + when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem); + + mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); + + verify(mMenu).add(anyInt(), eq(AccessibilitySettings.MENU_ID_SEND_FEEDBACK), + anyInt(), eq(mContext.getText(R.string.accessibility_send_feedback_title))); + } + + @Test + @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onCreateOptionsMenu_disableLowVisionGenericFeedback_shouldNotAddSendFeedbackMenu() { + setupFragment(); + mFragment.setFeedbackManager(new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME)); + when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem); + + mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); + + verify(mMenu, never()).add(anyInt(), eq(AccessibilitySettings.MENU_ID_SEND_FEEDBACK), + anyInt(), eq(mContext.getText(R.string.accessibility_send_feedback_title))); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onOptionsItemSelected_enableLowVisionGenericFeedback_shouldStartSendFeedback() { + setupFragment(); + mFragment.setFeedbackManager(new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME)); + when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem); + mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); + when(mMenuItem.getItemId()).thenReturn(AccessibilitySettings.MENU_ID_SEND_FEEDBACK); + + mFragment.onOptionsItemSelected(mMenuItem); + + Intent startedIntent = shadowOf(mFragment.getActivity()).getNextStartedActivity(); + assertThat(startedIntent).isNotNull(); + } + + @Test + @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onOptionsItemSelected_disableLowVisionGenericFeedback_shouldNotStartSendFeedback() { + setupFragment(); + mFragment.setFeedbackManager(new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME)); + when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem); + mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); + when(mMenuItem.getItemId()).thenReturn(AccessibilitySettings.MENU_ID_SEND_FEEDBACK); + + mFragment.onOptionsItemSelected(mMenuItem); + + Intent startedIntent = shadowOf(mFragment.getActivity()).getNextStartedActivity(); + assertThat(startedIntent).isNull(); + } + @Test public void testAccessibilityMenuInSystem_IncludedInInteractionControl() { mShadowAccessibilityManager.setInstalledAccessibilityServiceList(