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