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.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.ComponentName; 22 import android.os.RemoteException; 23 import android.util.Slog; 24 import android.util.SparseArray; 25 import android.view.IDisplayWindowInsetsController; 26 import android.view.IWindowManager; 27 import android.view.InsetsSourceControl; 28 import android.view.InsetsState; 29 import android.view.WindowInsets.Type.InsetsType; 30 import android.view.inputmethod.ImeTracker; 31 32 import androidx.annotation.BinderThread; 33 34 import com.android.wm.shell.shared.annotations.ShellMainThread; 35 import com.android.wm.shell.sysui.ShellInit; 36 37 import java.util.concurrent.CopyOnWriteArrayList; 38 39 /** 40 * Manages insets from the core. 41 */ 42 public class DisplayInsetsController implements DisplayController.OnDisplaysChangedListener { 43 private static final String TAG = "DisplayInsetsController"; 44 45 private final IWindowManager mWmService; 46 private final ShellExecutor mMainExecutor; 47 private final DisplayController mDisplayController; 48 private final SparseArray<PerDisplay> mInsetsPerDisplay = new SparseArray<>(); 49 private final SparseArray<CopyOnWriteArrayList<OnInsetsChangedListener>> mListeners = 50 new SparseArray<>(); 51 private final CopyOnWriteArrayList<OnInsetsChangedListener> mGlobalListeners = 52 new CopyOnWriteArrayList<>(); 53 DisplayInsetsController(IWindowManager wmService, ShellInit shellInit, DisplayController displayController, ShellExecutor mainExecutor)54 public DisplayInsetsController(IWindowManager wmService, 55 ShellInit shellInit, 56 DisplayController displayController, 57 ShellExecutor mainExecutor) { 58 mWmService = wmService; 59 mDisplayController = displayController; 60 mMainExecutor = mainExecutor; 61 shellInit.addInitCallback(this::onInit, this); 62 } 63 64 /** 65 * Starts listening for insets for each display. 66 **/ onInit()67 public void onInit() { 68 mDisplayController.addDisplayWindowListener(this); 69 } 70 71 /** 72 * Adds a callback to listen for insets changes for a particular display. Note that the 73 * listener will not be updated with the existing state of the insets on that display. 74 */ addInsetsChangedListener(int displayId, OnInsetsChangedListener listener)75 public void addInsetsChangedListener(int displayId, OnInsetsChangedListener listener) { 76 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId); 77 if (listeners == null) { 78 listeners = new CopyOnWriteArrayList<>(); 79 mListeners.put(displayId, listeners); 80 } 81 if (!listeners.contains(listener)) { 82 listeners.add(listener); 83 } 84 } 85 86 /** 87 * Adds a callback to listen for insets changes for any display. Note that the 88 * listener will not be updated with the existing state of the insets on any display. 89 */ addGlobalInsetsChangedListener(OnInsetsChangedListener listener)90 public void addGlobalInsetsChangedListener(OnInsetsChangedListener listener) { 91 if (!mGlobalListeners.contains(listener)) { 92 mGlobalListeners.add(listener); 93 } 94 } 95 96 /** 97 * Removes a callback listening for insets changes from a particular display. 98 */ removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener)99 public void removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener) { 100 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId); 101 if (listeners == null) { 102 return; 103 } 104 listeners.remove(listener); 105 } 106 107 /** 108 * Removes a callback listening for insets changes from any display. 109 */ removeGlobalInsetsChangedListener(OnInsetsChangedListener listener)110 public void removeGlobalInsetsChangedListener(OnInsetsChangedListener listener) { 111 mGlobalListeners.remove(listener); 112 } 113 114 @Override onDisplayAdded(int displayId)115 public void onDisplayAdded(int displayId) { 116 PerDisplay pd = new PerDisplay(displayId); 117 pd.register(); 118 mInsetsPerDisplay.put(displayId, pd); 119 } 120 121 @Override onDisplayRemoved(int displayId)122 public void onDisplayRemoved(int displayId) { 123 PerDisplay pd = mInsetsPerDisplay.get(displayId); 124 if (pd == null) { 125 return; 126 } 127 pd.unregister(); 128 mInsetsPerDisplay.remove(displayId); 129 } 130 131 /** 132 * An implementation of {@link IDisplayWindowInsetsController} for a given display id. 133 **/ 134 public class PerDisplay { 135 private final int mDisplayId; 136 private final DisplayWindowInsetsControllerImpl mInsetsControllerImpl = 137 new DisplayWindowInsetsControllerImpl(); 138 PerDisplay(int displayId)139 public PerDisplay(int displayId) { 140 mDisplayId = displayId; 141 } 142 register()143 public void register() { 144 try { 145 mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl); 146 } catch (RemoteException e) { 147 Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId); 148 } 149 } 150 unregister()151 public void unregister() { 152 try { 153 mWmService.setDisplayWindowInsetsController(mDisplayId, null); 154 } catch (RemoteException e) { 155 Slog.w(TAG, "Unable to remove insets controller on display " + mDisplayId); 156 } 157 } 158 insetsChanged(InsetsState insetsState)159 private void insetsChanged(InsetsState insetsState) { 160 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 161 if (listeners == null && mGlobalListeners.isEmpty()) { 162 return; 163 } 164 mDisplayController.updateDisplayInsets(mDisplayId, insetsState); 165 for (OnInsetsChangedListener listener : mGlobalListeners) { 166 listener.insetsChanged(mDisplayId, insetsState); 167 } 168 if (listeners != null) { 169 for (OnInsetsChangedListener listener : listeners) { 170 listener.insetsChanged(mDisplayId, insetsState); 171 } 172 } 173 } 174 insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)175 private void insetsControlChanged(InsetsState insetsState, 176 InsetsSourceControl[] activeControls) { 177 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 178 if (listeners == null) { 179 return; 180 } 181 for (OnInsetsChangedListener listener : listeners) { 182 listener.insetsControlChanged(insetsState, activeControls); 183 } 184 } 185 showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)186 private void showInsets(@InsetsType int types, boolean fromIme, 187 @Nullable ImeTracker.Token statsToken) { 188 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 189 if (listeners == null) { 190 ImeTracker.forLogging().onFailed( 191 statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER); 192 return; 193 } 194 ImeTracker.forLogging().onProgress( 195 statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER); 196 for (OnInsetsChangedListener listener : listeners) { 197 listener.showInsets(types, fromIme, statsToken); 198 } 199 } 200 hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)201 private void hideInsets(@InsetsType int types, boolean fromIme, 202 @Nullable ImeTracker.Token statsToken) { 203 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 204 if (listeners == null) { 205 ImeTracker.forLogging().onFailed( 206 statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER); 207 return; 208 } 209 ImeTracker.forLogging().onProgress( 210 statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER); 211 for (OnInsetsChangedListener listener : listeners) { 212 listener.hideInsets(types, fromIme, statsToken); 213 } 214 } 215 topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)216 private void topFocusedWindowChanged(ComponentName component, 217 @InsetsType int requestedVisibleTypes) { 218 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 219 if (listeners == null) { 220 return; 221 } 222 for (OnInsetsChangedListener listener : listeners) { 223 listener.topFocusedWindowChanged(component, requestedVisibleTypes); 224 } 225 } 226 setImeInputTargetRequestedVisibility(boolean visible, @NonNull ImeTracker.Token statsToken)227 private void setImeInputTargetRequestedVisibility(boolean visible, 228 @NonNull ImeTracker.Token statsToken) { 229 CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); 230 if (listeners == null) { 231 return; 232 } 233 for (OnInsetsChangedListener listener : listeners) { 234 listener.setImeInputTargetRequestedVisibility(visible, statsToken); 235 } 236 } 237 238 @BinderThread 239 private class DisplayWindowInsetsControllerImpl 240 extends IDisplayWindowInsetsController.Stub { 241 @Override topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)242 public void topFocusedWindowChanged(ComponentName component, 243 @InsetsType int requestedVisibleTypes) throws RemoteException { 244 mMainExecutor.execute(() -> { 245 PerDisplay.this.topFocusedWindowChanged(component, requestedVisibleTypes); 246 }); 247 } 248 249 @Override insetsChanged(InsetsState insetsState)250 public void insetsChanged(InsetsState insetsState) throws RemoteException { 251 mMainExecutor.execute(() -> { 252 PerDisplay.this.insetsChanged(insetsState); 253 }); 254 } 255 256 @Override insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)257 public void insetsControlChanged(InsetsState insetsState, 258 InsetsSourceControl[] activeControls) throws RemoteException { 259 mMainExecutor.execute(() -> { 260 PerDisplay.this.insetsControlChanged(insetsState, activeControls); 261 }); 262 } 263 264 @Override showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)265 public void showInsets(@InsetsType int types, boolean fromIme, 266 @Nullable ImeTracker.Token statsToken) throws RemoteException { 267 mMainExecutor.execute(() -> { 268 PerDisplay.this.showInsets(types, fromIme, statsToken); 269 }); 270 } 271 272 @Override hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)273 public void hideInsets(@InsetsType int types, boolean fromIme, 274 @Nullable ImeTracker.Token statsToken) throws RemoteException { 275 mMainExecutor.execute(() -> { 276 PerDisplay.this.hideInsets(types, fromIme, statsToken); 277 }); 278 } 279 280 @Override setImeInputTargetRequestedVisibility(boolean visible, @NonNull ImeTracker.Token statsToken)281 public void setImeInputTargetRequestedVisibility(boolean visible, 282 @NonNull ImeTracker.Token statsToken) 283 throws RemoteException { 284 mMainExecutor.execute(() -> { 285 PerDisplay.this.setImeInputTargetRequestedVisibility(visible, statsToken); 286 }); 287 } 288 } 289 } 290 291 /** 292 * Gets notified whenever the insets change. 293 * 294 * @see IDisplayWindowInsetsController 295 */ 296 @ShellMainThread 297 public interface OnInsetsChangedListener { 298 /** 299 * Called when top focused window changes to determine whether or not to take over insets 300 * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false. 301 * 302 * @param component The application component that is open in the top focussed window. 303 * @param requestedVisibleTypes The {@link InsetsType} requested visible by the focused 304 * window. 305 */ topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)306 default void topFocusedWindowChanged(ComponentName component, 307 @InsetsType int requestedVisibleTypes) {} 308 309 /** 310 * Called when the window insets configuration has changed. 311 */ insetsChanged(InsetsState insetsState)312 default void insetsChanged(InsetsState insetsState) {} 313 314 /** 315 * Called when the window insets configuration has changed for the given display. 316 */ insetsChanged(int displayId, InsetsState insetsState)317 default void insetsChanged(int displayId, InsetsState insetsState) { 318 insetsChanged(insetsState); 319 } 320 321 /** 322 * Called when this window retrieved control over a specified set of insets sources. 323 */ insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)324 default void insetsControlChanged(InsetsState insetsState, 325 InsetsSourceControl[] activeControls) {} 326 327 /** 328 * Called when a set of insets source window should be shown by policy. 329 * 330 * @param types {@link InsetsType} to show 331 * @param fromIme true if this request originated from IME (InputMethodService). 332 * @param statsToken the token tracking the current IME request or {@code null} otherwise. 333 */ showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)334 default void showInsets(@InsetsType int types, boolean fromIme, 335 @Nullable ImeTracker.Token statsToken) {} 336 337 /** 338 * Called when a set of insets source window should be hidden by policy. 339 * 340 * @param types {@link InsetsType} to hide 341 * @param fromIme true if this request originated from IME (InputMethodService). 342 * @param statsToken the token tracking the current IME request or {@code null} otherwise. 343 */ hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)344 default void hideInsets(@InsetsType int types, boolean fromIme, 345 @Nullable ImeTracker.Token statsToken) {} 346 347 /** 348 * Called to set the requested visibility of the IME in DisplayImeController. Invoked by 349 * {@link com.android.server.wm.DisplayContent.RemoteInsetsControlTarget}. 350 * @param visible requested status of the IME 351 * @param statsToken the token tracking the current IME request 352 */ setImeInputTargetRequestedVisibility(boolean visible, @NonNull ImeTracker.Token statsToken)353 default void setImeInputTargetRequestedVisibility(boolean visible, 354 @NonNull ImeTracker.Token statsToken) { 355 } 356 } 357 } 358