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