1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.dragndrop; 18 19 import android.os.SystemClock; 20 import android.view.DragEvent; 21 import android.view.MotionEvent; 22 23 import java.util.function.Consumer; 24 25 /** 26 * Base class for driving a drag/drop operation. 27 */ 28 public abstract class DragDriver { 29 30 protected final EventListener mEventListener; 31 protected final Consumer<MotionEvent> mSecondaryEventConsumer; 32 33 public interface EventListener { onDriverDragMove(float x, float y)34 void onDriverDragMove(float x, float y); onDriverDragExitWindow()35 void onDriverDragExitWindow(); onDriverDragEnd(float x, float y)36 void onDriverDragEnd(float x, float y); onDriverDragCancel()37 void onDriverDragCancel(); 38 } 39 DragDriver(EventListener eventListener, Consumer<MotionEvent> sec)40 public DragDriver(EventListener eventListener, Consumer<MotionEvent> sec) { 41 mEventListener = eventListener; 42 mSecondaryEventConsumer = sec; 43 } 44 45 /** 46 * Called to handle system touch event 47 */ onTouchEvent(MotionEvent ev)48 public boolean onTouchEvent(MotionEvent ev) { 49 return false; 50 } 51 52 /** 53 * Called to handle system touch intercept event 54 */ onInterceptTouchEvent(MotionEvent ev)55 public boolean onInterceptTouchEvent(MotionEvent ev) { 56 return false; 57 } 58 59 /** 60 * Called to handle system drag event 61 */ onDragEvent(DragEvent event)62 public boolean onDragEvent(DragEvent event) { 63 return false; 64 } 65 66 /** 67 * Created a driver for handing the actual events 68 */ create(DragController dragController, DragOptions options, Consumer<MotionEvent> sec)69 public static DragDriver create(DragController dragController, DragOptions options, 70 Consumer<MotionEvent> sec) { 71 if (options.simulatedDndStartPoint != null) { 72 if (options.isAccessibleDrag) { 73 return null; 74 } 75 return new SystemDragDriver(dragController, sec); 76 } else { 77 return new InternalDragDriver(dragController, sec); 78 } 79 } 80 81 /** 82 * Class for driving a system (i.e. framework) drag/drop operation. 83 */ 84 static class SystemDragDriver extends DragDriver { 85 86 private final long mDragStartTime; 87 float mLastX = 0; 88 float mLastY = 0; 89 SystemDragDriver(DragController dragController, Consumer<MotionEvent> sec)90 SystemDragDriver(DragController dragController, Consumer<MotionEvent> sec) { 91 super(dragController, sec); 92 mDragStartTime = SystemClock.uptimeMillis(); 93 } 94 95 @Override onInterceptTouchEvent(MotionEvent ev)96 public boolean onInterceptTouchEvent(MotionEvent ev) { 97 return false; 98 } 99 100 /** 101 * It creates a temporary {@link MotionEvent} object for secondary consumer 102 */ simulateSecondaryMotionEvent(DragEvent event)103 private void simulateSecondaryMotionEvent(DragEvent event) { 104 final int motionAction; 105 switch (event.getAction()) { 106 case DragEvent.ACTION_DRAG_STARTED: 107 motionAction = MotionEvent.ACTION_DOWN; 108 break; 109 case DragEvent.ACTION_DRAG_LOCATION: 110 motionAction = MotionEvent.ACTION_MOVE; 111 break; 112 case DragEvent.ACTION_DRAG_ENDED: 113 motionAction = MotionEvent.ACTION_UP; 114 break; 115 default: 116 return; 117 } 118 MotionEvent emulatedEvent = MotionEvent.obtain(mDragStartTime, 119 SystemClock.uptimeMillis(), motionAction, event.getX(), event.getY(), 0); 120 mSecondaryEventConsumer.accept(emulatedEvent); 121 emulatedEvent.recycle(); 122 } 123 124 @Override onDragEvent(DragEvent event)125 public boolean onDragEvent(DragEvent event) { 126 simulateSecondaryMotionEvent(event); 127 final int action = event.getAction(); 128 129 switch (action) { 130 case DragEvent.ACTION_DRAG_STARTED: 131 mLastX = event.getX(); 132 mLastY = event.getY(); 133 return true; 134 135 case DragEvent.ACTION_DRAG_ENTERED: 136 return true; 137 138 case DragEvent.ACTION_DRAG_LOCATION: 139 mLastX = event.getX(); 140 mLastY = event.getY(); 141 mEventListener.onDriverDragMove(event.getX(), event.getY()); 142 return true; 143 144 case DragEvent.ACTION_DROP: 145 mLastX = event.getX(); 146 mLastY = event.getY(); 147 mEventListener.onDriverDragMove(event.getX(), event.getY()); 148 mEventListener.onDriverDragEnd(mLastX, mLastY); 149 return true; 150 case DragEvent.ACTION_DRAG_EXITED: 151 mEventListener.onDriverDragExitWindow(); 152 return true; 153 154 case DragEvent.ACTION_DRAG_ENDED: 155 mEventListener.onDriverDragCancel(); 156 return true; 157 158 default: 159 return false; 160 } 161 } 162 } 163 164 /** 165 * Class for driving an internal (i.e. not using framework) drag/drop operation. 166 */ 167 static class InternalDragDriver extends DragDriver { 168 private final DragController mDragController; 169 InternalDragDriver(DragController dragController, Consumer<MotionEvent> sec)170 InternalDragDriver(DragController dragController, Consumer<MotionEvent> sec) { 171 super(dragController, sec); 172 mDragController = dragController; 173 } 174 175 @Override onTouchEvent(MotionEvent ev)176 public boolean onTouchEvent(MotionEvent ev) { 177 mSecondaryEventConsumer.accept(ev); 178 final int action = ev.getAction(); 179 180 switch (action) { 181 case MotionEvent.ACTION_MOVE: 182 mEventListener.onDriverDragMove(mDragController.getX(ev), 183 mDragController.getY(ev)); 184 break; 185 case MotionEvent.ACTION_UP: 186 mEventListener.onDriverDragMove(mDragController.getX(ev), 187 mDragController.getY(ev)); 188 mEventListener.onDriverDragEnd(mDragController.getX(ev), 189 mDragController.getY(ev)); 190 break; 191 case MotionEvent.ACTION_CANCEL: 192 mEventListener.onDriverDragCancel(); 193 break; 194 } 195 196 return true; 197 } 198 199 onInterceptTouchEvent(MotionEvent ev)200 public boolean onInterceptTouchEvent(MotionEvent ev) { 201 mSecondaryEventConsumer.accept(ev); 202 final int action = ev.getAction(); 203 204 switch (action) { 205 case MotionEvent.ACTION_UP: 206 mEventListener.onDriverDragEnd(mDragController.getX(ev), 207 mDragController.getY(ev)); 208 break; 209 case MotionEvent.ACTION_CANCEL: 210 mEventListener.onDriverDragCancel(); 211 break; 212 } 213 return true; 214 } 215 } 216 } 217 218 219