OSDN Git Service

Add gesture to drag in recents from navigation bar
authorJorim Jaggi <jjaggi@google.com>
Wed, 18 Nov 2015 23:57:38 +0000 (15:57 -0800)
committerJorim Jaggi <jjaggi@google.com>
Thu, 19 Nov 2015 22:10:28 +0000 (14:10 -0800)
Change-Id: I672ed08f1019835891411b87e2d0de0290defff7

16 files changed:
packages/SystemUI/src/com/android/systemui/RecentsComponent.java
packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
packages/SystemUI/src/com/android/systemui/recents/Recents.java
packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java [deleted file]
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

index 9a4cd93..f5f6acf 100644 (file)
@@ -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);
 }
index 949efc5..19e2992 100644 (file)
@@ -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
     };
 
     /**
index 79eca30..7cfe38e 100644 (file)
@@ -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);
 }
index 3806b46..348bd87 100644 (file)
@@ -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
index 85a2eda..618eb6f 100644 (file)
@@ -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);
index b091f05..d4d13f0 100644 (file)
@@ -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 (file)
index 0000000..9be8eb1
--- /dev/null
@@ -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 (file)
index 0000000..5e8bfd4
--- /dev/null
@@ -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;
+    }
+}
index e37b7dc..d18389f 100644 (file)
@@ -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.
      */
index dd894ce..50e010f 100644 (file)
@@ -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() {
index 5f983c5..69e90cc 100644 (file)
@@ -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;
index a520a33..3e317ff 100644 (file)
@@ -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 (file)
index 0000000..0466c14
--- /dev/null
@@ -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;
+    }
+}
index 33e514d..e1aec6f 100644 (file)
@@ -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 (file)
index fdfcdfb..0000000
+++ /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;
-    }
-}
index 0cddf1d..488b2e1 100644 (file)
@@ -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;