diff --git a/res/values/strings.xml b/res/values/strings.xml index 0d6eaeb2527..f0a2ba36d52 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9795,8 +9795,11 @@ Take selfies faster - - Swipe up on Home button + + System navigation + + + 2-button navigation To switch apps, swipe up on the Home button. Swipe up again to see all apps. Works from any screen. You’ll no longer have an Overview button on the bottom right of your screen. @@ -9804,6 +9807,16 @@ Turn on the new gesture to switch apps + + Fully gestural navigation + + To go Home, swipe up from the bottom of the screen. To go Back, swipe from either the left or right edge of the screen. To switch apps, start swiping up from the bottom of the screen and hold before releasing. + + + 3-button navigation + + Classic Android navigation mode where going Home, switching apps, and going Back are accessible via buttons. + Double-tap to check phone diff --git a/res/xml/gestures.xml b/res/xml/gestures.xml index b1250ec79db..f7056307826 100644 --- a/res/xml/gestures.xml +++ b/res/xml/gestures.xml @@ -52,10 +52,10 @@ settings:controller="com.android.settings.gestures.DoubleTwistPreferenceController" /> + android:key="gesture_system_navigation_input_summary" + android:title="@string/system_navigation_title" + android:fragment="com.android.settings.gestures.SystemNavigationGestureSettings" + settings:controller="com.android.settings.gestures.SystemNavigationLegacyPreferenceController" /> + android:key="gesture_system_navigation_screen" + android:title="@string/system_navigation_title"> - + + + app:controller="com.android.settings.gestures.SystemNavigationSwipeUpPreferenceController" /> + + \ No newline at end of file diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 7abd98d3efe..147d0be3169 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -82,7 +82,7 @@ import com.android.settings.gestures.DoubleTapScreenSettings; import com.android.settings.gestures.DoubleTwistGestureSettings; import com.android.settings.gestures.PickupGestureSettings; import com.android.settings.gestures.SwipeToNotificationSettings; -import com.android.settings.gestures.SwipeUpGestureSettings; +import com.android.settings.gestures.SystemNavigationGestureSettings; import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment; import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; import com.android.settings.inputmethod.PhysicalKeyboardFragment; @@ -201,7 +201,7 @@ public class SettingsGateway { DoubleTapScreenSettings.class.getName(), PickupGestureSettings.class.getName(), DoubleTwistGestureSettings.class.getName(), - SwipeUpGestureSettings.class.getName(), + SystemNavigationGestureSettings.class.getName(), CryptKeeperSettings.class.getName(), DataUsageSummary.class.getName(), DreamSettings.class.getName(), diff --git a/src/com/android/settings/gestures/SwipeUpPreferenceController.java b/src/com/android/settings/gestures/SwipeUpPreferenceController.java deleted file mode 100644 index 5e882c4e44f..00000000000 --- a/src/com/android/settings/gestures/SwipeUpPreferenceController.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2018 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 android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.UserManager; -import android.provider.Settings; -import android.text.TextUtils; - -import com.android.internal.R; - -public class SwipeUpPreferenceController extends GesturePreferenceController { - - private final int ON = 1; - private final int OFF = 0; - - private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; - private static final String PREF_KEY_VIDEO = "gesture_swipe_up_video"; - private final UserManager mUserManager; - - public SwipeUpPreferenceController(Context context, String key) { - super(context, key); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - } - - static boolean isGestureAvailable(Context context) { - if (!context.getResources().getBoolean(R.bool.config_swipe_up_gesture_setting_available)) { - return false; - } - - final ComponentName recentsComponentName = ComponentName.unflattenFromString( - context.getString(R.string.config_recentsComponentName)); - if (recentsComponentName == null) { - return false; - } - final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) - .setPackage(recentsComponentName.getPackageName()); - if (context.getPackageManager().resolveService(quickStepIntent, - PackageManager.MATCH_SYSTEM_ONLY) == null) { - return false; - } - return true; - } - - @Override - public int getAvailabilityStatus() { - return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; - } - - @Override - public boolean isSliceable() { - return TextUtils.equals(getPreferenceKey(), "gesture_swipe_up"); - } - - @Override - protected String getVideoPrefKey() { - return PREF_KEY_VIDEO; - } - - @Override - public boolean setChecked(boolean isChecked) { - setSwipeUpPreference(mContext, mUserManager, isChecked ? ON : OFF); - return true; - } - - public static void setSwipeUpPreference(Context context, UserManager userManager, - int enabled) { - Settings.Secure.putInt(context.getContentResolver(), - Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, enabled); - } - - @Override - public boolean isChecked() { - final int defaultValue = mContext.getResources() - .getBoolean(R.bool.config_swipe_up_gesture_default) ? ON : OFF; - final int swipeUpEnabled = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, defaultValue); - return swipeUpEnabled != OFF; - } -} diff --git a/src/com/android/settings/gestures/SystemNavigationEdgeToEdgePreferenceController.java b/src/com/android/settings/gestures/SystemNavigationEdgeToEdgePreferenceController.java new file mode 100644 index 00000000000..129cf909305 --- /dev/null +++ b/src/com/android/settings/gestures/SystemNavigationEdgeToEdgePreferenceController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 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 android.content.Context; +import android.text.TextUtils; + +import com.android.settings.widget.RadioButtonPreference; + +public class SystemNavigationEdgeToEdgePreferenceController extends + SystemNavigationPreferenceController { + static final String PREF_KEY_EDGE_TO_EDGE = "gesture_edge_to_edge"; + + public SystemNavigationEdgeToEdgePreferenceController(Context context, String key) { + super(context, key); + } + + @Override + public boolean isSliceable() { + return TextUtils.equals(PREF_KEY_EDGE_TO_EDGE, getPreferenceKey()); + } + + @Override + public void onRadioButtonClicked(RadioButtonPreference preference) { + setSwipeUpEnabled(mContext, true); + setEdgeToEdgeGestureEnabled(mContext, true); + selectRadioButtonInGroup(PREF_KEY_EDGE_TO_EDGE, mPreferenceScreen); + } + + @Override + public boolean isChecked() { + return isEdgeToEdgeEnabled(mContext); + } +} diff --git a/src/com/android/settings/gestures/SwipeUpGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java similarity index 85% rename from src/com/android/settings/gestures/SwipeUpGestureSettings.java rename to src/com/android/settings/gestures/SystemNavigationGestureSettings.java index a1256439224..ea5454b8204 100644 --- a/src/com/android/settings/gestures/SwipeUpGestureSettings.java +++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java @@ -32,12 +32,12 @@ import java.util.Arrays; import java.util.List; @SearchIndexable -public class SwipeUpGestureSettings extends DashboardFragment { +public class SystemNavigationGestureSettings extends DashboardFragment { - private static final String TAG = "SwipeUpGesture"; + private static final String TAG = "SystemNavigationGesture"; public static final String PREF_KEY_SUGGESTION_COMPLETE = - "pref_swipe_up_suggestion_complete"; + "pref_system_navigation_suggestion_complete"; @Override public void onAttach(Context context) { @@ -60,7 +60,7 @@ public class SwipeUpGestureSettings extends DashboardFragment { @Override protected int getPreferenceScreenResId() { - return R.xml.swipe_up_gesture_settings; + return R.xml.system_navigation_gesture_settings; } public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = @@ -69,13 +69,13 @@ public class SwipeUpGestureSettings extends DashboardFragment { public List getXmlResourcesToIndex( Context context, boolean enabled) { final SearchIndexableResource sir = new SearchIndexableResource(context); - sir.xmlResId = R.xml.swipe_up_gesture_settings; + sir.xmlResId = R.xml.system_navigation_gesture_settings; return Arrays.asList(sir); } @Override protected boolean isPageSearchEnabled(Context context) { - return SwipeUpPreferenceController.isGestureAvailable(context); + return SystemNavigationPreferenceController.isGestureAvailable(context); } }; } diff --git a/src/com/android/settings/gestures/SystemNavigationLegacyPreferenceController.java b/src/com/android/settings/gestures/SystemNavigationLegacyPreferenceController.java new file mode 100644 index 00000000000..f9c95a87545 --- /dev/null +++ b/src/com/android/settings/gestures/SystemNavigationLegacyPreferenceController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 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 android.content.Context; +import android.text.TextUtils; + +import com.android.settings.widget.RadioButtonPreference; + +public class SystemNavigationLegacyPreferenceController extends + SystemNavigationPreferenceController { + static final String PREF_KEY_LEGACY = "gesture_legacy"; + + public SystemNavigationLegacyPreferenceController(Context context, String key) { + super(context, key); + } + + @Override + public boolean isSliceable() { + return TextUtils.equals(PREF_KEY_LEGACY, getPreferenceKey()); + } + + @Override + public void onRadioButtonClicked(RadioButtonPreference preference) { + setEdgeToEdgeGestureEnabled(mContext, false); + setSwipeUpEnabled(mContext, false); + selectRadioButtonInGroup(PREF_KEY_LEGACY, mPreferenceScreen); + } + + @Override + public boolean isChecked() { + return !isEdgeToEdgeEnabled(mContext) && !isSwipeUpEnabled(mContext); + } +} diff --git a/src/com/android/settings/gestures/SystemNavigationPreferenceController.java b/src/com/android/settings/gestures/SystemNavigationPreferenceController.java new file mode 100644 index 00000000000..a11754efe18 --- /dev/null +++ b/src/com/android/settings/gestures/SystemNavigationPreferenceController.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2019 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.os.UserHandle.USER_SYSTEM; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.om.IOverlayManager; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.view.View; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.widget.RadioButtonPreference; + +public abstract class SystemNavigationPreferenceController extends GesturePreferenceController + implements RadioButtonPreference.OnClickListener { + + private static final int OFF = 0; + private static final int ON = 1; + + private static final String HIDE_BACK_BUTTON = "quickstepcontroller_hideback"; + private static final String HIDE_HOME_BUTTON = "quickstepcontroller_hidehome"; + private static final String HIDE_NAVBAR_DIVIDER = "hide_navigationbar_divider"; + private static final String SHOW_HANDLE = "quickstepcontroller_showhandle"; + private static final String ENABLE_CLICK_THROUGH = "quickstepcontroller_clickthrough"; + private static final String ENABLE_LAUNCHER_SWIPE_TO_HOME = "SWIPE_HOME"; + private static final String ENABLE_COLOR_ADAPT_FOR_HANDLE = "navbar_color_adapt_enable"; + private static final String ENABLE_ASSISTANT_GESTURE = "ENABLE_ASSISTANT_GESTURE"; + private static final String PROTOTYPE_ENABLED = "prototype_enabled"; + + private static final int EDGE_SENSITIVITY_WIDTH = 32; + private static final String EDGE_SENSITIVITY_KEY = "quickstepcontroller_edge_width_sensitivity"; + + private static final String GESTURES_MATCH_MAP_OFF = "000000"; + private static final String GESTURES_MATCH_MAP_ON = "071133"; + private static final String GESTURES_MATCH_MAP_KEY = "quickstepcontroller_gesture_match_map"; + + private static final String OVERLAY_NAVBAR_TYPE_INSET = + "com.android.internal.experiment.navbar.type.inset"; + private static final String OVERLAY_NAVBAR_TYPE_FLOATING = + "com.android.internal.experiment.navbar.type.floating"; + + private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; + private static final String PREF_KEY_VIDEO = "gesture_swipe_up_video"; + + private static final String[] RADIO_BUTTONS_IN_GROUP = { + SystemNavigationLegacyPreferenceController.PREF_KEY_LEGACY, + SystemNavigationSwipeUpPreferenceController.PREF_KEY_SWIPE_UP, + SystemNavigationEdgeToEdgePreferenceController.PREF_KEY_EDGE_TO_EDGE, + }; + + protected PreferenceScreen mPreferenceScreen; + + public SystemNavigationPreferenceController(Context context, String key) { + super(context, key); + } + + @Override + public int getAvailabilityStatus() { + return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreferenceScreen = screen; + + Preference preference = screen.findPreference(getPreferenceKey()); + if (preference != null && preference instanceof RadioButtonPreference) { + RadioButtonPreference radioPreference = (RadioButtonPreference) preference; + radioPreference.setOnClickListener(this); + radioPreference.setAppendixVisibility(View.GONE); + } + } + + @Override + public boolean setChecked(boolean isChecked) { + if (!isChecked || mPreferenceScreen == null) { + return false; + } + Preference preference = mPreferenceScreen.findPreference(getPreferenceKey()); + if (preference != null && preference instanceof RadioButtonPreference) { + onRadioButtonClicked((RadioButtonPreference) preference); + } + return true; + } + + @Override + public CharSequence getSummary() { + if (isEdgeToEdgeEnabled(mContext)) { + return mContext.getText(R.string.edge_to_edge_navigation_title); + } else if (isSwipeUpEnabled(mContext)) { + return mContext.getText(R.string.swipe_up_to_switch_apps_title); + } else { + return mContext.getText(R.string.legacy_navigation_title); + } + } + + @Override + protected String getVideoPrefKey() { + return PREF_KEY_VIDEO; + } + + static boolean isGestureAvailable(Context context) { + if (!context.getResources().getBoolean( + com.android.internal.R.bool.config_swipe_up_gesture_setting_available)) { + return false; + } + + final ComponentName recentsComponentName = ComponentName.unflattenFromString( + context.getString(com.android.internal.R.string.config_recentsComponentName)); + if (recentsComponentName == null) { + return false; + } + final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) + .setPackage(recentsComponentName.getPackageName()); + if (context.getPackageManager().resolveService(quickStepIntent, + PackageManager.MATCH_SYSTEM_ONLY) == null) { + return false; + } + return true; + } + + static void selectRadioButtonInGroup(String preferenceKey, PreferenceScreen screen) { + if (screen == null) { + return; + } + for (String key : RADIO_BUTTONS_IN_GROUP) { + ((RadioButtonPreference) screen.findPreference(key)).setChecked( + TextUtils.equals(key, preferenceKey)); + } + } + + static void setEdgeToEdgeGestureEnabled(Context context, boolean enable) { + // TODO(b/127366543): replace all of this with a single switch + setBooleanGlobalSetting(context, HIDE_BACK_BUTTON, enable); + setBooleanGlobalSetting(context, HIDE_HOME_BUTTON, enable); + setBooleanGlobalSetting(context, HIDE_NAVBAR_DIVIDER, enable); + setBooleanGlobalSetting(context, SHOW_HANDLE, enable); + setBooleanGlobalSetting(context, ENABLE_CLICK_THROUGH, enable); + setBooleanGlobalSetting(context, ENABLE_LAUNCHER_SWIPE_TO_HOME, enable); + setBooleanGlobalSetting(context, ENABLE_COLOR_ADAPT_FOR_HANDLE, enable); + setBooleanGlobalSetting(context, ENABLE_ASSISTANT_GESTURE, enable); + setBooleanGlobalSetting(context, PROTOTYPE_ENABLED, enable); + Settings.Global.putInt(context.getContentResolver(), EDGE_SENSITIVITY_KEY, + EDGE_SENSITIVITY_WIDTH); + Settings.Global.putString(context.getContentResolver(), GESTURES_MATCH_MAP_KEY, + enable ? GESTURES_MATCH_MAP_ON : GESTURES_MATCH_MAP_OFF); + + IOverlayManager overlayManager = IOverlayManager.Stub + .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)); + if (overlayManager != null) { + try { + overlayManager.setEnabled(OVERLAY_NAVBAR_TYPE_FLOATING, false, USER_SYSTEM); + overlayManager.setEnabled(OVERLAY_NAVBAR_TYPE_INSET, enable, USER_SYSTEM); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + static void setBooleanGlobalSetting(Context context, String name, boolean flag) { + Settings.Global.putInt(context.getContentResolver(), name, flag ? ON : OFF); + } + + static void setSwipeUpEnabled(Context context, boolean enabled) { + Settings.Secure.putInt(context.getContentResolver(), + Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, enabled ? ON : OFF); + } + + static boolean isSwipeUpEnabled(Context context) { + if (isEdgeToEdgeEnabled(context)) { + return false; + } + final int defaultSwipeUpValue = context.getResources() + .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_default) ? ON : OFF; + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, defaultSwipeUpValue) == ON; + } + + static boolean isEdgeToEdgeEnabled(Context context) { + return Settings.Global.getInt(context.getContentResolver(), PROTOTYPE_ENABLED, OFF) == ON; + } +} diff --git a/src/com/android/settings/gestures/SystemNavigationSwipeUpPreferenceController.java b/src/com/android/settings/gestures/SystemNavigationSwipeUpPreferenceController.java new file mode 100644 index 00000000000..25fafdb2f7f --- /dev/null +++ b/src/com/android/settings/gestures/SystemNavigationSwipeUpPreferenceController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 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 android.content.Context; +import android.text.TextUtils; + +import com.android.settings.widget.RadioButtonPreference; + +public class SystemNavigationSwipeUpPreferenceController extends + SystemNavigationPreferenceController { + static final String PREF_KEY_SWIPE_UP = "gesture_swipe_up"; + + public SystemNavigationSwipeUpPreferenceController(Context context, String key) { + super(context, key); + } + + @Override + public boolean isSliceable() { + return TextUtils.equals(PREF_KEY_SWIPE_UP, getPreferenceKey()); + } + + @Override + public void onRadioButtonClicked(RadioButtonPreference preference) { + setEdgeToEdgeGestureEnabled(mContext, false); + setSwipeUpEnabled(mContext, true); + selectRadioButtonInGroup(PREF_KEY_SWIPE_UP, mPreferenceScreen); + } + + @Override + public boolean isChecked() { + return isSwipeUpEnabled(mContext); + } +} diff --git a/tests/robotests/src/com/android/settings/gestures/SystemNavigationEdgeToEdgePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SystemNavigationEdgeToEdgePreferenceControllerTest.java new file mode 100644 index 00000000000..5e6cd553163 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/SystemNavigationEdgeToEdgePreferenceControllerTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2019 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.android.settings.gestures.SystemNavigationEdgeToEdgePreferenceController.PREF_KEY_EDGE_TO_EDGE; +import static com.android.settings.gestures.SystemNavigationLegacyPreferenceController.PREF_KEY_LEGACY; +import static com.android.settings.gestures.SystemNavigationSwipeUpPreferenceController.PREF_KEY_SWIPE_UP; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.provider.Settings; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.internal.R; +import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settings.widget.RadioButtonPreference; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowPackageManager; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = SettingsShadowResources.class) +public class SystemNavigationEdgeToEdgePreferenceControllerTest { + + private Context mContext; + private ShadowPackageManager mPackageManager; + + private SystemNavigationEdgeToEdgePreferenceController mController; + + private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; + + @Before + public void setUp() { + SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available, + true); + SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_default, true); + + mContext = RuntimeEnvironment.application; + Settings.Global.putInt(mContext.getContentResolver(), "prototype_enabled", 1); + + mPackageManager = Shadows.shadowOf(mContext.getPackageManager()); + + mController = new SystemNavigationEdgeToEdgePreferenceController(mContext, + PREF_KEY_EDGE_TO_EDGE); + } + + @After + public void tearDown() { + SettingsShadowResources.reset(); + } + + @Test + public void testIsGestureAvailable_matchingServiceExists_shouldReturnTrue() { + final ComponentName recentsComponentName = ComponentName.unflattenFromString( + mContext.getString(com.android.internal.R.string.config_recentsComponentName)); + final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) + .setPackage(recentsComponentName.getPackageName()); + final ResolveInfo info = new ResolveInfo(); + info.serviceInfo = new ServiceInfo(); + info.resolvePackageName = recentsComponentName.getPackageName(); + info.serviceInfo.packageName = info.resolvePackageName; + info.serviceInfo.name = recentsComponentName.getClassName(); + info.serviceInfo.applicationInfo = new ApplicationInfo(); + info.serviceInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + mPackageManager.addResolveInfoForIntent(quickStepIntent, info); + + assertThat(SystemNavigationEdgeToEdgePreferenceController.isGestureAvailable(mContext)) + .isTrue(); + } + + @Test + public void testIsGestureAvailable_overlayDisabled_matchingServiceExists_shouldReturnFalse() { + SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available, + false); + + final ComponentName recentsComponentName = ComponentName.unflattenFromString( + mContext.getString(com.android.internal.R.string.config_recentsComponentName)); + final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) + .setPackage(recentsComponentName.getPackageName()); + mPackageManager.addResolveInfoForIntent(quickStepIntent, new ResolveInfo()); + + assertThat(SystemNavigationEdgeToEdgePreferenceController.isGestureAvailable(mContext)) + .isFalse(); + } + + @Test + public void testIsGestureAvailable_noMatchingServiceExists_shouldReturnFalse() { + assertThat(SystemNavigationEdgeToEdgePreferenceController.isGestureAvailable(mContext)) + .isFalse(); + } + + @Test + public void testIsChecked_defaultIsTrue_shouldReturnTrue() { + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void testIsChecked_defaultIsFalse_shouldReturnFalse() { + Settings.Global.putInt(mContext.getContentResolver(), "prototype_enabled", 0); + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void testIsChecked_radioButtonClicked_shouldReturnTrue() { + // Set the setting to be enabled. + mController.onRadioButtonClicked(null); + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void testOnRadioButtonClicked_setsCorrectRadioButtonChecked() { + RadioButtonPreference radioLegacy = mock(RadioButtonPreference.class); + RadioButtonPreference radioSwipeUp = mock(RadioButtonPreference.class); + RadioButtonPreference radioEdgeToEdge = mock(RadioButtonPreference.class); + PreferenceScreen screen = mock(PreferenceScreen.class); + + when(screen.findPreference(PREF_KEY_LEGACY)).thenReturn(radioLegacy); + when(screen.findPreference(PREF_KEY_SWIPE_UP)).thenReturn(radioSwipeUp); + when(screen.findPreference(PREF_KEY_EDGE_TO_EDGE)).thenReturn(radioEdgeToEdge); + + mController.displayPreference(screen); + mController.onRadioButtonClicked(radioEdgeToEdge); + + verify(radioLegacy, times(1)).setChecked(false); + verify(radioSwipeUp, times(1)).setChecked(false); + verify(radioEdgeToEdge, times(1)).setChecked(true); + } + + @Test + public void isSliceableCorrectKey_returnsTrue() { + assertThat(mController.isSliceable()).isTrue(); + } + + @Test + public void isSliceableIncorrectKey_returnsFalse() { + final SystemNavigationEdgeToEdgePreferenceController controller = + new SystemNavigationEdgeToEdgePreferenceController(mContext, "bad_key"); + assertThat(controller.isSliceable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/gestures/SystemNavigationLegacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SystemNavigationLegacyPreferenceControllerTest.java new file mode 100644 index 00000000000..fe0a0617ba9 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/SystemNavigationLegacyPreferenceControllerTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2019 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.android.settings.gestures.SystemNavigationEdgeToEdgePreferenceController.PREF_KEY_EDGE_TO_EDGE; +import static com.android.settings.gestures.SystemNavigationLegacyPreferenceController.PREF_KEY_LEGACY; +import static com.android.settings.gestures.SystemNavigationSwipeUpPreferenceController.PREF_KEY_SWIPE_UP; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.internal.R; +import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settings.widget.RadioButtonPreference; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowPackageManager; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = SettingsShadowResources.class) +public class SystemNavigationLegacyPreferenceControllerTest { + + private Context mContext; + private ShadowPackageManager mPackageManager; + + private SystemNavigationLegacyPreferenceController mController; + + private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; + + @Before + public void setUp() { + SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available, + true); + SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_default, false); + + mContext = RuntimeEnvironment.application; + mPackageManager = Shadows.shadowOf(mContext.getPackageManager()); + + mController = new SystemNavigationLegacyPreferenceController(mContext, PREF_KEY_LEGACY); + } + + @After + public void tearDown() { + SettingsShadowResources.reset(); + } + + @Test + public void testIsGestureAvailable_matchingServiceExists_shouldReturnTrue() { + final ComponentName recentsComponentName = ComponentName.unflattenFromString( + mContext.getString(com.android.internal.R.string.config_recentsComponentName)); + final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) + .setPackage(recentsComponentName.getPackageName()); + final ResolveInfo info = new ResolveInfo(); + info.serviceInfo = new ServiceInfo(); + info.resolvePackageName = recentsComponentName.getPackageName(); + info.serviceInfo.packageName = info.resolvePackageName; + info.serviceInfo.name = recentsComponentName.getClassName(); + info.serviceInfo.applicationInfo = new ApplicationInfo(); + info.serviceInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + mPackageManager.addResolveInfoForIntent(quickStepIntent, info); + + assertThat(SystemNavigationLegacyPreferenceController.isGestureAvailable(mContext)) + .isTrue(); + } + + @Test + public void testIsGestureAvailable_overlayDisabled_matchingServiceExists_shouldReturnFalse() { + SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_setting_available, + false); + + final ComponentName recentsComponentName = ComponentName.unflattenFromString( + mContext.getString(com.android.internal.R.string.config_recentsComponentName)); + final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) + .setPackage(recentsComponentName.getPackageName()); + mPackageManager.addResolveInfoForIntent(quickStepIntent, new ResolveInfo()); + + assertThat( + SystemNavigationLegacyPreferenceController.isGestureAvailable(mContext)).isFalse(); + } + + @Test + public void testIsGestureAvailable_noMatchingServiceExists_shouldReturnFalse() { + assertThat( + SystemNavigationLegacyPreferenceController.isGestureAvailable(mContext)).isFalse(); + } + + @Test + public void testIsChecked_defaultIsTrue_shouldReturnTrue() { + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void testIsChecked_defaultIsFalse_shouldReturnFalse() { + // Turn on the Swipe Up mode (2-buttons) + SettingsShadowResources.overrideResource(R.bool.config_swipe_up_gesture_default, true); + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void testIsChecked_radioButtonClicked_shouldReturnTrue() { + // Set the setting to be enabled. + mController.onRadioButtonClicked(null); + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void testOnRadioButtonClicked_setsCorrectRadioButtonChecked() { + RadioButtonPreference radioLegacy = mock(RadioButtonPreference.class); + RadioButtonPreference radioSwipeUp = mock(RadioButtonPreference.class); + RadioButtonPreference radioEdgeToEdge = mock(RadioButtonPreference.class); + PreferenceScreen screen = mock(PreferenceScreen.class); + + when(screen.findPreference(PREF_KEY_LEGACY)).thenReturn(radioLegacy); + when(screen.findPreference(PREF_KEY_SWIPE_UP)).thenReturn(radioSwipeUp); + when(screen.findPreference(PREF_KEY_EDGE_TO_EDGE)).thenReturn(radioEdgeToEdge); + + mController.displayPreference(screen); + mController.onRadioButtonClicked(radioLegacy); + + verify(radioLegacy, times(1)).setChecked(true); + verify(radioSwipeUp, times(1)).setChecked(false); + verify(radioEdgeToEdge, times(1)).setChecked(false); + } + + @Test + public void isSliceableCorrectKey_returnsTrue() { + assertThat(mController.isSliceable()).isTrue(); + } + + @Test + public void isSliceableIncorrectKey_returnsFalse() { + final SystemNavigationLegacyPreferenceController controller = + new SystemNavigationLegacyPreferenceController(mContext, "bad_key"); + assertThat(controller.isSliceable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SystemNavigationSwipeUpPreferenceControllerTest.java similarity index 63% rename from tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java rename to tests/robotests/src/com/android/settings/gestures/SystemNavigationSwipeUpPreferenceControllerTest.java index e98bdc84caa..97b47621141 100644 --- a/tests/robotests/src/com/android/settings/gestures/SwipeUpPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/gestures/SystemNavigationSwipeUpPreferenceControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -16,8 +16,17 @@ package com.android.settings.gestures; +import static com.android.settings.gestures.SystemNavigationEdgeToEdgePreferenceController.PREF_KEY_EDGE_TO_EDGE; +import static com.android.settings.gestures.SystemNavigationLegacyPreferenceController.PREF_KEY_LEGACY; +import static com.android.settings.gestures.SystemNavigationSwipeUpPreferenceController.PREF_KEY_SWIPE_UP; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,8 +34,12 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + import com.android.internal.R; import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settings.widget.RadioButtonPreference; import org.junit.After; import org.junit.Before; @@ -40,14 +53,14 @@ import org.robolectric.shadows.ShadowPackageManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = SettingsShadowResources.class) -public class SwipeUpPreferenceControllerTest { +public class SystemNavigationSwipeUpPreferenceControllerTest { private Context mContext; private ShadowPackageManager mPackageManager; - private SwipeUpPreferenceController mController; + + private SystemNavigationSwipeUpPreferenceController mController; private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; - private static final String KEY_SWIPE_UP = "gesture_swipe_up"; @Before public void setUp() { @@ -57,7 +70,8 @@ public class SwipeUpPreferenceControllerTest { mContext = RuntimeEnvironment.application; mPackageManager = Shadows.shadowOf(mContext.getPackageManager()); - mController = new SwipeUpPreferenceController(mContext, KEY_SWIPE_UP); + + mController = new SystemNavigationSwipeUpPreferenceController(mContext, PREF_KEY_SWIPE_UP); } @After @@ -80,7 +94,8 @@ public class SwipeUpPreferenceControllerTest { info.serviceInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; mPackageManager.addResolveInfoForIntent(quickStepIntent, info); - assertThat(SwipeUpPreferenceController.isGestureAvailable(mContext)).isTrue(); + assertThat(SystemNavigationSwipeUpPreferenceController.isGestureAvailable(mContext)) + .isTrue(); } @Test @@ -94,12 +109,14 @@ public class SwipeUpPreferenceControllerTest { .setPackage(recentsComponentName.getPackageName()); mPackageManager.addResolveInfoForIntent(quickStepIntent, new ResolveInfo()); - assertThat(SwipeUpPreferenceController.isGestureAvailable(mContext)).isFalse(); + assertThat(SystemNavigationSwipeUpPreferenceController.isGestureAvailable(mContext)) + .isFalse(); } @Test public void testIsGestureAvailable_noMatchingServiceExists_shouldReturnFalse() { - assertThat(SwipeUpPreferenceController.isGestureAvailable(mContext)).isFalse(); + assertThat(SystemNavigationSwipeUpPreferenceController.isGestureAvailable(mContext)) + .isFalse(); } @Test @@ -114,30 +131,40 @@ public class SwipeUpPreferenceControllerTest { } @Test - public void testIsChecked_setCheckedTrue_shouldReturnTrue() { + public void testIsChecked_radioButtonClicked_shouldReturnTrue() { // Set the setting to be enabled. - mController.setChecked(true); + mController.onRadioButtonClicked(null); assertThat(mController.isChecked()).isTrue(); } @Test - public void testIsChecked_setCheckedFalse_shouldReturnFalse() { - // Set the setting to be disabled. - mController.setChecked(false); - assertThat(mController.isChecked()).isFalse(); + public void testOnRadioButtonClicked_setsCorrectRadioButtonChecked() { + RadioButtonPreference radioLegacy = mock(RadioButtonPreference.class); + RadioButtonPreference radioSwipeUp = mock(RadioButtonPreference.class); + RadioButtonPreference radioEdgeToEdge = mock(RadioButtonPreference.class); + PreferenceScreen screen = mock(PreferenceScreen.class); + + when(screen.findPreference(PREF_KEY_LEGACY)).thenReturn(radioLegacy); + when(screen.findPreference(PREF_KEY_SWIPE_UP)).thenReturn(radioSwipeUp); + when(screen.findPreference(PREF_KEY_EDGE_TO_EDGE)).thenReturn(radioEdgeToEdge); + + mController.displayPreference(screen); + mController.onRadioButtonClicked(radioSwipeUp); + + verify(radioLegacy, times(1)).setChecked(false); + verify(radioSwipeUp, times(1)).setChecked(true); + verify(radioEdgeToEdge, times(1)).setChecked(false); } @Test public void isSliceableCorrectKey_returnsTrue() { - final SwipeUpPreferenceController controller = - new SwipeUpPreferenceController(mContext, "gesture_swipe_up"); - assertThat(controller.isSliceable()).isTrue(); + assertThat(mController.isSliceable()).isTrue(); } @Test public void isSliceableIncorrectKey_returnsFalse() { - final SwipeUpPreferenceController controller = - new SwipeUpPreferenceController(mContext, "bad_key"); + final SystemNavigationSwipeUpPreferenceController controller = + new SystemNavigationSwipeUpPreferenceController(mContext, "bad_key"); assertThat(controller.isSliceable()).isFalse(); } }