1 /* 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 17 package com.android.server.wm; 18 19 import static android.view.InsetsState.TYPE_IME; 20 import static android.view.InsetsState.TYPE_NAVIGATION_BAR; 21 import static android.view.InsetsState.TYPE_TOP_BAR; 22 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; 23 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; 24 import static android.view.ViewRootImpl.sNewInsetsMode; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.util.ArrayMap; 29 import android.util.ArraySet; 30 import android.util.SparseArray; 31 import android.view.InsetsSource; 32 import android.view.InsetsSourceControl; 33 import android.view.InsetsState; 34 import android.view.ViewRootImpl; 35 36 import java.io.PrintWriter; 37 import java.util.ArrayList; 38 import java.util.function.Consumer; 39 40 /** 41 * Manages global window inset state in the system represented by {@link InsetsState}. 42 */ 43 class InsetsStateController { 44 45 private final InsetsState mLastState = new InsetsState(); 46 private final InsetsState mState = new InsetsState(); 47 private final DisplayContent mDisplayContent; 48 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<>(); 53 54 private final Consumer<WindowState> mDispatchInsetsChanged = w -> { 55 if (w.isVisible()) { 56 w.notifyInsetsChanged(); 57 } 58 }; 59 InsetsStateController(DisplayContent displayContent)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 */ getInsetsForDispatch(WindowState target)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 getControlsForDispatch(WindowState target)90 @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 103 /** 104 * @return The provider of a specific type. 105 */ getSourceProvider(int type)106 InsetsSourceProvider getSourceProvider(int type) { 107 return mControllers.computeIfAbsent(type, 108 key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent)); 109 } 110 111 /** 112 * Called when a layout pass has occurred. 113 */ onPostLayout()114 void onPostLayout() { 115 mState.setDisplayFrame(mDisplayContent.getBounds()); 116 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 onInsetsModified(WindowState windowState, InsetsState state)125 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 onImeTargetChanged(@ullable WindowState imeTarget)140 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 */ onBarControllingWindowChanged(@ullable WindowState controllingWindow)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 notifyControlRevoked(@onNull WindowState previousControllingWin, InsetsSourceProvider provider)161 void notifyControlRevoked(@NonNull WindowState previousControllingWin, 162 InsetsSourceProvider provider) { 163 removeFromControlMaps(previousControllingWin, provider.getSource().getType()); 164 } 165 onControlChanged(int type, @Nullable WindowState win)166 private void onControlChanged(int type, @Nullable WindowState win) { 167 final WindowState previous = mTypeWinControlMap.get(type); 168 if (win == previous) { 169 return; 170 } 171 final InsetsSourceProvider controller = getSourceProvider(type); 172 if (controller == null) { 173 return; 174 } 175 if (!controller.isControllable()) { 176 return; 177 } 178 controller.updateControlForTarget(win, false /* force */); 179 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 removeFromControlMaps(@onNull WindowState win, int type)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 addToControlMaps(@onNull WindowState win, int type)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 notifyControlChanged(WindowState target)208 void notifyControlChanged(WindowState target) { 209 mPendingControlChanged.add(target); 210 notifyPendingInsetsControlChanged(); 211 } 212 notifyPendingInsetsControlChanged()213 private void notifyPendingInsetsControlChanged() { 214 if (mPendingControlChanged.isEmpty()) { 215 return; 216 } 217 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 notifyInsetsChanged()226 private void notifyInsetsChanged() { 227 mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); 228 } 229 dump(String prefix, PrintWriter pw)230 void dump(String prefix, PrintWriter pw) { 231 pw.println(prefix + "WindowInsetsStateController"); 232 mState.dump(prefix + " ", pw); 233 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 } 239 } 240 } 241