diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java new file mode 100644 index 0000000000000..bd86407222bc5 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NavigationEdgeBackPlugin.java @@ -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(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 1c8e832d03d29..8ee964f3f771c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -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 { 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) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java index 4f223c385eb45..23573095e037e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -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) { 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 159a829fc47d9..462d952abe068 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -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