diff options
| author | 2019-02-04 22:44:22 +0000 | |
|---|---|---|
| committer | 2019-02-04 22:44:22 +0000 | |
| commit | c5f8397f0fce25f88632ad83b1fea98e66e2e43b (patch) | |
| tree | 43ef302ef153274445fee201a7cfea4b05c06edf | |
| parent | b031e8e7cb36326a1ba32b988de69fafd596c959 (diff) | |
| parent | b831fb4fd1446782bb88e7b43f4885d0de7a17ca (diff) | |
Merge "Implement screen edge swipe for prototype"
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); |