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; 768a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private boolean mDragging; 77d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev 78d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev /** 79d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * Interface definition for a callback to be invoked when a drag start gesture is detected. 80d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev */ 81d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public interface OnDragStartListener { 82d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev /** 83d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * Called when a drag start gesture has been detected. 84d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * 85d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * @param v The view over which the drag start gesture has been detected. 86d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * @param helper The DragStartHelper object which detected the gesture. 878a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * @return True if the listener has started the drag operation, false otherwise. 88d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev */ 89d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev boolean onDragStart(View v, DragStartHelper helper); 90d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev } 912196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 922196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 932196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Create a DragStartHelper associated with the specified view. 94d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * The newly created helper is not initially attached to the view, {@link #attach} must be 952196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * called explicitly. 962196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param view A View 972196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 98d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public DragStartHelper(View view, OnDragStartListener listener) { 992196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev mView = view; 100d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev mListener = listener; 1012196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1022196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1032196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1042196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Attach the helper to the view. 1052196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p> 1062196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * This will replace previously existing touch and long click listeners. 1072196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 1082196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev public void attach() { 109d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev mView.setOnLongClickListener(mLongClickListener); 110d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev mView.setOnTouchListener(mTouchListener); 1112196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1122196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1132196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1142196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Detach the helper from the view. 1152196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p> 1162196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * This will reset touch and long click listeners to {@code null}. 1172196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 118d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public void detach() { 1192196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev mView.setOnLongClickListener(null); 1202196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev mView.setOnTouchListener(null); 1212196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1222196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1232196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1242196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Handle a touch event. 1252196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param v The view the touch event has been dispatched to. 1262196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param event The MotionEvent object containing full information about 1272196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * the event. 1282196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @return True if the listener has consumed the event, false otherwise. 1292196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 130d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onTouch(View v, MotionEvent event) { 1318a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final int x = (int) event.getX(); 1328a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final int y = (int) event.getY(); 1338a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev switch (event.getAction()) { 1348a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_DOWN: 1358a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchX = x; 1368a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchY = y; 1378a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1388a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1398a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_MOVE: 1408a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev if (!MotionEventCompat.isFromSource(event, InputDeviceCompat.SOURCE_MOUSE) 1416ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas || (event.getButtonState() 1426ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas & MotionEvent.BUTTON_PRIMARY) == 0) { 1438a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1448a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1458a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev if (mDragging) { 1468a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Ignore ACTION_MOVE events once the drag operation is in progress. 1478a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1488a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1498a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev if (mLastTouchX == x && mLastTouchY == y) { 1508a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Do not call the listener unless the pointer position has actually changed. 1518a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1528a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1538a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchX = x; 1548a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchY = y; 1558a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mDragging = mListener.onDragStart(v, this); 1568a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return mDragging; 1578a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1588a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_UP: 1598a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_CANCEL: 1608a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mDragging = false; 1618a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1622196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1632196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev return false; 1642196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1652196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1662196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1672196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Handle a long click event. 1682196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param v The view that was clicked and held. 1692196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @return true if the callback consumed the long click, false otherwise. 1702196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 171d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onLongClick(View v) { 172d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev return mListener.onDragStart(v, this); 1732196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1742196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1752196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1762196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Compute the position of the touch event that started the drag operation. 1772196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param point The position of the touch event that started the drag operation. 1782196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 179d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public void getTouchPosition(Point point) { 180d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev point.set(mLastTouchX, mLastTouchY); 1812196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1822196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 183d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev private final View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() { 184d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev @Override 185d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onLongClick(View v) { 186d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev return DragStartHelper.this.onLongClick(v); 1872196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 188d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev }; 1892196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 190d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev private final View.OnTouchListener mTouchListener = new View.OnTouchListener() { 1912196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev @Override 192d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onTouch(View v, MotionEvent event) { 193d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev return DragStartHelper.this.onTouch(v, event); 1942196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 195d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev }; 1962196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev} 1972196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 198