From 97ea279c452d23231eb309d3830919e16599c141 Mon Sep 17 00:00:00 2001 From: Jernej Virag Date: Mon, 9 Aug 2021 18:11:28 +0000 Subject: [PATCH] Add setting to allow configuration of long press home gesture This allows configuration of long press home gesture on 2-/3-button navigation configurations. It allows the user to enable and disable the invocation of assistant app using said gesture. Bug: 191888710 Test: Unit tests and on a physical device. Change-Id: I18cb290058ac5c2a748d40802b942404f548b868 --- AndroidManifest.xml | 19 ++++ res/values/strings.xml | 11 ++ res/xml/button_navigation_settings.xml | 39 +++++++ src/com/android/settings/Settings.java | 2 + .../core/gateway/SettingsGateway.java | 2 + ...tonNavigationSettingsAssistController.java | 61 +++++++++++ .../ButtonNavigationSettingsFragment.java | 69 ++++++++++++ .../SystemNavigationGestureSettings.java | 12 ++- ...avigationSettingsAssistControllerTest.java | 100 ++++++++++++++++++ .../ButtonNavigationSettingsFragmentTest.java | 68 ++++++++++++ ...ssPowerButtonPreferenceControllerTest.java | 3 - 11 files changed, 382 insertions(+), 4 deletions(-) create mode 100644 res/xml/button_navigation_settings.xml create mode 100644 src/com/android/settings/gestures/ButtonNavigationSettingsAssistController.java create mode 100644 src/com/android/settings/gestures/ButtonNavigationSettingsFragment.java create mode 100644 tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsAssistControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsFragmentTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c23b93ed34c..27a6abd3131 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3932,6 +3932,25 @@ android:value="true" /> + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 77121a31f9b..3008965e79a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -11789,6 +11789,11 @@ Swipe up from a bottom corner to invoke digital assistant app. + + Hold Home to invoke assistant + + Press and hold the Home button to invoke digital assistant app. + Information @@ -11811,9 +11816,15 @@ Gesture Navigation Sensitivity + + Button navigation + gesture navigation, back sensitivity, back gesture + + navigation, home button + Double-tap to check phone diff --git a/res/xml/button_navigation_settings.xml b/res/xml/button_navigation_settings.xml new file mode 100644 index 00000000000..d42f020d140 --- /dev/null +++ b/res/xml/button_navigation_settings.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 231a1496421..4de795c41b5 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -243,6 +243,8 @@ public class Settings extends SettingsActivity { public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ } public static class GamesStorageActivity extends SettingsActivity { /* empty */ } public static class GestureNavigationSettingsActivity extends SettingsActivity { /* empty */ } + /** Activity to manage 2-/3-button navigation configuration. */ + public static class ButtonNavigationSettingsActivity extends SettingsActivity { /* empty */ } public static class InteractAcrossProfilesSettingsActivity extends SettingsActivity { /* empty */ } diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 1adf199fd39..3c594a714c0 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -94,6 +94,7 @@ import com.android.settings.fuelgauge.PowerUsageSummary; import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings; import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings; import com.android.settings.gestures.AssistGestureSettings; +import com.android.settings.gestures.ButtonNavigationSettingsFragment; import com.android.settings.gestures.DoubleTapPowerSettings; import com.android.settings.gestures.DoubleTapScreenSettings; import com.android.settings.gestures.DoubleTwistGestureSettings; @@ -312,6 +313,7 @@ public class SettingsGateway { DarkModeSettingsFragment.class.getName(), BugReportHandlerPicker.class.getName(), GestureNavigationSettingsFragment.class.getName(), + ButtonNavigationSettingsFragment.class.getName(), InteractAcrossProfilesSettings.class.getName(), InteractAcrossProfilesDetails.class.getName(), MediaControlsSettings.class.getName(), diff --git a/src/com/android/settings/gestures/ButtonNavigationSettingsAssistController.java b/src/com/android/settings/gestures/ButtonNavigationSettingsAssistController.java new file mode 100644 index 00000000000..6ecf7b119f7 --- /dev/null +++ b/src/com/android/settings/gestures/ButtonNavigationSettingsAssistController.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 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.gestures; + +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; + +import android.content.Context; +import android.provider.Settings; + +import com.android.settings.core.TogglePreferenceController; + +/** + * Configures behaviour of long press home button to invoke assistant app gesture. + */ +public class ButtonNavigationSettingsAssistController extends TogglePreferenceController { + + public ButtonNavigationSettingsAssistController(Context context, String key) { + super(context, key); + } + + @Override + public boolean isChecked() { + boolean onByDefault = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault); + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, onByDefault ? 1 : 0) == 1; + } + + @Override + public boolean setChecked(boolean isChecked) { + return Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, isChecked ? 1 : 0); + } + + @Override + public int getAvailabilityStatus() { + if (SystemNavigationPreferenceController.isOverlayPackageAvailable(mContext, + NAV_BAR_MODE_2BUTTON_OVERLAY) + || SystemNavigationPreferenceController.isOverlayPackageAvailable(mContext, + NAV_BAR_MODE_3BUTTON_OVERLAY)) { + return AVAILABLE; + } + + return UNSUPPORTED_ON_DEVICE; + } +} diff --git a/src/com/android/settings/gestures/ButtonNavigationSettingsFragment.java b/src/com/android/settings/gestures/ButtonNavigationSettingsFragment.java new file mode 100644 index 00000000000..af75e788c2b --- /dev/null +++ b/src/com/android/settings/gestures/ButtonNavigationSettingsFragment.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 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.gestures; + +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +/** + * A fragment that includes settings for 2- and 3-button navigation modes. + */ +@SearchIndexable(forTarget = SearchIndexable.MOBILE) +public class ButtonNavigationSettingsFragment extends DashboardFragment { + + private static final String TAG = "ButtonNavigationSettingsFragment"; + + public static final String BUTTON_NAVIGATION_SETTINGS = + "com.android.settings.BUTTON_NAVIGATION_SETTINGS"; + + + @Override + public int getMetricsCategory() { + return SettingsEnums.SETTINGS_BUTTON_NAV_DLG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.button_navigation_settings; + } + + @Override + protected String getLogTag() { + return TAG; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.button_navigation_settings) { + + @Override + protected boolean isPageSearchEnabled(Context context) { + return SystemNavigationPreferenceController.isOverlayPackageAvailable(context, + NAV_BAR_MODE_2BUTTON_OVERLAY) + || SystemNavigationPreferenceController.isOverlayPackageAvailable( + context, + NAV_BAR_MODE_3BUTTON_OVERLAY); + } + }; +} diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java index 1ef8dec5abd..7f0d7c338d0 100644 --- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java +++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java @@ -41,6 +41,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.accessibility.AccessibilityGestureNavigationTutorial; +import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; @@ -150,10 +151,19 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i pref.setSummary(((CandidateInfoExtra) info).loadSummary()); - if (info.getKey() == KEY_SYSTEM_NAV_GESTURAL) { + if (KEY_SYSTEM_NAV_GESTURAL.equals(info.getKey())) { pref.setExtraWidgetOnClickListener((v) -> startActivity(new Intent( GestureNavigationSettingsFragment.GESTURE_NAVIGATION_SETTINGS))); } + + if (KEY_SYSTEM_NAV_2BUTTONS.equals(info.getKey()) || KEY_SYSTEM_NAV_3BUTTONS.equals( + info.getKey())) { + pref.setExtraWidgetOnClickListener((v) -> + new SubSettingLauncher(getContext()) + .setDestination(ButtonNavigationSettingsFragment.class.getName()) + .setSourceMetricsCategory(SettingsEnums.SETTINGS_GESTURE_SWIPE_UP) + .launch()); + } } @Override diff --git a/tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsAssistControllerTest.java b/tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsAssistControllerTest.java new file mode 100644 index 00000000000..d961cdf161d --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsAssistControllerTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 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.gestures; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.provider.Settings; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class ButtonNavigationSettingsAssistControllerTest { + + private static final String KEY_LONG_PRESS_HOME_FOR_ASSIST = + "assistant_long_press_home_gesture"; + + private Context mContext; + private Resources mResources; + private ButtonNavigationSettingsAssistController mController; + + @Before + public void setUp() { + mContext = spy(ApplicationProvider.getApplicationContext()); + mResources = mock(Resources.class); + when(mContext.getResources()).thenReturn(mResources); + + mController = new ButtonNavigationSettingsAssistController( + mContext, KEY_LONG_PRESS_HOME_FOR_ASSIST); + } + + @Test + public void isChecked_valueUnknownDefaultTrue_shouldReturnTrue() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault)).thenReturn( + true); + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_valueUnknownDefaultFalse_shouldReturnFalse() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault)).thenReturn( + true); + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_valueTrue_shouldReturnTrue() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, 1); + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_valueFalse_shouldReturnFalse() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, 0); + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void setChecked_valueFalse_shouldSetFalse() { + mController.setChecked(false); + assertThat(Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, -1)).isEqualTo(0); + } + + @Test + public void setChecked_valueTrue_shouldSetTrue() { + mController.setChecked(true); + assertThat(Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, -1)).isEqualTo(1); + } + +} diff --git a/tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsFragmentTest.java new file mode 100644 index 00000000000..b014076ed57 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/ButtonNavigationSettingsFragmentTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 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.gestures; + +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; + +import static com.google.common.truth.Truth.assertThat; + +import static org.robolectric.Shadows.shadowOf; + +import android.content.Context; +import android.content.pm.PackageInfo; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowPackageManager; + +@RunWith(RobolectricTestRunner.class) +public class ButtonNavigationSettingsFragmentTest { + + @Test + public void getNonIndexableKeys_twoAndThreeButtonNavigationNotAvailable_allKeysNonIndexable() { + assertThat(ButtonNavigationSettingsFragment.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys( + ApplicationProvider.getApplicationContext())).isNotEmpty(); + } + + @Test + public void getNonIndexableKeys_twoButtonNavigationAvailable_allKeysIndexable() { + addPackageToPackageManager(ApplicationProvider.getApplicationContext(), + NAV_BAR_MODE_2BUTTON_OVERLAY); + assertThat(ButtonNavigationSettingsFragment.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys( + ApplicationProvider.getApplicationContext())).isEmpty(); + } + + @Test + public void getNonIndexableKeys_threeButtonNavigationAvailable_allKeysIndexable() { + addPackageToPackageManager(ApplicationProvider.getApplicationContext(), + NAV_BAR_MODE_3BUTTON_OVERLAY); + assertThat(ButtonNavigationSettingsFragment.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys( + ApplicationProvider.getApplicationContext())).isEmpty(); + } + + private static void addPackageToPackageManager(Context context, String pkg) { + ShadowPackageManager shadowPm = shadowOf(context.getPackageManager()); + PackageInfo pi = new PackageInfo(); + pi.packageName = pkg; + shadowPm.installPackage(pi); + } + +} diff --git a/tests/robotests/src/com/android/settings/gestures/LongPressPowerButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/LongPressPowerButtonPreferenceControllerTest.java index fa97754491c..5637e961c4e 100644 --- a/tests/robotests/src/com/android/settings/gestures/LongPressPowerButtonPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/gestures/LongPressPowerButtonPreferenceControllerTest.java @@ -152,9 +152,6 @@ public class LongPressPowerButtonPreferenceControllerTest { assertThat(Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo( LongPressPowerButtonPreferenceController.KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS); - verify(mController.mAssistSwitch).setSummary( - getString( - R.string.power_menu_summary_long_press_for_assist_enabled)); } @Test