• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.systemui.car.systembar;
17 
18 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
19 
20 import static com.android.systemui.car.Flags.displayCompatibilityV2;
21 import static com.android.systemui.car.systembar.CarSystemBarController.BOTTOM;
22 import static com.android.systemui.car.systembar.CarSystemBarController.LEFT;
23 import static com.android.systemui.car.systembar.CarSystemBarController.RIGHT;
24 import static com.android.systemui.car.systembar.CarSystemBarController.TOP;
25 
26 import android.annotation.IdRes;
27 import android.annotation.SuppressLint;
28 import android.content.Context;
29 import android.content.res.Resources;
30 import android.graphics.PixelFormat;
31 import android.os.Binder;
32 import android.util.ArrayMap;
33 import android.util.ArraySet;
34 import android.util.Log;
35 import android.view.Gravity;
36 import android.view.InsetsFrameProvider;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.WindowInsets;
40 import android.view.WindowManager;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.systemui.R;
44 import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
45 import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
46 import com.android.systemui.car.systembar.CarSystemBarController.SystemBarSide;
47 import com.android.systemui.dagger.qualifiers.Main;
48 
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Comparator;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Set;
55 
56 import javax.inject.Inject;
57 
58 /**
59  * Reads configs for system bars for each side (TOP, BOTTOM, LEFT, and RIGHT) and returns the
60  * corresponding {@link android.view.WindowManager.LayoutParams} per the configuration.
61  */
62 public class SystemBarConfigsImpl implements SystemBarConfigs {
63 
64     private static final String TAG = SystemBarConfigs.class.getSimpleName();
65     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
66 
67     // The z-order from which system bars will start to appear on top of HUN's.
68     @VisibleForTesting
69     static final int HUN_Z_ORDER = 10;
70 
71     private static final Binder INSETS_OWNER = new Binder();
72 
73     /*
74         NOTE: The elements' order in the map below must be preserved as-is since the correct
75         corresponding values are obtained by the index.
76      */
77     private static final InsetsFrameProvider[] BAR_PROVIDER_MAP = {
78             new InsetsFrameProvider(
79                     INSETS_OWNER, 0 /* index */, WindowInsets.Type.statusBars()),
80             new InsetsFrameProvider(
81                     INSETS_OWNER, 0 /* index */, WindowInsets.Type.navigationBars()),
82             new InsetsFrameProvider(
83                     INSETS_OWNER, 1 /* index */, WindowInsets.Type.statusBars()),
84             new InsetsFrameProvider(
85                     INSETS_OWNER, 1 /* index */, WindowInsets.Type.navigationBars()),
86     };
87 
88     private static final Map<@SystemBarSide Integer, Integer> BAR_GRAVITY_MAP = new ArrayMap<>();
89     private static final Map<@SystemBarSide Integer, String> BAR_TITLE_MAP = new ArrayMap<>();
90     private static final Map<@SystemBarSide Integer, InsetsFrameProvider> BAR_GESTURE_MAP =
91             new ArrayMap<>();
92 
93     private final Context mContext;
94     private final Resources mResources;
95     private final Map<@SystemBarSide Integer, SystemBarConfig> mSystemBarConfigMap =
96             new ArrayMap<>();
97     private final List<@SystemBarSide Integer> mSystemBarSidesByZOrder = new ArrayList<>();
98     /** Maps @WindowManager.LayoutParams.WindowType to window contexts for that type. */
99     private final Map<Integer, Context> mWindowContexts = new ArrayMap<>();
100 
101     private boolean mTopNavBarEnabled;
102     private boolean mBottomNavBarEnabled;
103     private boolean mLeftNavBarEnabled;
104     private boolean mRightNavBarEnabled;
105     private int mDisplayCompatToolbarState = 0;
106 
107     @Inject
SystemBarConfigsImpl(Context context, @Main Resources resources)108     public SystemBarConfigsImpl(Context context, @Main Resources resources) {
109         mContext = context;
110         mResources = resources;
111         init();
112     }
113 
init()114     private void init() {
115         populateMaps();
116         readConfigs();
117 
118         checkOnlyOneDisplayCompatIsEnabled();
119         checkEnabledBarsHaveUniqueBarTypes();
120         checkAllOverlappingBarsHaveDifferentZOrders();
121         checkSystemBarEnabledForNotificationPanel();
122         checkHideBottomBarForKeyboardConfigSync();
123 
124         setInsetPaddingsForOverlappingCorners();
125         sortSystemBarTypesByZOrder();
126     }
127 
128     /**
129      * Invalidate cached resources and fetch from resources config file.
130      * TODO: b/260206944, Can remove this after we have a fix for overlaid resources not applied.
131      * <p>
132      * Since SystemBarConfig is a Scoped(Dagger Singleton Annotation), We will have stale values, of
133      * all the resources after the RRO is applied.
134      * Another way is to remove the Scope(Singleton), but the downside is that it will be re-created
135      * everytime.
136      * </p>
137      */
138     @Override
resetSystemBarConfigs()139     public void resetSystemBarConfigs() {
140         init();
141     }
142 
143     @Override
resetSystemBarWindowContext()144     public void resetSystemBarWindowContext() {
145         for (int windowType : mWindowContexts.keySet()) {
146             Context context = mContext.createWindowContext(windowType, /* options= */ null);
147             mWindowContexts.put(windowType, context);
148         }
149     }
150 
151     @Override
getWindowContextBySide(@ystemBarSide int side)152     public Context getWindowContextBySide(@SystemBarSide int side) {
153         SystemBarConfig config = mSystemBarConfigMap.get(side);
154         if (config == null) {
155             return null;
156         }
157         int windowType = config.mapZOrderToBarType(config.getZOrder());
158         if (mWindowContexts.containsKey(windowType)) {
159             return mWindowContexts.get(windowType);
160         }
161         Context context = mContext.createWindowContext(windowType, /* options= */ null);
162         mWindowContexts.put(windowType, context);
163         return context;
164     }
165 
166     /**
167      * Returns the system bar layout for the given side. {@code null} if side is unknown.
168      */
169     @Override
getSystemBarLayoutBySide(@ystemBarSide int side, boolean isSetUp)170     public ViewGroup getSystemBarLayoutBySide(@SystemBarSide int side, boolean isSetUp) {
171         int layoutId = getSystemBarLayoutResBySide(side, isSetUp);
172         if (layoutId == 0) {
173             return null;
174         }
175 
176         return (ViewGroup) View.inflate(getWindowContextBySide(side), layoutId, /* root= */ null);
177     }
178 
getSystemBarLayoutResBySide(@ystemBarSide int side, boolean isSetUp)179     private int getSystemBarLayoutResBySide(@SystemBarSide int side, boolean isSetUp) {
180         switch (side) {
181             case LEFT:
182                 if (!isSetUp) {
183                     return R.layout.car_left_system_bar_unprovisioned;
184                 } else {
185                     return R.layout.car_left_system_bar;
186                 }
187             case TOP:
188                 if (!isSetUp) {
189                     return R.layout.car_top_system_bar_unprovisioned;
190                 } else {
191                     return R.layout.car_top_system_bar;
192                 }
193             case RIGHT:
194                 if (!isSetUp) {
195                     return R.layout.car_right_system_bar_unprovisioned;
196                 } else {
197                     return R.layout.car_right_system_bar;
198                 }
199             case BOTTOM:
200                 if (!isSetUp) {
201                     return R.layout.car_bottom_system_bar_unprovisioned;
202                 } else {
203                     return R.layout.car_bottom_system_bar;
204                 }
205             default:
206                 return 0;
207         }
208     }
209 
210     /**
211      * Returns the system bar window for the given side.
212      */
213     @Override
getWindowLayoutBySide(@ystemBarSide int side)214     public ViewGroup getWindowLayoutBySide(@SystemBarSide int side) {
215         int windowId = getWindowIdBySide(side);
216         if (windowId == 0) {
217             return null;
218         }
219         ViewGroup window = (ViewGroup) View.inflate(getWindowContextBySide(side),
220                 R.layout.navigation_bar_window, /* root= */ null);
221         // Setting a new id to each window because we're inflating the same layout and that layout
222         // already has an id. and we don't want to have the same id on all the system bar windows.
223         window.setId(windowId);
224         return window;
225     }
226 
227     /**
228      * Returns an id for the given side that can be set on the system bar window.
229      * 0 means the side is unknown.
230      */
231     @IdRes
getWindowIdBySide(@ystemBarSide int side)232     private int getWindowIdBySide(@SystemBarSide int side) {
233         return switch (side) {
234             case TOP -> R.id.car_top_bar_window;
235             case BOTTOM -> R.id.car_bottom_bar_window;
236             case LEFT -> R.id.car_left_bar_window;
237             case RIGHT -> R.id.car_right_bar_window;
238             default -> 0;
239         };
240     }
241 
242     @Override
getLayoutParamsBySide(@ystemBarSide int side)243     public WindowManager.LayoutParams getLayoutParamsBySide(@SystemBarSide int side) {
244         return mSystemBarConfigMap.get(side) != null
245                 ? mSystemBarConfigMap
246                 .get(side).getLayoutParams()
247                 : null;
248     }
249 
250     @Override
getEnabledStatusBySide(@ystemBarSide int side)251     public boolean getEnabledStatusBySide(@SystemBarSide int side) {
252         switch (side) {
253             case TOP:
254                 return mTopNavBarEnabled;
255             case BOTTOM:
256                 return mBottomNavBarEnabled;
257             case LEFT:
258                 return mLeftNavBarEnabled || isLeftDisplayCompatToolbarEnabled();
259             case RIGHT:
260                 return mRightNavBarEnabled || isRightDisplayCompatToolbarEnabled();
261             default:
262                 return false;
263         }
264     }
265 
266     @Override
getHideForKeyboardBySide(@ystemBarSide int side)267     public boolean getHideForKeyboardBySide(@SystemBarSide int side) {
268         return mSystemBarConfigMap.get(side) != null
269                 && mSystemBarConfigMap.get(side).getHideForKeyboard();
270     }
271 
272     @Override
insetSystemBar(@ystemBarSide int side, ViewGroup view)273     public void insetSystemBar(@SystemBarSide int side, ViewGroup view) {
274         if (mSystemBarConfigMap.get(side) == null) return;
275 
276         int[] paddings = mSystemBarConfigMap.get(side).getPaddings();
277         if (DEBUG) {
278             Log.d(TAG, "Set padding to side = " + side + ", to " + Arrays.toString(paddings));
279         }
280         view.setPadding(paddings[LEFT], paddings[TOP], paddings[RIGHT], paddings[BOTTOM]);
281     }
282 
283     @Override
getSystemBarSidesByZOrder()284     public List<@SystemBarSide Integer> getSystemBarSidesByZOrder() {
285         return mSystemBarSidesByZOrder;
286     }
287 
288     @Override
getSystemBarInsetTypeBySide(@ystemBarSide int side)289     public int getSystemBarInsetTypeBySide(@SystemBarSide int side) {
290         return mSystemBarConfigMap.get(side) != null
291                 ? mSystemBarConfigMap.get(side).getBarType() : -1;
292     }
293 
294     @Override
getInsetsFrameProvider(int index)295     public InsetsFrameProvider getInsetsFrameProvider(int index) {
296         return BAR_PROVIDER_MAP[index];
297     }
298 
299     @VisibleForTesting
updateInsetPaddings(@ystemBarSide int side, Map<@SystemBarSide Integer, Boolean> barVisibilities)300     void updateInsetPaddings(@SystemBarSide int side,
301             Map<@SystemBarSide Integer, Boolean> barVisibilities) {
302         SystemBarConfig currentConfig = mSystemBarConfigMap.get(side);
303 
304         if (currentConfig == null) return;
305 
306         int defaultLeftPadding = 0;
307         int defaultRightPadding = 0;
308         int defaultTopPadding = 0;
309         int defaultBottomPadding = 0;
310 
311         switch (side) {
312             case LEFT: {
313                 defaultLeftPadding = mResources
314                         .getDimensionPixelSize(R.dimen.car_left_system_bar_left_padding);
315                 defaultRightPadding = mResources
316                         .getDimensionPixelSize(R.dimen.car_left_system_bar_right_padding);
317                 defaultTopPadding = mResources
318                         .getDimensionPixelSize(R.dimen.car_left_system_bar_top_padding);
319                 defaultBottomPadding = mResources
320                         .getDimensionPixelSize(R.dimen.car_left_system_bar_bottom_padding);
321                 break;
322             }
323             case RIGHT: {
324                 defaultLeftPadding = mResources
325                         .getDimensionPixelSize(R.dimen.car_right_system_bar_left_padding);
326                 defaultRightPadding = mResources
327                         .getDimensionPixelSize(R.dimen.car_right_system_bar_right_padding);
328                 defaultTopPadding = mResources
329                         .getDimensionPixelSize(R.dimen.car_right_system_bar_top_padding);
330                 defaultBottomPadding = mResources
331                         .getDimensionPixelSize(R.dimen.car_right_system_bar_bottom_padding);
332                 break;
333             }
334             case TOP: {
335                 defaultLeftPadding = mResources
336                         .getDimensionPixelSize(R.dimen.car_top_system_bar_left_padding);
337                 defaultRightPadding = mResources
338                         .getDimensionPixelSize(R.dimen.car_top_system_bar_right_padding);
339                 defaultTopPadding = mResources
340                         .getDimensionPixelSize(R.dimen.car_top_system_bar_top_padding);
341                 defaultBottomPadding = mResources
342                         .getDimensionPixelSize(R.dimen.car_top_system_bar_bottom_padding);
343                 break;
344             }
345             case BOTTOM: {
346                 defaultLeftPadding = mResources
347                         .getDimensionPixelSize(R.dimen.car_bottom_system_bar_left_padding);
348                 defaultRightPadding = mResources
349                         .getDimensionPixelSize(R.dimen.car_bottom_system_bar_right_padding);
350                 defaultTopPadding = mResources
351                         .getDimensionPixelSize(R.dimen.car_bottom_system_bar_top_padding);
352                 defaultBottomPadding = mResources
353                         .getDimensionPixelSize(R.dimen.car_bottom_system_bar_bottom_padding);
354                 break;
355             }
356             default:
357         }
358 
359         currentConfig.setPaddingBySide(LEFT, defaultLeftPadding);
360         currentConfig.setPaddingBySide(RIGHT, defaultRightPadding);
361         currentConfig.setPaddingBySide(TOP, defaultTopPadding);
362         currentConfig.setPaddingBySide(BOTTOM, defaultBottomPadding);
363 
364         if (isHorizontalBar(side)) {
365             if (mLeftNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
366                     LEFT).getZOrder()) {
367                 currentConfig.setPaddingBySide(LEFT,
368                         barVisibilities.get(LEFT)
369                                 ? mSystemBarConfigMap.get(LEFT).getGirth()
370                                 : defaultLeftPadding);
371             }
372             if (mRightNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
373                     RIGHT).getZOrder()) {
374                 currentConfig.setPaddingBySide(RIGHT,
375                         barVisibilities.get(RIGHT)
376                                 ? mSystemBarConfigMap.get(RIGHT).getGirth()
377                                 : defaultRightPadding);
378             }
379         }
380         if (isVerticalBar(side)) {
381             if (mTopNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
382                     TOP).getZOrder()) {
383                 currentConfig.setPaddingBySide(TOP,
384                         barVisibilities.get(TOP)
385                                 ? mSystemBarConfigMap.get(TOP).getGirth()
386                                 : defaultTopPadding);
387             }
388             if (mBottomNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
389                     BOTTOM).getZOrder()) {
390                 currentConfig.setPaddingBySide(BOTTOM,
391                         barVisibilities.get(BOTTOM)
392                                 ? mSystemBarConfigMap.get(BOTTOM).getGirth()
393                                 : defaultBottomPadding);
394             }
395 
396         }
397         if (DEBUG) {
398             Log.d(TAG, "Update padding for side = " + side + " to "
399                     + Arrays.toString(currentConfig.getPaddings()));
400         }
401     }
402 
403     @SuppressLint("RtlHardcoded")
populateMaps()404     private static void populateMaps() {
405         BAR_GRAVITY_MAP.put(TOP, Gravity.TOP);
406         BAR_GRAVITY_MAP.put(BOTTOM, Gravity.BOTTOM);
407         BAR_GRAVITY_MAP.put(LEFT, Gravity.LEFT);
408         BAR_GRAVITY_MAP.put(RIGHT, Gravity.RIGHT);
409 
410         BAR_TITLE_MAP.put(TOP, "TopCarSystemBar");
411         BAR_TITLE_MAP.put(BOTTOM, "BottomCarSystemBar");
412         BAR_TITLE_MAP.put(LEFT, "LeftCarSystemBar");
413         BAR_TITLE_MAP.put(RIGHT, "RightCarSystemBar");
414 
415         BAR_GESTURE_MAP.put(TOP, new InsetsFrameProvider(
416                 INSETS_OWNER, 0 /* index */, WindowInsets.Type.mandatorySystemGestures()));
417         BAR_GESTURE_MAP.put(BOTTOM, new InsetsFrameProvider(
418                 INSETS_OWNER, 1 /* index */, WindowInsets.Type.mandatorySystemGestures()));
419         BAR_GESTURE_MAP.put(LEFT, new InsetsFrameProvider(
420                 INSETS_OWNER, 2 /* index */, WindowInsets.Type.mandatorySystemGestures()));
421         BAR_GESTURE_MAP.put(RIGHT, new InsetsFrameProvider(
422                 INSETS_OWNER, 3 /* index */, WindowInsets.Type.mandatorySystemGestures()));
423     }
424 
readConfigs()425     private void readConfigs() {
426         mTopNavBarEnabled = mResources.getBoolean(R.bool.config_enableTopSystemBar);
427         mBottomNavBarEnabled = mResources.getBoolean(R.bool.config_enableBottomSystemBar);
428         mLeftNavBarEnabled = mResources.getBoolean(R.bool.config_enableLeftSystemBar);
429         mRightNavBarEnabled = mResources.getBoolean(R.bool.config_enableRightSystemBar);
430         mDisplayCompatToolbarState =
431                 mResources.getInteger(R.integer.config_showDisplayCompatToolbarOnSystemBar);
432         mSystemBarConfigMap.clear();
433 
434         if ((mLeftNavBarEnabled && isLeftDisplayCompatToolbarEnabled())
435                 || (mRightNavBarEnabled && isRightDisplayCompatToolbarEnabled())) {
436             throw new IllegalStateException(
437                     "Navigation Bar and Display Compat toolbar can't be "
438                             + "on the same side");
439         }
440 
441         if (mTopNavBarEnabled) {
442             SystemBarConfig topBarConfig =
443                     new SystemBarConfigBuilder()
444                             .setSide(TOP)
445                             .setGirth(mResources.getDimensionPixelSize(
446                                     R.dimen.car_top_system_bar_height))
447                             .setBarType(
448                                     mResources.getInteger(R.integer.config_topSystemBarType))
449                             .setZOrder(
450                                     mResources.getInteger(R.integer.config_topSystemBarZOrder))
451                             .setHideForKeyboard(mResources.getBoolean(
452                                     R.bool.config_hideTopSystemBarForKeyboard))
453                             .build();
454             mSystemBarConfigMap.put(TOP, topBarConfig);
455         }
456 
457         if (mBottomNavBarEnabled) {
458             SystemBarConfig bottomBarConfig =
459                     new SystemBarConfigBuilder()
460                             .setSide(BOTTOM)
461                             .setGirth(mResources.getDimensionPixelSize(
462                                     R.dimen.car_bottom_system_bar_height))
463                             .setBarType(
464                                     mResources.getInteger(R.integer.config_bottomSystemBarType))
465                             .setZOrder(
466                                     mResources.getInteger(
467                                             R.integer.config_bottomSystemBarZOrder))
468                             .setHideForKeyboard(mResources.getBoolean(
469                                     R.bool.config_hideBottomSystemBarForKeyboard))
470                             .build();
471             mSystemBarConfigMap.put(BOTTOM, bottomBarConfig);
472         }
473 
474         if (mLeftNavBarEnabled || isLeftDisplayCompatToolbarEnabled()) {
475             SystemBarConfig leftBarConfig =
476                     new SystemBarConfigBuilder()
477                             .setSide(LEFT)
478                             .setGirth(mResources.getDimensionPixelSize(
479                                     R.dimen.car_left_system_bar_width))
480                             .setBarType(
481                                     mResources.getInteger(R.integer.config_leftSystemBarType))
482                             .setZOrder(
483                                     mResources.getInteger(R.integer.config_leftSystemBarZOrder))
484                             .setHideForKeyboard(mResources.getBoolean(
485                                     R.bool.config_hideLeftSystemBarForKeyboard))
486                             .build();
487             mSystemBarConfigMap.put(LEFT, leftBarConfig);
488         }
489 
490         if (mRightNavBarEnabled || isRightDisplayCompatToolbarEnabled()) {
491             SystemBarConfig rightBarConfig =
492                     new SystemBarConfigBuilder()
493                             .setSide(RIGHT)
494                             .setGirth(mResources.getDimensionPixelSize(
495                                     R.dimen.car_right_system_bar_width))
496                             .setBarType(
497                                     mResources.getInteger(R.integer.config_rightSystemBarType))
498                             .setZOrder(mResources.getInteger(
499                                     R.integer.config_rightSystemBarZOrder))
500                             .setHideForKeyboard(mResources.getBoolean(
501                                     R.bool.config_hideRightSystemBarForKeyboard))
502                             .build();
503             mSystemBarConfigMap.put(RIGHT, rightBarConfig);
504         }
505     }
506 
checkOnlyOneDisplayCompatIsEnabled()507     private void checkOnlyOneDisplayCompatIsEnabled() throws IllegalStateException {
508         boolean useRemoteLaunchTaskView =
509                 mResources.getBoolean(R.bool.config_useRemoteLaunchTaskView);
510         int displayCompatEnabled =
511                 mResources.getInteger(R.integer.config_showDisplayCompatToolbarOnSystemBar);
512         if (useRemoteLaunchTaskView && displayCompatEnabled != 0) {
513             throw new IllegalStateException("config_useRemoteLaunchTaskView is enabled but "
514                     + "config_showDisplayCompatToolbarOnSystemBar is non-zero");
515         }
516     }
517 
checkEnabledBarsHaveUniqueBarTypes()518     private void checkEnabledBarsHaveUniqueBarTypes() throws RuntimeException {
519         Set<Integer> barTypesUsed = new ArraySet<>();
520         int enabledNavBarCount = mSystemBarConfigMap.size();
521 
522         for (SystemBarConfig systemBarConfig : mSystemBarConfigMap.values()) {
523             barTypesUsed.add(systemBarConfig.getBarType());
524         }
525 
526         // The number of bar types used cannot be fewer than that of enabled system bars.
527         if (barTypesUsed.size() < enabledNavBarCount) {
528             throw new RuntimeException("Each enabled system bar must have a unique bar type. Check "
529                     + "the configuration in config.xml");
530         }
531     }
532 
checkAllOverlappingBarsHaveDifferentZOrders()533     private void checkAllOverlappingBarsHaveDifferentZOrders() {
534         checkOverlappingBarsHaveDifferentZOrders(TOP, LEFT);
535         checkOverlappingBarsHaveDifferentZOrders(TOP, RIGHT);
536         checkOverlappingBarsHaveDifferentZOrders(BOTTOM, LEFT);
537         checkOverlappingBarsHaveDifferentZOrders(BOTTOM, RIGHT);
538     }
539 
checkSystemBarEnabledForNotificationPanel()540     private void checkSystemBarEnabledForNotificationPanel() throws RuntimeException {
541         String notificationPanelMediatorName =
542                 mResources.getString(R.string.config_notificationPanelViewMediator);
543         if (notificationPanelMediatorName == null) {
544             return;
545         }
546 
547         Class<?> notificationPanelMediatorUsed = null;
548         try {
549             notificationPanelMediatorUsed = Class.forName(notificationPanelMediatorName);
550         } catch (ClassNotFoundException e) {
551             Log.e(TAG, "notification panel mediator class not found", e);
552         }
553 
554         if (!mTopNavBarEnabled && TopNotificationPanelViewMediator.class.isAssignableFrom(
555                 notificationPanelMediatorUsed)) {
556             throw new RuntimeException(
557                     "Top System Bar must be enabled to use " + notificationPanelMediatorName);
558         }
559 
560         if (!mBottomNavBarEnabled && BottomNotificationPanelViewMediator.class.isAssignableFrom(
561                 notificationPanelMediatorUsed)) {
562             throw new RuntimeException("Bottom System Bar must be enabled to use "
563                     + notificationPanelMediatorName);
564         }
565     }
566 
checkHideBottomBarForKeyboardConfigSync()567     private void checkHideBottomBarForKeyboardConfigSync() throws RuntimeException {
568         if (mBottomNavBarEnabled) {
569             boolean actual = mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard);
570             boolean expected = mResources.getBoolean(
571                     com.android.internal.R.bool.config_hideNavBarForKeyboard);
572 
573             if (actual != expected) {
574                 throw new RuntimeException("config_hideBottomSystemBarForKeyboard must not be "
575                         + "overlaid directly and should always refer to"
576                         + "config_hideNavBarForKeyboard. However, their values "
577                         + "currently do not sync. Set config_hideBottomSystemBarForKeyguard to "
578                         + "@*android:bool/config_hideNavBarForKeyboard. To change its "
579                         + "value, overlay config_hideNavBarForKeyboard in "
580                         + "framework/base/core/res/res.");
581             }
582         }
583     }
584 
setInsetPaddingsForOverlappingCorners()585     private void setInsetPaddingsForOverlappingCorners() {
586         Map<@SystemBarSide Integer, Boolean> systemBarVisibilityOnInit =
587                 getSystemBarsVisibilityOnInit();
588         updateInsetPaddings(TOP, systemBarVisibilityOnInit);
589         updateInsetPaddings(BOTTOM, systemBarVisibilityOnInit);
590         updateInsetPaddings(LEFT, systemBarVisibilityOnInit);
591         updateInsetPaddings(RIGHT, systemBarVisibilityOnInit);
592     }
593 
sortSystemBarTypesByZOrder()594     private void sortSystemBarTypesByZOrder() {
595         List<SystemBarConfig> systemBarsByZOrder = new ArrayList<>(mSystemBarConfigMap.values());
596 
597         systemBarsByZOrder.sort(new Comparator<SystemBarConfig>() {
598             @Override
599             public int compare(SystemBarConfig o1, SystemBarConfig o2) {
600                 return o1.getZOrder() - o2.getZOrder();
601             }
602         });
603 
604         mSystemBarSidesByZOrder.clear();
605         systemBarsByZOrder.forEach(systemBarConfig -> {
606             mSystemBarSidesByZOrder.add(systemBarConfig.getSide());
607         });
608     }
609 
610     // On init, system bars are visible as long as they are enabled.
getSystemBarsVisibilityOnInit()611     private Map<@SystemBarSide Integer, Boolean> getSystemBarsVisibilityOnInit() {
612         ArrayMap<@SystemBarSide Integer, Boolean> visibilityMap = new ArrayMap<>();
613         visibilityMap.put(TOP, mTopNavBarEnabled);
614         visibilityMap.put(BOTTOM, mBottomNavBarEnabled);
615         visibilityMap.put(LEFT, mLeftNavBarEnabled || isLeftDisplayCompatToolbarEnabled());
616         visibilityMap.put(RIGHT, mRightNavBarEnabled || isRightDisplayCompatToolbarEnabled());
617         return visibilityMap;
618     }
619 
checkOverlappingBarsHaveDifferentZOrders(@ystemBarSide int horizontalSide, @SystemBarSide int verticalSide)620     private void checkOverlappingBarsHaveDifferentZOrders(@SystemBarSide int horizontalSide,
621             @SystemBarSide int verticalSide) {
622 
623         if (isVerticalBar(horizontalSide) || isHorizontalBar(verticalSide)) {
624             Log.w(TAG, "configureBarPaddings: Returning immediately since the horizontal and "
625                     + "vertical sides were not provided correctly.");
626             return;
627         }
628 
629         SystemBarConfig horizontalBarConfig = mSystemBarConfigMap.get(horizontalSide);
630         SystemBarConfig verticalBarConfig = mSystemBarConfigMap.get(verticalSide);
631 
632         if (verticalBarConfig != null && horizontalBarConfig != null) {
633             int horizontalBarZOrder = horizontalBarConfig.getZOrder();
634             int verticalBarZOrder = verticalBarConfig.getZOrder();
635 
636             if (horizontalBarZOrder == verticalBarZOrder) {
637                 throw new RuntimeException(
638                         BAR_TITLE_MAP.get(horizontalSide) + " " + BAR_TITLE_MAP.get(verticalSide)
639                                 + " have the same Z-Order, and so their placing order cannot be "
640                                 + "determined. Determine which bar should be placed on top of the "
641                                 + "other bar and change the Z-order in config.xml accordingly."
642                 );
643             }
644         }
645     }
646 
isHorizontalBar(@ystemBarSide int side)647     private static boolean isHorizontalBar(@SystemBarSide int side) {
648         return side == TOP || side == BOTTOM;
649     }
650 
isVerticalBar(@ystemBarSide int side)651     private static boolean isVerticalBar(@SystemBarSide int side) {
652         return side == LEFT || side == RIGHT;
653     }
654 
655     @Override
isLeftDisplayCompatToolbarEnabled()656     public boolean isLeftDisplayCompatToolbarEnabled() {
657         return displayCompatibilityV2() && mDisplayCompatToolbarState == 1;
658     }
659 
660     @Override
isRightDisplayCompatToolbarEnabled()661     public boolean isRightDisplayCompatToolbarEnabled() {
662         return displayCompatibilityV2() && mDisplayCompatToolbarState == 2;
663     }
664 
665     private static final class SystemBarConfig {
666         private final int mSide;
667         private final int mBarType;
668         private final int mGirth;
669         private final int mZOrder;
670         private final boolean mHideForKeyboard;
671 
672         private int[] mPaddings = new int[]{0, 0, 0, 0};
673 
SystemBarConfig(@ystemBarSide int side, int barType, int girth, int zOrder, boolean hideForKeyboard)674         private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder,
675                 boolean hideForKeyboard) {
676             mSide = side;
677             mBarType = barType;
678             mGirth = girth;
679             mZOrder = zOrder;
680             mHideForKeyboard = hideForKeyboard;
681         }
682 
getSide()683         private int getSide() {
684             return mSide;
685         }
686 
getBarType()687         private int getBarType() {
688             return mBarType;
689         }
690 
getGirth()691         private int getGirth() {
692             return mGirth;
693         }
694 
getZOrder()695         private int getZOrder() {
696             return mZOrder;
697         }
698 
getHideForKeyboard()699         private boolean getHideForKeyboard() {
700             return mHideForKeyboard;
701         }
702 
getPaddings()703         private int[] getPaddings() {
704             return mPaddings;
705         }
706 
getLayoutParams()707         private WindowManager.LayoutParams getLayoutParams() {
708             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
709                     isHorizontalBar(mSide) ? ViewGroup.LayoutParams.MATCH_PARENT : mGirth,
710                     isHorizontalBar(mSide) ? mGirth : ViewGroup.LayoutParams.MATCH_PARENT,
711                     mapZOrderToBarType(mZOrder),
712                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
713                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
714                             | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
715                             | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
716                     PixelFormat.TRANSLUCENT);
717             lp.setTitle(BAR_TITLE_MAP.get(mSide));
718             lp.providedInsets = new InsetsFrameProvider[]{
719                     BAR_PROVIDER_MAP[mBarType],
720                     BAR_GESTURE_MAP.get(mSide)
721             };
722             lp.setFitInsetsTypes(0);
723             lp.windowAnimations = 0;
724             lp.gravity = BAR_GRAVITY_MAP.get(mSide);
725             lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
726             lp.privateFlags = lp.privateFlags
727                     | WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
728             return lp;
729         }
730 
mapZOrderToBarType(int zOrder)731         private int mapZOrderToBarType(int zOrder) {
732             return zOrder >= HUN_Z_ORDER ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL
733                     : WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
734         }
735 
setPaddingBySide(@ystemBarSide int side, int padding)736         private void setPaddingBySide(@SystemBarSide int side, int padding) {
737             mPaddings[side] = padding;
738         }
739     }
740 
741     private static final class SystemBarConfigBuilder {
742         private int mSide;
743         private int mBarType;
744         private int mGirth;
745         private int mZOrder;
746         private boolean mHideForKeyboard;
747 
setSide(@ystemBarSide int side)748         private SystemBarConfigBuilder setSide(@SystemBarSide int side) {
749             mSide = side;
750             return this;
751         }
752 
setBarType(int type)753         private SystemBarConfigBuilder setBarType(int type) {
754             mBarType = type;
755             return this;
756         }
757 
setGirth(int girth)758         private SystemBarConfigBuilder setGirth(int girth) {
759             mGirth = girth;
760             return this;
761         }
762 
setZOrder(int zOrder)763         private SystemBarConfigBuilder setZOrder(int zOrder) {
764             mZOrder = zOrder;
765             return this;
766         }
767 
setHideForKeyboard(boolean hide)768         private SystemBarConfigBuilder setHideForKeyboard(boolean hide) {
769             mHideForKeyboard = hide;
770             return this;
771         }
772 
build()773         private SystemBarConfig build() {
774             return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder, mHideForKeyboard);
775         }
776     }
777 }
778