From dd98d41e3a65b3bcb37007ea5b29371cf013f563 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Wed, 18 Nov 2015 15:57:38 -0800 Subject: [PATCH] Add gesture to drag in recents from navigation bar Change-Id: I672ed08f1019835891411b87e2d0de0290defff7 --- .../android/systemui/RecentsComponent.java | 17 +- .../android/systemui/SystemUIApplication.java | 2 +- .../IRecentsNonSystemUserCallbacks.aidl | 4 +- .../com/android/systemui/recents/Recents.java | 58 +++- .../android/systemui/recents/RecentsImpl.java | 27 +- .../systemui/recents/events/EventBus.java | 13 + .../ui/DraggingInRecentsEndedEvent.java | 15 ++ .../events/ui/DraggingInRecentsEvent.java | 15 ++ .../systemui/recents/views/RecentsView.java | 11 + .../systemui/stackdivider/Divider.java | 12 +- .../stackdivider/DividerSnapAlgorithm.java | 4 +- .../systemui/stackdivider/DividerView.java | 51 ++-- .../phone/NavigationBarGestureHelper.java | 250 ++++++++++++++++++ .../statusbar/phone/NavigationBarView.java | 17 +- .../NavigationBarViewTaskSwitchHelper.java | 114 -------- .../statusbar/phone/PhoneStatusBar.java | 5 +- 16 files changed, 453 insertions(+), 162 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java delete mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java index 9a4cd93652837..f5f6acf1f3e2d 100644 --- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java @@ -31,5 +31,20 @@ public interface RecentsComponent { /** * Docks the top-most task and opens recents. */ - void dockTopTask(); + void dockTopTask(boolean draggingInRecents); + + /** + * Called during a drag-from-navbar-in gesture. + * + * @param distanceFromTop the distance of the current drag in gesture from the top of the + * screen + */ + void onDraggingInRecents(float distanceFromTop); + + /** + * Called when the gesture to drag in recents ended. + * + * @param velocity the velocity of the finger when releasing it in pixels per second + */ + void onDraggingInRecentsEnded(float velocity); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 949efc588dd9b..19e299254aef6 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -48,12 +48,12 @@ public class SystemUIApplication extends Application { com.android.systemui.keyguard.KeyguardViewMediator.class, com.android.systemui.recents.Recents.class, com.android.systemui.volume.VolumeUI.class, + Divider.class, com.android.systemui.statusbar.SystemBars.class, com.android.systemui.usb.StorageNotification.class, com.android.systemui.power.PowerUI.class, com.android.systemui.media.RingtonePlayer.class, com.android.systemui.keyboard.KeyboardUI.class, - Divider.class }; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl index 79eca30d2c9a7..7cfe38e14ded9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl @@ -24,8 +24,10 @@ package com.android.systemui.recents; oneway interface IRecentsNonSystemUserCallbacks { void preloadRecents(); void cancelPreloadingRecents(); - void showRecents(boolean triggeredFromAltTab); + void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents); void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); void toggleRecents(); void onConfigurationChanged(); + void onDraggingInRecents(float distanceFromTop); + void onDraggingInRecentsEnded(float velocity); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 3806b4682e7ab..348bd87930306 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -73,6 +73,7 @@ public class Recents extends SystemUI private Handler mHandler; private RecentsImpl mImpl; + private int mDraggingInRecentsCurrentUser; // Only For system user, this is the callbacks instance we return to each secondary user private RecentsSystemUser mSystemUserCallbacks; @@ -213,14 +214,14 @@ public class Recents extends SystemUI int currentUser = sSystemServicesProxy.getCurrentUser(); if (sSystemServicesProxy.isSystemUser(currentUser)) { - mImpl.showRecents(triggeredFromAltTab); + mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */); } else { if (mSystemUserCallbacks != null) { IRecentsNonSystemUserCallbacks callbacks = mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser); if (callbacks != null) { try { - callbacks.showRecents(triggeredFromAltTab); + callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */); } catch (RemoteException e) { Log.e(TAG, "Callback failed", e); } @@ -361,8 +362,57 @@ public class Recents extends SystemUI } @Override - public void dockTopTask() { - mImpl.dockTopTask(); + public void dockTopTask(boolean draggingInRecents) { + mImpl.dockTopTask(draggingInRecents); + if (draggingInRecents) { + mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser(); + } + } + + @Override + public void onDraggingInRecents(float distanceFromTop) { + if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { + mImpl.onDraggingInRecents(distanceFromTop); + } else { + if (mSystemUserCallbacks != null) { + IRecentsNonSystemUserCallbacks callbacks = + mSystemUserCallbacks.getNonSystemUserRecentsForUser( + mDraggingInRecentsCurrentUser); + if (callbacks != null) { + try { + callbacks.onDraggingInRecents(distanceFromTop); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } else { + Log.e(TAG, "No SystemUI callbacks found for user: " + + mDraggingInRecentsCurrentUser); + } + } + } + } + + @Override + public void onDraggingInRecentsEnded(float velocity) { + if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { + mImpl.onDraggingInRecentsEnded(velocity); + } else { + if (mSystemUserCallbacks != null) { + IRecentsNonSystemUserCallbacks callbacks = + mSystemUserCallbacks.getNonSystemUserRecentsForUser( + mDraggingInRecentsCurrentUser); + if (callbacks != null) { + try { + callbacks.onDraggingInRecentsEnded(velocity); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } else { + Log.e(TAG, "No SystemUI callbacks found for user: " + + mDraggingInRecentsCurrentUser); + } + } + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 85a2edab64371..618eb6f8bba34 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -46,6 +46,8 @@ import com.android.systemui.recents.events.activity.IterateRecentsEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; +import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; +import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.ForegroundThread; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -140,6 +142,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements RecentsAppWidgetHost mAppWidgetHost; boolean mBootCompleted; boolean mCanReuseTaskStackViews = true; + boolean mDraggingInRecents; // Task launching Rect mSearchBarBounds = new Rect(); @@ -164,14 +167,13 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements public void run() { // When this fires, then the user has not released alt-tab for at least // FAST_ALT_TAB_DELAY_MS milliseconds - showRecents(mTriggeredFromAltTab); + showRecents(mTriggeredFromAltTab, false /* draggingInRecents */); } }); Bitmap mThumbnailTransitionBitmapCache; Task mThumbnailTransitionBitmapCacheKey; - public RecentsImpl(Context context) { mContext = context; mHandler = new Handler(); @@ -248,8 +250,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements } @Override - public void showRecents(boolean triggeredFromAltTab) { + public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents) { mTriggeredFromAltTab = triggeredFromAltTab; + mDraggingInRecents = draggingInRecents; if (mFastAltTabTrigger.hasTriggered()) { // We are calling this from the doze trigger, so just fall through to show Recents mFastAltTabTrigger.resetTrigger(); @@ -315,6 +318,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements return; } + mDraggingInRecents = false; mTriggeredFromAltTab = false; try { @@ -385,6 +389,16 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements // Do nothing } + @Override + public void onDraggingInRecents(float distanceFromTop) { + EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop)); + } + + @Override + public void onDraggingInRecentsEnded(float velocity) { + EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity)); + } + /** * Transitions to the next recent task in the stack. */ @@ -521,13 +535,13 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements showRelativeAffiliatedTask(false); } - public void dockTopTask() { + public void dockTopTask(boolean draggingInRecents) { SystemServicesProxy ssp = Recents.getSystemServices(); ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask(); if (topTask != null && !SystemServicesProxy.isHomeStack(topTask.stackId)) { ssp.startTaskInDockedMode(topTask.id, ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT); - showRecents(false /* triggeredFromAltTab */); + showRecents(false /* triggeredFromAltTab */, draggingInRecents); } } @@ -856,7 +870,8 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements launchState.launchedNumVisibleTasks = vr.numVisibleTasks; launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails; launchState.launchedHasConfigurationChanged = false; - launchState.startHidden = topTask != null && topTask.stackId == FREEFORM_WORKSPACE_STACK_ID; + launchState.startHidden = topTask != null && topTask.stackId == FREEFORM_WORKSPACE_STACK_ID + || mDraggingInRecents; Intent intent = new Intent(); intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY); diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java index b091f050a340b..d4d13f0d70af7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java @@ -502,6 +502,19 @@ public class EventBus extends BroadcastReceiver { queueEvent(event); } + /** + * If this method is called from the main thread, it will be handled directly. If this method + * is not called from the main thread, it will be posted onto the main thread. + */ + public void sendOntoMainThread(Event event) { + long callingThreadId = Thread.currentThread().getId(); + if (callingThreadId != mHandler.getLooper().getThread().getId()) { + post(event); + } else { + send(event); + } + } + /** Prevent post()ing an InterprocessEvent */ @Deprecated public void post(InterprocessEvent event) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java new file mode 100644 index 0000000000000..9be8eb1cde4ec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java @@ -0,0 +1,15 @@ +package com.android.systemui.recents.events.ui; + +import com.android.systemui.recents.events.EventBus.Event; + +/** + * This event is sent when the user finished dragging in recents. + */ +public class DraggingInRecentsEndedEvent extends Event { + + public final float velocity; + + public DraggingInRecentsEndedEvent(float velocity) { + this.velocity = velocity; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java new file mode 100644 index 0000000000000..5e8bfd410e31b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java @@ -0,0 +1,15 @@ +package com.android.systemui.recents.events.ui; + +import com.android.systemui.recents.events.EventBus.Event; + +/** + * This event is sent when the user changed how far they are dragging in recents. + */ +public class DraggingInRecentsEvent extends Event { + + public final float distanceFromTop; + + public DraggingInRecentsEvent(float distanceFromTop) { + this.distanceFromTop = distanceFromTop; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index e37b7dc7cd969..d18389f43a1ff 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -41,6 +41,8 @@ import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; +import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; +import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; @@ -542,6 +544,15 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } } + public final void onBusEvent(DraggingInRecentsEvent event) { + setStackViewVisibility(View.VISIBLE); + setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY()); + } + + public final void onBusEvent(DraggingInRecentsEndedEvent event) { + animate().translationY(0f); + } + /** * Updates the dock region to match the specified dock state. */ diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index dd894ce418a32..50e010fc29281 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -32,6 +32,7 @@ public class Divider extends SystemUI { private static final String TAG = "Divider"; private int mDividerWindowWidth; private DividerWindowManager mWindowManager; + private DividerView mView; @Override public void start() { @@ -39,6 +40,7 @@ public class Divider extends SystemUI { mDividerWindowWidth = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_thickness); update(mContext.getResources().getConfiguration()); + putComponent(Divider.class, this); } @Override @@ -47,14 +49,18 @@ public class Divider extends SystemUI { update(newConfig); } + public DividerView getView() { + return mView; + } + private void addDivider(Configuration configuration) { - DividerView view = (DividerView) + mView = (DividerView) LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null); final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE; final int width = landscape ? mDividerWindowWidth : MATCH_PARENT; final int height = landscape ? MATCH_PARENT : mDividerWindowWidth; - mWindowManager.add(view, width, height); - view.setWindowManager(mWindowManager); + mWindowManager.add(mView, width, height); + mView.setWindowManager(mWindowManager); } private void removeDivider() { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java index 5f983c5435e19..69e90cc724f35 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java @@ -98,10 +98,10 @@ public class DividerSnapAlgorithm { // TODO: Better calculation targets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START)); - targets.add(new SnapTarget((int) (0.35f * dividerMax) - mDividerSize / 2, + targets.add(new SnapTarget((int) (0.38f * dividerMax) - mDividerSize / 2, SnapTarget.FLAG_NONE)); targets.add(new SnapTarget(dividerMax / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE)); - targets.add(new SnapTarget((int) (0.65f * dividerMax) - mDividerSize / 2, + targets.add(new SnapTarget((int) (0.62f * dividerMax) - mDividerSize / 2, SnapTarget.FLAG_NONE)); targets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END)); return targets; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index a520a3387071b..3e317ff93fb46 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -127,6 +127,28 @@ public class DividerView extends FrameLayout implements OnTouchListener, mWindowManager = windowManager; } + public WindowManagerProxy getWindowManagerProxy() { + return mWindowManagerProxy; + } + + public boolean startDragging() { + mDockSide = mWindowManagerProxy.getDockSide(); + if (mDockSide != WindowManager.DOCKED_INVALID) { + mWindowManagerProxy.setResizing(true); + mWindowManager.setSlippery(false); + liftBackground(); + return true; + } else { + return false; + } + } + + public void stopDragging(int position, float velocity) { + fling(position, velocity); + mWindowManager.setSlippery(true); + releaseBackground(); + } + @Override public boolean onTouch(View v, MotionEvent event) { convertToScreenCoordinates(event); @@ -138,20 +160,13 @@ public class DividerView extends FrameLayout implements OnTouchListener, mStartX = (int) event.getX(); mStartY = (int) event.getY(); getLocationOnScreen(mTempInt2); - mDockSide = mWindowManagerProxy.getDockSide(); + boolean result = startDragging(); if (isHorizontalDivision()) { mStartPosition = mTempInt2[1] + mDividerInsets; } else { mStartPosition = mTempInt2[0] + mDividerInsets; } - if (mDockSide != WindowManager.DOCKED_INVALID) { - mWindowManagerProxy.setResizing(true); - mWindowManager.setSlippery(false); - liftBackground(); - return true; - } else { - return false; - } + return result; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); int x = (int) event.getX(); @@ -168,10 +183,9 @@ public class DividerView extends FrameLayout implements OnTouchListener, y = (int) event.getRawY(); mVelocityTracker.computeCurrentVelocity(1000); - fling(x, y, mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); - - mWindowManager.setSlippery(true); - releaseBackground(); + int position = calculatePosition(x, y); + stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity() + : mVelocityTracker.getXVelocity()); break; } return true; @@ -181,9 +195,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, event.setLocation(event.getRawX(), event.getRawY()); } - private void fling(int x, int y, float xVelocity, float yVelocity) { - int position = calculatePosition(x, y); - float velocity = isHorizontalDivision() ? yVelocity : xVelocity; + private void fling(int position, float velocity) { final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity); @@ -277,9 +289,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX); } - private boolean isHorizontalDivision() { - return mDockSide == WindowManager.DOCKED_TOP - || mDockSide == WindowManager.DOCKED_BOTTOM; + public boolean isHorizontalDivision() { + return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; } private int calculateXPosition(int touchX) { @@ -290,7 +301,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, return mStartPosition + touchY - mStartY; } - private void resizeStack(int position) { + public void resizeStack(int position) { mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight); switch (mDockSide) { case WindowManager.DOCKED_LEFT: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java new file mode 100644 index 0000000000000..0466c14c0417b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2014 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.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.SystemProperties; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.WindowManager; + +import com.android.systemui.R; +import com.android.systemui.RecentsComponent; +import com.android.systemui.stackdivider.Divider; +import com.android.systemui.statusbar.BaseStatusBar; + +import static android.view.WindowManager.*; + +/** + * Class to detect gestures on the navigation bar. + */ +public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureListener { + + private static final String DOCK_WINDOW_GESTURE_ENABLED_PROP = "persist.dock_gesture_enabled"; + + /** + * When dragging from the navigation bar, we drag in recents. + */ + private static final int DRAG_MODE_RECENTS = 0; + + /** + * When dragging from the navigation bar, we drag the divider. + */ + private static final int DRAG_MODE_DIVIDER = 1; + + private RecentsComponent mRecentsComponent; + private Divider mDivider; + private boolean mIsVertical; + private boolean mIsRTL; + + private final GestureDetector mTaskSwitcherDetector; + private final int mScrollTouchSlop; + private final int mTouchSlop; + private final int mMinFlingVelocity; + private int mTouchDownX; + private int mTouchDownY; + private VelocityTracker mVelocityTracker; + + private boolean mDockWindowEnabled; + private boolean mDockWindowTouchSlopExceeded; + private int mDragMode; + + public NavigationBarGestureHelper(Context context) { + ViewConfiguration configuration = ViewConfiguration.get(context); + Resources r = context.getResources(); + mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance); + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity(); + mTaskSwitcherDetector = new GestureDetector(context, this); + mDockWindowEnabled = SystemProperties.getBoolean(DOCK_WINDOW_GESTURE_ENABLED_PROP, false); + } + + public void setComponents(RecentsComponent recentsComponent, Divider divider) { + mRecentsComponent = recentsComponent; + mDivider = divider; + } + + public void setBarState(boolean isVertical, boolean isRTL) { + mIsVertical = isVertical; + mIsRTL = isRTL; + } + + public boolean onInterceptTouchEvent(MotionEvent event) { + // If we move more than a fixed amount, then start capturing for the + // task switcher detector + mTaskSwitcherDetector.onTouchEvent(event); + int action = event.getAction(); + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { + mTouchDownX = (int) event.getX(); + mTouchDownY = (int) event.getY(); + break; + } + case MotionEvent.ACTION_MOVE: { + int x = (int) event.getX(); + int y = (int) event.getY(); + int xDiff = Math.abs(x - mTouchDownX); + int yDiff = Math.abs(y - mTouchDownY); + boolean exceededTouchSlop = !mIsVertical + ? xDiff > mScrollTouchSlop && xDiff > yDiff + : yDiff > mScrollTouchSlop && yDiff > xDiff; + if (exceededTouchSlop) { + return true; + } + break; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + break; + } + return mDockWindowEnabled && interceptDockWindowEvent(event); + } + + private boolean interceptDockWindowEvent(MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + handleDragActionDownEvent(event); + break; + case MotionEvent.ACTION_MOVE: + return handleDragActionMoveEvent(event); + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + handleDragActionUpEvent(event); + break; + } + return false; + } + + private boolean handleDockWindowEvent(MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + handleDragActionDownEvent(event); + break; + case MotionEvent.ACTION_MOVE: + handleDragActionMoveEvent(event); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + handleDragActionUpEvent(event); + break; + } + return true; + } + + private void handleDragActionDownEvent(MotionEvent event) { + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(event); + mDockWindowTouchSlopExceeded = false; + mTouchDownX = (int) event.getX(); + mTouchDownY = (int) event.getY(); + } + + private boolean handleDragActionMoveEvent(MotionEvent event) { + mVelocityTracker.addMovement(event); + int x = (int) event.getX(); + int y = (int) event.getY(); + int xDiff = Math.abs(x - mTouchDownX); + int yDiff = Math.abs(y - mTouchDownY); + if (!mDockWindowTouchSlopExceeded) { + boolean touchSlopExceeded = !mIsVertical + ? yDiff > mTouchSlop && yDiff > xDiff + : xDiff > mTouchSlop && xDiff > yDiff; + if (touchSlopExceeded && mDivider.getView().getWindowManagerProxy().getDockSide() + == DOCKED_INVALID) { + mDragMode = calculateDragMode(); + mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS); + if (mDragMode == DRAG_MODE_DIVIDER) { + mDivider.getView().startDragging(); + } + mDockWindowTouchSlopExceeded = true; + return true; + } + } else { + if (mDragMode == DRAG_MODE_DIVIDER) { + mDivider.getView().resizeStack( + !mIsVertical ? (int) event.getRawY() : (int) event.getRawX()); + } else if (mDragMode == DRAG_MODE_RECENTS) { + mRecentsComponent.onDraggingInRecents(event.getRawY()); + } + } + return false; + } + + private void handleDragActionUpEvent(MotionEvent event) { + mVelocityTracker.addMovement(event); + mVelocityTracker.computeCurrentVelocity(1000); + if (mDockWindowTouchSlopExceeded) { + if (mDragMode == DRAG_MODE_DIVIDER) { + mDivider.getView().stopDragging(!mIsVertical + ? (int) event.getRawY() + : (int) event.getRawX(), + !mIsVertical + ? mVelocityTracker.getXVelocity() + : mVelocityTracker.getYVelocity()); + } else if (mDragMode == DRAG_MODE_RECENTS) { + mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity()); + } + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + + private int calculateDragMode() { + if (mIsVertical && !mDivider.getView().isHorizontalDivision()) { + return DRAG_MODE_DIVIDER; + } + if (!mIsVertical && mDivider.getView().isHorizontalDivision()) { + return DRAG_MODE_DIVIDER; + } + return DRAG_MODE_RECENTS; + } + + public boolean onTouchEvent(MotionEvent event) { + boolean result = mTaskSwitcherDetector.onTouchEvent(event); + if (mDockWindowEnabled) { + result |= handleDockWindowEvent(event); + } + return result; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + float absVelX = Math.abs(velocityX); + float absVelY = Math.abs(velocityY); + boolean isValidFling = absVelX > mMinFlingVelocity && + mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY); + if (isValidFling) { + boolean showNext; + if (!mIsRTL) { + showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0); + } else { + // In RTL, vertical is still the same, but horizontal is flipped + showNext = mIsVertical ? (velocityY < 0) : (velocityX > 0); + } + if (showNext) { + mRecentsComponent.showNextAffiliatedTask(); + } else { + mRecentsComponent.showPrevAffiliatedTask(); + } + } + return true; + } +} 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 33e514d0a79bf..e1aec6f5d8683 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -40,7 +40,6 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.ViewGroup; -import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; @@ -48,6 +47,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.systemui.R; +import com.android.systemui.RecentsComponent; +import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonView; @@ -78,7 +79,7 @@ public class NavigationBarView extends LinearLayout { private Drawable mRecentIcon; private Drawable mRecentLandIcon; - private NavigationBarViewTaskSwitchHelper mTaskSwitchHelper; + private NavigationBarGestureHelper mGestureHelper; private DeadZone mDeadZone; private final NavigationBarTransitions mBarTransitions; @@ -180,7 +181,7 @@ public class NavigationBarView extends LinearLayout { mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size); mVertical = false; mShowMenu = false; - mTaskSwitchHelper = new NavigationBarViewTaskSwitchHelper(context); + mGestureHelper = new NavigationBarGestureHelper(context); getIcons(res); @@ -191,8 +192,8 @@ public class NavigationBarView extends LinearLayout { return mBarTransitions; } - public void setBar(PhoneStatusBar phoneStatusBar) { - mTaskSwitchHelper.setBar(phoneStatusBar); + public void setComponents(RecentsComponent recentsComponent, Divider divider) { + mGestureHelper.setComponents(recentsComponent, divider); } public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { @@ -202,7 +203,7 @@ public class NavigationBarView extends LinearLayout { @Override public boolean onTouchEvent(MotionEvent event) { - if (mTaskSwitchHelper.onTouchEvent(event)) { + if (mGestureHelper.onTouchEvent(event)) { return true; } if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) { @@ -213,7 +214,7 @@ public class NavigationBarView extends LinearLayout { @Override public boolean onInterceptTouchEvent(MotionEvent event) { - return mTaskSwitchHelper.onInterceptTouchEvent(event); + return mGestureHelper.onInterceptTouchEvent(event); } public void abortCurrentGesture() { @@ -488,7 +489,7 @@ public class NavigationBarView extends LinearLayout { private void updateTaskSwitchHelper() { boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); - mTaskSwitchHelper.setBarState(mVertical, isRtl); + mGestureHelper.setBarState(mVertical, isRtl); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java deleted file mode 100644 index fdfcdfbda4af8..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2014 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.content.Context; -import android.content.res.Resources; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.ViewConfiguration; -import com.android.systemui.R; -import com.android.systemui.statusbar.BaseStatusBar; - -public class NavigationBarViewTaskSwitchHelper extends GestureDetector.SimpleOnGestureListener { - - private BaseStatusBar mBar; - private boolean mIsVertical; - private boolean mIsRTL; - - private final GestureDetector mTaskSwitcherDetector; - private final int mScrollTouchSlop; - private final int mMinFlingVelocity; - private int mTouchDownX; - private int mTouchDownY; - - public NavigationBarViewTaskSwitchHelper(Context context) { - ViewConfiguration configuration = ViewConfiguration.get(context); - Resources r = context.getResources(); - mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance); - mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity(); - mTaskSwitcherDetector = new GestureDetector(context, this); - } - - public void setBar(BaseStatusBar phoneStatusBar) { - mBar = phoneStatusBar; - } - - public void setBarState(boolean isVertical, boolean isRTL) { - mIsVertical = isVertical; - mIsRTL = isRTL; - } - - public boolean onInterceptTouchEvent(MotionEvent event) { - // If we move more than a fixed amount, then start capturing for the - // task switcher detector - mTaskSwitcherDetector.onTouchEvent(event); - int action = event.getAction(); - boolean intercepted = false; - switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: { - mTouchDownX = (int) event.getX(); - mTouchDownY = (int) event.getY(); - break; - } - case MotionEvent.ACTION_MOVE: { - int x = (int) event.getX(); - int y = (int) event.getY(); - int xDiff = Math.abs(x - mTouchDownX); - int yDiff = Math.abs(y - mTouchDownY); - boolean exceededTouchSlop = !mIsVertical - ? xDiff > mScrollTouchSlop && xDiff > yDiff - : yDiff > mScrollTouchSlop && yDiff > xDiff; - if (exceededTouchSlop) { - return true; - } - break; - } - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - break; - } - return intercepted; - } - - public boolean onTouchEvent(MotionEvent event) { - return mTaskSwitcherDetector.onTouchEvent(event); - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - float absVelX = Math.abs(velocityX); - float absVelY = Math.abs(velocityY); - boolean isValidFling = absVelX > mMinFlingVelocity && - mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY); - if (isValidFling) { - boolean showNext; - if (!mIsRTL) { - showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0); - } else { - // In RTL, vertical is still the same, but horizontal is flipped - showNext = mIsVertical ? (velocityY < 0) : (velocityX > 0); - } - if (showNext) { - mBar.showNextAffiliatedTask(); - } else { - mBar.showPreviousAffiliatedTask(); - } - } - return true; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 0cddf1d851ffa..488b2e1074fb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -117,6 +117,7 @@ import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.QSPanel; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BackDropView; import com.android.systemui.statusbar.BaseStatusBar; @@ -739,7 +740,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, context, R.layout.navigation_bar, null); } mNavigationBarView.setDisabledFlags(mDisabled1); - mNavigationBarView.setBar(this); + mNavigationBarView.setComponents(mRecents, getComponent(Divider.class)); mNavigationBarView.setOnVerticalChangedListener( new NavigationBarView.OnVerticalChangedListener() { @Override @@ -1135,7 +1136,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public boolean onLongClick(View v) { if (mRecents != null) { - mRecents.dockTopTask(); + mRecents.dockTopTask(false /* draggingInRecents */); return true; } return false;