diff options
| author | 2019-01-30 11:20:48 -0800 | |
|---|---|---|
| committer | 2019-02-01 16:47:25 -0800 | |
| commit | b831fb4fd1446782bb88e7b43f4885d0de7a17ca (patch) | |
| tree | 9c1a881814ee01fefbca27a87ba9370a72458e36 | |
| parent | 03f6205e3e4746dbc768a8a6ddc025f5a0a382ac (diff) | |
Implement screen edge swipe for prototype
Removed nav bar edge swipes and replaced for screen edge swipe. The two
new gestures always stay left and right of the screen except for
landscape where only the side opposite of the navigation bar will exist.
The gestures will only activate past a second slop or if user executes
gesture really quickly (less than a tap timeout 100ms).
Bug: 112934365
Test: atest QuickStepControllerTest
Change-Id: I8ae98bd638943d9f76e455bc000a601f96e68d2c
7 files changed, 320 insertions, 148 deletions
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1060211cebf5..81c00bc86ddc 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -23,8 +23,9 @@ <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_height</dimen> <!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. --> <dimen name="navigation_bar_min_swipe_distance">48dp</dimen> - <!-- The distance from a side of device of the navigation bar to start an edge swipe --> - <dimen name="navigation_bar_edge_swipe_threshold">48dp</dimen> + <!-- The default distance from a side of the device to start an edge swipe from --> + <dimen name="navigation_bar_default_edge_width">48dp</dimen> + <dimen name="navigation_bar_default_edge_height">500dp</dimen> <!-- thickness (height) of the dead zone at the top of the navigation bar, reducing false presses on navbar buttons; approx 2mm --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 56d9bf4c5f8f..5365dcf72f11 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1829,6 +1829,9 @@ <!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] --> <string name="nav_bar">Navigation bar</string> + <!-- Label for navigation edge panel for gestures [CHAR LIMIT=60] --> + <string name="nav_bar_edge_panel" translatable="false">Navigation bar Edge Panel</string> + <!-- SysUI Tuner: Button that controls layout of navigation bar [CHAR LIMIT=60] --> <string name="nav_bar_layout">Layout</string> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java new file mode 100644 index 000000000000..dae4da7355c7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -0,0 +1,78 @@ +/* + * 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.systemui.statusbar.phone; + +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.PixelFormat; +import android.view.View; +import android.view.WindowManager; + +import com.android.systemui.R; + +public class NavigationBarEdgePanel extends View { + private static final String TAG = "NavigationBarEdgePanel"; + + public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height, + int gravity) { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + lp.gravity = gravity; + lp.setTitle(TAG + context.getDisplayId()); + lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel); + lp.windowAnimations = 0; + NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context); + panel.setLayoutParams(lp); + return panel; + } + + private NavigationBarEdgePanel(Context context) { + super(context); + } + + public void setWindowFlag(int flags, boolean enable) { + WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); + if (lp == null || enable == ((lp.flags & flags) != 0)) { + return; + } + if (enable) { + lp.flags |= flags; + } else { + lp.flags &= ~flags; + } + updateLayout(lp); + } + + public void setDimensions(int width, int height) { + final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); + if (lp.width != width || lp.height != height) { + lp.width = width; + lp.height = height; + updateLayout(lp); + } + } + + private void updateLayout(WindowManager.LayoutParams lp) { + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + wm.updateViewLayout(this, lp); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 02683c16d935..651670cbf2c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; @@ -35,6 +37,8 @@ import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.DrawableRes; +import android.annotation.IntDef; +import android.annotation.SuppressLint; import android.app.StatusBarManager; import android.content.Context; import android.content.res.Configuration; @@ -51,6 +55,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.Gravity; import android.view.MotionEvent; import android.view.Surface; import android.view.View; @@ -87,12 +92,21 @@ import com.android.systemui.statusbar.policy.KeyButtonDrawable; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.function.Consumer; public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> { final static boolean DEBUG = false; final static String TAG = "StatusBar/NavBarView"; + @Retention(RetentionPolicy.SOURCE) + @IntDef({WINDOW_TARGET_BOTTOM, WINDOW_TARGET_LEFT, WINDOW_TARGET_RIGHT}) + public @interface WindowTarget{} + public static final int WINDOW_TARGET_BOTTOM = 0; + public static final int WINDOW_TARGET_LEFT = 1; + public static final int WINDOW_TARGET_RIGHT = 2; + // slippery nav bar when everything is disabled, e.g. during setup final static boolean SLIPPERY_WHEN_DISABLED = true; @@ -109,6 +123,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav int mNavigationIconHints = 0; private @NavigationBarCompat.HitTarget int mDownHitTarget = HIT_TARGET_NONE; + private @WindowTarget int mWindowHitTarget = WINDOW_TARGET_BOTTOM; private Rect mHomeButtonBounds = new Rect(); private Rect mBackButtonBounds = new Rect(); private Rect mRecentsButtonBounds = new Rect(); @@ -160,6 +175,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private NavigationAssistantAction mAssistantAction; private NavigationNotificationPanelAction mNotificationPanelAction; + private NavigationBarEdgePanel mLeftEdgePanel; + private NavigationBarEdgePanel mRightEdgePanel; + /** * Helper that is responsible for showing the right toast when a disallowed activity operation * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in @@ -222,6 +240,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } }; + private final OnTouchListener mEdgePanelTouchListener = new OnTouchListener() { + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == ACTION_DOWN) { + mWindowHitTarget = v == mLeftEdgePanel ? WINDOW_TARGET_LEFT : WINDOW_TARGET_RIGHT; + mDownHitTarget = HIT_TARGET_NONE; + } + return mGestureHelper.onTouchEvent(event); + } + }; + private class H extends Handler { public void handleMessage(Message m) { switch (m.what) { @@ -297,6 +327,16 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mColorAdaptionController.end(); } } + + @Override + public void onEdgeSensitivityChanged(int width, int height) { + if (mLeftEdgePanel != null) { + mLeftEdgePanel.setDimensions(width, height); + } + if (mRightEdgePanel != null) { + mRightEdgePanel.setDimensions(width, height); + } + } }; public NavigationBarView(Context context, AttributeSet attrs) { @@ -433,6 +473,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav int x = (int) event.getX(); int y = (int) event.getY(); mDownHitTarget = HIT_TARGET_NONE; + mWindowHitTarget = WINDOW_TARGET_BOTTOM; if (deadZoneConsumed) { mDownHitTarget = HIT_TARGET_DEAD_ZONE; } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) { @@ -483,6 +524,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return mDownHitTarget; } + public @WindowTarget int getWindowTarget() { + return mWindowHitTarget; + } + public void abortCurrentGesture() { getHomeButton().abortCurrentGesture(); } @@ -837,24 +882,32 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } private void setSlippery(boolean slippery) { - boolean changed = false; + setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery); + } + + public void setWindowTouchable(boolean flag) { + setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag); + if (mLeftEdgePanel != null) { + mLeftEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag); + } + if (mRightEdgePanel != null) { + mRightEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag); + } + } + + private void setWindowFlag(int flags, boolean enable) { final ViewGroup navbarView = ((ViewGroup) getParent()); - final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView - .getLayoutParams(); - if (lp == null) { + WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams(); + if (lp == null || enable == ((lp.flags & flags) != 0)) { return; } - if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) { - lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; - changed = true; - } else if (!slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0) { - lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY; - changed = true; - } - if (changed) { - WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); - wm.updateViewLayout(navbarView, lp); + if (enable) { + lp.flags |= flags; + } else { + lp.flags &= ~flags; } + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + wm.updateViewLayout(navbarView, lp); } public void setMenuVisibility(final boolean show) { @@ -1016,6 +1069,17 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } catch (RemoteException e) { Slog.e(TAG, "Failed to get nav bar position.", e); } + + // For landscape, hide the panel that would interfere with navigation bar layout + if (mLeftEdgePanel != null && mRightEdgePanel != null) { + mLeftEdgePanel.setVisibility(VISIBLE); + mRightEdgePanel.setVisibility(VISIBLE); + if (navBarPos == NAV_BAR_LEFT) { + mLeftEdgePanel.setVisibility(GONE); + } else if (navBarPos == NAV_BAR_RIGHT) { + mRightEdgePanel.setVisibility(GONE); + } + } mGestureHelper.setBarState(isRtl, navBarPos); } @@ -1142,6 +1206,21 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav NavGesture.class, false /* Only one */); setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); mColorAdaptionController.start(); + + if (mPrototypeController.isEnabled()) { + WindowManager wm = (WindowManager) getContext() + .getSystemService(Context.WINDOW_SERVICE); + int width = mPrototypeController.getEdgeSensitivityWidth(); + int height = mPrototypeController.getEdgeSensitivityHeight(); + mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height, + Gravity.START | Gravity.BOTTOM); + mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height, + Gravity.END | Gravity.BOTTOM); + mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener); + mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener); + wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams()); + wm.addView(mRightEdgePanel, mRightEdgePanel.getLayoutParams()); + } } @Override @@ -1157,6 +1236,17 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); } + + if (mPrototypeController.isEnabled()) { + WindowManager wm = (WindowManager) getContext() + .getSystemService(Context.WINDOW_SERVICE); + if (mLeftEdgePanel != null) { + wm.removeView(mLeftEdgePanel); + } + if (mRightEdgePanel != null) { + wm.removeView(mRightEdgePanel); + } + } } private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java index b4feb25fba7f..8421e23e97b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import android.annotation.IntDef; import android.content.Context; +import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; @@ -34,7 +35,12 @@ import java.lang.annotation.RetentionPolicy; public class NavigationPrototypeController extends ContentObserver { private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback"; private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome"; + private static final String PROTOTYPE_ENABLED = "prototype_enabled"; + private static final String EDGE_SENSITIVITY_HEIGHT_SETTING = + "quickstepcontroller_edge_height_sensitivity"; + public static final String EDGE_SENSITIVITY_WIDTH_SETTING = + "quickstepcontroller_edge_width_sensitivity"; private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map"; public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable"; @@ -79,6 +85,8 @@ public class NavigationPrototypeController extends ContentObserver { registerObserver(HIDE_HOME_BUTTON_SETTING); registerObserver(GESTURE_MATCH_SETTING); registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING); + registerObserver(EDGE_SENSITIVITY_WIDTH_SETTING); + registerObserver(EDGE_SENSITIVITY_HEIGHT_SETTING); } /** @@ -106,10 +114,26 @@ public class NavigationPrototypeController extends ContentObserver { } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) { mListener.onColorAdaptChanged( NavBarTintController.isEnabled(mContext)); + } else if (path.endsWith(EDGE_SENSITIVITY_WIDTH_SETTING) + || path.endsWith(EDGE_SENSITIVITY_HEIGHT_SETTING)) { + mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(), + getEdgeSensitivityHeight()); } } } + public int getEdgeSensitivityWidth() { + return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_WIDTH_SETTING, 0)); + } + + public int getEdgeSensitivityHeight() { + return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_HEIGHT_SETTING, 0)); + } + + public boolean isEnabled() { + return getGlobalBool(PROTOTYPE_ENABLED, false); + } + /** * Retrieve the action map to apply to the quick step controller * @return an action map @@ -144,15 +168,24 @@ public class NavigationPrototypeController extends ContentObserver { return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal ? 1 : 0) == 1; } + private int getGlobalInt(String name, int defaultVal) { + return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal); + } + private void registerObserver(String name) { mContext.getContentResolver() .registerContentObserver(Settings.Global.getUriFor(name), false, this); } + private static int convertDpToPixel(float dp) { + return (int) (dp * Resources.getSystem().getDisplayMetrics().density); + } + public interface OnPrototypeChangedListener { void onGestureRemap(@GestureAction int[] actions); void onBackButtonVisibilityChanged(boolean visible); void onHomeButtonVisibilityChanged(boolean visible); void onColorAdaptChanged(boolean enabled); + void onEdgeSensitivityChanged(int width, int height); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java index d5d283c46b0a..84f1cef19b77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -28,9 +28,12 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; +import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM; +import static com.android.systemui.statusbar.phone.NavigationPrototypeController.EDGE_SENSITIVITY_WIDTH_SETTING; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; @@ -42,12 +45,9 @@ import android.util.Log; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; +import android.view.ViewConfiguration; import android.view.ViewPropertyAnimator; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; - import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -72,6 +72,7 @@ public class QuickStepController implements GestureHelper { /** Experiment to swipe home button left to execute a back key press */ private static final String HIDE_BACK_BUTTON_PROP = "quickstepcontroller_hideback"; private static final String ENABLE_CLICK_THROUGH_NAV_PROP = "quickstepcontroller_clickthrough"; + private static final String GESTURE_REGION_THRESHOLD_SETTING = "gesture_region_threshold"; private static final long BACK_BUTTON_FADE_IN_ALPHA = 150; private static final long CLICK_THROUGH_TAP_DELAY = 70; private static final long CLICK_THROUGH_TAP_RESET_DELAY = 100; @@ -109,10 +110,10 @@ public class QuickStepController implements GestureHelper { private float mMaxDragLimit; private float mMinDragLimit; private float mDragDampeningFactor; - private float mEdgeSwipeThreshold; private boolean mClickThroughPressed; private float mClickThroughPressX; private float mClickThroughPressY; + private int mGestureRegionThreshold; private NavigationGestureAction mCurrentAction; private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES]; @@ -139,7 +140,7 @@ public class QuickStepController implements GestureHelper { }; private final Runnable mClickThroughResetTap = () -> { - setWindowTouchable(true); + mNavigationBarView.setWindowTouchable(true); mClickThroughPressed = false; }; @@ -210,7 +211,8 @@ public class QuickStepController implements GestureHelper { // The same down event was just sent on intercept and therefore can be ignored here final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN - && mOverviewEventSender.getProxy() != null; + && mOverviewEventSender.getProxy() != null + && mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM; return ignoreProxyDownEvent || handleTouchEvent(event); } @@ -268,12 +270,15 @@ public class QuickStepController implements GestureHelper { mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix); mAllowGestureDetection = true; mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed(); - mEdgeSwipeThreshold = mContext.getResources() - .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold); + final int defaultRegionThreshold = mContext.getResources() + .getDimensionPixelOffset(R.dimen.navigation_bar_default_edge_width); + mGestureRegionThreshold = convertDpToPixel(getIntGlobalSetting(mContext, + EDGE_SENSITIVITY_WIDTH_SETTING, defaultRegionThreshold)); break; } case MotionEvent.ACTION_MOVE: { - if (!mAllowGestureDetection) { + if (!mAllowGestureDetection + || mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) { break; } int x = (int) event.getX(); @@ -330,18 +335,12 @@ public class QuickStepController implements GestureHelper { } else if (exceededSwipeHorizontalTouchSlop) { if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) { // Swiping left (rtl) gesture - int index = mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] != null - && isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive) - ? ACTION_SWIPE_LEFT_FROM_EDGE_INDEX : ACTION_SWIPE_LEFT_INDEX; - tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */, - event); + tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX], + true /* alignedWithNavBar */, event); } else { // Swiping right (ltr) gesture - int index = mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] != null - && isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive) - ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX : ACTION_SWIPE_RIGHT_INDEX; - tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */, - event); + tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX], + true /* alignedWithNavBar */, event); } } } @@ -354,24 +353,34 @@ public class QuickStepController implements GestureHelper { case MotionEvent.ACTION_UP: if (mCurrentAction != null) { mCurrentAction.endGesture(); - } else if (action == MotionEvent.ACTION_UP - && getBoolGlobalSetting(mContext, ENABLE_CLICK_THROUGH_NAV_PROP) - && !mClickThroughPressed) { - // Enable click through functionality where no gesture has been detected and not - // passed the drag slop so inject a touch event at the same location - // after making the navigation bar window untouchable. After a some time, the - // navigation bar will be able to take input events again - float diffX = Math.abs(event.getX() - mTouchDownX); - float diffY = Math.abs(event.getY() - mTouchDownY); - - if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx() - && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) { - setWindowTouchable(false); - mClickThroughPressX = event.getRawX(); - mClickThroughPressY = event.getRawY(); - mClickThroughPressed = true; - mNavigationBarView.postDelayed(mClickThroughSendTap, - CLICK_THROUGH_TAP_DELAY); + } else if (action == MotionEvent.ACTION_UP) { + if (canTriggerEdgeSwipe(event)) { + int index = mNavigationBarView.getWindowTarget() == NAV_BAR_LEFT + ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX + : ACTION_SWIPE_LEFT_FROM_EDGE_INDEX; + tryToStartGesture(mGestureActions[index], false /* alignedWithNavBar */, + event); + if (mCurrentAction != null) { + mCurrentAction.endGesture(); + } + } else if (getBoolGlobalSetting(mContext, ENABLE_CLICK_THROUGH_NAV_PROP) + && !mClickThroughPressed) { + // Enable click through functionality where no gesture has been detected and + // not passed the drag slop so inject a touch event at the same location + // after making the navigation bar window untouchable. After a some time, + // the navigation bar will be able to take input events again + float diffX = Math.abs(event.getX() - mTouchDownX); + float diffY = Math.abs(event.getY() - mTouchDownY); + + if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx() + && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) { + mNavigationBarView.setWindowTouchable(false); + mClickThroughPressX = event.getRawX(); + mClickThroughPressY = event.getRawY(); + mClickThroughPressed = true; + mNavigationBarView.postDelayed(mClickThroughSendTap, + CLICK_THROUGH_TAP_DELAY); + } } } @@ -403,30 +412,6 @@ public class QuickStepController implements GestureHelper { return mCurrentAction != null || deadZoneConsumed; } - private void setWindowTouchable(boolean flag) { - final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) - ((ViewGroup) mNavigationBarView.getParent()).getLayoutParams(); - if (flag) { - lp.flags &= ~LayoutParams.FLAG_NOT_TOUCHABLE; - } else { - lp.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; - } - final WindowManager wm = (WindowManager) mNavigationBarView.getContext() - .getSystemService(Context.WINDOW_SERVICE); - wm.updateViewLayout((View) mNavigationBarView.getParent(), lp); - } - - private boolean isEdgeSwipeAlongNavBar(int touchDown, boolean dragPositiveDirection) { - // Detect edge swipe from side of 0 -> threshold - if (dragPositiveDirection) { - return touchDown < mEdgeSwipeThreshold; - } - // Detect edge swipe from side of size -> (size - threshold) - final int largeSide = isNavBarVertical() - ? mNavigationBarView.getHeight() : mNavigationBarView.getWidth(); - return touchDown > largeSide - mEdgeSwipeThreshold; - } - private void handleDragHitTarget(int position, int touchDown) { // Drag the hit target if gesture action requires it if (mHitTarget != null && (mGestureVerticalDragsButton || mGestureHorizontalDragsButton)) { @@ -448,6 +433,10 @@ public class QuickStepController implements GestureHelper { } private boolean shouldProxyEvents(int action) { + // Do not send events for side navigation bar panels + if (mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) { + return false; + } final boolean actionValid = (mCurrentAction == null || !mCurrentAction.disableProxyEvents()); if (actionValid && !mIsInScreenPinning) { @@ -619,6 +608,32 @@ public class QuickStepController implements GestureHelper { } } + /** + * To trigger an edge swipe, the user must start from the left or right edges of certain height + * from the bottom then past the drag slope towards the center of the screen, followed by either + * a timed trigger for fast swipes or distance if held on the screen longer. + * For time, user must swipe up quickly before the Tap Timeout (typically 100ms) and for + * distance, the user can drag back to cancel if the touch up has not past the threshold. + * @param event Touch up event + * @return whether or not edge swipe gesture occurs + */ + private boolean canTriggerEdgeSwipe(MotionEvent event) { + if (mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM) { + return false; + } + int x = (int) event.getX(); + int y = (int) event.getY(); + int xDiff = Math.abs(x - mTouchDownX); + int yDiff = Math.abs(y - mTouchDownY); + final boolean exceededSwipeTouchSlop = xDiff > NavigationBarCompat.getQuickStepDragSlopPx() + && xDiff > yDiff; + if (exceededSwipeTouchSlop) { + long timeDiff = event.getEventTime() - event.getDownTime(); + return xDiff > mGestureRegionThreshold || timeDiff < ViewConfiguration.getTapTimeout(); + } + return false; + } + private boolean canPerformAnyAction() { for (NavigationGestureAction action: mGestureActions) { if (action != null && action.isEnabled()) { @@ -684,10 +699,18 @@ public class QuickStepController implements GestureHelper { return mNavBarPosition == NAV_BAR_LEFT || mNavBarPosition == NAV_BAR_RIGHT; } + private static int convertDpToPixel(float dp) { + return (int) (dp * Resources.getSystem().getDisplayMetrics().density); + } + static boolean getBoolGlobalSetting(Context context, String key) { return Settings.Global.getInt(context.getContentResolver(), key, 0) != 0; } + static int getIntGlobalSetting(Context context, String key, int defaultValue) { + return Settings.Global.getInt(context.getContentResolver(), key, defaultValue); + } + public static boolean shouldhideBackButton(Context context) { return getBoolGlobalSetting(context, HIDE_BACK_BUTTON_PROP); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java index 382dde9ce043..dbf00a379c5f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java @@ -23,6 +23,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; +import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -63,7 +64,6 @@ import org.mockito.MockitoAnnotations; public class QuickStepControllerTest extends SysuiTestCase { private static final int NAVBAR_WIDTH = 1000; private static final int NAVBAR_HEIGHT = 300; - private static final int EDGE_THRESHOLD = 100; private QuickStepController mController; private NavigationBarView mNavigationBarView; @@ -77,8 +77,6 @@ public class QuickStepControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); final ButtonDispatcher backButton = mock(ButtonDispatcher.class); mResources = mock(Resources.class); - doReturn(EDGE_THRESHOLD).when(mResources) - .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold); mProxyService = mock(OverviewProxyService.class); mProxy = mock(IOverviewProxy.Stub.class); @@ -95,6 +93,7 @@ public class QuickStepControllerTest extends SysuiTestCase { doReturn(true).when(mNavigationBarView).isNotificationsFullyCollapsed(); doReturn(true).when(mNavigationBarView).isQuickScrubEnabled(); doReturn(HIT_TARGET_NONE).when(mNavigationBarView).getDownHitTarget(); + doReturn(WINDOW_TARGET_BOTTOM).when(mNavigationBarView).getWindowTarget(); doReturn(backButton).when(mNavigationBarView).getBackButton(); doReturn(mResources).when(mNavigationBarView).getResources(); doReturn(mContext).when(mNavigationBarView).getContext(); @@ -196,10 +195,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeUp, 1, 100, 5, 1); @@ -209,10 +206,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5); - // Swipe Left from Edge - assertGestureTriggersAction(swipeLeftFromEdge, NAVBAR_WIDTH, 1, 5, 1); - // Swipe Right from Edge - assertGestureTriggersAction(swipeRightFromEdge, 0, 1, NAVBAR_WIDTH, 5); } @Test @@ -224,10 +217,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // In landscape mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT); @@ -240,10 +231,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeUp, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeDown, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -256,10 +243,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1); @@ -269,10 +254,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeDown, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeUp, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -286,10 +267,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up in RTL assertGestureTriggersAction(swipeUp, 1, 100, 5, 1); @@ -299,10 +278,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1); // Swipe Right in RTL assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0); - // Swipe Left from Edge - assertGestureTriggersAction(swipeRightFromEdge, NAVBAR_WIDTH, 1, 5, 1); - // Swipe Right from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, NAVBAR_WIDTH, 5); } @Test @@ -316,10 +291,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1); @@ -329,10 +302,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeUp, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeDown, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -346,10 +315,8 @@ public class QuickStepControllerTest extends SysuiTestCase { NavigationGestureAction swipeDown = mockAction(true); NavigationGestureAction swipeLeft = mockAction(true); NavigationGestureAction swipeRight = mockAction(true); - NavigationGestureAction swipeLeftFromEdge = mockAction(true); - NavigationGestureAction swipeRightFromEdge = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge, - swipeRightFromEdge); + mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, + null /* leftEdgeSwipe */, null /* rightEdgeSwipe */); // Swipe Up assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1); @@ -359,10 +326,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureTriggersAction(swipeDown, 100, 1, 5, 1); // Swipe Right assertGestureTriggersAction(swipeUp, 1, 1, 100, 5); - // Swipe Up from Edge - assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0); - // Swipe Down from Edge - assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH); } @Test @@ -602,25 +565,6 @@ public class QuickStepControllerTest extends SysuiTestCase { assertGestureDragsHitTargetAllDirections(buttonView, true /* isRTL */, NAV_BAR_LEFT); } - @Test - public void testNoEdgeActionsTriggerNormalActions() { - doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth(); - doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight(); - - NavigationGestureAction swipeUp = mockAction(true); - NavigationGestureAction swipeDown = mockAction(true); - NavigationGestureAction swipeLeft = mockAction(true); - NavigationGestureAction swipeRight = mockAction(true); - mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, - null /* swipeLeftFromEdgeAction */, - null /* swipeLeftFromEdgeAction */); - - // Swipe Left from Edge - assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH, 1, 5, 1); - // Swipe Right from Edge - assertGestureTriggersAction(swipeRight, 0, 1, NAVBAR_WIDTH, 5); - } - private void assertGestureDragsHitTargetAllDirections(View buttonView, boolean isRTL, int navPos) { mController.setBarState(isRTL, navPos); |