1 /* 2 * Copyright (C) 2021 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.common; 18 19 import android.os.RemoteException; 20 import android.util.Slog; 21 import android.util.SparseArray; 22 import android.view.IDisplayWindowInsetsController; 23 import android.view.IWindowManager; 24 import android.view.InsetsSourceControl; 25 import android.view.InsetsState; 26 27 import androidx.annotation.BinderThread; 28 29 import com.android.wm.shell.common.annotations.ShellMainThread; 30 31 import java.util.concurrent.CopyOnWriteArrayList; 32 33 /** 34 * Manages insets from the core. 35 */ 36 public class DisplayInsetsController implements DisplayController.OnDisplaysChangedListener { 37 private static final String TAG = "DisplayInsetsController"; 38 39 private final IWindowManager mWmService; 40 private final ShellExecutor mMainExecutor; 41 private final DisplayController mDisplayController; 42 private final SparseArray<PerDisplay> mInsetsPerDisplay = new SparseArray<>(); 43 private final SparseArray<CopyOnWriteArrayList<OnInsetsChangedListener>> mListeners = 44 new SparseArray<>(); 45 DisplayInsetsController(IWindowManager wmService, DisplayController displayController, ShellExecutor mainExecutor)46 public DisplayInsetsController(IWindowManager wmService, DisplayController displayController, 47 ShellExecutor mainExecutor) { 48 mWmService = wmService; 49 mDisplayController = displayController; 50 mMainExecutor = mainExecutor; 51 } 52 53 /** 54 * Starts listening for insets for each display. 55 **/ initialize()56 public void initialize() { 57 mDisplayController.addDisplayWindowListener(this); 58 } 59 60 /** 61 * Adds a callback to listen for insets changes for a particular display. Note that the 62 * listener will not be updated with the existing state of the insets on that display. 63 */ addInsetsChangedListener(int displayId, OnInsetsChangedListener listener)64 public void addInsetsChangedListener(int displayId, OnInsetsChangedListener listener) { 65 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId); 66 if (listeners == null) { 67 listeners = new CopyOnWriteArrayList<>(); 68 mListeners.put(displayId, listeners); 69 } 70 if (!listeners.contains(listener)) { 71 listeners.add(listener); 72 } 73 } 74 75 /** 76 * Removes a callback listening for insets changes from a particular display. 77 */ removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener)78 public void removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener) { 79 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId); 80 if (listeners == null) { 81 return; 82 } 83 listeners.remove(listener); 84 } 85 86 @Override onDisplayAdded(int displayId)87 public void onDisplayAdded(int displayId) { 88 PerDisplay pd = new PerDisplay(displayId); 89 pd.register(); 90 mInsetsPerDisplay.put(displayId, pd); 91 } 92 93 @Override onDisplayRemoved(int displayId)94 public void onDisplayRemoved(int displayId) { 95 PerDisplay pd = mInsetsPerDisplay.get(displayId); 96 if (pd == null) { 97 return; 98 } 99 pd.unregister(); 100 mInsetsPerDisplay.remove(displayId); 101 } 102 103 /** 104 * An implementation of {@link IDisplayWindowInsetsController} for a given display id. 105 **/ 106 public class PerDisplay { 107 private final int mDisplayId; 108 private final DisplayWindowInsetsControllerImpl mInsetsControllerImpl = 109 new DisplayWindowInsetsControllerImpl(); 110 PerDisplay(int displayId)111 public PerDisplay(int displayId) { 112 mDisplayId = displayId; 113 } 114 register()115 public void register() { 116 try { 117 mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl); 118 } catch (RemoteException e) { 119 Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId); 120 } 121 } 122 unregister()123 public void unregister() { 124 try { 125 mWmService.setDisplayWindowInsetsController(mDisplayId, null); 126 } catch (RemoteException e) { 127 Slog.w(TAG, "Unable to remove insets controller on display " + mDisplayId); 128 } 129 } 130 insetsChanged(InsetsState insetsState)131 private void insetsChanged(InsetsState insetsState) { 132 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 133 if (listeners == null) { 134 return; 135 } 136 mDisplayController.updateDisplayInsets(mDisplayId, insetsState); 137 for (OnInsetsChangedListener listener : listeners) { 138 listener.insetsChanged(insetsState); 139 } 140 } 141 insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)142 private void insetsControlChanged(InsetsState insetsState, 143 InsetsSourceControl[] activeControls) { 144 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 145 if (listeners == null) { 146 return; 147 } 148 for (OnInsetsChangedListener listener : listeners) { 149 listener.insetsControlChanged(insetsState, activeControls); 150 } 151 } 152 showInsets(int types, boolean fromIme)153 private void showInsets(int types, boolean fromIme) { 154 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 155 if (listeners == null) { 156 return; 157 } 158 for (OnInsetsChangedListener listener : listeners) { 159 listener.showInsets(types, fromIme); 160 } 161 } 162 hideInsets(int types, boolean fromIme)163 private void hideInsets(int types, boolean fromIme) { 164 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 165 if (listeners == null) { 166 return; 167 } 168 for (OnInsetsChangedListener listener : listeners) { 169 listener.hideInsets(types, fromIme); 170 } 171 } 172 topFocusedWindowChanged(String packageName)173 private void topFocusedWindowChanged(String packageName) { 174 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 175 if (listeners == null) { 176 return; 177 } 178 for (OnInsetsChangedListener listener : listeners) { 179 listener.topFocusedWindowChanged(packageName); 180 } 181 } 182 183 @BinderThread 184 private class DisplayWindowInsetsControllerImpl 185 extends IDisplayWindowInsetsController.Stub { 186 @Override topFocusedWindowChanged(String packageName)187 public void topFocusedWindowChanged(String packageName) throws RemoteException { 188 mMainExecutor.execute(() -> { 189 PerDisplay.this.topFocusedWindowChanged(packageName); 190 }); 191 } 192 193 @Override insetsChanged(InsetsState insetsState)194 public void insetsChanged(InsetsState insetsState) throws RemoteException { 195 mMainExecutor.execute(() -> { 196 PerDisplay.this.insetsChanged(insetsState); 197 }); 198 } 199 200 @Override insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)201 public void insetsControlChanged(InsetsState insetsState, 202 InsetsSourceControl[] activeControls) throws RemoteException { 203 mMainExecutor.execute(() -> { 204 PerDisplay.this.insetsControlChanged(insetsState, activeControls); 205 }); 206 } 207 208 @Override showInsets(int types, boolean fromIme)209 public void showInsets(int types, boolean fromIme) throws RemoteException { 210 mMainExecutor.execute(() -> { 211 PerDisplay.this.showInsets(types, fromIme); 212 }); 213 } 214 215 @Override hideInsets(int types, boolean fromIme)216 public void hideInsets(int types, boolean fromIme) throws RemoteException { 217 mMainExecutor.execute(() -> { 218 PerDisplay.this.hideInsets(types, fromIme); 219 }); 220 } 221 } 222 } 223 224 /** 225 * Gets notified whenever the insets change. 226 * 227 * @see IDisplayWindowInsetsController 228 */ 229 @ShellMainThread 230 public interface OnInsetsChangedListener { 231 /** 232 * Called when top focused window changes to determine whether or not to take over insets 233 * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false. 234 * @param packageName: Passes the top package name 235 */ topFocusedWindowChanged(String packageName)236 default void topFocusedWindowChanged(String packageName) {} 237 238 /** 239 * Called when the window insets configuration has changed. 240 */ insetsChanged(InsetsState insetsState)241 default void insetsChanged(InsetsState insetsState) {} 242 243 /** 244 * Called when this window retrieved control over a specified set of insets sources. 245 */ insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)246 default void insetsControlChanged(InsetsState insetsState, 247 InsetsSourceControl[] activeControls) {} 248 249 /** 250 * Called when a set of insets source window should be shown by policy. 251 * 252 * @param types internal insets types (WindowInsets.Type.InsetsType) to show 253 * @param fromIme true if this request originated from IME (InputMethodService). 254 */ showInsets(int types, boolean fromIme)255 default void showInsets(int types, boolean fromIme) {} 256 257 /** 258 * Called when a set of insets source window should be hidden by policy. 259 * 260 * @param types internal insets types (WindowInsets.Type.InsetsType) to hide 261 * @param fromIme true if this request originated from IME (InputMethodService). 262 */ hideInsets(int types, boolean fromIme)263 default void hideInsets(int types, boolean fromIme) {} 264 } 265 }