• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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