1 /* 2 * Copyright (C) 2020 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.systemui.wm; 18 19 import static android.content.Intent.ACTION_OVERLAY_CHANGED; 20 import static android.view.WindowInsets.Type.navigationBars; 21 import static android.view.WindowInsets.Type.statusBars; 22 import static android.view.WindowInsets.Type.systemBars; 23 24 import static com.android.systemui.car.users.CarSystemUIUserUtil.isSecondaryMUMDSystemUI; 25 26 import android.annotation.Nullable; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.os.Handler; 33 import android.os.PatternMatcher; 34 import android.os.RemoteException; 35 import android.os.UserHandle; 36 import android.util.Log; 37 import android.util.Slog; 38 import android.util.SparseArray; 39 import android.view.IDisplayWindowInsetsController; 40 import android.view.IWindowManager; 41 import android.view.InsetsController; 42 import android.view.InsetsSourceControl; 43 import android.view.InsetsState; 44 import android.view.WindowInsets; 45 import android.view.WindowInsets.Type.InsetsType; 46 import android.view.inputmethod.ImeTracker; 47 import android.view.inputmethod.InputMethodManager; 48 49 import androidx.annotation.VisibleForTesting; 50 51 import com.android.systemui.R; 52 import com.android.systemui.dagger.qualifiers.Main; 53 import com.android.wm.shell.common.DisplayController; 54 import com.android.wm.shell.common.DisplayInsetsController; 55 56 import java.util.Arrays; 57 58 /** 59 * Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to 60 * give system bar control to SystemUI. 61 * {@link R.bool#config_remoteInsetsControllerControlsSystemBars} determines whether this controller 62 * takes control or not. 63 */ 64 public class DisplaySystemBarsController implements DisplayController.OnDisplaysChangedListener { 65 66 private static final String TAG = DisplaySystemBarsController.class.getSimpleName(); 67 private static final int STATE_NON_IMMERSIVE = systemBars(); 68 private static final int STATE_IMMERSIVE_WITH_NAV_BAR = navigationBars(); 69 private static final int STATE_IMMERSIVE_WITH_STATUS_BAR = statusBars(); 70 private static final int STATE_IMMERSIVE = 0; 71 private static final boolean DEBUG = Log.isLoggable(DisplayController.class.getSimpleName(), 72 Log.DEBUG); 73 74 protected final Context mContext; 75 protected final IWindowManager mWmService; 76 protected final DisplayInsetsController mDisplayInsetsController; 77 protected final Handler mHandler; 78 79 private final int[] mDefaultVisibilities = 80 new int[]{WindowInsets.Type.systemBars(), 0}; 81 private final int[] mImmersiveWithNavBarVisibilities = new int[]{ 82 WindowInsets.Type.navigationBars() | WindowInsets.Type.captionBar() 83 | WindowInsets.Type.systemOverlays(), 84 WindowInsets.Type.statusBars() 85 }; 86 private final int[] mImmersiveWithStatusBarVisibilities = new int[]{ 87 WindowInsets.Type.statusBars() | WindowInsets.Type.captionBar() 88 | WindowInsets.Type.systemOverlays(), 89 WindowInsets.Type.navigationBars() 90 }; 91 private final int[] mImmersiveVisibilities = 92 new int[]{0, WindowInsets.Type.systemBars()}; 93 94 @VisibleForTesting 95 SparseArray<PerDisplay> mPerDisplaySparseArray; 96 @InsetsType 97 private int mWindowRequestedVisibleTypes = WindowInsets.Type.defaultVisible(); 98 @InsetsType 99 private int mAppRequestedVisibleTypes = WindowInsets.Type.defaultVisible(); 100 @InsetsType 101 private int mImmersiveState = systemBars(); 102 DisplaySystemBarsController( Context context, IWindowManager wmService, DisplayController displayController, DisplayInsetsController displayInsetsController, @Main Handler mainHandler)103 public DisplaySystemBarsController( 104 Context context, 105 IWindowManager wmService, 106 DisplayController displayController, 107 DisplayInsetsController displayInsetsController, 108 @Main Handler mainHandler) { 109 mContext = context; 110 mWmService = wmService; 111 mDisplayInsetsController = displayInsetsController; 112 mHandler = mainHandler; 113 if (!isSecondaryMUMDSystemUI()) { 114 // This WM controller should only be initialized once for the primary SystemUI, as it 115 // will affect insets on all displays. 116 // TODO(b/262773276): support per-user remote inset controllers 117 displayController.addDisplayWindowListener(this); 118 } 119 } 120 121 @Override onDisplayAdded(int displayId)122 public void onDisplayAdded(int displayId) { 123 PerDisplay pd = new PerDisplay(displayId); 124 pd.register(); 125 // Lazy loading policy control filters instead of during boot. 126 if (mPerDisplaySparseArray == null) { 127 mPerDisplaySparseArray = new SparseArray<>(); 128 BarControlPolicy.reloadFromSetting(mContext); 129 BarControlPolicy.registerContentObserver(mContext, mHandler, () -> { 130 int size = mPerDisplaySparseArray.size(); 131 for (int i = 0; i < size; i++) { 132 mPerDisplaySparseArray.valueAt(i).updateDisplayWindowRequestedVisibleTypes(); 133 } 134 }); 135 } 136 mPerDisplaySparseArray.put(displayId, pd); 137 } 138 139 @Override onDisplayRemoved(int displayId)140 public void onDisplayRemoved(int displayId) { 141 PerDisplay pd = mPerDisplaySparseArray.get(displayId); 142 pd.unregister(); 143 mPerDisplaySparseArray.remove(displayId); 144 } 145 146 class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener { 147 private static final String OVERLAY_FILTER_DATA_SCHEME = "package"; 148 149 int mDisplayId; 150 InsetsController mInsetsController; 151 @InsetsType 152 int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible(); 153 String mPackageName; 154 int mBehavior = 0; 155 PerDisplay(int displayId)156 PerDisplay(int displayId) { 157 mDisplayId = displayId; 158 InputMethodManager inputMethodManager = 159 mContext.getSystemService(InputMethodManager.class); 160 mInsetsController = new InsetsController( 161 new DisplaySystemBarsInsetsControllerHost(mHandler, requestedVisibleTypes -> { 162 mRequestedVisibleTypes = requestedVisibleTypes; 163 updateDisplayWindowRequestedVisibleTypes(); 164 }, inputMethodManager) 165 ); 166 mBehavior = mContext.getResources().getInteger( 167 R.integer.config_systemBarPersistency); 168 registerOverlayChangeBroadcastReceiver(); 169 } 170 register()171 public void register() { 172 mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this); 173 } 174 unregister()175 public void unregister() { 176 mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this); 177 } 178 179 @Override insetsChanged(InsetsState insetsState)180 public void insetsChanged(InsetsState insetsState) { 181 mInsetsController.onStateChanged(insetsState); 182 updateDisplayWindowRequestedVisibleTypes(); 183 } 184 185 @Override hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)186 public void hideInsets(@InsetsType int types, boolean fromIme, 187 @Nullable ImeTracker.Token statsToken) { 188 if ((types & WindowInsets.Type.ime()) == 0) { 189 mInsetsController.hide(types, /* fromIme = */ false, statsToken); 190 } 191 } 192 193 @Override showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)194 public void showInsets(@InsetsType int types, boolean fromIme, 195 @Nullable ImeTracker.Token statsToken) { 196 if ((types & WindowInsets.Type.ime()) == 0) { 197 mInsetsController.show(types, /* fromIme= */ false, statsToken); 198 } 199 } 200 201 @Override insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)202 public void insetsControlChanged(InsetsState insetsState, 203 InsetsSourceControl[] activeControls) { 204 InsetsSourceControl[] nonImeControls = null; 205 // Need to filter out IME control to prevent control after leash is released 206 if (activeControls != null) { 207 nonImeControls = Arrays.stream(activeControls).filter( 208 c -> c.getType() != WindowInsets.Type.ime()).toArray( 209 InsetsSourceControl[]::new); 210 } 211 mInsetsController.onControlsChanged(nonImeControls); 212 } 213 214 @Override topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)215 public void topFocusedWindowChanged(ComponentName component, 216 @InsetsType int requestedVisibleTypes) { 217 String packageName = component != null ? component.getPackageName() : null; 218 boolean showNavRequest = 219 (requestedVisibleTypes & navigationBars()) == navigationBars(); 220 boolean showStatusRequest = 221 (requestedVisibleTypes & statusBars()) == statusBars(); 222 223 224 boolean maybeUpdate = (mWindowRequestedVisibleTypes != requestedVisibleTypes || ( 225 mPackageName != null && !mPackageName.equals(packageName))); 226 227 if (DEBUG) { 228 Slog.d(TAG, "topFocusedWindowChanged behavior = " + mBehavior 229 + ", component = " + component 230 + ", requestedVisibleTypes = " + requestedVisibleTypes 231 + ", showNavRequest = " + showNavRequest 232 + ", showStatusRequest = " + showStatusRequest 233 + ", mWindowRequestedVisibleTypes = " + mWindowRequestedVisibleTypes 234 + ", mPackageName = " + mPackageName 235 + ", maybeUpdate = " + maybeUpdate); 236 } 237 238 if (maybeUpdate) { 239 if (mBehavior == 1) { 240 mImmersiveState = 0; 241 if (showNavRequest) { 242 mImmersiveState |= navigationBars(); 243 } 244 if (showStatusRequest) { 245 mImmersiveState |= statusBars(); 246 } 247 } else if (mBehavior == 2) { 248 mImmersiveState = navigationBars(); 249 if (showStatusRequest) { 250 mImmersiveState |= statusBars(); 251 } 252 } 253 } else { 254 mImmersiveState = STATE_NON_IMMERSIVE; 255 } 256 257 mPackageName = packageName; 258 updateDisplayWindowRequestedVisibleTypes(); 259 } 260 261 @Override setImeInputTargetRequestedVisibility(boolean visible)262 public void setImeInputTargetRequestedVisibility(boolean visible) { 263 // TODO 264 } 265 registerOverlayChangeBroadcastReceiver()266 private void registerOverlayChangeBroadcastReceiver() { 267 IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED); 268 overlayFilter.addDataScheme(OVERLAY_FILTER_DATA_SCHEME); 269 overlayFilter.addDataSchemeSpecificPart(mContext.getPackageName(), 270 PatternMatcher.PATTERN_LITERAL); 271 BroadcastReceiver receiver = new BroadcastReceiver() { 272 @Override 273 public void onReceive(Context context, Intent intent) { 274 Slog.d(TAG, "topFocusedWindowChanged behavior = "); 275 mBehavior = mContext.getResources().getInteger( 276 R.integer.config_systemBarPersistency); 277 Slog.d(TAG, "Refresh system bar persistency behavior on overlay change" 278 + mBehavior); 279 } 280 }; 281 mContext.registerReceiverAsUser(receiver, UserHandle.ALL, 282 overlayFilter, /* broadcastPermission= */null, /* handler= */ null); 283 } 284 updateDisplayWindowRequestedVisibleTypes()285 protected void updateDisplayWindowRequestedVisibleTypes() { 286 if (mPackageName == null) { 287 return; 288 } 289 290 int[] barVisibilities; 291 if (mImmersiveState == STATE_IMMERSIVE_WITH_NAV_BAR) { 292 barVisibilities = mImmersiveWithNavBarVisibilities; 293 } else if (mImmersiveState == STATE_IMMERSIVE_WITH_STATUS_BAR) { 294 barVisibilities = mImmersiveWithStatusBarVisibilities; 295 } else if (mImmersiveState == STATE_IMMERSIVE) { 296 barVisibilities = mImmersiveVisibilities; 297 } else if (mImmersiveState == STATE_NON_IMMERSIVE) { 298 barVisibilities = mDefaultVisibilities; 299 } else { 300 barVisibilities = mDefaultVisibilities; 301 } 302 if (DEBUG) { 303 Slog.d(TAG, "mImmersiveState = " + mImmersiveState + "barVisibilities to " 304 + Arrays.toString(barVisibilities)); 305 } 306 307 updateRequestedVisibleTypes(barVisibilities[0], /* visible= */ true); 308 updateRequestedVisibleTypes(barVisibilities[1], /* visible= */ false); 309 310 if (mAppRequestedVisibleTypes == mRequestedVisibleTypes) { 311 return; 312 } 313 mAppRequestedVisibleTypes = mRequestedVisibleTypes; 314 315 showInsets(barVisibilities[0], /* fromIme= */ false, /* statsToken= */ null); 316 hideInsets(barVisibilities[1], /* fromIme= */ false, /* statsToken = */ null); 317 try { 318 mWmService.updateDisplayWindowRequestedVisibleTypes(mDisplayId, 319 mRequestedVisibleTypes); 320 } catch (RemoteException e) { 321 Slog.w(TAG, "Unable to update window manager service."); 322 } 323 } 324 updateRequestedVisibleTypes(@nsetsType int types, boolean visible)325 protected void updateRequestedVisibleTypes(@InsetsType int types, boolean visible) { 326 mRequestedVisibleTypes = visible 327 ? (mRequestedVisibleTypes | types) 328 : (mRequestedVisibleTypes & ~types); 329 } 330 } 331 } 332