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