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