12196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev/*
22196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Copyright (C) 2016 The Android Open Source Project
32196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev *
42196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Licensed under the Apache License, Version 2.0 (the "License");
52196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * you may not use this file except in compliance with the License.
62196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * You may obtain a copy of the License at
72196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev *
82196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev *      http://www.apache.org/licenses/LICENSE-2.0
92196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev *
102196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Unless required by applicable law or agreed to in writing, software
112196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * distributed under the License is distributed on an "AS IS" BASIS,
122196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * See the License for the specific language governing permissions and
142196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * limitations under the License.
152196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */
162196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
172196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevpackage android.support.v13.view;
182196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
192196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
202196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.graphics.Point;
212196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.support.v4.view.InputDeviceCompat;
222196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.support.v4.view.MotionEventCompat;
232196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.view.MotionEvent;
242196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.view.View;
252196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
262196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev/**
272196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * DragStartHelper is a utility class for implementing drag and drop support.
282196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p>
292196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It detects gestures commonly used to start drag (long click for any input source,
302196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * click and drag for mouse).
312196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p>
322196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It also keeps track of the screen location where the drag started, and helps determining
33d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * the hot spot position for a drag shadow.
342196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p>
35d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * Implement {@link DragStartHelper.OnDragStartListener} to start the drag operation:
362196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre>
37d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * DragStartHelper.OnDragStartListener listener = new DragStartHelper.OnDragStartListener {
38d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     protected void onDragStart(View view, DragStartHelper helper) {
39d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
40d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *             public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
41d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *                 super.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
42d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *                 helper.getTouchPosition(shadowTouchPoint);
43d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *             }
44d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         };
45d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         view.startDrag(mClipData, shadowBuilder, mLocalState, mDragFlags);
462196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev *     }
472196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * };
48d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * mDragStartHelper = new DragStartHelper(mDraggableView, listener);
492196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre>
502196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Once created, DragStartHelper can be attached to a view (this will replace existing long click
512196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * and touch listeners):
522196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre>
532196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * mDragStartHelper.attach();
542196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre>
552196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It may also be used in combination with existing listeners:
562196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre>
572196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * public boolean onTouch(View view, MotionEvent event) {
58d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     if (mDragStartHelper.onTouch(view, event)) {
59d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         return true;
60d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     }
61d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     return handleTouchEvent(view, event);
622196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * }
632196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * public boolean onLongClick(View view) {
64d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     if (mDragStartHelper.onLongClick(view)) {
65d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         return true;
66d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     }
67d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     return handleLongClickEvent(view);
682196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * }
692196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre>
702196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */
71d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheevpublic class DragStartHelper {
722196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    final private View mView;
73d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    final private OnDragStartListener mListener;
74d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev
75d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    private int mLastTouchX, mLastTouchY;
76d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev
77d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    /**
78d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev     * Interface definition for a callback to be invoked when a drag start gesture is detected.
79d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev     */
80d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public interface OnDragStartListener {
81d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        /**
82d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         * Called when a drag start gesture has been detected.
83d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         *
84d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         * @param v The view over which the drag start gesture has been detected.
85d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         * @param helper The DragStartHelper object which detected the gesture.
86d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         * @return True if the listener has consumed the event, false otherwise.
87d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         */
88d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        boolean onDragStart(View v, DragStartHelper helper);
89d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    }
902196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
912196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
922196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Create a DragStartHelper associated with the specified view.
93d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev     * The newly created helper is not initially attached to the view, {@link #attach} must be
942196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * called explicitly.
952196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param view A View
962196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
97d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public DragStartHelper(View view, OnDragStartListener listener) {
982196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        mView = view;
99d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        mListener = listener;
1002196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1012196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1022196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1032196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Attach the helper to the view.
1042196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * <p>
1052196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * This will replace previously existing touch and long click listeners.
1062196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
1072196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    public void attach() {
108d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        mView.setOnLongClickListener(mLongClickListener);
109d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        mView.setOnTouchListener(mTouchListener);
1102196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1112196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1122196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1132196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Detach the helper from the view.
1142196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * <p>
1152196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * This will reset touch and long click listeners to {@code null}.
1162196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
117d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public void detach() {
1182196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        mView.setOnLongClickListener(null);
1192196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        mView.setOnTouchListener(null);
1202196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1212196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1222196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1232196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Handle a touch event.
1242196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param v The view the touch event has been dispatched to.
1252196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param event The MotionEvent object containing full information about
1262196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     *        the event.
1272196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @return True if the listener has consumed the event, false otherwise.
1282196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
129d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public boolean onTouch(View v, MotionEvent event) {
1302196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        if (event.getAction() == MotionEvent.ACTION_DOWN ||
1312196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev                event.getAction() == MotionEvent.ACTION_MOVE) {
132d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev            mLastTouchX = (int) event.getX();
133d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev            mLastTouchY = (int) event.getY();
1342196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        }
1352196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        if (event.getAction() == MotionEvent.ACTION_MOVE &&
1362196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev                MotionEventCompat.isFromSource(event, InputDeviceCompat.SOURCE_MOUSE) &&
1372196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev                (MotionEventCompat.getButtonState(event) & MotionEventCompat.BUTTON_PRIMARY) != 0) {
138d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev            return mListener.onDragStart(v, this);
1392196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        }
1402196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        return false;
1412196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1422196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1432196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1442196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Handle a long click event.
1452196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param v The view that was clicked and held.
1462196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @return true if the callback consumed the long click, false otherwise.
1472196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
148d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public boolean onLongClick(View v) {
149d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        return mListener.onDragStart(v, this);
1502196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1512196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1522196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1532196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Compute the position of the touch event that started the drag operation.
1542196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param point The position of the touch event that started the drag operation.
1552196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
156d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public void getTouchPosition(Point point) {
157d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        point.set(mLastTouchX, mLastTouchY);
1582196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1592196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
160d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    private final View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
161d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        @Override
162d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        public boolean onLongClick(View v) {
163d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev            return DragStartHelper.this.onLongClick(v);
1642196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        }
165d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    };
1662196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
167d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
1682196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        @Override
169d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        public boolean onTouch(View v, MotionEvent event) {
170d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev            return DragStartHelper.this.onTouch(v, event);
1712196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        }
172d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    };
1732196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev}
1742196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
175