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