1 /* 2 * Copyright (C) 2024 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 package com.android.wm.shell.draganddrop 17 18 import android.app.ActivityManager 19 import android.os.RemoteException 20 import android.os.Trace 21 import android.os.Trace.TRACE_TAG_WINDOW_MANAGER 22 import android.util.Log 23 import android.view.DragEvent 24 import android.view.IWindowManager 25 import android.window.IGlobalDragListener 26 import android.window.IUnhandledDragCallback 27 import androidx.annotation.VisibleForTesting 28 import com.android.internal.protolog.common.ProtoLog 29 import com.android.wm.shell.common.ShellExecutor 30 import com.android.wm.shell.protolog.ShellProtoLogGroup 31 import java.util.function.Consumer 32 import kotlin.random.Random 33 34 /** 35 * Manages the listener and callbacks for unhandled global drags. 36 * This is only used by DragAndDropController and should not be used directly by other classes. 37 */ 38 class GlobalDragListener( 39 private val wmService: IWindowManager, 40 private val mainExecutor: ShellExecutor 41 ) { 42 private var callback: GlobalDragListenerCallback? = null 43 44 private val globalDragListener: IGlobalDragListener = 45 object : IGlobalDragListener.Stub() { onCrossWindowDropnull46 override fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) { 47 mainExecutor.execute() { 48 this@GlobalDragListener.onCrossWindowDrop(taskInfo) 49 } 50 } 51 onUnhandledDropnull52 override fun onUnhandledDrop(event: DragEvent, callback: IUnhandledDragCallback) { 53 mainExecutor.execute() { 54 this@GlobalDragListener.onUnhandledDrop(event, callback) 55 } 56 } 57 } 58 59 /** 60 * Callbacks for global drag events. 61 */ 62 interface GlobalDragListenerCallback { 63 /** 64 * Called when a global drag is successfully handled by another window. 65 */ onCrossWindowDropnull66 fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {} 67 68 /** 69 * Called when a global drag is unhandled (ie. dropped outside of all visible windows, or 70 * dropped on a window that does not want to handle it). 71 * 72 * The implementer _must_ call onFinishedCallback, and if it consumes the drop, then it is 73 * also responsible for releasing up the drag surface provided via the drag event. 74 */ onUnhandledDropnull75 fun onUnhandledDrop(dragEvent: DragEvent, onFinishedCallback: Consumer<Boolean>) {} 76 } 77 78 /** 79 * Sets a listener for callbacks when an unhandled drag happens. 80 */ setListenernull81 fun setListener(listener: GlobalDragListenerCallback?) { 82 val updateWm = (callback == null && listener != null) 83 || (callback != null && listener == null) 84 callback = listener 85 if (updateWm) { 86 try { 87 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, 88 "%s unhandled drag listener", 89 if (callback != null) "Registering" else "Unregistering") 90 wmService.setGlobalDragListener( 91 if (callback != null) globalDragListener else null) 92 } catch (e: RemoteException) { 93 Log.e(TAG, "Failed to set unhandled drag listener") 94 } 95 } 96 } 97 98 @VisibleForTesting onCrossWindowDropnull99 fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) { 100 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, 101 "onCrossWindowDrop: %s", taskInfo) 102 callback?.onCrossWindowDrop(taskInfo) 103 } 104 105 @VisibleForTesting onUnhandledDropnull106 fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) { 107 val traceCookie = Random.nextInt() 108 Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop", 109 traceCookie); 110 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, 111 "onUnhandledDrop: %s", dragEvent) 112 if (callback == null) { 113 wmCallback.notifyUnhandledDropComplete(false) 114 Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop", 115 traceCookie); 116 return 117 } 118 119 callback?.onUnhandledDrop(dragEvent) { 120 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, 121 "Notifying onUnhandledDrop complete: %b", it) 122 wmCallback.notifyUnhandledDropComplete(it) 123 Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop", 124 traceCookie); 125 } 126 } 127 128 companion object { 129 private val TAG = GlobalDragListener::class.java.simpleName 130 } 131 } 132