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