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
This commit is contained in:
Jernej Virag
2021-08-09 18:11:28 +00:00
parent 666d0984d4
commit 97ea279c45
11 changed files with 382 additions and 4 deletions

View File

@@ -3932,6 +3932,25 @@
android:value="true" /> android:value="true" />
</activity> </activity>
<activity
android:name="Settings$ButtonNavigationSettingsActivity"
android:label="Button Navigation Settings"
android:exported="true"
android:enabled="true">
<intent-filter android:priority="32">
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="com.android.settings.BUTTON_NAVIGATION_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.gestures.ButtonNavigationSettingsFragment" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
<activity android:name="Settings$MediaControlsSettingsActivity" <activity android:name="Settings$MediaControlsSettingsActivity"
android:exported="true" android:exported="true"
android:label="@strings/media_controls_title"> android:label="@strings/media_controls_title">

View File

@@ -11789,6 +11789,11 @@
<!-- Summary text for swiping from bottom corners to invoke the digital assistant app. [CHAR LIMIT=NONE] --> <!-- Summary text for swiping from bottom corners to invoke the digital assistant app. [CHAR LIMIT=NONE] -->
<string name="assistant_corner_gesture_summary">Swipe up from a bottom corner to invoke digital assistant app.</string> <string name="assistant_corner_gesture_summary">Swipe up from a bottom corner to invoke digital assistant app.</string>
<!-- Title text for holding a long press on Home button to invoke the digital assistant app. [CHAR LIMIT=60] -->
<string name="assistant_long_press_home_gesture_title">Hold Home to invoke assistant</string>
<!-- Summary text for holding a long press on Home button to invoke the digital assistant app. [CHAR LIMIT=NONE] -->
<string name="assistant_long_press_home_gesture_summary">Press and hold the Home button to invoke digital assistant app.</string>
<!-- Content description for the Information icon [CHAR LIMIT=30] --> <!-- Content description for the Information icon [CHAR LIMIT=30] -->
<string name="information_label">Information</string> <string name="information_label">Information</string>
@@ -11811,9 +11816,15 @@
<!-- Title for the screen to show all the gesture navigation settings [CHAR LIMIT=80] --> <!-- Title for the screen to show all the gesture navigation settings [CHAR LIMIT=80] -->
<string name="gesture_settings_activity_title">Gesture Navigation Sensitivity</string> <string name="gesture_settings_activity_title">Gesture Navigation Sensitivity</string>
<!-- Title for the screen to show all the 2- and 3-button navigation settings. [CHAR LIMIT=80] -->
<string name="button_navigation_settings_activity_title">Button navigation</string>
<!-- Keywords for the gesture navigation settings. [CHAR LIMIT=NONE] --> <!-- Keywords for the gesture navigation settings. [CHAR LIMIT=NONE] -->
<string name="keywords_gesture_navigation_settings">gesture navigation, back sensitivity, back gesture</string> <string name="keywords_gesture_navigation_settings">gesture navigation, back sensitivity, back gesture</string>
<!-- Keywords for the 2-/3-button navigation settings. [CHAR LIMIT=NONE] -->
<string name="keywords_button_navigation_settings">navigation, home button</string>
<!-- Preference and settings suggestion title text for ambient display double tap (phone) [CHAR LIMIT=60]--> <!-- Preference and settings suggestion title text for ambient display double tap (phone) [CHAR LIMIT=60]-->
<string name="ambient_display_title" product="default">Double-tap to check phone</string> <string name="ambient_display_title" product="default">Double-tap to check phone</string>
<!-- Preference and settings suggestion title text for ambient display double tap (tablet) [CHAR LIMIT=60]--> <!-- Preference and settings suggestion title text for ambient display double tap (tablet) [CHAR LIMIT=60]-->

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="button_navigation_settings_page"
android:title="@string/button_navigation_settings_activity_title"
settings:keywords="@string/keywords_button_navigation_settings">
<PreferenceCategory
android:key="assistant_button_navigation_category"
android:persistent="false"
android:title="@string/assistant_gesture_category_title">
<SwitchPreference
android:key="assistant_long_press_home_gesture"
android:title="@string/assistant_long_press_home_gesture_title"
android:summary="@string/assistant_long_press_home_gesture_summary"
settings:controller="com.android.settings.gestures.ButtonNavigationSettingsAssistController"
/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -243,6 +243,8 @@ public class Settings extends SettingsActivity {
public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ } public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ }
public static class GamesStorageActivity extends SettingsActivity { /* empty */ } public static class GamesStorageActivity extends SettingsActivity { /* empty */ }
public static class GestureNavigationSettingsActivity 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 { public static class InteractAcrossProfilesSettingsActivity extends SettingsActivity {
/* empty */ /* empty */
} }

View File

@@ -94,6 +94,7 @@ import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings; import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings;
import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings; import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
import com.android.settings.gestures.AssistGestureSettings; import com.android.settings.gestures.AssistGestureSettings;
import com.android.settings.gestures.ButtonNavigationSettingsFragment;
import com.android.settings.gestures.DoubleTapPowerSettings; import com.android.settings.gestures.DoubleTapPowerSettings;
import com.android.settings.gestures.DoubleTapScreenSettings; import com.android.settings.gestures.DoubleTapScreenSettings;
import com.android.settings.gestures.DoubleTwistGestureSettings; import com.android.settings.gestures.DoubleTwistGestureSettings;
@@ -312,6 +313,7 @@ public class SettingsGateway {
DarkModeSettingsFragment.class.getName(), DarkModeSettingsFragment.class.getName(),
BugReportHandlerPicker.class.getName(), BugReportHandlerPicker.class.getName(),
GestureNavigationSettingsFragment.class.getName(), GestureNavigationSettingsFragment.class.getName(),
ButtonNavigationSettingsFragment.class.getName(),
InteractAcrossProfilesSettings.class.getName(), InteractAcrossProfilesSettings.class.getName(),
InteractAcrossProfilesDetails.class.getName(), InteractAcrossProfilesDetails.class.getName(),
MediaControlsSettings.class.getName(), MediaControlsSettings.class.getName(),

View File

@@ -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;
}
}

View File

@@ -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);
}
};
}

View File

@@ -41,6 +41,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityGestureNavigationTutorial; import com.android.settings.accessibility.AccessibilityGestureNavigationTutorial;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
@@ -150,10 +151,19 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
pref.setSummary(((CandidateInfoExtra) info).loadSummary()); 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( pref.setExtraWidgetOnClickListener((v) -> startActivity(new Intent(
GestureNavigationSettingsFragment.GESTURE_NAVIGATION_SETTINGS))); 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 @Override

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -152,9 +152,6 @@ public class LongPressPowerButtonPreferenceControllerTest {
assertThat(Settings.Global.getInt(mContext.getContentResolver(), assertThat(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo( Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(
LongPressPowerButtonPreferenceController.KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS); LongPressPowerButtonPreferenceController.KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS);
verify(mController.mAssistSwitch).setSummary(
getString(
R.string.power_menu_summary_long_press_for_assist_enabled));
} }
@Test @Test