172b079e4031f42729056e999c20bf87281798eb9Michael Jurka/*
272b079e4031f42729056e999c20bf87281798eb9Michael Jurka * Copyright (C) 2010 The Android Open Source Project
372b079e4031f42729056e999c20bf87281798eb9Michael Jurka *
472b079e4031f42729056e999c20bf87281798eb9Michael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
572b079e4031f42729056e999c20bf87281798eb9Michael Jurka * you may not use this file except in compliance with the License.
672b079e4031f42729056e999c20bf87281798eb9Michael Jurka * You may obtain a copy of the License at
772b079e4031f42729056e999c20bf87281798eb9Michael Jurka *
872b079e4031f42729056e999c20bf87281798eb9Michael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
972b079e4031f42729056e999c20bf87281798eb9Michael Jurka *
1072b079e4031f42729056e999c20bf87281798eb9Michael Jurka * Unless required by applicable law or agreed to in writing, software
1172b079e4031f42729056e999c20bf87281798eb9Michael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
1272b079e4031f42729056e999c20bf87281798eb9Michael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1372b079e4031f42729056e999c20bf87281798eb9Michael Jurka * See the License for the specific language governing permissions and
1472b079e4031f42729056e999c20bf87281798eb9Michael Jurka * limitations under the License.
1572b079e4031f42729056e999c20bf87281798eb9Michael Jurka */
1672b079e4031f42729056e999c20bf87281798eb9Michael Jurka
1772b079e4031f42729056e999c20bf87281798eb9Michael Jurkapackage com.android.launcher2;
1872b079e4031f42729056e999c20bf87281798eb9Michael Jurka
1972b079e4031f42729056e999c20bf87281798eb9Michael Jurkaimport android.content.Context;
2072b079e4031f42729056e999c20bf87281798eb9Michael Jurkaimport android.util.AttributeSet;
2172b079e4031f42729056e999c20bf87281798eb9Michael Jurkaimport android.view.MotionEvent;
2272b079e4031f42729056e999c20bf87281798eb9Michael Jurkaimport android.view.View;
2372b079e4031f42729056e999c20bf87281798eb9Michael Jurka
2472b079e4031f42729056e999c20bf87281798eb9Michael Jurka
2572b079e4031f42729056e999c20bf87281798eb9Michael Jurka/* Class that does most of the work of enabling dragging items out of a PagedView by performing a
2672b079e4031f42729056e999c20bf87281798eb9Michael Jurka * vertical drag. Used by both CustomizePagedView and AllAppsPagedView.
2772b079e4031f42729056e999c20bf87281798eb9Michael Jurka * Subclasses must do the following:
2872b079e4031f42729056e999c20bf87281798eb9Michael Jurka *   * call setDragSlopeThreshold after making an instance of the PagedViewWithDraggableItems
2972b079e4031f42729056e999c20bf87281798eb9Michael Jurka *   * call child.setOnLongClickListener(this) and child.setOnTouchListener(this) on all children
3072b079e4031f42729056e999c20bf87281798eb9Michael Jurka *       (good place to do it is in syncPageItems)
3172b079e4031f42729056e999c20bf87281798eb9Michael Jurka *   * override beginDragging(View) (but be careful to call super.beginDragging(View)
3272b079e4031f42729056e999c20bf87281798eb9Michael Jurka *
3372b079e4031f42729056e999c20bf87281798eb9Michael Jurka */
3472b079e4031f42729056e999c20bf87281798eb9Michael Jurkapublic abstract class PagedViewWithDraggableItems extends PagedView
3572b079e4031f42729056e999c20bf87281798eb9Michael Jurka    implements View.OnLongClickListener, View.OnTouchListener {
3672b079e4031f42729056e999c20bf87281798eb9Michael Jurka    private View mLastTouchedItem;
3772b079e4031f42729056e999c20bf87281798eb9Michael Jurka    private boolean mIsDragging;
3894569f47c2fe3c2e04e993379d36e0597925e19fWinson Chung    private boolean mIsDragEnabled;
3972b079e4031f42729056e999c20bf87281798eb9Michael Jurka    private float mDragSlopeThreshold;
40fc53cd22c9a78708c78c85946443f23ec8c59502Adam Cohen    private Launcher mLauncher;
4172b079e4031f42729056e999c20bf87281798eb9Michael Jurka
4272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public PagedViewWithDraggableItems(Context context) {
43fc53cd22c9a78708c78c85946443f23ec8c59502Adam Cohen        this(context, null);
4472b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
4572b079e4031f42729056e999c20bf87281798eb9Michael Jurka
4672b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public PagedViewWithDraggableItems(Context context, AttributeSet attrs) {
47fc53cd22c9a78708c78c85946443f23ec8c59502Adam Cohen        this(context, attrs, 0);
4872b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
4972b079e4031f42729056e999c20bf87281798eb9Michael Jurka
5072b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public PagedViewWithDraggableItems(Context context, AttributeSet attrs, int defStyle) {
5172b079e4031f42729056e999c20bf87281798eb9Michael Jurka        super(context, attrs, defStyle);
52fc53cd22c9a78708c78c85946443f23ec8c59502Adam Cohen        mLauncher = (Launcher) context;
5372b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
5472b079e4031f42729056e999c20bf87281798eb9Michael Jurka
5572b079e4031f42729056e999c20bf87281798eb9Michael Jurka    protected boolean beginDragging(View v) {
56304dcde0e301c2f1a0b2bdc80ea8617930691b6eWinson Chung        boolean wasDragging = mIsDragging;
5772b079e4031f42729056e999c20bf87281798eb9Michael Jurka        mIsDragging = true;
58304dcde0e301c2f1a0b2bdc80ea8617930691b6eWinson Chung        return !wasDragging;
5972b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
6072b079e4031f42729056e999c20bf87281798eb9Michael Jurka
617d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    protected void cancelDragging() {
627d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        mIsDragging = false;
637d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        mLastTouchedItem = null;
6494569f47c2fe3c2e04e993379d36e0597925e19fWinson Chung        mIsDragEnabled = false;
657d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    }
667d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung
677d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    private void handleTouchEvent(MotionEvent ev) {
6872b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int action = ev.getAction();
6972b079e4031f42729056e999c20bf87281798eb9Michael Jurka        switch (action & MotionEvent.ACTION_MASK) {
7072b079e4031f42729056e999c20bf87281798eb9Michael Jurka            case MotionEvent.ACTION_DOWN:
717d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung                cancelDragging();
7294569f47c2fe3c2e04e993379d36e0597925e19fWinson Chung                mIsDragEnabled = true;
7372b079e4031f42729056e999c20bf87281798eb9Michael Jurka                break;
7472b079e4031f42729056e999c20bf87281798eb9Michael Jurka            case MotionEvent.ACTION_MOVE:
7594569f47c2fe3c2e04e993379d36e0597925e19fWinson Chung                if (mTouchState != TOUCH_STATE_SCROLLING && !mIsDragging && mIsDragEnabled) {
7672b079e4031f42729056e999c20bf87281798eb9Michael Jurka                    determineDraggingStart(ev);
7772b079e4031f42729056e999c20bf87281798eb9Michael Jurka                }
7872b079e4031f42729056e999c20bf87281798eb9Michael Jurka                break;
7972b079e4031f42729056e999c20bf87281798eb9Michael Jurka        }
8072b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
8172b079e4031f42729056e999c20bf87281798eb9Michael Jurka
8272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    @Override
837d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
847d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        handleTouchEvent(ev);
857d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        return super.onInterceptTouchEvent(ev);
8672b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
8772b079e4031f42729056e999c20bf87281798eb9Michael Jurka
8872b079e4031f42729056e999c20bf87281798eb9Michael Jurka    @Override
8972b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public boolean onTouchEvent(MotionEvent ev) {
907d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        handleTouchEvent(ev);
9172b079e4031f42729056e999c20bf87281798eb9Michael Jurka        return super.onTouchEvent(ev);
9272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
9372b079e4031f42729056e999c20bf87281798eb9Michael Jurka
9472b079e4031f42729056e999c20bf87281798eb9Michael Jurka    @Override
957d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    public boolean onTouch(View v, MotionEvent event) {
967d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        mLastTouchedItem = v;
9794569f47c2fe3c2e04e993379d36e0597925e19fWinson Chung        mIsDragEnabled = true;
987d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        return false;
997d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    }
1007d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung
1017d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    @Override
10272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public boolean onLongClick(View v) {
10372b079e4031f42729056e999c20bf87281798eb9Michael Jurka        // Return early if this is not initiated from a touch
10472b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (!v.isInTouchMode()) return false;
10572b079e4031f42729056e999c20bf87281798eb9Michael Jurka        // Return early if we are still animating the pages
10672b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (mNextPage != INVALID_PAGE) return false;
107fc53cd22c9a78708c78c85946443f23ec8c59502Adam Cohen        // When we have exited all apps or are in transition, disregard long clicks
108c93e5ae12018bb214099ff88a48cc21580aec4c3Winson Chung        if (!mLauncher.isAllAppsVisible() ||
109fc53cd22c9a78708c78c85946443f23ec8c59502Adam Cohen                mLauncher.getWorkspace().isSwitchingState()) return false;
11036a62fe917be0a2520c457f985075fb5d3d09d1cWinson Chung        // Return if global dragging is not enabled
11136a62fe917be0a2520c457f985075fb5d3d09d1cWinson Chung        if (!mLauncher.isDraggingEnabled()) return false;
112fc53cd22c9a78708c78c85946443f23ec8c59502Adam Cohen
11372b079e4031f42729056e999c20bf87281798eb9Michael Jurka        return beginDragging(v);
11472b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
11572b079e4031f42729056e999c20bf87281798eb9Michael Jurka
11672b079e4031f42729056e999c20bf87281798eb9Michael Jurka    /*
11772b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * Determines if we should change the touch state to start scrolling after the
11872b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * user moves their touch point too far.
11972b079e4031f42729056e999c20bf87281798eb9Michael Jurka     */
12072b079e4031f42729056e999c20bf87281798eb9Michael Jurka    protected void determineScrollingStart(MotionEvent ev) {
12172b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (!mIsDragging) super.determineScrollingStart(ev);
12272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
12372b079e4031f42729056e999c20bf87281798eb9Michael Jurka
12472b079e4031f42729056e999c20bf87281798eb9Michael Jurka    /*
12572b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * Determines if we should change the touch state to start dragging after the
12672b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * user moves their touch point far enough.
12772b079e4031f42729056e999c20bf87281798eb9Michael Jurka     */
12872b079e4031f42729056e999c20bf87281798eb9Michael Jurka    protected void determineDraggingStart(MotionEvent ev) {
12972b079e4031f42729056e999c20bf87281798eb9Michael Jurka        /*
13072b079e4031f42729056e999c20bf87281798eb9Michael Jurka         * Locally do absolute value. mLastMotionX is set to the y value
13172b079e4031f42729056e999c20bf87281798eb9Michael Jurka         * of the down event.
13272b079e4031f42729056e999c20bf87281798eb9Michael Jurka         */
13372b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
13472b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final float x = ev.getX(pointerIndex);
13572b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final float y = ev.getY(pointerIndex);
13672b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int xDiff = (int) Math.abs(x - mLastMotionX);
13772b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int yDiff = (int) Math.abs(y - mLastMotionY);
13872b079e4031f42729056e999c20bf87281798eb9Michael Jurka
13972b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int touchSlop = mTouchSlop;
14072b079e4031f42729056e999c20bf87281798eb9Michael Jurka        boolean yMoved = yDiff > touchSlop;
14172b079e4031f42729056e999c20bf87281798eb9Michael Jurka        boolean isUpwardMotion = (yDiff / (float) xDiff) > mDragSlopeThreshold;
14272b079e4031f42729056e999c20bf87281798eb9Michael Jurka
14372b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (isUpwardMotion && yMoved && mLastTouchedItem != null) {
14472b079e4031f42729056e999c20bf87281798eb9Michael Jurka            // Drag if the user moved far enough along the Y axis
14572b079e4031f42729056e999c20bf87281798eb9Michael Jurka            beginDragging(mLastTouchedItem);
14672b079e4031f42729056e999c20bf87281798eb9Michael Jurka
14772b079e4031f42729056e999c20bf87281798eb9Michael Jurka            // Cancel any pending long press
14872b079e4031f42729056e999c20bf87281798eb9Michael Jurka            if (mAllowLongPress) {
14972b079e4031f42729056e999c20bf87281798eb9Michael Jurka                mAllowLongPress = false;
15072b079e4031f42729056e999c20bf87281798eb9Michael Jurka                // Try canceling the long press. It could also have been scheduled
15172b079e4031f42729056e999c20bf87281798eb9Michael Jurka                // by a distant descendant, so use the mAllowLongPress flag to block
15272b079e4031f42729056e999c20bf87281798eb9Michael Jurka                // everything
15372b079e4031f42729056e999c20bf87281798eb9Michael Jurka                final View currentPage = getPageAt(mCurrentPage);
15472b079e4031f42729056e999c20bf87281798eb9Michael Jurka                if (currentPage != null) {
15572b079e4031f42729056e999c20bf87281798eb9Michael Jurka                    currentPage.cancelLongPress();
15672b079e4031f42729056e999c20bf87281798eb9Michael Jurka                }
15772b079e4031f42729056e999c20bf87281798eb9Michael Jurka            }
15872b079e4031f42729056e999c20bf87281798eb9Michael Jurka        }
15972b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
16072b079e4031f42729056e999c20bf87281798eb9Michael Jurka
16172b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public void setDragSlopeThreshold(float dragSlopeThreshold) {
16272b079e4031f42729056e999c20bf87281798eb9Michael Jurka        mDragSlopeThreshold = dragSlopeThreshold;
16372b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
1642313eff05b8f52e72ebe06c589c419941fa019d6Patrick Dubroy
1652313eff05b8f52e72ebe06c589c419941fa019d6Patrick Dubroy    @Override
1662313eff05b8f52e72ebe06c589c419941fa019d6Patrick Dubroy    protected void onDetachedFromWindow() {
16794569f47c2fe3c2e04e993379d36e0597925e19fWinson Chung        cancelDragging();
1682313eff05b8f52e72ebe06c589c419941fa019d6Patrick Dubroy        super.onDetachedFromWindow();
1692313eff05b8f52e72ebe06c589c419941fa019d6Patrick Dubroy    }
1701afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung
1711afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung    /** Show the scrolling indicators when we move the page */
1721afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung    protected void onPageBeginMoving() {
1731afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung        showScrollingIndicator(false);
1741afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung    }
1751afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung    protected void onPageEndMoving() {
1761afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung        hideScrollingIndicator(false);
1771afedc379f8271b9bf86a8d887c3c020d5e59214Winson Chung    }
17872b079e4031f42729056e999c20bf87281798eb9Michael Jurka}
179