Adds NavigationEdgeBackPlugin to iterate on different Back implementations.
NavigationBarEdgePanel now implements the new plugin. As part of that, some of the UI-specific logic from EdgeBackGestureHandler has been moved to NavigationBarEdgePanel (e.g. Dark region detector and logic around the arrow position). Test: Built and manually tested existing back behavior. Fixes: 143907351 Change-Id: I0e01581d6b69e6a8a03a4338be83ed5ff8b2da65
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.plugins;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
||||
/** Plugin to handle navigation edge gestures for Back. */
|
||||
@ProvidesInterface(
|
||||
action = NavigationEdgeBackPlugin.ACTION,
|
||||
version = NavigationEdgeBackPlugin.VERSION)
|
||||
public interface NavigationEdgeBackPlugin extends Plugin {
|
||||
String ACTION = "com.android.systemui.action.PLUGIN_NAVIGATION_EDGE_BACK_ACTION";
|
||||
int VERSION = 1;
|
||||
|
||||
|
||||
/** Specifies if the UI should be rendered on the left side of the screen. */
|
||||
void setIsLeftPanel(boolean isLeftPanel);
|
||||
|
||||
/** Sets the insets for the gesture handling area. */
|
||||
void setInsets(int leftInset, int rightInset);
|
||||
|
||||
/** Sets the display size. */
|
||||
void setDisplaySize(Point displaySize);
|
||||
|
||||
/** Sets the callback that should be invoked when a Back gesture is detected. */
|
||||
void setBackCallback(BackCallback callback);
|
||||
|
||||
/** Sets the base LayoutParams for the UI. */
|
||||
void setLayoutParams(WindowManager.LayoutParams layoutParams);
|
||||
|
||||
/** Updates the UI based on the motion events passed in device coordinates. */
|
||||
void onMotionEvent(MotionEvent motionEvent);
|
||||
|
||||
/** Callback to let the system react to the detected back gestures. */
|
||||
interface BackCallback {
|
||||
/** Indicates that a Back gesture was recognized and the system should go back. */
|
||||
void triggerBack();
|
||||
|
||||
/** Indicates that the gesture was cancelled and the system should not go back. */
|
||||
void cancelBack();
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
@@ -32,9 +31,7 @@ import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.StatsLog;
|
||||
import android.view.Gravity;
|
||||
import android.view.ISystemGestureExclusionListener;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputDevice;
|
||||
@@ -44,7 +41,6 @@ import android.view.InputMonitor;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
@@ -53,7 +49,10 @@ import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.bubbles.BubbleController;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.recents.OverviewProxyService;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -62,7 +61,8 @@ import java.util.concurrent.Executor;
|
||||
/**
|
||||
* Utility class to handle edge swipes for back gesture
|
||||
*/
|
||||
public class EdgeBackGestureHandler implements DisplayListener {
|
||||
public class EdgeBackGestureHandler implements DisplayListener,
|
||||
PluginListener<NavigationEdgeBackPlugin> {
|
||||
|
||||
private static final String TAG = "EdgeBackGestureHandler";
|
||||
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
|
||||
@@ -85,6 +85,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
|
||||
private final Context mContext;
|
||||
private final OverviewProxyService mOverviewProxyService;
|
||||
private PluginManager mPluginManager;
|
||||
|
||||
private final Point mDisplaySize = new Point();
|
||||
private final int mDisplayId;
|
||||
@@ -100,14 +101,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
private final float mTouchSlop;
|
||||
// Duration after which we consider the event as longpress.
|
||||
private final int mLongPressTimeout;
|
||||
// The threshold where the touch needs to be at most, such that the arrow is displayed above the
|
||||
// finger, otherwise it will be below
|
||||
private final int mMinArrowPosition;
|
||||
// The amount by which the arrow is shifted to avoid the finger
|
||||
private final int mFingerOffset;
|
||||
|
||||
|
||||
private final int mNavBarHeight;
|
||||
|
||||
private final PointF mDownPoint = new PointF();
|
||||
private boolean mThresholdCrossed = false;
|
||||
@@ -123,24 +116,49 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
private InputMonitor mInputMonitor;
|
||||
private InputEventReceiver mInputEventReceiver;
|
||||
|
||||
private final WindowManager mWm;
|
||||
|
||||
private NavigationBarEdgePanel mEdgePanel;
|
||||
private WindowManager.LayoutParams mEdgePanelLp;
|
||||
private final Rect mSamplingRect = new Rect();
|
||||
private RegionSamplingHelper mRegionSamplingHelper;
|
||||
private NavigationEdgeBackPlugin mEdgeBackPlugin;
|
||||
private int mLeftInset;
|
||||
private int mRightInset;
|
||||
private int mSysUiFlags;
|
||||
|
||||
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
|
||||
new NavigationEdgeBackPlugin.BackCallback() {
|
||||
@Override
|
||||
public void triggerBack() {
|
||||
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
|
||||
sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
|
||||
|
||||
mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
|
||||
(int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
|
||||
int backtype = (mInRejectedExclusion
|
||||
? StatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED :
|
||||
StatsLog.BACK_GESTURE__TYPE__COMPLETED);
|
||||
StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
|
||||
(int) mDownPoint.y, mIsOnLeftEdge
|
||||
? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
|
||||
StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelBack() {
|
||||
mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x,
|
||||
(int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
|
||||
int backtype = StatsLog.BACK_GESTURE__TYPE__INCOMPLETE;
|
||||
StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
|
||||
(int) mDownPoint.y, mIsOnLeftEdge
|
||||
? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
|
||||
StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
|
||||
}
|
||||
};
|
||||
|
||||
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
|
||||
SysUiState sysUiFlagContainer) {
|
||||
SysUiState sysUiFlagContainer, PluginManager pluginManager) {
|
||||
final Resources res = context.getResources();
|
||||
mContext = context;
|
||||
mDisplayId = context.getDisplayId();
|
||||
mMainExecutor = context.getMainExecutor();
|
||||
mWm = context.getSystemService(WindowManager.class);
|
||||
mOverviewProxyService = overviewProxyService;
|
||||
mPluginManager = pluginManager;
|
||||
|
||||
// Reduce the default touch slop to ensure that we can intercept the gesture
|
||||
// before the app starts to react to it.
|
||||
@@ -149,9 +167,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
|
||||
ViewConfiguration.getLongPressTimeout());
|
||||
|
||||
mNavBarHeight = res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height);
|
||||
mMinArrowPosition = res.getDimensionPixelSize(R.dimen.navigation_edge_arrow_min_y);
|
||||
mFingerOffset = res.getDimensionPixelSize(R.dimen.navigation_edge_finger_offset);
|
||||
updateCurrentUserResources(res);
|
||||
sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
|
||||
}
|
||||
@@ -206,15 +221,14 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
mIsEnabled = isEnabled;
|
||||
disposeInputChannel();
|
||||
|
||||
if (mEdgePanel != null) {
|
||||
mWm.removeView(mEdgePanel);
|
||||
mEdgePanel = null;
|
||||
mRegionSamplingHelper.stop();
|
||||
mRegionSamplingHelper = null;
|
||||
if (mEdgeBackPlugin != null) {
|
||||
mEdgeBackPlugin.onDestroy();
|
||||
mEdgeBackPlugin = null;
|
||||
}
|
||||
|
||||
if (!mIsEnabled) {
|
||||
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
|
||||
mPluginManager.removePluginListener(this);
|
||||
|
||||
try {
|
||||
WindowManagerGlobal.getWindowManagerService()
|
||||
@@ -244,41 +258,51 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
mInputMonitor.getInputChannel(), Looper.getMainLooper());
|
||||
|
||||
// Add a nav bar panel window
|
||||
mEdgePanel = new NavigationBarEdgePanel(mContext);
|
||||
mEdgePanelLp = new WindowManager.LayoutParams(
|
||||
mContext.getResources()
|
||||
.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
|
||||
mContext.getResources()
|
||||
.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
mEdgePanelLp.privateFlags |=
|
||||
WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
mEdgePanelLp.setTitle(TAG + mDisplayId);
|
||||
mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
|
||||
mEdgePanelLp.windowAnimations = 0;
|
||||
mEdgePanel.setLayoutParams(mEdgePanelLp);
|
||||
mWm.addView(mEdgePanel, mEdgePanelLp);
|
||||
mRegionSamplingHelper = new RegionSamplingHelper(mEdgePanel,
|
||||
new RegionSamplingHelper.SamplingCallback() {
|
||||
@Override
|
||||
public void onRegionDarknessChanged(boolean isRegionDark) {
|
||||
mEdgePanel.setIsDark(!isRegionDark, true /* animate */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getSampledRegion(View sampledView) {
|
||||
return mSamplingRect;
|
||||
}
|
||||
});
|
||||
mRegionSamplingHelper.setWindowVisible(true);
|
||||
setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
|
||||
mPluginManager.addPluginListener(
|
||||
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) {
|
||||
setEdgeBackPlugin(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
|
||||
setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
|
||||
}
|
||||
|
||||
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
|
||||
if (mEdgeBackPlugin != null) {
|
||||
mEdgeBackPlugin.onDestroy();
|
||||
}
|
||||
mEdgeBackPlugin = edgeBackPlugin;
|
||||
mEdgeBackPlugin.setBackCallback(mBackCallback);
|
||||
mEdgeBackPlugin.setLayoutParams(createLayoutParams());
|
||||
updateDisplaySize();
|
||||
}
|
||||
|
||||
private WindowManager.LayoutParams createLayoutParams() {
|
||||
Resources resources = mContext.getResources();
|
||||
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
|
||||
resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
|
||||
resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
layoutParams.privateFlags |=
|
||||
WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
layoutParams.setTitle(TAG + mContext.getDisplayId());
|
||||
layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
|
||||
layoutParams.windowAnimations = 0;
|
||||
return layoutParams;
|
||||
}
|
||||
|
||||
private void onInputEvent(InputEvent ev) {
|
||||
if (ev instanceof MotionEvent) {
|
||||
onMotionEvent((MotionEvent) ev);
|
||||
@@ -316,7 +340,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
mInRejectedExclusion = false;
|
||||
MotionEvent cancelEv = MotionEvent.obtain(ev);
|
||||
cancelEv.setAction(MotionEvent.ACTION_CANCEL);
|
||||
mEdgePanel.handleTouch(cancelEv);
|
||||
mEdgeBackPlugin.onMotionEvent(cancelEv);
|
||||
cancelEv.recycle();
|
||||
}
|
||||
|
||||
@@ -330,14 +354,8 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
|
||||
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
|
||||
if (mAllowGesture) {
|
||||
mEdgePanelLp.gravity = mIsOnLeftEdge
|
||||
? (Gravity.LEFT | Gravity.TOP)
|
||||
: (Gravity.RIGHT | Gravity.TOP);
|
||||
mEdgePanel.setIsLeftPanel(mIsOnLeftEdge);
|
||||
mEdgePanel.handleTouch(ev);
|
||||
updateEdgePanelPosition(ev.getY());
|
||||
mWm.updateViewLayout(mEdgePanel, mEdgePanelLp);
|
||||
mRegionSamplingHelper.start(mSamplingRect);
|
||||
mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
|
||||
mEdgeBackPlugin.onMotionEvent(ev);
|
||||
|
||||
mDownPoint.set(ev.getX(), ev.getY());
|
||||
mThresholdCrossed = false;
|
||||
@@ -369,53 +387,10 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
}
|
||||
|
||||
// forward touch
|
||||
mEdgePanel.handleTouch(ev);
|
||||
|
||||
boolean isUp = action == MotionEvent.ACTION_UP;
|
||||
if (isUp) {
|
||||
boolean performAction = mEdgePanel.shouldTriggerBack();
|
||||
if (performAction) {
|
||||
// Perform back
|
||||
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
|
||||
sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
|
||||
}
|
||||
mOverviewProxyService.notifyBackAction(performAction, (int) mDownPoint.x,
|
||||
(int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
|
||||
int backtype = performAction ? (mInRejectedExclusion
|
||||
? StatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED :
|
||||
StatsLog.BACK_GESTURE__TYPE__COMPLETED) :
|
||||
StatsLog.BACK_GESTURE__TYPE__INCOMPLETE;
|
||||
StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
|
||||
(int) mDownPoint.y, mIsOnLeftEdge
|
||||
? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
|
||||
StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
|
||||
}
|
||||
if (isUp || action == MotionEvent.ACTION_CANCEL) {
|
||||
mRegionSamplingHelper.stop();
|
||||
} else {
|
||||
updateSamplingRect();
|
||||
mRegionSamplingHelper.updateSamplingRect();
|
||||
}
|
||||
mEdgeBackPlugin.onMotionEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateEdgePanelPosition(float touchY) {
|
||||
float position = touchY - mFingerOffset;
|
||||
position = Math.max(position, mMinArrowPosition);
|
||||
position = (position - mEdgePanelLp.height / 2.0f);
|
||||
mEdgePanelLp.y = MathUtils.constrain((int) position, 0, mDisplaySize.y);
|
||||
updateSamplingRect();
|
||||
}
|
||||
|
||||
private void updateSamplingRect() {
|
||||
int top = mEdgePanelLp.y;
|
||||
int left = mIsOnLeftEdge ? mLeftInset : mDisplaySize.x - mRightInset - mEdgePanelLp.width;
|
||||
int right = left + mEdgePanelLp.width;
|
||||
int bottom = top + mEdgePanelLp.height;
|
||||
mSamplingRect.set(left, top, right, bottom);
|
||||
mEdgePanel.adjustRectToBoundingBox(mSamplingRect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) { }
|
||||
|
||||
@@ -433,6 +408,9 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
mContext.getSystemService(DisplayManager.class)
|
||||
.getDisplay(mDisplayId)
|
||||
.getRealSize(mDisplaySize);
|
||||
if (mEdgeBackPlugin != null) {
|
||||
mEdgeBackPlugin.setDisplaySize(mDisplaySize);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendEvent(int action, int code) {
|
||||
@@ -454,6 +432,9 @@ public class EdgeBackGestureHandler implements DisplayListener {
|
||||
public void setInsets(int leftInset, int rightInset) {
|
||||
mLeftInset = leftInset;
|
||||
mRightInset = rightInset;
|
||||
if (mEdgeBackPlugin != null) {
|
||||
mEdgeBackPlugin.setInsets(leftInset, rightInset);
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
|
||||
@@ -19,34 +19,40 @@ package com.android.systemui.statusbar.phone;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.os.VibrationEffect;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.MathUtils;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.VibratorHelper;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
public class NavigationBarEdgePanel extends View {
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
|
||||
import com.android.systemui.statusbar.VibratorHelper;
|
||||
|
||||
public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPlugin {
|
||||
|
||||
private static final String TAG = "NavigationBarEdgePanel";
|
||||
|
||||
private static final long COLOR_ANIMATION_DURATION_MS = 120;
|
||||
private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80;
|
||||
@@ -114,6 +120,7 @@ public class NavigationBarEdgePanel extends View {
|
||||
private static final Interpolator RUBBER_BAND_INTERPOLATOR_APPEAR
|
||||
= new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
private final WindowManager mWindowManager;
|
||||
private final VibratorHelper mVibratorHelper;
|
||||
|
||||
/**
|
||||
@@ -134,9 +141,14 @@ public class NavigationBarEdgePanel extends View {
|
||||
* The minimum delta needed in movement for the arrow to change direction / stop triggering back
|
||||
*/
|
||||
private final float mMinDeltaForSwitch;
|
||||
// The closest to y = 0 that the arrow will be displayed.
|
||||
private int mMinArrowPosition;
|
||||
// The amount the arrow is shifted to avoid the finger.
|
||||
private int mFingerOffset;
|
||||
|
||||
private final float mSwipeThreshold;
|
||||
private final Path mArrowPath = new Path();
|
||||
private final Point mDisplaySize = new Point();
|
||||
|
||||
private final SpringAnimation mAngleAnimation;
|
||||
private final SpringAnimation mTranslationAnimation;
|
||||
@@ -158,6 +170,11 @@ public class NavigationBarEdgePanel extends View {
|
||||
private int mArrowColorDark;
|
||||
private int mProtectionColor;
|
||||
private int mArrowColor;
|
||||
private RegionSamplingHelper mRegionSamplingHelper;
|
||||
private final Rect mSamplingRect = new Rect();
|
||||
private WindowManager.LayoutParams mLayoutParams;
|
||||
private int mLeftInset;
|
||||
private int mRightInset;
|
||||
|
||||
/**
|
||||
* True if the panel is currently on the left of the screen
|
||||
@@ -242,10 +259,12 @@ public class NavigationBarEdgePanel extends View {
|
||||
return object.getVerticalTranslation();
|
||||
}
|
||||
};
|
||||
private BackCallback mBackCallback;
|
||||
|
||||
public NavigationBarEdgePanel(Context context) {
|
||||
super(context);
|
||||
|
||||
mWindowManager = context.getSystemService(WindowManager.class);
|
||||
mVibratorHelper = Dependency.get(VibratorHelper.class);
|
||||
|
||||
mDensity = context.getResources().getDisplayMetrics().density;
|
||||
@@ -263,13 +282,10 @@ public class NavigationBarEdgePanel extends View {
|
||||
|
||||
mArrowColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
|
||||
mArrowColorAnimator.setDuration(COLOR_ANIMATION_DURATION_MS);
|
||||
mArrowColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
int newColor = ColorUtils.blendARGB(mArrowStartColor, mArrowColor,
|
||||
animation.getAnimatedFraction());
|
||||
setCurrentArrowColor(newColor);
|
||||
}
|
||||
mArrowColorAnimator.addUpdateListener(animation -> {
|
||||
int newColor = ColorUtils.blendARGB(
|
||||
mArrowStartColor, mArrowColor, animation.getAnimatedFraction());
|
||||
setCurrentArrowColor(newColor);
|
||||
});
|
||||
|
||||
mArrowDisappearAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
|
||||
@@ -317,6 +333,27 @@ public class NavigationBarEdgePanel extends View {
|
||||
mSwipeThreshold = context.getResources()
|
||||
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
|
||||
setVisibility(GONE);
|
||||
|
||||
mRegionSamplingHelper = new RegionSamplingHelper(this,
|
||||
new RegionSamplingHelper.SamplingCallback() {
|
||||
@Override
|
||||
public void onRegionDarknessChanged(boolean isRegionDark) {
|
||||
setIsDark(!isRegionDark, true /* animate */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getSampledRegion(View sampledView) {
|
||||
return mSamplingRect;
|
||||
}
|
||||
});
|
||||
mRegionSamplingHelper.setWindowVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mWindowManager.removeView(this);
|
||||
mRegionSamplingHelper.stop();
|
||||
mRegionSamplingHelper = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -324,30 +361,51 @@ public class NavigationBarEdgePanel extends View {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldTriggerBack() {
|
||||
return mTriggerBack;
|
||||
}
|
||||
|
||||
public void setIsDark(boolean isDark, boolean animate) {
|
||||
private void setIsDark(boolean isDark, boolean animate) {
|
||||
mIsDark = isDark;
|
||||
updateIsDark(animate);
|
||||
}
|
||||
|
||||
public void setShowProtection(boolean showProtection) {
|
||||
private void setShowProtection(boolean showProtection) {
|
||||
mShowProtection = showProtection;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIsLeftPanel(boolean isLeftPanel) {
|
||||
mIsLeftPanel = isLeftPanel;
|
||||
mLayoutParams.gravity = mIsLeftPanel
|
||||
? (Gravity.LEFT | Gravity.TOP)
|
||||
: (Gravity.RIGHT | Gravity.TOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(int leftInset, int rightInset) {
|
||||
mLeftInset = leftInset;
|
||||
mRightInset = rightInset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySize(Point displaySize) {
|
||||
mDisplaySize.set(displaySize.x, displaySize.y);
|
||||
mScreenSize = Math.min(mDisplaySize.x, mDisplaySize.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackCallback(BackCallback callback) {
|
||||
mBackCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayoutParams(WindowManager.LayoutParams layoutParams) {
|
||||
mLayoutParams = layoutParams;
|
||||
mWindowManager.addView(this, mLayoutParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the rect to conform the the actual visible bounding box of the arrow.
|
||||
*
|
||||
* @param samplingRect the existing bounding box in screen coordinates, to be modified
|
||||
* Adjusts the sampling rect to conform to the actual visible bounding box of the arrow.
|
||||
*/
|
||||
public void adjustRectToBoundingBox(Rect samplingRect) {
|
||||
private void adjustSamplingRectToBoundingBox() {
|
||||
float translation = mDesiredTranslation;
|
||||
if (!mTriggerBack) {
|
||||
// Let's take the resting position and bounds as the sampling rect, since we are not
|
||||
@@ -361,7 +419,7 @@ public class NavigationBarEdgePanel extends View {
|
||||
}
|
||||
}
|
||||
float left = translation - mArrowThickness / 2.0f;
|
||||
left = mIsLeftPanel ? left : samplingRect.width() - left;
|
||||
left = mIsLeftPanel ? left : mSamplingRect.width() - left;
|
||||
|
||||
// Let's calculate the position of the end based on the angle
|
||||
float width = getStaticArrowWidth();
|
||||
@@ -371,49 +429,49 @@ public class NavigationBarEdgePanel extends View {
|
||||
}
|
||||
|
||||
float top = (getHeight() * 0.5f) + mDesiredVerticalTranslation - height / 2.0f;
|
||||
samplingRect.offset((int) left, (int) top);
|
||||
samplingRect.set(samplingRect.left, samplingRect.top,
|
||||
(int) (samplingRect.left + width),
|
||||
(int) (samplingRect.top + height));
|
||||
mSamplingRect.offset((int) left, (int) top);
|
||||
mSamplingRect.set(mSamplingRect.left, mSamplingRect.top,
|
||||
(int) (mSamplingRect.left + width),
|
||||
(int) (mSamplingRect.top + height));
|
||||
mRegionSamplingHelper.updateSamplingRect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI based on the motion events passed in device co-ordinates
|
||||
*/
|
||||
public void handleTouch(MotionEvent event) {
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent event) {
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
}
|
||||
mVelocityTracker.addMovement(event);
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN : {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mDragSlopPassed = false;
|
||||
resetOnDown();
|
||||
mStartX = event.getX();
|
||||
mStartY = event.getY();
|
||||
setVisibility(VISIBLE);
|
||||
updatePosition(event.getY());
|
||||
mRegionSamplingHelper.start(mSamplingRect);
|
||||
mWindowManager.updateViewLayout(this, mLayoutParams);
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
handleMoveEvent(event);
|
||||
break;
|
||||
}
|
||||
// Fall through
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
if (mTriggerBack) {
|
||||
triggerBack();
|
||||
} else {
|
||||
if (mTranslationAnimation.isRunning()) {
|
||||
mTranslationAnimation.addEndListener(mSetGoneEndListener);
|
||||
} else {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
cancelBack();
|
||||
}
|
||||
mRegionSamplingHelper.stop();
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
cancelBack();
|
||||
mRegionSamplingHelper.stop();
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,10 +510,10 @@ public class NavigationBarEdgePanel extends View {
|
||||
}
|
||||
|
||||
private void loadDimens() {
|
||||
mArrowPaddingEnd = getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.navigation_edge_panel_padding);
|
||||
DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||
mScreenSize = Math.min(metrics.widthPixels, metrics.heightPixels);
|
||||
Resources res = getResources();
|
||||
mArrowPaddingEnd = res.getDimensionPixelSize(R.dimen.navigation_edge_panel_padding);
|
||||
mMinArrowPosition = res.getDimensionPixelSize(R.dimen.navigation_edge_arrow_min_y);
|
||||
mFingerOffset = res.getDimensionPixelSize(R.dimen.navigation_edge_finger_offset);
|
||||
}
|
||||
|
||||
private void updateArrowDirection() {
|
||||
@@ -531,6 +589,11 @@ public class NavigationBarEdgePanel extends View {
|
||||
}
|
||||
|
||||
private void triggerBack() {
|
||||
mBackCallback.triggerBack();
|
||||
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
}
|
||||
mVelocityTracker.computeCurrentVelocity(1000);
|
||||
// Only do the extra translation if we're not already flinging
|
||||
boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500;
|
||||
@@ -573,7 +636,16 @@ public class NavigationBarEdgePanel extends View {
|
||||
} else {
|
||||
translationEnd.run();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelBack() {
|
||||
mBackCallback.cancelBack();
|
||||
|
||||
if (mTranslationAnimation.isRunning()) {
|
||||
mTranslationAnimation.addEndListener(mSetGoneEndListener);
|
||||
} else {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetOnDown() {
|
||||
@@ -680,6 +752,24 @@ public class NavigationBarEdgePanel extends View {
|
||||
float verticalTranslation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
|
||||
* maxYOffset * Math.signum(yOffset);
|
||||
setDesiredVerticalTransition(verticalTranslation, true /* animated */);
|
||||
updateSamplingRect();
|
||||
}
|
||||
|
||||
private void updatePosition(float touchY) {
|
||||
float position = touchY - mFingerOffset;
|
||||
position = Math.max(position, mMinArrowPosition);
|
||||
position -= mLayoutParams.height / 2.0f;
|
||||
mLayoutParams.y = MathUtils.constrain((int) position, 0, mDisplaySize.y);
|
||||
updateSamplingRect();
|
||||
}
|
||||
|
||||
private void updateSamplingRect() {
|
||||
int top = mLayoutParams.y;
|
||||
int left = mIsLeftPanel ? mLeftInset : mDisplaySize.x - mRightInset - mLayoutParams.width;
|
||||
int right = left + mLayoutParams.width;
|
||||
int bottom = top + mLayoutParams.height;
|
||||
mSamplingRect.set(left, top, right, bottom);
|
||||
adjustSamplingRectToBoundingBox();
|
||||
}
|
||||
|
||||
private void setDesiredVerticalTransition(float verticalTranslation, boolean animated) {
|
||||
|
||||
@@ -73,6 +73,7 @@ import com.android.systemui.model.SysUiState;
|
||||
import com.android.systemui.recents.OverviewProxyService;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.recents.RecentsOnboarding;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
@@ -97,6 +98,7 @@ public class NavigationBarView extends FrameLayout implements
|
||||
private final RegionSamplingHelper mRegionSamplingHelper;
|
||||
private final int mNavColorSampleMargin;
|
||||
private final SysUiState mSysUiFlagContainer;
|
||||
private final PluginManager mPluginManager;
|
||||
|
||||
View mCurrentView = null;
|
||||
private View mVertical;
|
||||
@@ -272,6 +274,7 @@ public class NavigationBarView extends FrameLayout implements
|
||||
boolean isGesturalMode = isGesturalMode(mNavBarMode);
|
||||
|
||||
mSysUiFlagContainer = Dependency.get(SysUiState.class);
|
||||
mPluginManager = Dependency.get(PluginManager.class);
|
||||
// Set up the context group of buttons
|
||||
mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
|
||||
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
|
||||
@@ -315,8 +318,8 @@ public class NavigationBarView extends FrameLayout implements
|
||||
|
||||
mNavColorSampleMargin = getResources()
|
||||
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
|
||||
mEdgeBackGestureHandler =
|
||||
new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiFlagContainer);
|
||||
mEdgeBackGestureHandler = new EdgeBackGestureHandler(
|
||||
context, mOverviewProxyService, mSysUiFlagContainer, mPluginManager);
|
||||
mRegionSamplingHelper = new RegionSamplingHelper(this,
|
||||
new RegionSamplingHelper.SamplingCallback() {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user