Merge "Implement screen edge swipe for prototype"

This commit is contained in:
TreeHugger Robot
2019-02-04 22:44:22 +00:00
committed by Android (Google) Code Review
7 changed files with 319 additions and 147 deletions

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

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

View File

@@ -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);
} 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())) {
setWindowTouchable(false);
mClickThroughPressX = event.getRawX();
mClickThroughPressY = event.getRawY();
mClickThroughPressed = true;
mNavigationBarView.postDelayed(mClickThroughSendTap,
CLICK_THROUGH_TAP_DELAY);
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);
}

View File

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