1 /* 2 * Copyright (C) 2023 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.wm.shell.draganddrop; 18 19 import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; 20 import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; 21 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; 22 23 import android.app.PendingIntent; 24 import android.content.ClipData; 25 import android.content.ClipDescription; 26 import android.view.DragEvent; 27 import android.view.View; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.Nullable; 31 32 /** Collection of utility classes for handling drag and drop. */ 33 public class DragUtils { 34 private static final String TAG = "DragUtils"; 35 36 /** 37 * Returns whether we can handle this particular drag. 38 */ canHandleDrag(DragEvent event)39 public static boolean canHandleDrag(DragEvent event) { 40 if (event.getClipData().getItemCount() <= 0) { 41 // No clip data, ignore this drag 42 return false; 43 } 44 if (isAppDrag(event.getClipDescription())) { 45 // Clip data contains an app drag initiated from SysUI, handle it 46 return true; 47 } 48 if (com.android.window.flags.Flags.delegateUnhandledDrags() 49 && getLaunchIntent(event) != null) { 50 // Clip data contains a launchable intent drag, handle it 51 return true; 52 } 53 // Otherwise ignore 54 return false; 55 } 56 57 /** 58 * Returns whether this clip data description represents an app drag. 59 */ isAppDrag(ClipDescription description)60 public static boolean isAppDrag(ClipDescription description) { 61 return description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY) 62 || description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT) 63 || description.hasMimeType(MIMETYPE_APPLICATION_TASK); 64 } 65 66 /** 67 * Returns a launchable intent in the given `DragEvent` or `null` if there is none. 68 */ 69 @Nullable getLaunchIntent(@onNull DragEvent dragEvent)70 public static PendingIntent getLaunchIntent(@NonNull DragEvent dragEvent) { 71 return getLaunchIntent(dragEvent.getClipData(), dragEvent.getDragFlags()); 72 } 73 74 /** 75 * Returns a launchable intent in the given `ClipData` or `null` if there is none. 76 */ 77 @Nullable getLaunchIntent(@onNull ClipData data, int dragFlags)78 public static PendingIntent getLaunchIntent(@NonNull ClipData data, int dragFlags) { 79 if ((dragFlags & View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) == 0) { 80 // Disallow launching the intent if the app does not want to delegate it to the system 81 return null; 82 } 83 for (int i = 0; i < data.getItemCount(); i++) { 84 final ClipData.Item item = data.getItemAt(i); 85 if (item.getIntentSender() != null) { 86 final PendingIntent intent = new PendingIntent(item.getIntentSender().getTarget()); 87 if (intent != null && intent.isActivity()) { 88 return intent; 89 } 90 } 91 } 92 return null; 93 } 94 95 /** 96 * Returns a list of the mime types provided in the clip description. 97 */ getMimeTypesConcatenated(ClipDescription description)98 public static String getMimeTypesConcatenated(ClipDescription description) { 99 String mimeTypes = ""; 100 for (int i = 0; i < description.getMimeTypeCount(); i++) { 101 if (i > 0) { 102 mimeTypes += ", "; 103 } 104 mimeTypes += description.getMimeType(i); 105 } 106 return mimeTypes; 107 } 108 } 109