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 17 package com.android.quickstep.util 18 19 import android.graphics.Region 20 import android.os.RemoteException 21 import android.util.Log 22 import android.view.Display.DEFAULT_DISPLAY 23 import android.view.ISystemGestureExclusionListener 24 import android.view.IWindowManager 25 import android.view.WindowManagerGlobal 26 import androidx.annotation.BinderThread 27 import androidx.annotation.VisibleForTesting 28 import com.android.launcher3.util.Executors 29 30 /** Wrapper over system gesture exclusion listener to optimize for multiple RPCs */ 31 class GestureExclusionManager(private val windowManager: IWindowManager) { 32 33 private val listeners = mutableListOf<ExclusionListener>() 34 35 private var lastExclusionRegion: Region? = null 36 private var lastUnrestrictedOrNull: Region? = null 37 38 @VisibleForTesting 39 val exclusionListener = 40 object : ISystemGestureExclusionListener.Stub() { 41 @BinderThread onSystemGestureExclusionChangednull42 override fun onSystemGestureExclusionChanged( 43 displayId: Int, 44 exclusionRegion: Region?, 45 unrestrictedOrNull: Region? 46 ) { 47 if (displayId != DEFAULT_DISPLAY) { 48 return 49 } 50 Executors.MAIN_EXECUTOR.execute { 51 lastExclusionRegion = exclusionRegion 52 lastUnrestrictedOrNull = unrestrictedOrNull 53 listeners.forEach { 54 it.onGestureExclusionChanged(exclusionRegion, unrestrictedOrNull) 55 } 56 } 57 } 58 } 59 60 /** Adds a listener for receiving gesture exclusion regions */ addListenernull61 fun addListener(listener: ExclusionListener) { 62 val wasEmpty = listeners.isEmpty() 63 listeners.add(listener) 64 if (wasEmpty) { 65 Executors.UI_HELPER_EXECUTOR.execute { 66 try { 67 windowManager.registerSystemGestureExclusionListener( 68 exclusionListener, 69 DEFAULT_DISPLAY 70 ) 71 } catch (e: RemoteException) { 72 Log.e(TAG, "Failed to register gesture exclusion listener", e) 73 } 74 } 75 } else { 76 // If we had already registered before, dispatch the last known value, 77 // otherwise registering the listener will initiate a dispatch 78 listener.onGestureExclusionChanged(lastExclusionRegion, lastUnrestrictedOrNull) 79 } 80 } 81 82 /** Removes a previously added exclusion listener */ removeListenernull83 fun removeListener(listener: ExclusionListener) { 84 if (listeners.remove(listener) && listeners.isEmpty()) { 85 Executors.UI_HELPER_EXECUTOR.execute { 86 try { 87 windowManager.unregisterSystemGestureExclusionListener( 88 exclusionListener, 89 DEFAULT_DISPLAY 90 ) 91 } catch (e: RemoteException) { 92 Log.e(TAG, "Failed to unregister gesture exclusion listener", e) 93 } 94 } 95 } 96 } 97 98 interface ExclusionListener { onGestureExclusionChangednull99 fun onGestureExclusionChanged(exclusionRegion: Region?, unrestrictedOrNull: Region?) 100 } 101 102 companion object { 103 104 private const val TAG = "GestureExclusionManager" 105 106 @JvmField 107 val INSTANCE = GestureExclusionManager(WindowManagerGlobal.getWindowManagerService()!!) 108 109 @JvmField val EMPTY_REGION = Region() 110 } 111 } 112