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 17package com.android.launcher3.dragndrop; 18 19import android.content.ClipData; 20import android.content.ClipDescription; 21import android.content.Context; 22import android.content.Intent; 23import android.view.DragEvent; 24import android.view.MotionEvent; 25 26import com.android.launcher3.DropTarget; 27import com.android.launcher3.DropTarget.DragObject; 28import com.android.launcher3.InstallShortcutReceiver; 29import com.android.launcher3.ShortcutInfo; 30import com.android.launcher3.Utilities; 31 32import java.util.ArrayList; 33 34/** 35 * Base class for driving a drag/drop operation. 36 */ 37public abstract class DragDriver { 38 protected final EventListener mEventListener; 39 40 public interface EventListener { 41 void onDriverDragMove(float x, float y); 42 void onDriverDragExitWindow(); 43 void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride); 44 void onDriverDragCancel(); 45 } 46 47 public DragDriver(EventListener eventListener) { 48 mEventListener = eventListener; 49 } 50 51 /** 52 * Handles ending of the DragView animation. 53 */ 54 public void onDragViewAnimationEnd() { } 55 56 public boolean onTouchEvent(MotionEvent ev) { 57 final int action = ev.getAction(); 58 59 switch (action) { 60 case MotionEvent.ACTION_MOVE: 61 mEventListener.onDriverDragMove(ev.getX(), ev.getY()); 62 break; 63 case MotionEvent.ACTION_UP: 64 mEventListener.onDriverDragMove(ev.getX(), ev.getY()); 65 mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null); 66 break; 67 case MotionEvent.ACTION_CANCEL: 68 mEventListener.onDriverDragCancel(); 69 break; 70 } 71 72 return true; 73 } 74 75 public abstract boolean onDragEvent (DragEvent event); 76 77 78 public boolean onInterceptTouchEvent(MotionEvent ev) { 79 final int action = ev.getAction(); 80 81 switch (action) { 82 case MotionEvent.ACTION_UP: 83 mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null); 84 break; 85 case MotionEvent.ACTION_CANCEL: 86 mEventListener.onDriverDragCancel(); 87 break; 88 } 89 90 return true; 91 } 92 93 public static DragDriver create(Context context, DragController dragController, 94 DragObject dragObject, DragOptions options) { 95 if (Utilities.isNycOrAbove() && options.systemDndStartPoint != null) { 96 return new SystemDragDriver(dragController, context, dragObject); 97 } else { 98 return new InternalDragDriver(dragController); 99 } 100 } 101} 102 103/** 104 * Class for driving a system (i.e. framework) drag/drop operation. 105 */ 106class SystemDragDriver extends DragDriver { 107 108 private final DragObject mDragObject; 109 private final Context mContext; 110 111 boolean mReceivedDropEvent = false; 112 float mLastX = 0; 113 float mLastY = 0; 114 115 public SystemDragDriver(DragController dragController, Context context, DragObject dragObject) { 116 super(dragController); 117 mDragObject = dragObject; 118 mContext = context; 119 } 120 121 @Override 122 public boolean onTouchEvent(MotionEvent ev) { 123 return false; 124 } 125 126 @Override 127 public boolean onInterceptTouchEvent(MotionEvent ev) { 128 return false; 129 } 130 131 @Override 132 public boolean onDragEvent (DragEvent event) { 133 final int action = event.getAction(); 134 135 switch (action) { 136 case DragEvent.ACTION_DRAG_STARTED: 137 mLastX = event.getX(); 138 mLastY = event.getY(); 139 return true; 140 141 case DragEvent.ACTION_DRAG_ENTERED: 142 return true; 143 144 case DragEvent.ACTION_DRAG_LOCATION: 145 mLastX = event.getX(); 146 mLastY = event.getY(); 147 mEventListener.onDriverDragMove(event.getX(), event.getY()); 148 return true; 149 150 case DragEvent.ACTION_DROP: 151 mLastX = event.getX(); 152 mLastY = event.getY(); 153 mReceivedDropEvent = 154 updateInfoFromClipData(event.getClipData(), event.getClipDescription()); 155 return mReceivedDropEvent; 156 157 case DragEvent.ACTION_DRAG_EXITED: 158 mEventListener.onDriverDragExitWindow(); 159 return true; 160 161 case DragEvent.ACTION_DRAG_ENDED: 162 if (mReceivedDropEvent) { 163 mEventListener.onDriverDragEnd(mLastX, mLastY, null); 164 } else { 165 mEventListener.onDriverDragCancel(); 166 } 167 return true; 168 169 default: 170 return false; 171 } 172 } 173 174 private boolean updateInfoFromClipData(ClipData data, ClipDescription desc) { 175 if (data == null) { 176 return false; 177 } 178 ArrayList<Intent> intents = new ArrayList<>(); 179 int itemCount = data.getItemCount(); 180 for (int i = 0; i < itemCount; i++) { 181 Intent intent = data.getItemAt(i).getIntent(); 182 if (intent == null) { 183 continue; 184 } 185 186 // Give preference to shortcut intents. 187 if (!Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction())) { 188 intents.add(intent); 189 continue; 190 } 191 ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, intent); 192 if (info != null) { 193 mDragObject.dragInfo = info; 194 return true; 195 } 196 return true; 197 } 198 199 // Try creating shortcuts just using the intent and label 200 Intent fullIntent = new Intent().putExtra(Intent.EXTRA_SHORTCUT_NAME, desc.getLabel()); 201 for (Intent intent : intents) { 202 fullIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent); 203 ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, fullIntent); 204 if (info != null) { 205 mDragObject.dragInfo = info; 206 return true; 207 } 208 } 209 210 return false; 211 } 212} 213 214/** 215 * Class for driving an internal (i.e. not using framework) drag/drop operation. 216 */ 217class InternalDragDriver extends DragDriver { 218 public InternalDragDriver(DragController dragController) { 219 super(dragController); 220 } 221 222 @Override 223 public boolean onDragEvent (DragEvent event) { return false; } 224}; 225