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
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.view;
182196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
192196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
202196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.graphics.Point;
212196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.view.MotionEvent;
222196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.view.View;
232196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
242196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev/**
252196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * DragStartHelper is a utility class for implementing drag and drop support.
262196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p>
272196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It detects gestures commonly used to start drag (long click for any input source,
282196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * click and drag for mouse).
292196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p>
302196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It also keeps track of the screen location where the drag started, and helps determining
31d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * the hot spot position for a drag shadow.
322196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p>
33d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * Implement {@link DragStartHelper.OnDragStartListener} to start the drag operation:
342196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre>
35d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * DragStartHelper.OnDragStartListener listener = new DragStartHelper.OnDragStartListener {
36d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     protected void onDragStart(View view, DragStartHelper helper) {
37d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
38d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *             public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
39d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *                 super.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
40d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *                 helper.getTouchPosition(shadowTouchPoint);
41d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *             }
42d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         };
43d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         view.startDrag(mClipData, shadowBuilder, mLocalState, mDragFlags);
442196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev *     }
452196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * };
46d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * mDragStartHelper = new DragStartHelper(mDraggableView, listener);
472196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre>
482196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Once created, DragStartHelper can be attached to a view (this will replace existing long click
492196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * and touch listeners):
502196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre>
512196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * mDragStartHelper.attach();
522196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre>
532196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It may also be used in combination with existing listeners:
542196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre>
552196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * public boolean onTouch(View view, MotionEvent event) {
56d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     if (mDragStartHelper.onTouch(view, event)) {
57d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         return true;
58d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     }
59d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     return handleTouchEvent(view, event);
602196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * }
612196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * public boolean onLongClick(View view) {
62d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     if (mDragStartHelper.onLongClick(view)) {
63d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *         return true;
64d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     }
65d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev *     return handleLongClickEvent(view);
662196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * }
672196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre>
682196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */
69d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheevpublic class DragStartHelper {
7077c4475e11ced0ba944995553aedb7aa4fbbf355Aurimas Liutikas    private final View mView;
7177c4475e11ced0ba944995553aedb7aa4fbbf355Aurimas Liutikas    private final OnDragStartListener mListener;
72d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev
73d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    private int mLastTouchX, mLastTouchY;
748a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev    private boolean mDragging;
75d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev
76d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    /**
77d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev     * Interface definition for a callback to be invoked when a drag start gesture is detected.
78d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev     */
79d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public interface OnDragStartListener {
80d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        /**
81d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         * Called when a drag start gesture has been detected.
82d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         *
83d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         * @param v The view over which the drag start gesture has been detected.
84d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         * @param helper The DragStartHelper object which detected the gesture.
858a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev         * @return True if the listener has started the drag operation, false otherwise.
86d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev         */
87d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        boolean onDragStart(View v, DragStartHelper helper);
88d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    }
892196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
902196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
912196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Create a DragStartHelper associated with the specified view.
92d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev     * The newly created helper is not initially attached to the view, {@link #attach} must be
932196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * called explicitly.
942196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param view A View
952196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
96d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public DragStartHelper(View view, OnDragStartListener listener) {
972196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        mView = view;
98d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        mListener = listener;
992196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1002196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1012196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1022196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Attach the helper to the view.
1032196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * <p>
1042196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * This will replace previously existing touch and long click listeners.
1052196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
1062196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    public void attach() {
107d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        mView.setOnLongClickListener(mLongClickListener);
108d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        mView.setOnTouchListener(mTouchListener);
1092196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1102196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1112196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1122196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Detach the helper from the view.
1132196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * <p>
1142196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * This will reset touch and long click listeners to {@code null}.
1152196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
116d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public void detach() {
1172196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        mView.setOnLongClickListener(null);
1182196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        mView.setOnTouchListener(null);
1192196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1202196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1212196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1222196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Handle a touch event.
1232196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param v The view the touch event has been dispatched to.
1242196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param event The MotionEvent object containing full information about
1252196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     *        the event.
1262196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @return True if the listener has consumed the event, false otherwise.
1272196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
128d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public boolean onTouch(View v, MotionEvent event) {
1298a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev        final int x = (int) event.getX();
1308a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev        final int y = (int) event.getY();
1318a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev        switch (event.getAction()) {
1328a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev            case MotionEvent.ACTION_DOWN:
1338a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                mLastTouchX = x;
1348a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                mLastTouchY = y;
1358a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                break;
1368a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev
1378a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev            case MotionEvent.ACTION_MOVE:
1388a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                if (!MotionEventCompat.isFromSource(event, InputDeviceCompat.SOURCE_MOUSE)
1396ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                        || (event.getButtonState()
1406ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                                & MotionEvent.BUTTON_PRIMARY) == 0) {
1418a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                    break;
1428a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                }
1438a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                if (mDragging) {
1448a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                    // Ignore ACTION_MOVE events once the drag operation is in progress.
1458a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                    break;
1468a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                }
1478a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                if (mLastTouchX == x && mLastTouchY == y) {
1488a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                    // Do not call the listener unless the pointer position has actually changed.
1498a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                    break;
1508a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                }
1518a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                mLastTouchX = x;
1528a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                mLastTouchY = y;
1538a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                mDragging = mListener.onDragStart(v, this);
1548a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                return mDragging;
1558a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev
1568a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev            case MotionEvent.ACTION_UP:
1578a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev            case MotionEvent.ACTION_CANCEL:
1588a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                mDragging = false;
1598a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev                break;
1602196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        }
1612196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        return false;
1622196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1632196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1642196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1652196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Handle a long click event.
1662196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param v The view that was clicked and held.
1672196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @return true if the callback consumed the long click, false otherwise.
1682196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
169d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public boolean onLongClick(View v) {
170d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        return mListener.onDragStart(v, this);
1712196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1722196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
1732196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    /**
1742196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * Compute the position of the touch event that started the drag operation.
1752196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     * @param point The position of the touch event that started the drag operation.
1762196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev     */
177d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    public void getTouchPosition(Point point) {
178d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        point.set(mLastTouchX, mLastTouchY);
1792196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev    }
1802196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
181d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    private final View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
182d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        @Override
183d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        public boolean onLongClick(View v) {
184d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev            return DragStartHelper.this.onLongClick(v);
1852196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        }
186d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    };
1872196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
188d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
1892196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        @Override
190d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev        public boolean onTouch(View v, MotionEvent event) {
191d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev            return DragStartHelper.this.onTouch(v, event);
1922196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev        }
193d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev    };
1942196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev}
1952196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev
196