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