diff --git a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java index 4e9cd92d6c0..be7a73ba85a 100644 --- a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java @@ -232,6 +232,7 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment { .getAccessibilityMetricsFeatureProvider() .getDownloadedFeatureMetricsCategory(componentName); extras.putInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY, metricsCategory); + extras.putInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY, metricsCategory); 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 127906bf1c8..2c8247f959c 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 implements static final String EXTRA_HTML_DESCRIPTION = "html_description"; static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool"; static final String EXTRA_METRICS_CATEGORY = "metrics_category"; + static final String EXTRA_FEEDBACK_CATEGORY = "feedback_category"; // Timeout before we update the services if packages are added/removed // since the AccessibilityManagerService has to do that processing first diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index 3287ce8ec51..c6995b01af7 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -57,6 +57,11 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature return getArguments().getInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY); } + @Override + public int getFeedbackCategory() { + return getArguments().getInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java index 5c18be8626c..ae6239af562 100644 --- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java +++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java @@ -217,6 +217,7 @@ public class RestrictedPreferenceHelper { extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, imageRes); extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription); extras.putInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY, metricsCategory); + extras.putInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY, metricsCategory); } /** diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 3cd4bb6b3ed..a11ad466003 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -73,6 +73,11 @@ public class ToggleAccessibilityServicePreferenceFragment extends return getArguments().getInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY); } + @Override + public int getFeedbackCategory() { + return getArguments().getInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 93672516339..66c32df1798 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -40,6 +40,9 @@ 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; @@ -48,6 +51,7 @@ import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -89,6 +93,7 @@ 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; @@ -102,6 +107,7 @@ 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; @@ -240,6 +246,24 @@ 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) { @@ -739,4 +763,28 @@ 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/ToggleScreenReaderPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java index 10813a7e262..eb0c93b755f 100644 --- a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java +++ b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java @@ -79,6 +79,12 @@ public class ToggleScreenReaderPreferenceFragmentForSetupWizard return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER; } + @Override + public int getFeedbackCategory() { + // The feedback options should not be displayed on the setup wizard page. + return SettingsEnums.PAGE_UNKNOWN; + } + @Override public void onStop() { // Log the final choice in value if it's different from the previous value. diff --git a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java index 10796b5d218..14dc0bc1caf 100644 --- a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java +++ b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java @@ -79,6 +79,12 @@ public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK; } + @Override + public int getFeedbackCategory() { + // The feedback options should not be displayed on the setup wizard page. + return SettingsEnums.PAGE_UNKNOWN; + } + @Override public void onStop() { // Log the final choice in value if it's different from the previous value. diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index 8f9d2e1fbd0..571075cba31 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -23,9 +23,12 @@ 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; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,10 +40,13 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.icu.text.CaseMap; 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; @@ -66,8 +72,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; @@ -83,6 +90,8 @@ import java.util.Locale; ShadowAccessibilityManager.class }) public class ToggleFeaturePreferenceFragmentTest { + @Rule + public final MockitoRule mocks = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -96,6 +105,7 @@ public class ToggleFeaturePreferenceFragmentTest { 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"; @@ -120,10 +130,13 @@ public class ToggleFeaturePreferenceFragmentTest { private ContentResolver mContentResolver; @Mock private PackageManager mPackageManager; + @Mock + private Menu mMenu; + @Mock + private MenuItem mMenuItem; @Before public void setUpTestFragment() { - MockitoAnnotations.initMocks(this); mShadowAccessibilityManager = Shadow.extract( mContext.getSystemService(AccessibilityManager.class)); @@ -169,6 +182,61 @@ 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;