diff --git a/res/values/strings.xml b/res/values/strings.xml
index 028ac796e9e..79db15c6a8f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10887,6 +10887,11 @@
Double-tap to check device
+
+ Swipe for notifications
+
+ Swipe down on the bottom of the screen to check the notification
+
One-Handed mode
diff --git a/res/xml/gestures.xml b/res/xml/gestures.xml
index 1569900d734..970ad217bfd 100644
--- a/res/xml/gestures.xml
+++ b/res/xml/gestures.xml
@@ -33,6 +33,13 @@
settings:searchable="false"
settings:controller="com.android.settings.gestures.SwipeToNotificationPreferenceController" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java b/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
new file mode 100644
index 00000000000..5eba5394388
--- /dev/null
+++ b/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.provider.Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
+
+import static com.android.settings.gestures.OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Handles swipe bottom to expand notification panel gesture.
+ **/
+public class SwipeBottomToNotificationPreferenceController extends TogglePreferenceController {
+
+ private static final int ON = 1;
+ private static final int OFF = 0;
+
+ private static final String PREF_KEY = "gesture_swipe_bottom_to_notification";
+
+ public SwipeBottomToNotificationPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ /** Indicates whether the gesture is available or not. */
+ public static boolean isGestureAvailable(Context context) {
+ // Disable the gesture once One-Handed mode gesture enabled.
+ if (SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ return !OneHandedSettingsUtils.isOneHandedModeEnabled(context);
+ }
+ return true;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return isGestureAvailable(mContext) ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return TextUtils.equals(getPreferenceKey(), PREF_KEY);
+ }
+
+ @Override
+ public boolean isPublicSlice() {
+ return true;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, isChecked ? ON : OFF);
+ return true;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, OFF) == ON;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ // This toggle preference summary will be updated in gesture preference page since we bound
+ // it with entry preference in gesture.xml
+ return mContext.getText(
+ isChecked() ? R.string.gesture_setting_on : R.string.gesture_setting_off);
+ }
+}
diff --git a/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java b/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java
new file mode 100644
index 00000000000..d0441f37a36
--- /dev/null
+++ b/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+
+/**
+ * The Fragment for swipe bottom to notification gesture settings.
+ */
+public class SwipeBottomToNotificationSettings extends DashboardFragment {
+
+ private static final String TAG = "SwipeBottomToNotificationSettings";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.SETTINGS_SWIPE_BOTTOM_TO_NOTIFICATION;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.swipe_bottom_to_notification_settings;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.swipe_bottom_to_notification_settings) {
+
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ return SwipeBottomToNotificationPreferenceController
+ .isGestureAvailable(context);
+ }
+ };
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java
index d2cd7450cfc..11128f303e9 100644
--- a/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java
@@ -39,7 +39,6 @@ public class OneHandedEnablePreferenceControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
- @Mock
private OneHandedEnablePreferenceController mController;
@Before
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
new file mode 100644
index 00000000000..db51dffcec0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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.provider.Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.provider.Settings;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class SwipeBottomToNotificationPreferenceControllerTest {
+
+ private static final String KEY = "gesture_swipe_bottom_to_notification";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private SwipeBottomToNotificationPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new SwipeBottomToNotificationPreferenceController(mContext, KEY);
+ }
+
+ @Test
+ public void setChecked_toggledOn_enablesSwipeBottomToNotification() {
+ mController.setChecked(true);
+
+ assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+ SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0)).isEqualTo(1);
+ }
+
+ @Test
+ public void setChecked_toggledOff_disablesSwipeBottomToNotification() {
+ mController.setChecked(false);
+
+ assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+ SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0)).isEqualTo(0);
+ }
+
+ @Test
+ public void getAvailabilityStatus_oneHandedUnsupported_returnsAvailable() {
+ SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "false");
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_oneHandedDisabled_returnsAvailable() {
+ SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ONE_HANDED_MODE_ENABLED, 0);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_oneHandedEnabled_returnsDisabled() {
+ SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void getSummary_gestureEnabled_returnsOnSummary() {
+ mController.setChecked(true);
+
+ assertThat(mController.getSummary()).isEqualTo(
+ mContext.getText(R.string.gesture_setting_on));
+ }
+
+ @Test
+ public void getSummary_gestureDisabled_returnsOffSummary() {
+ mController.setChecked(false);
+
+ assertThat(mController.getSummary()).isEqualTo(
+ mContext.getText(R.string.gesture_setting_off));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationSettingsTest.java
new file mode 100644
index 00000000000..ad8104c05fd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationSettingsTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 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 android.content.Context;
+import android.os.SystemProperties;
+import android.provider.SearchIndexableResource;
+import android.provider.Settings;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class SwipeBottomToNotificationSettingsTest {
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private SwipeBottomToNotificationSettings mSettings = new SwipeBottomToNotificationSettings();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void getPreferenceScreenResId_shouldReturnsXml() {
+ assertThat(mSettings.getPreferenceScreenResId())
+ .isEqualTo(R.xml.swipe_bottom_to_notification_settings);
+ }
+
+ @Test
+ public void searchIndexProvider_shouldIndexResource() {
+ final List indexRes =
+ SwipeBottomToNotificationSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(
+ mContext, true /* enabled */);
+
+ assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId());
+ }
+
+ @Test
+ public void isPageSearchEnabled_oneHandedUnsupported_shouldReturnTrue() {
+ SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "false");
+
+ final Object obj = ReflectionHelpers.callInstanceMethod(
+ SwipeBottomToNotificationSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
+ ReflectionHelpers.ClassParameter.from(Context.class, mContext));
+
+ final boolean isEnabled = (Boolean) obj;
+ assertThat(isEnabled).isTrue();
+ }
+
+ @Test
+ public void isPageSearchEnabled_oneHandedDisabled_shouldReturnTrue() {
+ SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ONE_HANDED_MODE_ENABLED, 0);
+
+ final Object obj = ReflectionHelpers.callInstanceMethod(
+ SwipeBottomToNotificationSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
+ ReflectionHelpers.ClassParameter.from(Context.class, mContext));
+
+ final boolean isEnabled = (Boolean) obj;
+ assertThat(isEnabled).isTrue();
+ }
+
+ @Test
+ public void isPageSearchEnabled_oneHandedEnabled_shouldReturnFalse() {
+ SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
+
+ final Object obj = ReflectionHelpers.callInstanceMethod(
+ SwipeBottomToNotificationSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
+ ReflectionHelpers.ClassParameter.from(Context.class, mContext));
+
+ final boolean isEnabled = (Boolean) obj;
+ assertThat(isEnabled).isFalse();
+ }
+}