From a389ff59abddaae036ddf12a68467a7b839c7d44 Mon Sep 17 00:00:00 2001 From: Menghan Li Date: Fri, 7 Mar 2025 03:54:35 +0000 Subject: [PATCH] refactor(A11yFeedback): Simply feedback logic into menu controller Bug: 393980229 Test: atest FeedbackMenuControllerTest Flag: com.android.server.accessibility.enable_low_vision_generic_feedback Change-Id: I03af00957c2bcca1d1cc81970eccad6dd69bb4ac --- .../accessibility/AccessibilitySettings.java | 39 +---- .../ToggleFeaturePreferenceFragment.java | 62 ++----- .../actionbar/FeedbackMenuController.java | 94 +++++++++++ .../AccessibilitySettingsTest.java | 62 ------- ...eColorInversionPreferenceFragmentTest.java | 4 + .../ToggleFeaturePreferenceFragmentTest.java | 75 +-------- .../actionbar/FeedbackMenuControllerTest.java | 158 ++++++++++++++++++ 7 files changed, 276 insertions(+), 218 deletions(-) create mode 100644 src/com/android/settings/accessibility/actionbar/FeedbackMenuController.java create mode 100644 tests/robotests/src/com/android/settings/accessibility/actionbar/FeedbackMenuControllerTest.java diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 2c8247f959c..b4b1ef69a08 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -30,9 +30,6 @@ 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; @@ -45,6 +42,7 @@ import com.android.internal.accessibility.util.AccessibilityUtils; import com.android.internal.content.PackageMonitor; import com.android.settings.R; import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType; +import com.android.settings.accessibility.actionbar.FeedbackMenuController; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; @@ -105,8 +103,6 @@ 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() { @@ -151,8 +147,6 @@ public class AccessibilitySettings extends DashboardFragment implements private AccessibilitySettingsContentObserver mSettingsContentObserver; - private FeedbackManager mFeedbackManager; - private final Map mCategoryToPrefCategoryMap = new ArrayMap<>(); private final List mServicePreferences = new ArrayList<>(); @@ -216,6 +210,7 @@ public class AccessibilitySettings extends DashboardFragment implements mNeedPreferencesUpdate = false; registerContentMonitors(); registerInputDeviceListener(); + FeedbackMenuController.init(this, SettingsEnums.ACCESSIBILITY); } @Override @@ -252,24 +247,6 @@ 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, - 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; @@ -280,18 +257,6 @@ 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(), SettingsEnums.ACCESSIBILITY); - } - return mFeedbackManager; - } - /** * Returns the summary for the current state of this accessibilityService. * diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index d8c39856368..367f55710ba 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -40,9 +40,6 @@ import android.service.quicksettings.TileService; import android.text.Html; import android.text.TextUtils; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; @@ -63,6 +60,7 @@ import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.util.ShortcutUtils; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.accessibility.actionbar.FeedbackMenuController; import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.flags.Flags; @@ -94,7 +92,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment // , a11y settings will get the resources successfully. private static final String IMG_PREFIX = "R.drawable."; private static final String DRAWABLE_FOLDER = "drawable"; - static final int MENU_ID_SEND_FEEDBACK = 0; protected TopIntroPreference mTopIntroPreference; protected SettingsMainSwitchPreference mToggleServiceSwitchPreference; @@ -108,7 +105,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment protected Intent mSettingsIntent; // The mComponentName maybe null, such as Magnify protected ComponentName mComponentName; - @Nullable private FeedbackManager mFeedbackManager; protected CharSequence mFeatureName; protected Uri mImageUri; protected CharSequence mHtmlDescription; @@ -142,6 +138,8 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler()); registerKeysToObserverCallback(mSettingsContentObserver); + + FeedbackMenuController.init(this, getFeedbackCategory()); } protected void registerKeysToObserverCallback( @@ -247,24 +245,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment removeActionBarToggleSwitch(); } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - if (getFeedbackManager().isAvailable()) { - menu.add(Menu.NONE, MENU_ID_SEND_FEEDBACK, Menu.NONE, - 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 public int getDialogMetricsCategory(int dialogId) { switch (dialogId) { @@ -280,6 +260,18 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment return SettingsEnums.ACCESSIBILITY_SERVICE; } + /** + * Returns the category of the feedback page. + * + *

By default, this method returns {@link SettingsEnums#PAGE_UNKNOWN}. This indicates that + * the feedback category is unknown, and the absence of a feedback menu. + * + * @return The feedback category, which is {@link SettingsEnums#PAGE_UNKNOWN} by default. + */ + protected int getFeedbackCategory() { + return SettingsEnums.PAGE_UNKNOWN; + } + @Override public int getHelpResource() { return 0; @@ -785,28 +777,4 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment super.onCreateRecyclerView(inflater, parent, savedInstanceState); return AccessibilityFragmentUtils.addCollectionInfoToAccessibilityDelegate(recyclerView); } - - @VisibleForTesting - void setFeedbackManager(FeedbackManager feedbackManager) { - this.mFeedbackManager = feedbackManager; - } - - private FeedbackManager getFeedbackManager() { - if (mFeedbackManager == null) { - mFeedbackManager = new FeedbackManager(getActivity(), getFeedbackCategory()); - } - return mFeedbackManager; - } - - /** - * Returns the category of the feedback page. - * - *

By default, this method returns {@link SettingsEnums#PAGE_UNKNOWN}. This indicates that - * the feedback category is unknown, and the absence of a feedback menu. - * - * @return The feedback category, which is {@link SettingsEnums#PAGE_UNKNOWN} by default. - */ - protected int getFeedbackCategory() { - return SettingsEnums.PAGE_UNKNOWN; - } } diff --git a/src/com/android/settings/accessibility/actionbar/FeedbackMenuController.java b/src/com/android/settings/accessibility/actionbar/FeedbackMenuController.java new file mode 100644 index 00000000000..ee08276b6e5 --- /dev/null +++ b/src/com/android/settings/accessibility/actionbar/FeedbackMenuController.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.accessibility.actionbar; + +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import androidx.annotation.NonNull; + +import com.android.settings.R; +import com.android.settings.accessibility.FeedbackManager; +import com.android.settings.core.InstrumentedPreferenceFragment; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu; +import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected; + +/** + * A controller that adds feedback menu to any Settings page. + */ +public class FeedbackMenuController implements LifecycleObserver, OnCreateOptionsMenu, + OnOptionsItemSelected { + + /** + * The menu item ID for the feedback menu option. + */ + public static final int MENU_FEEDBACK = Menu.FIRST + 10; + + /** + * The menu item ID for the feedback menu option. + */ + private final FeedbackManager mFeedbackManager; + + /** + * Initializes the FeedbackMenuController for an InstrumentedPreferenceFragment with a provided + * pade ID. + * + * @param host The InstrumentedPreferenceFragment to which the menu controller will be added. + * @param pageId The page ID used for feedback tracking. + */ + public static void init(@NonNull InstrumentedPreferenceFragment host, int pageId) { + host.getSettingsLifecycle().addObserver( + new FeedbackMenuController( + new FeedbackManager(host.getActivity(), pageId))); + } + + /** + * Initializes the FeedbackMenuController for an InstrumentedPreferenceFragment with a provided + * FeedbackManager. + * + * @param host The InstrumentedPreferenceFragment to which the menu controller will be added. + * @param feedbackManager The FeedbackManager to use for handling feedback actions. + */ + public static void init(@NonNull InstrumentedPreferenceFragment host, + @NonNull FeedbackManager feedbackManager) { + host.getSettingsLifecycle().addObserver( + new FeedbackMenuController(feedbackManager)); + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + if (!mFeedbackManager.isAvailable()) { + return; + } + menu.add(Menu.NONE, MENU_FEEDBACK, Menu.NONE, R.string.accessibility_send_feedback_title); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem menuItem) { + if (menuItem.getItemId() == MENU_FEEDBACK) { + mFeedbackManager.sendFeedback(); + return true; + } + return false; + } + + private FeedbackMenuController(@NonNull FeedbackManager feedbackManager) { + mFeedbackManager = feedbackManager; + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index 6710da90788..a077e47a39e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -21,10 +21,7 @@ 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.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; @@ -46,8 +43,6 @@ 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; @@ -112,7 +107,6 @@ public class AccessibilitySettingsTest { private static final String EMPTY_STRING = ""; private static final String DEFAULT_SUMMARY = "default summary"; private static final String DEFAULT_DESCRIPTION = "default description"; - private static final String DEFAULT_CATEGORY = "default category"; private static final String DEFAULT_LABEL = "default label"; private static final Boolean SERVICE_ENABLED = true; private static final Boolean SERVICE_DISABLED = false; @@ -128,10 +122,6 @@ public class AccessibilitySettingsTest { private ShadowAccessibilityManager mShadowAccessibilityManager; @Mock private LocalBluetoothManager mLocalBluetoothManager; - @Mock - private Menu mMenu; - @Mock - private MenuItem mMenuItem; private ActivityController mActivityController; @@ -451,58 +441,6 @@ 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, DEFAULT_CATEGORY)); - - mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); - - verify(mMenu).add(anyInt(), anyInt(), anyInt(), anyInt()); - } - - @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, DEFAULT_CATEGORY)); - - mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); - - verify(mMenu, never()).add(anyInt(), anyInt(), anyInt(), anyInt()); - } - - @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, DEFAULT_CATEGORY)); - 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, DEFAULT_CATEGORY)); - 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( diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragmentTest.java index ff73e7feece..9d304ba186f 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragmentTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; +import android.content.res.Resources; import android.os.Bundle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; @@ -82,6 +83,8 @@ public class ToggleColorInversionPreferenceFragmentTest { private PreferenceManager mPreferenceManager; @Mock private FragmentActivity mActivity; + @Mock + private Resources mResources; @Before public void setUpTestFragment() { @@ -93,6 +96,7 @@ public class ToggleColorInversionPreferenceFragmentTest { when(mFragment.getContext()).thenReturn(mContext); when(mFragment.getActivity()).thenReturn(mActivity); when(mActivity.getContentResolver()).thenReturn(mContext.getContentResolver()); + when(mActivity.getResources()).thenReturn(mResources); mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null)); when(mScreen.findPreference(mFragment.getUseServicePreferenceKey())) diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index f72b591353a..cdd309f4c09 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -23,8 +23,6 @@ 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.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -38,16 +36,14 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.icu.text.CaseMap; import android.net.Uri; import android.os.Bundle; -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.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; @@ -107,19 +103,10 @@ public class ToggleFeaturePreferenceFragmentTest { PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName( PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME); - private static final String PLACEHOLDER_TILE_TOOLTIP_CONTENT = - PLACEHOLDER_PACKAGE_NAME + "tooltip_content"; - private static final String PLACEHOLDER_CATEGORY = "category"; - private static final String PLACEHOLDER_DIALOG_TITLE = "title"; private static final String DEFAULT_SUMMARY = "default summary"; private static final String DEFAULT_DESCRIPTION = "default description"; private static final String DEFAULT_TOP_INTRO = "default top intro"; - 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 TestToggleFeaturePreferenceFragment mFragment; @Spy private final Context mContext = ApplicationProvider.getApplicationContext(); @@ -135,9 +122,7 @@ public class ToggleFeaturePreferenceFragmentTest { @Mock private PackageManager mPackageManager; @Mock - private Menu mMenu; - @Mock - private MenuItem mMenuItem; + private Resources mResources; @Before public void setUpTestFragment() { @@ -150,6 +135,7 @@ public class ToggleFeaturePreferenceFragmentTest { when(mFragment.getContext()).thenReturn(mContext); when(mFragment.getActivity()).thenReturn(mActivity); when(mActivity.getContentResolver()).thenReturn(mContentResolver); + when(mActivity.getResources()).thenReturn(mResources); when(mContext.getPackageManager()).thenReturn(mPackageManager); final PreferenceScreen screen = spy(new PreferenceScreen(mContext, null)); when(screen.getPreferenceManager()).thenReturn(mPreferenceManager); @@ -186,61 +172,6 @@ public class ToggleFeaturePreferenceFragmentTest { any(AccessibilitySettingsContentObserver.class)); } - @Test - @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) - public void onCreateOptionsMenu_enableLowVisionGenericFeedback_shouldAddSendFeedbackMenu() { - mFragment.setFeedbackManager( - new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY)); - - mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); - - verify(mMenu).add(anyInt(), eq(ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK), - anyInt(), eq(R.string.accessibility_send_feedback_title)); - } - - @Test - @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) - public void onCreateOptionsMenu_disableLowVisionGenericFeedback_shouldNotAddSendFeedbackMenu() { - mFragment.setFeedbackManager( - new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY)); - - mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null); - - verify(mMenu, never()).add(anyInt(), - eq(ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK), anyInt(), - eq(R.string.accessibility_send_feedback_title)); - } - - @Test - @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) - public void onOptionsItemSelected_enableLowVisionGenericFeedback_shouldStartSendFeedback() { - mFragment.setFeedbackManager( - new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY)); - when(mMenuItem.getItemId()).thenReturn( - ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK); - - mFragment.onOptionsItemSelected(mMenuItem); - - verify(mActivity).startActivityForResult( - argThat(intent -> intent != null - && Intent.ACTION_BUG_REPORT.equals(intent.getAction())), anyInt()); - } - - @Test - @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) - public void onOptionsItemSelected_disableLowVisionGenericFeedback_shouldNotStartSendFeedback() { - mFragment.setFeedbackManager( - new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY)); - when(mMenuItem.getItemId()).thenReturn( - ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK); - - mFragment.onOptionsItemSelected(mMenuItem); - - verify(mActivity, never()).startActivityForResult( - argThat(intent -> intent != null - && Intent.ACTION_BUG_REPORT.equals(intent.getAction())), anyInt()); - } - @Test public void updateShortcutPreferenceData_assignDefaultValueToVariable() { mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME; diff --git a/tests/robotests/src/com/android/settings/accessibility/actionbar/FeedbackMenuControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/actionbar/FeedbackMenuControllerTest.java new file mode 100644 index 00000000000..7208f496755 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/actionbar/FeedbackMenuControllerTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.accessibility.actionbar; + +import static com.android.settings.accessibility.actionbar.FeedbackMenuController.MENU_FEEDBACK; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.app.settings.SettingsEnums; +import android.content.Intent; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.view.Menu; +import android.view.MenuItem; + +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.testing.EmptyFragmentActivity; +import androidx.test.ext.junit.rules.ActivityScenarioRule; + +import com.android.settings.accessibility.FeedbackManager; +import com.android.settings.core.InstrumentedPreferenceFragment; +import com.android.settingslib.core.lifecycle.Lifecycle; + +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.annotation.Config; + +/** Tests for {@link FeedbackMenuController} */ +@Config(shadows = { + com.android.settings.testutils.shadow.ShadowFragment.class, +}) +@RunWith(RobolectricTestRunner.class) +public class FeedbackMenuControllerTest { + private static final String PACKAGE_NAME = "com.android.test"; + private static final String DEFAULT_CATEGORY = "default category"; + + @Rule + public final MockitoRule mMocks = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public ActivityScenarioRule mActivityScenario = + new ActivityScenarioRule<>(EmptyFragmentActivity.class); + + private FragmentActivity mActivity; + private InstrumentedPreferenceFragment mHost; + private FeedbackManager mFeedbackManager; + @Mock + private Lifecycle mLifecycle; + @Mock + private Menu mMenu; + @Mock + private MenuItem mMenuItem; + + @Before + public void setUp() { + mActivityScenario.getScenario().onActivity(activity -> mActivity = activity); + mHost = spy(new InstrumentedPreferenceFragment() { + @Override + public int getMetricsCategory() { + return 0; + } + }); + when(mHost.getActivity()).thenReturn(mActivity); + when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem); + when(mMenuItem.getItemId()).thenReturn(MENU_FEEDBACK); + mFeedbackManager = new FeedbackManager(mActivity, PACKAGE_NAME, DEFAULT_CATEGORY); + } + + @Test + public void init_withPageId_shouldAttachToLifecycle() { + when(mHost.getSettingsLifecycle()).thenReturn(mLifecycle); + + FeedbackMenuController.init(mHost, SettingsEnums.ACCESSIBILITY); + + verify(mLifecycle).addObserver(any(FeedbackMenuController.class)); + } + + @Test + public void init_withFeedbackManager_shouldAttachToLifecycle() { + when(mHost.getSettingsLifecycle()).thenReturn(mLifecycle); + + FeedbackMenuController.init(mHost, mFeedbackManager); + + verify(mLifecycle).addObserver(any(FeedbackMenuController.class)); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onCreateOptionsMenu_enableLowVisionGenericFeedback_shouldAddSendFeedbackMenu() { + FeedbackMenuController.init(mHost, mFeedbackManager); + + mHost.getSettingsLifecycle().onCreateOptionsMenu(mMenu, /* inflater= */ null); + + verify(mMenu).add(anyInt(), anyInt(), anyInt(), anyInt()); + } + + @Test + @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onCreateOptionsMenu_disableLowVisionGenericFeedback_shouldNotAddSendFeedbackMenu() { + FeedbackMenuController.init(mHost, mFeedbackManager); + + mHost.getSettingsLifecycle().onCreateOptionsMenu(mMenu, /* inflater= */ null); + + verify(mMenu, never()).add(anyInt(), anyInt(), anyInt(), anyInt()); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onOptionsItemSelected_enableLowVisionGenericFeedback_shouldStartSendFeedback() { + FeedbackMenuController.init(mHost, mFeedbackManager); + + mHost.getSettingsLifecycle().onOptionsItemSelected(mMenuItem); + + Intent intent = shadowOf(mActivity).getNextStartedActivity(); + assertThat(intent.getAction()).isEqualTo(Intent.ACTION_BUG_REPORT); + } + + @Test + @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK) + public void onOptionsItemSelected_disableLowVisionGenericFeedback_shouldNotStartSendFeedback() { + FeedbackMenuController.init(mHost, mFeedbackManager); + + mHost.getSettingsLifecycle().onOptionsItemSelected(mMenuItem); + + Intent intent = shadowOf(mActivity).getNextStartedActivity(); + assertThat(intent).isNull(); + } +}