PagedViewWithDraggableItems.java revision 304dcde0e301c2f1a0b2bdc80ea8617930691b6e
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;
3872b079e4031f42729056e999c20bf87281798eb9Michael Jurka    private float mDragSlopeThreshold;
3972b079e4031f42729056e999c20bf87281798eb9Michael Jurka
4072b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public PagedViewWithDraggableItems(Context context) {
4172b079e4031f42729056e999c20bf87281798eb9Michael Jurka        super(context, null);
4272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
4372b079e4031f42729056e999c20bf87281798eb9Michael Jurka
4472b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public PagedViewWithDraggableItems(Context context, AttributeSet attrs) {
4572b079e4031f42729056e999c20bf87281798eb9Michael Jurka        super(context, attrs, 0);
4672b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
4772b079e4031f42729056e999c20bf87281798eb9Michael Jurka
4872b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public PagedViewWithDraggableItems(Context context, AttributeSet attrs, int defStyle) {
4972b079e4031f42729056e999c20bf87281798eb9Michael Jurka        super(context, attrs, defStyle);
5072b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
5172b079e4031f42729056e999c20bf87281798eb9Michael Jurka
5272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    protected boolean beginDragging(View v) {
53304dcde0e301c2f1a0b2bdc80ea8617930691b6eWinson Chung        boolean wasDragging = mIsDragging;
5472b079e4031f42729056e999c20bf87281798eb9Michael Jurka        mIsDragging = true;
55304dcde0e301c2f1a0b2bdc80ea8617930691b6eWinson Chung        return !wasDragging;
5672b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
5772b079e4031f42729056e999c20bf87281798eb9Michael Jurka
587d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    protected void cancelDragging() {
597d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        mIsDragging = false;
607d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        mLastTouchedItem = null;
617d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    }
627d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung
637d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    private void handleTouchEvent(MotionEvent ev) {
6472b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int action = ev.getAction();
6572b079e4031f42729056e999c20bf87281798eb9Michael Jurka        switch (action & MotionEvent.ACTION_MASK) {
6672b079e4031f42729056e999c20bf87281798eb9Michael Jurka            case MotionEvent.ACTION_DOWN:
677d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung                cancelDragging();
6872b079e4031f42729056e999c20bf87281798eb9Michael Jurka                break;
6972b079e4031f42729056e999c20bf87281798eb9Michael Jurka            case MotionEvent.ACTION_MOVE:
7072b079e4031f42729056e999c20bf87281798eb9Michael Jurka                if (mTouchState != TOUCH_STATE_SCROLLING && !mIsDragging) {
7172b079e4031f42729056e999c20bf87281798eb9Michael Jurka                    determineDraggingStart(ev);
7272b079e4031f42729056e999c20bf87281798eb9Michael Jurka                }
7372b079e4031f42729056e999c20bf87281798eb9Michael Jurka                break;
7472b079e4031f42729056e999c20bf87281798eb9Michael Jurka        }
7572b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
7672b079e4031f42729056e999c20bf87281798eb9Michael Jurka
7772b079e4031f42729056e999c20bf87281798eb9Michael Jurka    @Override
787d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    public boolean onInterceptTouchEvent(MotionEvent ev) {
797d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        handleTouchEvent(ev);
807d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        return super.onInterceptTouchEvent(ev);
8172b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
8272b079e4031f42729056e999c20bf87281798eb9Michael Jurka
8372b079e4031f42729056e999c20bf87281798eb9Michael Jurka    @Override
8472b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public boolean onTouchEvent(MotionEvent ev) {
857d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        handleTouchEvent(ev);
8672b079e4031f42729056e999c20bf87281798eb9Michael Jurka        return super.onTouchEvent(ev);
8772b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
8872b079e4031f42729056e999c20bf87281798eb9Michael Jurka
8972b079e4031f42729056e999c20bf87281798eb9Michael Jurka    @Override
907d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    public boolean onTouch(View v, MotionEvent event) {
917d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        mLastTouchedItem = v;
927d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung        return false;
937d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    }
947d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung
957d1fcbc409015a46219f088c6d5edbd8ab5012f8Winson Chung    @Override
9672b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public boolean onLongClick(View v) {
9772b079e4031f42729056e999c20bf87281798eb9Michael Jurka        // Return early if this is not initiated from a touch
9872b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (!v.isInTouchMode()) return false;
9972b079e4031f42729056e999c20bf87281798eb9Michael Jurka        // Return early if we are still animating the pages
10072b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (mNextPage != INVALID_PAGE) return false;
10172b079e4031f42729056e999c20bf87281798eb9Michael Jurka        return beginDragging(v);
10272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
10372b079e4031f42729056e999c20bf87281798eb9Michael Jurka
10472b079e4031f42729056e999c20bf87281798eb9Michael Jurka
10572b079e4031f42729056e999c20bf87281798eb9Michael Jurka    /*
10672b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * Determines if we should change the touch state to start scrolling after the
10772b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * user moves their touch point too far.
10872b079e4031f42729056e999c20bf87281798eb9Michael Jurka     */
10972b079e4031f42729056e999c20bf87281798eb9Michael Jurka    protected void determineScrollingStart(MotionEvent ev) {
11072b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (!mIsDragging) super.determineScrollingStart(ev);
11172b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
11272b079e4031f42729056e999c20bf87281798eb9Michael Jurka
11372b079e4031f42729056e999c20bf87281798eb9Michael Jurka    /*
11472b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * Determines if we should change the touch state to start dragging after the
11572b079e4031f42729056e999c20bf87281798eb9Michael Jurka     * user moves their touch point far enough.
11672b079e4031f42729056e999c20bf87281798eb9Michael Jurka     */
11772b079e4031f42729056e999c20bf87281798eb9Michael Jurka    protected void determineDraggingStart(MotionEvent ev) {
11872b079e4031f42729056e999c20bf87281798eb9Michael Jurka        /*
11972b079e4031f42729056e999c20bf87281798eb9Michael Jurka         * Locally do absolute value. mLastMotionX is set to the y value
12072b079e4031f42729056e999c20bf87281798eb9Michael Jurka         * of the down event.
12172b079e4031f42729056e999c20bf87281798eb9Michael Jurka         */
12272b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
12372b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final float x = ev.getX(pointerIndex);
12472b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final float y = ev.getY(pointerIndex);
12572b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int xDiff = (int) Math.abs(x - mLastMotionX);
12672b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int yDiff = (int) Math.abs(y - mLastMotionY);
12772b079e4031f42729056e999c20bf87281798eb9Michael Jurka
12872b079e4031f42729056e999c20bf87281798eb9Michael Jurka        final int touchSlop = mTouchSlop;
12972b079e4031f42729056e999c20bf87281798eb9Michael Jurka        boolean yMoved = yDiff > touchSlop;
13072b079e4031f42729056e999c20bf87281798eb9Michael Jurka        boolean isUpwardMotion = (yDiff / (float) xDiff) > mDragSlopeThreshold;
13172b079e4031f42729056e999c20bf87281798eb9Michael Jurka
13272b079e4031f42729056e999c20bf87281798eb9Michael Jurka        if (isUpwardMotion && yMoved && mLastTouchedItem != null) {
13372b079e4031f42729056e999c20bf87281798eb9Michael Jurka            // Drag if the user moved far enough along the Y axis
13472b079e4031f42729056e999c20bf87281798eb9Michael Jurka            beginDragging(mLastTouchedItem);
13572b079e4031f42729056e999c20bf87281798eb9Michael Jurka
13672b079e4031f42729056e999c20bf87281798eb9Michael Jurka            // Cancel any pending long press
13772b079e4031f42729056e999c20bf87281798eb9Michael Jurka            if (mAllowLongPress) {
13872b079e4031f42729056e999c20bf87281798eb9Michael Jurka                mAllowLongPress = false;
13972b079e4031f42729056e999c20bf87281798eb9Michael Jurka                // Try canceling the long press. It could also have been scheduled
14072b079e4031f42729056e999c20bf87281798eb9Michael Jurka                // by a distant descendant, so use the mAllowLongPress flag to block
14172b079e4031f42729056e999c20bf87281798eb9Michael Jurka                // everything
14272b079e4031f42729056e999c20bf87281798eb9Michael Jurka                final View currentPage = getPageAt(mCurrentPage);
14372b079e4031f42729056e999c20bf87281798eb9Michael Jurka                if (currentPage != null) {
14472b079e4031f42729056e999c20bf87281798eb9Michael Jurka                    currentPage.cancelLongPress();
14572b079e4031f42729056e999c20bf87281798eb9Michael Jurka                }
14672b079e4031f42729056e999c20bf87281798eb9Michael Jurka            }
14772b079e4031f42729056e999c20bf87281798eb9Michael Jurka        }
14872b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
14972b079e4031f42729056e999c20bf87281798eb9Michael Jurka
15072b079e4031f42729056e999c20bf87281798eb9Michael Jurka    public void setDragSlopeThreshold(float dragSlopeThreshold) {
15172b079e4031f42729056e999c20bf87281798eb9Michael Jurka        mDragSlopeThreshold = dragSlopeThreshold;
15272b079e4031f42729056e999c20bf87281798eb9Michael Jurka    }
15372b079e4031f42729056e999c20bf87281798eb9Michael Jurka}
154