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.common.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 202 @BinderThread 203 private class DisplayWindowInsetsControllerImpl 204 extends IDisplayWindowInsetsController.Stub { 205 @Override topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)206 public void topFocusedWindowChanged(ComponentName component, 207 @InsetsType int requestedVisibleTypes) throws RemoteException { 208 mMainExecutor.execute(() -> { 209 PerDisplay.this.topFocusedWindowChanged(component, requestedVisibleTypes); 210 }); 211 } 212 213 @Override insetsChanged(InsetsState insetsState)214 public void insetsChanged(InsetsState insetsState) throws RemoteException { 215 mMainExecutor.execute(() -> { 216 PerDisplay.this.insetsChanged(insetsState); 217 }); 218 } 219 220 @Override insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)221 public void insetsControlChanged(InsetsState insetsState, 222 InsetsSourceControl[] activeControls) throws RemoteException { 223 mMainExecutor.execute(() -> { 224 PerDisplay.this.insetsControlChanged(insetsState, activeControls); 225 }); 226 } 227 228 @Override showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)229 public void showInsets(@InsetsType int types, boolean fromIme, 230 @Nullable ImeTracker.Token statsToken) throws RemoteException { 231 mMainExecutor.execute(() -> { 232 PerDisplay.this.showInsets(types, fromIme, statsToken); 233 }); 234 } 235 236 @Override hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)237 public void hideInsets(@InsetsType int types, boolean fromIme, 238 @Nullable ImeTracker.Token statsToken) throws RemoteException { 239 mMainExecutor.execute(() -> { 240 PerDisplay.this.hideInsets(types, fromIme, statsToken); 241 }); 242 } 243 } 244 } 245 246 /** 247 * Gets notified whenever the insets change. 248 * 249 * @see IDisplayWindowInsetsController 250 */ 251 @ShellMainThread 252 public interface OnInsetsChangedListener { 253 /** 254 * Called when top focused window changes to determine whether or not to take over insets 255 * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false. 256 * 257 * @param component The application component that is open in the top focussed window. 258 * @param requestedVisibleTypes The {@link InsetsType} requested visible by the focused 259 * window. 260 */ topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)261 default void topFocusedWindowChanged(ComponentName component, 262 @InsetsType int requestedVisibleTypes) {} 263 264 /** 265 * Called when the window insets configuration has changed. 266 */ insetsChanged(InsetsState insetsState)267 default void insetsChanged(InsetsState insetsState) {} 268 269 /** 270 * Called when this window retrieved control over a specified set of insets sources. 271 */ insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)272 default void insetsControlChanged(InsetsState insetsState, 273 InsetsSourceControl[] activeControls) {} 274 275 /** 276 * Called when a set of insets source window should be shown by policy. 277 * 278 * @param types {@link InsetsType} to show 279 * @param fromIme true if this request originated from IME (InputMethodService). 280 * @param statsToken the token tracking the current IME show request 281 * or {@code null} otherwise. 282 */ showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)283 default void showInsets(@InsetsType int types, boolean fromIme, 284 @Nullable ImeTracker.Token statsToken) {} 285 286 /** 287 * Called when a set of insets source window should be hidden by policy. 288 * 289 * @param types {@link InsetsType} to hide 290 * @param fromIme true if this request originated from IME (InputMethodService). 291 * @param statsToken the token tracking the current IME hide request 292 * or {@code null} otherwise. 293 */ hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)294 default void hideInsets(@InsetsType int types, boolean fromIme, 295 @Nullable ImeTracker.Token statsToken) {} 296 } 297 } 298