1dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi/*
2dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * Copyright (C) 2014 The Android Open Source Project
3dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi *
4dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License");
5dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * you may not use this file except in compliance with the License.
6dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * You may obtain a copy of the License at
7dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi *
8dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi *      http://www.apache.org/licenses/LICENSE-2.0
9dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi *
10dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * Unless required by applicable law or agreed to in writing, software
11dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS,
12dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * See the License for the specific language governing permissions and
14dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * limitations under the License.
15dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi */
16dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
17dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggipackage com.android.systemui.statusbar.phone;
18dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
19213cbe665b1c45112f46c006f8b7db19b0c7663cJorim Jaggiimport android.app.ActivityManager;
20dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport android.content.Context;
21dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport android.content.res.Resources;
229ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggiimport android.graphics.Rect;
23dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport android.view.GestureDetector;
24dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport android.view.MotionEvent;
25dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport android.view.VelocityTracker;
26298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggiimport android.view.View;
27dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport android.view.ViewConfiguration;
28dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
2992de6b63819d7af4a93f1e1da33e89d453a0ad65Jorim Jaggiimport com.android.internal.logging.MetricsLogger;
30f6e9228b8a97603d3ceb8f0d61e8d87cf19bd21fChris Wrenimport com.android.internal.logging.MetricsProto.MetricsEvent;
31c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
32dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport com.android.systemui.R;
33dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport com.android.systemui.RecentsComponent;
34dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggiimport com.android.systemui.stackdivider.Divider;
35e759cd58bda6652e5413e7ca2a39b77bb776a610Winsonimport com.android.systemui.tuner.TunerService;
36dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
37c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport static android.view.WindowManager.DOCKED_INVALID;
38c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport static android.view.WindowManager.DOCKED_LEFT;
39c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport static android.view.WindowManager.DOCKED_TOP;
40dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
41dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi/**
42dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi * Class to detect gestures on the navigation bar.
43dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi */
44e759cd58bda6652e5413e7ca2a39b77bb776a610Winsonpublic class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureListener
45e759cd58bda6652e5413e7ca2a39b77bb776a610Winson        implements TunerService.Tunable {
46dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
47e759cd58bda6652e5413e7ca2a39b77bb776a610Winson    private static final String KEY_DOCK_WINDOW_GESTURE = "overview_nav_bar_gesture";
48cdb06caebb5f6f554b2ed8c76963970d8cc0ab54Jorim Jaggi    /**
49cdb06caebb5f6f554b2ed8c76963970d8cc0ab54Jorim Jaggi     * When dragging from the navigation bar, we drag in recents.
50cdb06caebb5f6f554b2ed8c76963970d8cc0ab54Jorim Jaggi     */
51cdb06caebb5f6f554b2ed8c76963970d8cc0ab54Jorim Jaggi    public static final int DRAG_MODE_NONE = -1;
52dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
53dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    /**
54dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi     * When dragging from the navigation bar, we drag in recents.
55dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi     */
56cdb06caebb5f6f554b2ed8c76963970d8cc0ab54Jorim Jaggi    public static final int DRAG_MODE_RECENTS = 0;
57dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
58dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    /**
59dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi     * When dragging from the navigation bar, we drag the divider.
60dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi     */
61cdb06caebb5f6f554b2ed8c76963970d8cc0ab54Jorim Jaggi    public static final int DRAG_MODE_DIVIDER = 1;
62dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
63dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private RecentsComponent mRecentsComponent;
64dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private Divider mDivider;
65213cbe665b1c45112f46c006f8b7db19b0c7663cJorim Jaggi    private Context mContext;
66298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggi    private NavigationBarView mNavigationBarView;
67dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private boolean mIsVertical;
68dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private boolean mIsRTL;
69dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
70dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private final GestureDetector mTaskSwitcherDetector;
71dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private final int mScrollTouchSlop;
72dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private final int mMinFlingVelocity;
73dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private int mTouchDownX;
74dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private int mTouchDownY;
75298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggi    private boolean mDownOnRecents;
76dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private VelocityTracker mVelocityTracker;
77dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
78dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private boolean mDockWindowEnabled;
79dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private boolean mDockWindowTouchSlopExceeded;
80dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private int mDragMode;
81dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
82dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    public NavigationBarGestureHelper(Context context) {
83213cbe665b1c45112f46c006f8b7db19b0c7663cJorim Jaggi        mContext = context;
84dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        ViewConfiguration configuration = ViewConfiguration.get(context);
85dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        Resources r = context.getResources();
86dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
87dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
88dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mTaskSwitcherDetector = new GestureDetector(context, this);
89e759cd58bda6652e5413e7ca2a39b77bb776a610Winson        TunerService.get(context).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
90dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
91dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
92298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggi    public void setComponents(RecentsComponent recentsComponent, Divider divider,
93298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggi            NavigationBarView navigationBarView) {
94dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mRecentsComponent = recentsComponent;
95dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mDivider = divider;
96298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggi        mNavigationBarView = navigationBarView;
97dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
98dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
99dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    public void setBarState(boolean isVertical, boolean isRTL) {
100dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mIsVertical = isVertical;
101dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mIsRTL = isRTL;
102dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
103dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
104dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    public boolean onInterceptTouchEvent(MotionEvent event) {
105dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        // If we move more than a fixed amount, then start capturing for the
106dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        // task switcher detector
107dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mTaskSwitcherDetector.onTouchEvent(event);
108dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        int action = event.getAction();
109dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        switch (action & MotionEvent.ACTION_MASK) {
110dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_DOWN: {
111dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                mTouchDownX = (int) event.getX();
112dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                mTouchDownY = (int) event.getY();
113dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
114dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            }
115dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_MOVE: {
116dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                int x = (int) event.getX();
117dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                int y = (int) event.getY();
118dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                int xDiff = Math.abs(x - mTouchDownX);
119dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                int yDiff = Math.abs(y - mTouchDownY);
120dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                boolean exceededTouchSlop = !mIsVertical
121dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                        ? xDiff > mScrollTouchSlop && xDiff > yDiff
122dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                        : yDiff > mScrollTouchSlop && yDiff > xDiff;
123dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                if (exceededTouchSlop) {
124dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                    return true;
125dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                }
126dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
127dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            }
128dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_CANCEL:
129dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_UP:
130dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
131dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
132dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        return mDockWindowEnabled && interceptDockWindowEvent(event);
133dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
134dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
135dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private boolean interceptDockWindowEvent(MotionEvent event) {
136dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        switch (event.getActionMasked()) {
137dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_DOWN:
138dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                handleDragActionDownEvent(event);
139dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
140dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_MOVE:
141dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                return handleDragActionMoveEvent(event);
142dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_UP:
143dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_CANCEL:
144dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                handleDragActionUpEvent(event);
145dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
146dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
147dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        return false;
148dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
149dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
150dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private boolean handleDockWindowEvent(MotionEvent event) {
151dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        switch (event.getActionMasked()) {
152dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_DOWN:
153dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                handleDragActionDownEvent(event);
154dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
155dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_MOVE:
156dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                handleDragActionMoveEvent(event);
157dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
158dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_UP:
159dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            case MotionEvent.ACTION_CANCEL:
160dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                handleDragActionUpEvent(event);
161dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                break;
162dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
163dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        return true;
164dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
165dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
166dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private void handleDragActionDownEvent(MotionEvent event) {
167dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mVelocityTracker = VelocityTracker.obtain();
168dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mVelocityTracker.addMovement(event);
169dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mDockWindowTouchSlopExceeded = false;
170dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mTouchDownX = (int) event.getX();
171dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mTouchDownY = (int) event.getY();
172ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen
173ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen        if (mNavigationBarView != null) {
174a20818257e6927560a676d99dbc563a0394f74d8Jason Monk            View recentsButton = mNavigationBarView.getRecentsButton().getCurrentView();
175ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen            if (recentsButton != null) {
176ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen                mDownOnRecents = mTouchDownX >= recentsButton.getLeft()
177ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen                        && mTouchDownX <= recentsButton.getRight()
178ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen                        && mTouchDownY >= recentsButton.getTop()
179ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen                        && mTouchDownY <= recentsButton.getBottom();
180a20818257e6927560a676d99dbc563a0394f74d8Jason Monk            } else {
181a20818257e6927560a676d99dbc563a0394f74d8Jason Monk                mDownOnRecents = false;
182ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen            }
183ada13040d020a289805ddc1f2f49079570cc0e0fAnthony Chen        }
184dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
185dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
186dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private boolean handleDragActionMoveEvent(MotionEvent event) {
187dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mVelocityTracker.addMovement(event);
188dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        int x = (int) event.getX();
189dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        int y = (int) event.getY();
190dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        int xDiff = Math.abs(x - mTouchDownX);
191dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        int yDiff = Math.abs(y - mTouchDownY);
1929b8018ce9d5d8d2bbc6242989dd18c27d503d925Victor Chan        if (mDivider == null || mRecentsComponent == null) {
1939b8018ce9d5d8d2bbc6242989dd18c27d503d925Victor Chan            return false;
1949b8018ce9d5d8d2bbc6242989dd18c27d503d925Victor Chan        }
195dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        if (!mDockWindowTouchSlopExceeded) {
196dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            boolean touchSlopExceeded = !mIsVertical
19747cf6381a88baee1213eb40de090ed13da892416Jorim Jaggi                    ? yDiff > mScrollTouchSlop && yDiff > xDiff
19847cf6381a88baee1213eb40de090ed13da892416Jorim Jaggi                    : xDiff > mScrollTouchSlop && xDiff > yDiff;
199298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggi            if (mDownOnRecents && touchSlopExceeded
200298c08115110a5615a1972884fa7ee24dd9c59d6Jorim Jaggi                    && mDivider.getView().getWindowManagerProxy().getDockSide() == DOCKED_INVALID) {
2019ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                Rect initialBounds = null;
202e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                int dragMode = calculateDragMode();
203213cbe665b1c45112f46c006f8b7db19b0c7663cJorim Jaggi                int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
204e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                if (dragMode == DRAG_MODE_DIVIDER) {
2059ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                    initialBounds = new Rect();
2069ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                    mDivider.getView().calculateBoundsForPosition(mIsVertical
2079ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                                    ? (int) event.getRawX()
2089ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                                    : (int) event.getRawY(),
2099ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                            mDivider.getView().isHorizontalDivision()
2109ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                                    ? DOCKED_TOP
2119ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                                    : DOCKED_LEFT,
2129ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                            initialBounds);
213e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                } else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX
214213cbe665b1c45112f46c006f8b7db19b0c7663cJorim Jaggi                        < mContext.getResources().getDisplayMetrics().widthPixels / 2) {
215213cbe665b1c45112f46c006f8b7db19b0c7663cJorim Jaggi                    createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
2169ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                }
21729379ec859bf7118dec9f3248c63ee369218ad6bJorim Jaggi                boolean docked = mRecentsComponent.dockTopTask(dragMode, createMode, initialBounds,
21829379ec859bf7118dec9f3248c63ee369218ad6bJorim Jaggi                        MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
219e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                if (docked) {
220e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                    mDragMode = dragMode;
221e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                    if (mDragMode == DRAG_MODE_DIVIDER) {
22211cc01df271fce5ec3eb05a7281032d1271cc782Jorim Jaggi                        mDivider.getView().startDragging(false /* animate */, true /* touching*/);
223e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                    }
224e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                    mDockWindowTouchSlopExceeded = true;
225e6309aa57ee2e33cf1aa0f177948f5b454d8bb12Winson                    return true;
226dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                }
227dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            }
228dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        } else {
229dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            if (mDragMode == DRAG_MODE_DIVIDER) {
230dc249c4ae7c7838928f53f151bfda8a6817b8784Jorim Jaggi                int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX();
231e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi                SnapTarget snapTarget = mDivider.getView().getSnapAlgorithm()
232df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi                        .calculateSnapTarget(position, 0f /* velocity */, false /* hardDismiss */);
233e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi                mDivider.getView().resizeStack(position, snapTarget.position, snapTarget);
234dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            } else if (mDragMode == DRAG_MODE_RECENTS) {
235dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                mRecentsComponent.onDraggingInRecents(event.getRawY());
236dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            }
237dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
238dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        return false;
239dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
240dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
241dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private void handleDragActionUpEvent(MotionEvent event) {
242dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mVelocityTracker.addMovement(event);
243dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mVelocityTracker.computeCurrentVelocity(1000);
2449b8018ce9d5d8d2bbc6242989dd18c27d503d925Victor Chan        if (mDockWindowTouchSlopExceeded && mDivider != null && mRecentsComponent != null) {
245dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            if (mDragMode == DRAG_MODE_DIVIDER) {
2469ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                mDivider.getView().stopDragging(mIsVertical
2479ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                                ? (int) event.getRawX()
2489ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                                : (int) event.getRawY(),
2499ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                        mIsVertical
2509ea2f7ba3155a455724f64e2d6a964ad4e99dfa5Jorim Jaggi                                ? mVelocityTracker.getXVelocity()
251df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi                                : mVelocityTracker.getYVelocity(),
25229379ec859bf7118dec9f3248c63ee369218ad6bJorim Jaggi                        true /* avoidDismissStart */, false /* logMetrics */);
253dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            } else if (mDragMode == DRAG_MODE_RECENTS) {
254dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity());
255dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            }
256dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
257dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mVelocityTracker.recycle();
258dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        mVelocityTracker = null;
259dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
260dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
261dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    private int calculateDragMode() {
262dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        if (mIsVertical && !mDivider.getView().isHorizontalDivision()) {
263dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            return DRAG_MODE_DIVIDER;
264dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
265dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        if (!mIsVertical && mDivider.getView().isHorizontalDivision()) {
266dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            return DRAG_MODE_DIVIDER;
267dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
268dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        return DRAG_MODE_RECENTS;
269dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
270dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
271dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    public boolean onTouchEvent(MotionEvent event) {
272dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        boolean result = mTaskSwitcherDetector.onTouchEvent(event);
273dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        if (mDockWindowEnabled) {
274dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            result |= handleDockWindowEvent(event);
275dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
276dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        return result;
277dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
278dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi
279dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    @Override
280dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
281dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        float absVelX = Math.abs(velocityX);
282dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        float absVelY = Math.abs(velocityY);
283dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        boolean isValidFling = absVelX > mMinFlingVelocity &&
284dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY);
2859b8018ce9d5d8d2bbc6242989dd18c27d503d925Victor Chan        if (isValidFling && mRecentsComponent != null) {
286dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            boolean showNext;
287dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            if (!mIsRTL) {
288dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0);
289dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            } else {
290dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                // In RTL, vertical is still the same, but horizontal is flipped
291dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                showNext = mIsVertical ? (velocityY < 0) : (velocityX > 0);
292dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            }
293dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            if (showNext) {
294dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                mRecentsComponent.showNextAffiliatedTask();
295dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            } else {
296dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi                mRecentsComponent.showPrevAffiliatedTask();
297dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi            }
298dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        }
299dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi        return true;
300dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi    }
301e759cd58bda6652e5413e7ca2a39b77bb776a610Winson
302e759cd58bda6652e5413e7ca2a39b77bb776a610Winson    @Override
303e759cd58bda6652e5413e7ca2a39b77bb776a610Winson    public void onTuningChanged(String key, String newValue) {
304e759cd58bda6652e5413e7ca2a39b77bb776a610Winson        switch (key) {
305e759cd58bda6652e5413e7ca2a39b77bb776a610Winson            case KEY_DOCK_WINDOW_GESTURE:
3068f2a44f934afd5492248935f564d0b3f2f676d93Jorim Jaggi                mDockWindowEnabled = newValue != null && (Integer.parseInt(newValue) != 0);
307e759cd58bda6652e5413e7ca2a39b77bb776a610Winson                break;
308e759cd58bda6652e5413e7ca2a39b77bb776a610Winson        }
309e759cd58bda6652e5413e7ca2a39b77bb776a610Winson    }
310dd98d41e3a65b3bcb37007ea5b29371cf013f563Jorim Jaggi}
311