blob: a1b52f424fee7093a24f6cf1c10b5f988ec8b20b [file] [log] [blame]
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001/*
2 * Copyright (C) 2018 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
17package com.android.server.wm;
18
19import static android.view.InsetsState.TYPE_IME;
20import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
21import static android.view.InsetsState.TYPE_TOP_BAR;
Jorim Jaggi73f3e8a2019-01-14 13:06:23 +010022import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
23import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
24import static android.view.ViewRootImpl.sNewInsetsMode;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020025
Jorim Jaggib6030952018-10-23 18:31:52 +020026import android.annotation.NonNull;
27import android.annotation.Nullable;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020028import android.util.ArrayMap;
Jorim Jaggib6030952018-10-23 18:31:52 +020029import android.util.ArraySet;
30import android.util.SparseArray;
Jorim Jaggie35c0592018-11-06 16:21:08 +010031import android.view.InsetsSource;
Jorim Jaggib6030952018-10-23 18:31:52 +020032import android.view.InsetsSourceControl;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020033import android.view.InsetsState;
Jorim Jaggib6030952018-10-23 18:31:52 +020034import android.view.ViewRootImpl;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020035
36import java.io.PrintWriter;
Jorim Jaggib6030952018-10-23 18:31:52 +020037import java.util.ArrayList;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020038import java.util.function.Consumer;
39
40/**
41 * Manages global window inset state in the system represented by {@link InsetsState}.
42 */
43class InsetsStateController {
44
45 private final InsetsState mLastState = new InsetsState();
46 private final InsetsState mState = new InsetsState();
47 private final DisplayContent mDisplayContent;
Jorim Jaggib6030952018-10-23 18:31:52 +020048
49 private final ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
50 private final ArrayMap<WindowState, ArrayList<Integer>> mWinControlTypeMap = new ArrayMap<>();
51 private final SparseArray<WindowState> mTypeWinControlMap = new SparseArray<>();
52 private final ArraySet<WindowState> mPendingControlChanged = new ArraySet<>();
Jorim Jaggif96c90a2018-09-26 16:55:15 +020053
54 private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
55 if (w.isVisible()) {
56 w.notifyInsetsChanged();
57 }
58 };
59
60 InsetsStateController(DisplayContent displayContent) {
61 mDisplayContent = displayContent;
62 }
63
64 /**
65 * When dispatching window state to the client, we'll need to exclude the source that represents
66 * the window that is being dispatched.
67 *
68 * @param target The client we dispatch the state to.
69 * @return The state stripped of the necessary information.
70 */
71 InsetsState getInsetsForDispatch(WindowState target) {
72 final InsetsSourceProvider provider = target.getInsetProvider();
73 if (provider == null) {
74 return mState;
75 }
76
77 final InsetsState state = new InsetsState();
78 state.set(mState);
79 final int type = provider.getSource().getType();
80 state.removeSource(type);
81
82 // Navigation bar doesn't get influenced by anything else
83 if (type == TYPE_NAVIGATION_BAR) {
84 state.removeSource(TYPE_IME);
85 state.removeSource(TYPE_TOP_BAR);
86 }
87 return state;
88 }
89
Jorim Jaggib6030952018-10-23 18:31:52 +020090 @Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) {
91 ArrayList<Integer> controlled = mWinControlTypeMap.get(target);
92 if (controlled == null) {
93 return null;
94 }
95 final int size = controlled.size();
96 final InsetsSourceControl[] result = new InsetsSourceControl[size];
97 for (int i = 0; i < size; i++) {
98 result[i] = mControllers.get(controlled.get(i)).getControl();
99 }
100 return result;
101 }
102
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200103 /**
104 * @return The provider of a specific type.
105 */
106 InsetsSourceProvider getSourceProvider(int type) {
107 return mControllers.computeIfAbsent(type,
Jorim Jaggib6030952018-10-23 18:31:52 +0200108 key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200109 }
110
111 /**
112 * Called when a layout pass has occurred.
113 */
114 void onPostLayout() {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800115 mState.setDisplayFrame(mDisplayContent.getBounds());
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200116 for (int i = mControllers.size() - 1; i>= 0; i--) {
117 mControllers.valueAt(i).onPostLayout();
118 }
119 if (!mLastState.equals(mState)) {
120 mLastState.set(mState, true /* copySources */);
121 notifyInsetsChanged();
122 }
123 }
124
Jorim Jaggie35c0592018-11-06 16:21:08 +0100125 void onInsetsModified(WindowState windowState, InsetsState state) {
126 boolean changed = false;
127 for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
128 final InsetsSource source = state.sourceAt(i);
129 final InsetsSourceProvider provider = mControllers.get(source.getType());
130 if (provider == null) {
131 continue;
132 }
133 changed |= provider.onInsetsModified(windowState, source);
134 }
135 if (changed) {
136 notifyInsetsChanged();
137 }
138 }
139
Jorim Jaggib6030952018-10-23 18:31:52 +0200140 void onImeTargetChanged(@Nullable WindowState imeTarget) {
141 onControlChanged(TYPE_IME, imeTarget);
142 notifyPendingInsetsControlChanged();
143 }
144
145 /**
146 * Called when the top opaque fullscreen window that is able to control the system bars changes.
147 *
148 * @param controllingWindow The window that is now able to control the system bars appearance
149 * and visibility.
150 */
151 void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) {
152 // TODO: Apply policy that determines whether controllingWindow is able to control system
153 // bars
154
155 // TODO: Depending on the form factor, mapping is different
156 onControlChanged(TYPE_TOP_BAR, controllingWindow);
157 onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow);
158 notifyPendingInsetsControlChanged();
159 }
160
161 void notifyControlRevoked(@NonNull WindowState previousControllingWin,
162 InsetsSourceProvider provider) {
163 removeFromControlMaps(previousControllingWin, provider.getSource().getType());
164 }
165
166 private void onControlChanged(int type, @Nullable WindowState win) {
Jorim Jaggib6030952018-10-23 18:31:52 +0200167 final WindowState previous = mTypeWinControlMap.get(type);
168 if (win == previous) {
169 return;
170 }
Jorim Jaggia2759b22019-01-24 13:21:40 +0100171 final InsetsSourceProvider controller = getSourceProvider(type);
Jorim Jaggib6030952018-10-23 18:31:52 +0200172 if (controller == null) {
173 return;
174 }
Jorim Jaggia2759b22019-01-24 13:21:40 +0100175 if (!controller.isControllable()) {
176 return;
177 }
178 controller.updateControlForTarget(win, false /* force */);
Jorim Jaggib6030952018-10-23 18:31:52 +0200179 if (previous != null) {
180 removeFromControlMaps(previous, type);
181 mPendingControlChanged.add(previous);
182 }
183 if (win != null) {
184 addToControlMaps(win, type);
185 mPendingControlChanged.add(win);
186 }
187 }
188
189 private void removeFromControlMaps(@NonNull WindowState win, int type) {
190 final ArrayList<Integer> array = mWinControlTypeMap.get(win);
191 if (array == null) {
192 return;
193 }
194 array.remove((Integer) type);
195 if (array.isEmpty()) {
196 mWinControlTypeMap.remove(win);
197 }
198 mTypeWinControlMap.remove(type);
199 }
200
201 private void addToControlMaps(@NonNull WindowState win, int type) {
202 final ArrayList<Integer> array = mWinControlTypeMap.computeIfAbsent(win,
203 key -> new ArrayList<>());
204 array.add(type);
205 mTypeWinControlMap.put(type, win);
206 }
207
Tarandeep Singh215929b2019-01-11 18:24:37 -0800208 void notifyControlChanged(WindowState target) {
209 mPendingControlChanged.add(target);
210 notifyPendingInsetsControlChanged();
211 }
212
Jorim Jaggib6030952018-10-23 18:31:52 +0200213 private void notifyPendingInsetsControlChanged() {
Jorim Jaggif86eb492019-01-09 17:37:08 +0100214 if (mPendingControlChanged.isEmpty()) {
215 return;
216 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200217 mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
218 for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
219 final WindowState controllingWin = mPendingControlChanged.valueAt(i);
220 controllingWin.notifyInsetsControlChanged();
221 }
222 mPendingControlChanged.clear();
223 });
224 }
225
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200226 private void notifyInsetsChanged() {
227 mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
228 }
229
230 void dump(String prefix, PrintWriter pw) {
231 pw.println(prefix + "WindowInsetsStateController");
232 mState.dump(prefix + " ", pw);
Jorim Jaggicfd6f3b2018-11-07 15:30:18 +0100233 pw.println(prefix + " " + "Control map:");
234 for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) {
235 pw.print(prefix + " ");
236 pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> "
237 + mTypeWinControlMap.valueAt(i));
238 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200239 }
240}