diff --git a/aconfig/Android.bp b/aconfig/Android.bp index 7828e995840..51d977ab131 100644 --- a/aconfig/Android.bp +++ b/aconfig/Android.bp @@ -6,6 +6,7 @@ aconfig_declarations { name: "aconfig_settings_flags", package: "com.android.settings.flags", srcs: [ + "settings_accessibility_flag_declarations.aconfig", "settings_connecteddevice_flag_declarations.aconfig", "settings_globalintl_flag_declarations.aconfig", "settings_apn_flag_declarations.aconfig", diff --git a/aconfig/settings_accessibility_flag_declarations.aconfig b/aconfig/settings_accessibility_flag_declarations.aconfig new file mode 100644 index 00000000000..9a24399aea5 --- /dev/null +++ b/aconfig/settings_accessibility_flag_declarations.aconfig @@ -0,0 +1,8 @@ +package: "com.android.settings.flags" + +flag { + name: "separate_accessibility_vibration_settings_fragments" + namespace: "accessibility" + description: "Splits VibrationSettings into two fragments, one per XML resource" + bug: "289967175" +} diff --git a/res/xml/accessibility_text_reading_options.xml b/res/xml/accessibility_text_reading_options.xml index 8a09f6720f0..09954b3639e 100644 --- a/res/xml/accessibility_text_reading_options.xml +++ b/res/xml/accessibility_text_reading_options.xml @@ -49,13 +49,17 @@ android:key="toggle_force_bold_text" android:persistent="false" android:title="@string/force_bold_text" + settings:controller= + "com.android.settings.accessibility.FontWeightAdjustmentPreferenceController" settings:keywords="@string/keywords_bold_text" /> + android:title="@string/accessibility_toggle_high_text_contrast_preference_title" + settings:controller= + "com.android.settings.accessibility.HighTextContrastPreferenceController" /> @@ -44,17 +44,17 @@ @@ -62,17 +62,17 @@ diff --git a/res/xml/accessibility_vibration_settings.xml b/res/xml/accessibility_vibration_settings.xml index 25be499ef67..5e2f923ad4c 100644 --- a/res/xml/accessibility_vibration_settings.xml +++ b/res/xml/accessibility_vibration_settings.xml @@ -20,23 +20,23 @@ android:title="@string/accessibility_vibration_settings_title"> @@ -44,17 +44,17 @@ @@ -62,17 +62,17 @@ diff --git a/src/com/android/settings/accessibility/VibrationIntensitySettingsFragment.java b/src/com/android/settings/accessibility/VibrationIntensitySettingsFragment.java new file mode 100644 index 00000000000..41e81983d10 --- /dev/null +++ b/src/com/android/settings/accessibility/VibrationIntensitySettingsFragment.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 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 android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Vibrator; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.VisibleForTesting; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +/** Accessibility settings for the vibration. */ +@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) +public class VibrationIntensitySettingsFragment extends DashboardFragment { + + private static final String TAG = "VibrationIntensitySettings"; + + @Override + public int getMetricsCategory() { + return SettingsEnums.ACCESSIBILITY_VIBRATION; + } + + @Override + public int getHelpResource() { + return R.string.help_uri_accessibility_vibration; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.accessibility_vibration_intensity_settings; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @VisibleForTesting + static boolean isPageSearchEnabled(Context context) { + final int supportedIntensityLevels = context.getResources().getInteger( + R.integer.config_vibration_supported_intensity_levels); + final boolean hasVibrator = context.getSystemService(Vibrator.class).hasVibrator(); + if (Flags.separateAccessibilityVibrationSettingsFragments()) { + return hasVibrator && supportedIntensityLevels > 1; + } else { + return false; + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = super.onCreateView(inflater, container, savedInstanceState); + final Resources res = view.getResources(); + final RecyclerView rv = getListView(); + if (rv != null) { + final int bottom_padding = res.getDimensionPixelSize( + com.android.settingslib.widget.theme.R.dimen + .settingslib_listPreferredItemPaddingEnd); + rv.setPaddingRelative(rv.getPaddingStart(), rv.getPaddingTop(), rv.getPaddingEnd(), + rv.getPaddingBottom() + bottom_padding); + } + return view; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.accessibility_vibration_intensity_settings) { + @Override + protected boolean isPageSearchEnabled(Context context) { + return VibrationIntensitySettingsFragment.isPageSearchEnabled(context); + } + }; +} diff --git a/src/com/android/settings/accessibility/VibrationPreferenceController.java b/src/com/android/settings/accessibility/VibrationPreferenceController.java index 61606b8f397..d4302490b9e 100644 --- a/src/com/android/settings/accessibility/VibrationPreferenceController.java +++ b/src/com/android/settings/accessibility/VibrationPreferenceController.java @@ -21,9 +21,15 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.content.Context; import android.os.Vibrator; import android.provider.Settings; +import android.text.TextUtils; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.flags.Flags; /** Controller for "Vibration & haptics" settings page. */ public class VibrationPreferenceController extends BasePreferenceController { @@ -49,4 +55,29 @@ public class VibrationPreferenceController extends BasePreferenceController { ? R.string.accessibility_vibration_settings_state_on : R.string.accessibility_vibration_settings_state_off); } + + @VisibleForTesting + void launchVibrationSettingsFragment(Class klass) { + new SubSettingLauncher(mContext) + .setSourceMetricsCategory(getMetricsCategory()) + .setDestination(klass.getName()) + .launch(); + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (Flags.separateAccessibilityVibrationSettingsFragments() + && TextUtils.equals(preference.getKey(), getPreferenceKey())) { + if (mContext.getResources().getInteger( + R.integer.config_vibration_supported_intensity_levels) > 1) { + launchVibrationSettingsFragment(VibrationIntensitySettingsFragment.class); + } else { + launchVibrationSettingsFragment(VibrationSettings.class); + } + return true; + } + return super.handlePreferenceTreeClick(preference); + } + + } diff --git a/src/com/android/settings/accessibility/VibrationSettings.java b/src/com/android/settings/accessibility/VibrationSettings.java index 53592a7ed37..4b36dde642b 100644 --- a/src/com/android/settings/accessibility/VibrationSettings.java +++ b/src/com/android/settings/accessibility/VibrationSettings.java @@ -26,10 +26,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.search.SearchIndexable; @@ -43,6 +45,9 @@ public class VibrationSettings extends DashboardFragment { private static final String TAG = "VibrationSettings"; private static int getVibrationXmlResourceId(Context context) { + if (Flags.separateAccessibilityVibrationSettingsFragments()) { + return R.xml.accessibility_vibration_settings; + } final int supportedIntensities = context.getResources().getInteger( R.integer.config_vibration_supported_intensity_levels); return supportedIntensities > 1 @@ -74,6 +79,9 @@ public class VibrationSettings extends DashboardFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (Flags.separateAccessibilityVibrationSettingsFragments()) { + return super.onCreateView(inflater, container, savedInstanceState); + } final View view = super.onCreateView(inflater, container, savedInstanceState); final RecyclerView rv = getListView(); final Resources res = view.getResources(); @@ -88,12 +96,23 @@ public class VibrationSettings extends DashboardFragment { return view; } + @VisibleForTesting + static boolean isPageSearchEnabled(Context context) { + final int supportedIntensityLevels = context.getResources().getInteger( + R.integer.config_vibration_supported_intensity_levels); + final boolean hasVibrator = context.getSystemService(Vibrator.class).hasVibrator(); + if (Flags.separateAccessibilityVibrationSettingsFragments()) { + return hasVibrator && supportedIntensityLevels == 1; + } else { + return hasVibrator; + } + } + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { - @Override protected boolean isPageSearchEnabled(Context context) { - return context.getSystemService(Vibrator.class).hasVibrator(); + return VibrationSettings.isPageSearchEnabled(context); } @Override diff --git a/tests/robotests/assets/exempt_slice_controller_not_in_xml b/tests/robotests/assets/exempt_slice_controller_not_in_xml index 88c716f4937..4c88d5aae3d 100644 --- a/tests/robotests/assets/exempt_slice_controller_not_in_xml +++ b/tests/robotests/assets/exempt_slice_controller_not_in_xml @@ -1,4 +1,7 @@ com.android.settings.accessibility.AccessibilitySlicePreferenceController +com.android.settings.accessibility.MagnificationAlwaysOnPreferenceController +com.android.settings.accessibility.MagnificationFollowTypingPreferenceController +com.android.settings.accessibility.MagnificationJoystickPreferenceController com.android.settings.accessibility.ReduceBrightColorsIntensityPreferenceController com.android.settings.accessibility.ReduceBrightColorsPersistencePreferenceController com.android.settings.biometrics.face.FaceSettingsAttentionPreferenceController diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationIntensitySettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationIntensitySettingsFragmentTest.java new file mode 100644 index 00000000000..045c9dbcc63 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/VibrationIntensitySettingsFragmentTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 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.spy; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.res.Resources; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link VibrationIntensitySettingsFragment}. */ +@RunWith(RobolectricTestRunner.class) +@RequiresFlagsEnabled(Flags.FLAG_SEPARATE_ACCESSIBILITY_VIBRATION_SETTINGS_FRAGMENTS) +public class VibrationIntensitySettingsFragmentTest { + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private Context mContext; + private Resources mResources; + private VibrationIntensitySettingsFragment mFragment; + + @Before + public void setUp() { + mContext = spy(ApplicationProvider.getApplicationContext()); + mResources = spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(mResources); + mFragment = new VibrationIntensitySettingsFragment(); + } + + @Test + public void getMetricsCategory_returnsCorrectCategory() { + assertThat(mFragment.getMetricsCategory()).isEqualTo( + SettingsEnums.ACCESSIBILITY_VIBRATION); + } + + @Test + public void getHelpResource_returnsCorrectString() { + assertThat(mFragment.getHelpResource()).isEqualTo( + R.string.help_uri_accessibility_vibration); + } + + @Test + public void getPreferenceScreenResId_returnsCorrectXml() { + assertThat(mFragment.getPreferenceScreenResId()).isEqualTo( + R.xml.accessibility_vibration_intensity_settings); + } + + @Test + public void getLogTag_returnsCorrectTag() { + assertThat(mFragment.getLogTag()).isEqualTo("VibrationIntensitySettings"); + } + + @Test + public void isPageSearchEnabled_oneIntensityLevel_returnsFalse() { + when(mResources.getInteger(R.integer.config_vibration_supported_intensity_levels)) + .thenReturn(1); + assertThat(VibrationIntensitySettingsFragment.isPageSearchEnabled(mContext)).isFalse(); + } + + @Test + public void isPageSearchEnabled_multipleIntensityLevels_returnsTrue() { + when(mResources.getInteger(R.integer.config_vibration_supported_intensity_levels)) + .thenReturn(2); + assertThat(VibrationIntensitySettingsFragment.isPageSearchEnabled(mContext)).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceControllerTest.java index a045deaa81a..cf33173e664 100644 --- a/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceControllerTest.java @@ -18,14 +18,20 @@ package com.android.settings.accessibility; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; - import static com.google.common.truth.Truth.assertThat; - +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.res.Resources; import android.os.Vibrator; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import androidx.preference.Preference; @@ -33,8 +39,10 @@ import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settings.flags.Flags; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -44,6 +52,9 @@ import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class VibrationPreferenceControllerTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final String PREFERENCE_KEY = "preference_key"; private static final int OFF = 0; private static final int ON = 1; @@ -52,13 +63,17 @@ public class VibrationPreferenceControllerTest { @Mock private PreferenceScreen mScreen; private Context mContext; + private Resources mResources; private Preference mPreference; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(ApplicationProvider.getApplicationContext()); + mResources = spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(mResources); when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator); + when(mVibrator.hasVibrator()).thenReturn(true); } @Test @@ -85,7 +100,6 @@ public class VibrationPreferenceControllerTest { @Test public void getSummary_vibrateSettingNotSet_returnsOnText() { - when(mVibrator.hasVibrator()).thenReturn(true); Settings.System.putString(mContext.getContentResolver(), Settings.System.VIBRATE_ON, /* value= */ null); VibrationPreferenceController controller = createPreferenceController(); @@ -97,7 +111,6 @@ public class VibrationPreferenceControllerTest { @Test public void getSummary_vibrateSettingOn_returnsOnText() { - when(mVibrator.hasVibrator()).thenReturn(true); Settings.System.putInt(mContext.getContentResolver(), Settings.System.VIBRATE_ON, ON); VibrationPreferenceController controller = createPreferenceController(); controller.updateState(mPreference); @@ -108,7 +121,6 @@ public class VibrationPreferenceControllerTest { @Test public void getSummary_vibrateSettingOff_returnsOffText() { - when(mVibrator.hasVibrator()).thenReturn(true); Settings.System.putInt(mContext.getContentResolver(), Settings.System.VIBRATE_ON, OFF); VibrationPreferenceController controller = createPreferenceController(); controller.updateState(mPreference); @@ -117,11 +129,39 @@ public class VibrationPreferenceControllerTest { mContext.getString(R.string.accessibility_vibration_settings_state_off)); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_SEPARATE_ACCESSIBILITY_VIBRATION_SETTINGS_FRAGMENTS) + public void handlePreferenceTreeClick_oneIntensityLevel_opensVibrationSettings() { + when(mResources.getInteger(R.integer.config_vibration_supported_intensity_levels)) + .thenReturn(1); + VibrationPreferenceController controller = spy(createPreferenceController()); + + doNothing().when(controller).launchVibrationSettingsFragment(any()); + controller.handlePreferenceTreeClick(mPreference); + + verify(controller).launchVibrationSettingsFragment(eq(VibrationSettings.class)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_SEPARATE_ACCESSIBILITY_VIBRATION_SETTINGS_FRAGMENTS) + public void handlePreferenceTreeClick_multipleIntensityLevels_opensVibrationIntensity() { + when(mResources.getInteger(R.integer.config_vibration_supported_intensity_levels)) + .thenReturn(2); + VibrationPreferenceController controller = spy(createPreferenceController()); + + doNothing().when(controller).launchVibrationSettingsFragment(any()); + controller.handlePreferenceTreeClick(mPreference); + + verify(controller).launchVibrationSettingsFragment( + eq(VibrationIntensitySettingsFragment.class)); + } + private VibrationPreferenceController createPreferenceController() { VibrationPreferenceController controller = new VibrationPreferenceController(mContext, PREFERENCE_KEY); mPreference = new Preference(mContext); mPreference.setSummary("Test summary"); + mPreference.setKey(PREFERENCE_KEY); when(mScreen.findPreference(controller.getPreferenceKey())).thenReturn(mPreference); controller.displayPreference(mScreen); return controller; diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationSettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationSettingsTest.java new file mode 100644 index 00000000000..f5ea39e5824 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/VibrationSettingsTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 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.spy; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.res.Resources; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link VibrationSettings} fragment. */ +@RunWith(RobolectricTestRunner.class) +@RequiresFlagsEnabled(Flags.FLAG_SEPARATE_ACCESSIBILITY_VIBRATION_SETTINGS_FRAGMENTS) +public class VibrationSettingsTest { + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private Context mContext; + private Resources mResources; + private VibrationSettings mFragment; + + @Before + public void setUp() { + mContext = spy(ApplicationProvider.getApplicationContext()); + mResources = spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(mResources); + mFragment = new VibrationSettings(); + } + + @Test + public void getMetricsCategory_returnsCorrectCategory() { + assertThat(mFragment.getMetricsCategory()).isEqualTo( + SettingsEnums.ACCESSIBILITY_VIBRATION); + } + + @Test + public void getHelpResource_returnsCorrectString() { + assertThat(mFragment.getHelpResource()).isEqualTo( + R.string.help_uri_accessibility_vibration); + } + + @Test + public void getPreferenceScreenResId_returnsCorrectXml() { + assertThat(mFragment.getPreferenceScreenResId()).isEqualTo( + R.xml.accessibility_vibration_settings); + } + + @Test + public void getLogTag_returnsCorrectTag() { + assertThat(mFragment.getLogTag()).isEqualTo("VibrationSettings"); + } + + @Test + public void isPageSearchEnabled_oneIntensityLevel_returnsTrue() { + when(mResources.getInteger(R.integer.config_vibration_supported_intensity_levels)) + .thenReturn(1); + assertThat(VibrationSettings.isPageSearchEnabled(mContext)).isTrue(); + } + + @Test + public void isPageSearchEnabled_multipleIntensityLevels_returnsFalse() { + when(mResources.getInteger(R.integer.config_vibration_supported_intensity_levels)) + .thenReturn(2); + assertThat(VibrationSettings.isPageSearchEnabled(mContext)).isFalse(); + } +}