Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 | |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 19 | import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; |
| 20 | import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 21 | import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; |
| 22 | import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 23 | import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; |
| 24 | import static android.view.InsetsState.ITYPE_STATUS_BAR; |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 25 | import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; |
| 26 | import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; |
| 27 | import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; |
| 28 | |
| 29 | import android.annotation.Nullable; |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 30 | import android.app.StatusBarManager; |
| 31 | import android.util.IntArray; |
| 32 | import android.view.InsetsState; |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 33 | import android.view.InsetsState.InternalInsetsType; |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 34 | import android.view.ViewRootImpl; |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 35 | |
| 36 | /** |
| 37 | * Policy that implements who gets control over the windows generating insets. |
| 38 | */ |
| 39 | class InsetsPolicy { |
| 40 | |
| 41 | private final InsetsStateController mStateController; |
| 42 | private final DisplayContent mDisplayContent; |
| 43 | private final DisplayPolicy mPolicy; |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 44 | private final TransientControlTarget mTransientControlTarget = new TransientControlTarget(); |
| 45 | private final IntArray mShowingTransientTypes = new IntArray(); |
| 46 | |
| 47 | private WindowState mFocusedWin; |
| 48 | private BarWindow mTopBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); |
| 49 | private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 50 | |
| 51 | InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { |
| 52 | mStateController = stateController; |
| 53 | mDisplayContent = displayContent; |
| 54 | mPolicy = displayContent.getDisplayPolicy(); |
| 55 | } |
| 56 | |
| 57 | /** Updates the target which can control system bars. */ |
| 58 | void updateBarControlTarget(@Nullable WindowState focusedWin) { |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 59 | mFocusedWin = focusedWin; |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 60 | mStateController.onBarControlTargetChanged(getTopControlTarget(focusedWin), |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 61 | getFakeTopControlTarget(focusedWin), |
| 62 | getNavControlTarget(focusedWin), |
| 63 | getFakeNavControlTarget(focusedWin)); |
| 64 | if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) { |
| 65 | return; |
| 66 | } |
| 67 | mTopBar.setVisible(focusedWin == null |
| 68 | || focusedWin != getTopControlTarget(focusedWin) |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 69 | || focusedWin.getClientInsetsState().getSource(ITYPE_STATUS_BAR).isVisible()); |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 70 | mNavBar.setVisible(focusedWin == null |
| 71 | || focusedWin != getNavControlTarget(focusedWin) |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 72 | || focusedWin.getClientInsetsState().getSource(ITYPE_NAVIGATION_BAR).isVisible()); |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 73 | } |
| 74 | |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 75 | boolean isHidden(@InternalInsetsType int type) { |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 76 | final InsetsSourceProvider provider = mStateController.peekSourceProvider(type); |
| 77 | return provider != null && provider.hasWindow() && !provider.getSource().isVisible(); |
| 78 | } |
| 79 | |
| 80 | void showTransient(IntArray types) { |
| 81 | boolean changed = false; |
| 82 | for (int i = types.size() - 1; i >= 0; i--) { |
| 83 | final int type = types.get(i); |
| 84 | if (mShowingTransientTypes.indexOf(type) != -1) { |
| 85 | continue; |
| 86 | } |
| 87 | if (!isHidden(type)) { |
| 88 | continue; |
| 89 | } |
| 90 | mShowingTransientTypes.add(type); |
| 91 | changed = true; |
| 92 | } |
| 93 | if (changed) { |
| 94 | updateBarControlTarget(mFocusedWin); |
| 95 | mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(), |
| 96 | mShowingTransientTypes.toArray()); |
| 97 | mStateController.notifyInsetsChanged(); |
| 98 | // TODO(b/118118435): Animation |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | void hideTransient() { |
| 103 | if (mShowingTransientTypes.size() == 0) { |
| 104 | return; |
| 105 | } |
| 106 | |
| 107 | // TODO(b/118118435): Animation |
| 108 | mShowingTransientTypes.clear(); |
| 109 | updateBarControlTarget(mFocusedWin); |
| 110 | mStateController.notifyInsetsChanged(); |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * @see InsetsStateController#getInsetsForDispatch |
| 115 | */ |
| 116 | InsetsState getInsetsForDispatch(WindowState target) { |
| 117 | InsetsState state = mStateController.getInsetsForDispatch(target); |
| 118 | if (mShowingTransientTypes.size() == 0) { |
| 119 | return state; |
| 120 | } |
| 121 | for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { |
| 122 | state.setSourceVisible(mShowingTransientTypes.get(i), false); |
| 123 | } |
| 124 | return state; |
| 125 | } |
| 126 | |
| 127 | void onInsetsModified(WindowState windowState, InsetsState state) { |
| 128 | mStateController.onInsetsModified(windowState, state); |
| 129 | checkAbortTransient(windowState, state); |
| 130 | if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) { |
| 131 | return; |
| 132 | } |
| 133 | if (windowState == getTopControlTarget(mFocusedWin)) { |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 134 | mTopBar.setVisible(state.getSource(ITYPE_STATUS_BAR).isVisible()); |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 135 | } |
| 136 | if (windowState == getNavControlTarget(mFocusedWin)) { |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 137 | mNavBar.setVisible(state.getSource(ITYPE_NAVIGATION_BAR).isVisible()); |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 138 | } |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * Called when a window modified the insets state. If the window set a insets source to visible |
| 143 | * while it is shown transiently, we need to abort the transient state. |
| 144 | * |
| 145 | * @param windowState who changed the insets state. |
| 146 | * @param state the modified insets state. |
| 147 | */ |
| 148 | private void checkAbortTransient(WindowState windowState, InsetsState state) { |
| 149 | if (mShowingTransientTypes.size() != 0) { |
| 150 | IntArray abortTypes = new IntArray(); |
| 151 | for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { |
| 152 | final int type = mShowingTransientTypes.get(i); |
| 153 | if (mStateController.isFakeTarget(type, windowState) |
| 154 | && state.getSource(type).isVisible()) { |
| 155 | mShowingTransientTypes.remove(i); |
| 156 | abortTypes.add(type); |
| 157 | } |
| 158 | } |
| 159 | if (abortTypes.size() > 0) { |
| 160 | mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(), |
| 161 | abortTypes.toArray()); |
| 162 | updateBarControlTarget(mFocusedWin); |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | private @Nullable InsetsControlTarget getFakeTopControlTarget(@Nullable WindowState focused) { |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 168 | if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) { |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 169 | return focused; |
| 170 | } |
| 171 | return null; |
| 172 | } |
| 173 | |
| 174 | private @Nullable InsetsControlTarget getFakeNavControlTarget(@Nullable WindowState focused) { |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 175 | if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) { |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 176 | return focused; |
| 177 | } |
| 178 | return null; |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | private @Nullable InsetsControlTarget getTopControlTarget(@Nullable WindowState focusedWin) { |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 182 | if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) { |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 183 | return mTransientControlTarget; |
| 184 | } |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 185 | if (areSystemBarsForciblyVisible() || isStatusBarForciblyVisible()) { |
| 186 | return null; |
| 187 | } |
| 188 | return focusedWin; |
| 189 | } |
| 190 | |
| 191 | private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) { |
Tiger Huang | 332793b | 2019-10-29 23:21:27 +0800 | [diff] [blame] | 192 | if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) { |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 193 | return mTransientControlTarget; |
| 194 | } |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 195 | if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) { |
| 196 | return null; |
| 197 | } |
| 198 | return focusedWin; |
| 199 | } |
| 200 | |
| 201 | private boolean isStatusBarForciblyVisible() { |
| 202 | final WindowState statusBar = mPolicy.getStatusBar(); |
| 203 | if (statusBar == null) { |
| 204 | return false; |
| 205 | } |
| 206 | final int privateFlags = statusBar.mAttrs.privateFlags; |
| 207 | |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 208 | // TODO(b/118118435): Pretend to the app that it's still able to control it? |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 209 | if ((privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { |
| 210 | return true; |
| 211 | } |
| 212 | if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { |
| 213 | return true; |
| 214 | } |
| 215 | return false; |
| 216 | } |
| 217 | |
| 218 | private boolean isNavBarForciblyVisible() { |
| 219 | final WindowState statusBar = mPolicy.getStatusBar(); |
| 220 | if (statusBar == null) { |
| 221 | return false; |
| 222 | } |
| 223 | if ((statusBar.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0) { |
| 224 | return true; |
| 225 | } |
| 226 | return false; |
| 227 | } |
| 228 | |
| 229 | private boolean areSystemBarsForciblyVisible() { |
| 230 | final boolean isDockedStackVisible = |
| 231 | mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); |
| 232 | final boolean isFreeformStackVisible = |
| 233 | mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM); |
| 234 | final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing(); |
| 235 | |
| 236 | // We need to force system bars when the docked stack is visible, when the freeform stack |
| 237 | // is visible but also when we are resizing for the transitions when docked stack |
| 238 | // visibility changes. |
| 239 | return isDockedStackVisible || isFreeformStackVisible || isResizing; |
| 240 | } |
| 241 | |
Jorim Jaggi | 956ca41 | 2019-01-07 14:49:14 +0100 | [diff] [blame] | 242 | private class BarWindow { |
| 243 | |
| 244 | private final int mId; |
| 245 | private @StatusBarManager.WindowVisibleState int mState = |
| 246 | StatusBarManager.WINDOW_STATE_SHOWING; |
| 247 | |
| 248 | BarWindow(int id) { |
| 249 | mId = id; |
| 250 | } |
| 251 | |
| 252 | private void setVisible(boolean visible) { |
| 253 | final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN; |
| 254 | if (mState != state) { |
| 255 | mState = state; |
| 256 | mPolicy.getStatusBarManagerInternal().setWindowState( |
| 257 | mDisplayContent.getDisplayId(), mId, state); |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | // TODO(b/118118435): Implement animations for it (with SurfaceAnimator) |
| 263 | private class TransientControlTarget implements InsetsControlTarget { |
| 264 | |
| 265 | @Override |
| 266 | public void notifyInsetsControlChanged() { |
| 267 | } |
| 268 | } |
Jorim Jaggi | 2862047 | 2019-01-02 23:21:49 +0100 | [diff] [blame] | 269 | } |