• 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 
17 package com.android.systemui.car.systembar;
18 
19 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
20 
21 import android.annotation.IntDef;
22 import android.content.res.Resources;
23 import android.graphics.PixelFormat;
24 import android.util.ArrayMap;
25 import android.util.ArraySet;
26 import android.util.Log;
27 import android.view.Gravity;
28 import android.view.InsetsFrameProvider;
29 import android.view.InsetsState;
30 import android.view.ViewGroup;
31 import android.view.WindowManager;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.systemui.R;
35 import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
36 import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
37 import com.android.systemui.dagger.SysUISingleton;
38 import com.android.systemui.dagger.qualifiers.Main;
39 
40 import java.lang.annotation.ElementType;
41 import java.lang.annotation.Target;
42 import java.util.ArrayList;
43 import java.util.Comparator;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 
48 import javax.inject.Inject;
49 
50 /**
51  * Reads configs for system bars for each side (TOP, BOTTOM, LEFT, and RIGHT) and returns the
52  * corresponding {@link android.view.WindowManager.LayoutParams} per the configuration.
53  */
54 @SysUISingleton
55 public class SystemBarConfigs {
56 
57     private static final String TAG = SystemBarConfigs.class.getSimpleName();
58     // The z-order from which system bars will start to appear on top of HUN's.
59     private static final int HUN_ZORDER = 10;
60 
61     @IntDef(value = {TOP, BOTTOM, LEFT, RIGHT})
62     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
63     private @interface SystemBarSide {
64     }
65 
66     public static final int TOP = 0;
67     public static final int BOTTOM = 1;
68     public static final int LEFT = 2;
69     public static final int RIGHT = 3;
70 
71     /*
72         NOTE: The elements' order in the map below must be preserved as-is since the correct
73         corresponding values are obtained by the index.
74      */
75     private static final int[] BAR_TYPE_MAP = {
76             InsetsState.ITYPE_STATUS_BAR,
77             InsetsState.ITYPE_NAVIGATION_BAR,
78             InsetsState.ITYPE_CLIMATE_BAR,
79             InsetsState.ITYPE_EXTRA_NAVIGATION_BAR
80     };
81 
82     private static final Map<@SystemBarSide Integer, Integer> BAR_GRAVITY_MAP = new ArrayMap<>();
83     private static final Map<@SystemBarSide Integer, String> BAR_TITLE_MAP = new ArrayMap<>();
84     private static final Map<@SystemBarSide Integer, Integer> BAR_GESTURE_MAP = new ArrayMap<>();
85 
86     private final Resources mResources;
87     private final Map<@SystemBarSide Integer, SystemBarConfig> mSystemBarConfigMap =
88             new ArrayMap<>();
89     private final List<@SystemBarSide Integer> mSystemBarSidesByZOrder = new ArrayList<>();
90 
91     private boolean mTopNavBarEnabled;
92     private boolean mBottomNavBarEnabled;
93     private boolean mLeftNavBarEnabled;
94     private boolean mRightNavBarEnabled;
95 
96     @Inject
SystemBarConfigs(@ain Resources resources)97     public SystemBarConfigs(@Main Resources resources) {
98         mResources = resources;
99 
100         populateMaps();
101         readConfigs();
102 
103         checkEnabledBarsHaveUniqueBarTypes();
104         checkAllOverlappingBarsHaveDifferentZOrders();
105         checkSystemBarEnabledForNotificationPanel();
106         checkHideBottomBarForKeyboardConfigSync();
107 
108         setInsetPaddingsForOverlappingCorners();
109         sortSystemBarSidesByZOrder();
110     }
111 
getLayoutParamsBySide(@ystemBarSide int side)112     protected WindowManager.LayoutParams getLayoutParamsBySide(@SystemBarSide int side) {
113         return mSystemBarConfigMap.get(side) != null
114                 ? mSystemBarConfigMap.get(side).getLayoutParams() : null;
115     }
116 
getEnabledStatusBySide(@ystemBarSide int side)117     protected boolean getEnabledStatusBySide(@SystemBarSide int side) {
118         switch (side) {
119             case TOP:
120                 return mTopNavBarEnabled;
121             case BOTTOM:
122                 return mBottomNavBarEnabled;
123             case LEFT:
124                 return mLeftNavBarEnabled;
125             case RIGHT:
126                 return mRightNavBarEnabled;
127             default:
128                 return false;
129         }
130     }
131 
getHideForKeyboardBySide(@ystemBarSide int side)132     protected boolean getHideForKeyboardBySide(@SystemBarSide int side) {
133         return mSystemBarConfigMap.get(side) != null
134                 && mSystemBarConfigMap.get(side).getHideForKeyboard();
135     }
136 
insetSystemBar(@ystemBarSide int side, CarSystemBarView view)137     protected void insetSystemBar(@SystemBarSide int side, CarSystemBarView view) {
138         if (mSystemBarConfigMap.get(side) == null) return;
139 
140         int[] paddings = mSystemBarConfigMap.get(side).getPaddings();
141         view.setPadding(paddings[2], paddings[0], paddings[3], paddings[1]);
142     }
143 
getSystemBarSidesByZOrder()144     protected List<Integer> getSystemBarSidesByZOrder() {
145         return mSystemBarSidesByZOrder;
146     }
147 
148     @VisibleForTesting
updateInsetPaddings(@ystemBarSide int side, Map<@SystemBarSide Integer, Boolean> barVisibilities)149     void updateInsetPaddings(@SystemBarSide int side,
150             Map<@SystemBarSide Integer, Boolean> barVisibilities) {
151         SystemBarConfig currentConfig = mSystemBarConfigMap.get(side);
152 
153         if (currentConfig == null) return;
154 
155         if (isHorizontalBar(side)) {
156             if (mLeftNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
157                     LEFT).getZOrder()) {
158                 currentConfig.setPaddingBySide(LEFT,
159                         barVisibilities.get(LEFT) ? mSystemBarConfigMap.get(LEFT).getGirth() : 0);
160             }
161             if (mRightNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
162                     RIGHT).getZOrder()) {
163                 currentConfig.setPaddingBySide(RIGHT,
164                         barVisibilities.get(RIGHT) ? mSystemBarConfigMap.get(RIGHT).getGirth() : 0);
165             }
166         }
167         if (isVerticalBar(side)) {
168             if (mTopNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
169                     TOP).getZOrder()) {
170                 currentConfig.setPaddingBySide(TOP,
171                         barVisibilities.get(TOP) ? mSystemBarConfigMap.get(TOP).getGirth() : 0);
172             }
173             if (mBottomNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
174                     BOTTOM).getZOrder()) {
175                 currentConfig.setPaddingBySide(BOTTOM,
176                         barVisibilities.get(BOTTOM) ? mSystemBarConfigMap.get(BOTTOM).getGirth()
177                                 : 0);
178             }
179         }
180     }
181 
182     @VisibleForTesting
getHunZOrder()183     static int getHunZOrder() {
184         return HUN_ZORDER;
185     }
186 
populateMaps()187     private static void populateMaps() {
188         BAR_GRAVITY_MAP.put(TOP, Gravity.TOP);
189         BAR_GRAVITY_MAP.put(BOTTOM, Gravity.BOTTOM);
190         BAR_GRAVITY_MAP.put(LEFT, Gravity.LEFT);
191         BAR_GRAVITY_MAP.put(RIGHT, Gravity.RIGHT);
192 
193         BAR_TITLE_MAP.put(TOP, "TopCarSystemBar");
194         BAR_TITLE_MAP.put(BOTTOM, "BottomCarSystemBar");
195         BAR_TITLE_MAP.put(LEFT, "LeftCarSystemBar");
196         BAR_TITLE_MAP.put(RIGHT, "RightCarSystemBar");
197 
198         BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_MANDATORY_GESTURES);
199         BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES);
200         BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_MANDATORY_GESTURES);
201         BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_MANDATORY_GESTURES);
202     }
203 
readConfigs()204     private void readConfigs() {
205         mTopNavBarEnabled = mResources.getBoolean(R.bool.config_enableTopSystemBar);
206         mBottomNavBarEnabled = mResources.getBoolean(R.bool.config_enableBottomSystemBar);
207         mLeftNavBarEnabled = mResources.getBoolean(R.bool.config_enableLeftSystemBar);
208         mRightNavBarEnabled = mResources.getBoolean(R.bool.config_enableRightSystemBar);
209 
210         if (mTopNavBarEnabled) {
211             SystemBarConfig topBarConfig =
212                     new SystemBarConfigBuilder()
213                             .setSide(TOP)
214                             .setGirth(mResources.getDimensionPixelSize(
215                                     R.dimen.car_top_system_bar_height))
216                             .setBarType(mResources.getInteger(R.integer.config_topSystemBarType))
217                             .setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))
218                             .setHideForKeyboard(mResources.getBoolean(
219                                     R.bool.config_hideTopSystemBarForKeyboard))
220                             .build();
221             mSystemBarConfigMap.put(TOP, topBarConfig);
222         }
223 
224         if (mBottomNavBarEnabled) {
225             SystemBarConfig bottomBarConfig =
226                     new SystemBarConfigBuilder()
227                             .setSide(BOTTOM)
228                             .setGirth(mResources.getDimensionPixelSize(
229                                     R.dimen.car_bottom_system_bar_height))
230                             .setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))
231                             .setZOrder(
232                                     mResources.getInteger(R.integer.config_bottomSystemBarZOrder))
233                             .setHideForKeyboard(mResources.getBoolean(
234                                     R.bool.config_hideBottomSystemBarForKeyboard))
235                             .build();
236             mSystemBarConfigMap.put(BOTTOM, bottomBarConfig);
237         }
238 
239         if (mLeftNavBarEnabled) {
240             SystemBarConfig leftBarConfig =
241                     new SystemBarConfigBuilder()
242                             .setSide(LEFT)
243                             .setGirth(mResources.getDimensionPixelSize(
244                                     R.dimen.car_left_system_bar_width))
245                             .setBarType(mResources.getInteger(R.integer.config_leftSystemBarType))
246                             .setZOrder(mResources.getInteger(R.integer.config_leftSystemBarZOrder))
247                             .setHideForKeyboard(mResources.getBoolean(
248                                     R.bool.config_hideLeftSystemBarForKeyboard))
249                             .build();
250             mSystemBarConfigMap.put(LEFT, leftBarConfig);
251         }
252 
253         if (mRightNavBarEnabled) {
254             SystemBarConfig rightBarConfig =
255                     new SystemBarConfigBuilder()
256                             .setSide(RIGHT)
257                             .setGirth(mResources.getDimensionPixelSize(
258                                     R.dimen.car_right_system_bar_width))
259                             .setBarType(mResources.getInteger(R.integer.config_rightSystemBarType))
260                             .setZOrder(mResources.getInteger(R.integer.config_rightSystemBarZOrder))
261                             .setHideForKeyboard(mResources.getBoolean(
262                                     R.bool.config_hideRightSystemBarForKeyboard))
263                             .build();
264             mSystemBarConfigMap.put(RIGHT, rightBarConfig);
265         }
266     }
267 
checkEnabledBarsHaveUniqueBarTypes()268     private void checkEnabledBarsHaveUniqueBarTypes() throws RuntimeException {
269         Set<Integer> barTypesUsed = new ArraySet<>();
270         int enabledNavBarCount = mSystemBarConfigMap.size();
271 
272         for (SystemBarConfig systemBarConfig : mSystemBarConfigMap.values()) {
273             barTypesUsed.add(systemBarConfig.getBarType());
274         }
275 
276         // The number of bar types used cannot be fewer than that of enabled system bars.
277         if (barTypesUsed.size() < enabledNavBarCount) {
278             throw new RuntimeException("Each enabled system bar must have a unique bar type. Check "
279                     + "the configuration in config.xml");
280         }
281     }
282 
checkAllOverlappingBarsHaveDifferentZOrders()283     private void checkAllOverlappingBarsHaveDifferentZOrders() {
284         checkOverlappingBarsHaveDifferentZOrders(TOP, LEFT);
285         checkOverlappingBarsHaveDifferentZOrders(TOP, RIGHT);
286         checkOverlappingBarsHaveDifferentZOrders(BOTTOM, LEFT);
287         checkOverlappingBarsHaveDifferentZOrders(BOTTOM, RIGHT);
288     }
289 
checkSystemBarEnabledForNotificationPanel()290     private void checkSystemBarEnabledForNotificationPanel() throws RuntimeException {
291         String notificationPanelMediatorName =
292                 mResources.getString(R.string.config_notificationPanelViewMediator);
293         if (notificationPanelMediatorName == null) {
294             return;
295         }
296 
297         Class<?> notificationPanelMediatorUsed = null;
298         try {
299             notificationPanelMediatorUsed = Class.forName(notificationPanelMediatorName);
300         } catch (ClassNotFoundException e) {
301             e.printStackTrace();
302         }
303 
304         if (!mTopNavBarEnabled && TopNotificationPanelViewMediator.class.isAssignableFrom(
305                 notificationPanelMediatorUsed)) {
306             throw new RuntimeException(
307                     "Top System Bar must be enabled to use " + notificationPanelMediatorName);
308         }
309 
310         if (!mBottomNavBarEnabled && BottomNotificationPanelViewMediator.class.isAssignableFrom(
311                 notificationPanelMediatorUsed)) {
312             throw new RuntimeException("Bottom System Bar must be enabled to use "
313                     + notificationPanelMediatorName);
314         }
315     }
316 
checkHideBottomBarForKeyboardConfigSync()317     private void checkHideBottomBarForKeyboardConfigSync() throws RuntimeException {
318         if (mBottomNavBarEnabled) {
319             boolean actual = mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard);
320             boolean expected = mResources.getBoolean(
321                     com.android.internal.R.bool.config_hideNavBarForKeyboard);
322 
323             if (actual != expected) {
324                 throw new RuntimeException("config_hideBottomSystemBarForKeyboard must not be "
325                         + "overlaid directly and should always refer to"
326                         + "config_hideNavBarForKeyboard. However, their values "
327                         + "currently do not sync. Set config_hideBottomSystemBarForKeyguard to "
328                         + "@*android:bool/config_hideNavBarForKeyboard. To change its "
329                         + "value, overlay config_hideNavBarForKeyboard in "
330                         + "framework/base/core/res/res.");
331             }
332         }
333     }
334 
setInsetPaddingsForOverlappingCorners()335     private void setInsetPaddingsForOverlappingCorners() {
336         Map<@SystemBarSide Integer, Boolean> systemBarVisibilityOnInit =
337                 getSystemBarsVisibilityOnInit();
338         updateInsetPaddings(TOP, systemBarVisibilityOnInit);
339         updateInsetPaddings(BOTTOM, systemBarVisibilityOnInit);
340         updateInsetPaddings(LEFT, systemBarVisibilityOnInit);
341         updateInsetPaddings(RIGHT, systemBarVisibilityOnInit);
342     }
343 
sortSystemBarSidesByZOrder()344     private void sortSystemBarSidesByZOrder() {
345         List<SystemBarConfig> systemBarsByZOrder = new ArrayList<>(mSystemBarConfigMap.values());
346 
347         systemBarsByZOrder.sort(new Comparator<SystemBarConfig>() {
348             @Override
349             public int compare(SystemBarConfig o1, SystemBarConfig o2) {
350                 return o1.getZOrder() - o2.getZOrder();
351             }
352         });
353 
354         systemBarsByZOrder.forEach(systemBarConfig -> {
355             mSystemBarSidesByZOrder.add(systemBarConfig.getSide());
356         });
357     }
358 
359     @InsetsState.InternalInsetsType
getSystemBarTypeBySide(@ystemBarSide int side)360     private int getSystemBarTypeBySide(@SystemBarSide int side) {
361         return mSystemBarConfigMap.get(side) != null
362                 ? mSystemBarConfigMap.get(side).getBarType() : null;
363     }
364 
365     // On init, system bars are visible as long as they are enabled.
getSystemBarsVisibilityOnInit()366     private Map<@SystemBarSide Integer, Boolean> getSystemBarsVisibilityOnInit() {
367         ArrayMap<@SystemBarSide Integer, Boolean> visibilityMap = new ArrayMap<>();
368         visibilityMap.put(TOP, mTopNavBarEnabled);
369         visibilityMap.put(BOTTOM, mBottomNavBarEnabled);
370         visibilityMap.put(LEFT, mLeftNavBarEnabled);
371         visibilityMap.put(RIGHT, mRightNavBarEnabled);
372         return visibilityMap;
373     }
374 
checkOverlappingBarsHaveDifferentZOrders(@ystemBarSide int horizontalSide, @SystemBarSide int verticalSide)375     private void checkOverlappingBarsHaveDifferentZOrders(@SystemBarSide int horizontalSide,
376             @SystemBarSide int verticalSide) {
377 
378         if (isVerticalBar(horizontalSide) || isHorizontalBar(verticalSide)) {
379             Log.w(TAG, "configureBarPaddings: Returning immediately since the horizontal and "
380                     + "vertical sides were not provided correctly.");
381             return;
382         }
383 
384         SystemBarConfig horizontalBarConfig = mSystemBarConfigMap.get(horizontalSide);
385         SystemBarConfig verticalBarConfig = mSystemBarConfigMap.get(verticalSide);
386 
387         if (verticalBarConfig != null && horizontalBarConfig != null) {
388             int horizontalBarZOrder = horizontalBarConfig.getZOrder();
389             int verticalBarZOrder = verticalBarConfig.getZOrder();
390 
391             if (horizontalBarZOrder == verticalBarZOrder) {
392                 throw new RuntimeException(
393                         BAR_TITLE_MAP.get(horizontalSide) + " " + BAR_TITLE_MAP.get(verticalSide)
394                                 + " have the same Z-Order, and so their placing order cannot be "
395                                 + "determined. Determine which bar should be placed on top of the "
396                                 + "other bar and change the Z-order in config.xml accordingly."
397                 );
398             }
399         }
400     }
401 
isHorizontalBar(@ystemBarSide int side)402     private static boolean isHorizontalBar(@SystemBarSide int side) {
403         return side == TOP || side == BOTTOM;
404     }
405 
isVerticalBar(@ystemBarSide int side)406     private static boolean isVerticalBar(@SystemBarSide int side) {
407         return side == LEFT || side == RIGHT;
408     }
409 
410     private static final class SystemBarConfig {
411         private final int mSide;
412         private final int mBarType;
413         private final int mGirth;
414         private final int mZOrder;
415         private final boolean mHideForKeyboard;
416 
417         private int[] mPaddings = new int[]{0, 0, 0, 0};
418 
SystemBarConfig(@ystemBarSide int side, int barType, int girth, int zOrder, boolean hideForKeyboard)419         private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder,
420                 boolean hideForKeyboard) {
421             mSide = side;
422             mBarType = barType;
423             mGirth = girth;
424             mZOrder = zOrder;
425             mHideForKeyboard = hideForKeyboard;
426         }
427 
getSide()428         private int getSide() {
429             return mSide;
430         }
431 
getBarType()432         private int getBarType() {
433             return mBarType;
434         }
435 
getGirth()436         private int getGirth() {
437             return mGirth;
438         }
439 
getZOrder()440         private int getZOrder() {
441             return mZOrder;
442         }
443 
getHideForKeyboard()444         private boolean getHideForKeyboard() {
445             return mHideForKeyboard;
446         }
447 
getPaddings()448         private int[] getPaddings() {
449             return mPaddings;
450         }
451 
getLayoutParams()452         private WindowManager.LayoutParams getLayoutParams() {
453             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
454                     isHorizontalBar(mSide) ? ViewGroup.LayoutParams.MATCH_PARENT : mGirth,
455                     isHorizontalBar(mSide) ? mGirth : ViewGroup.LayoutParams.MATCH_PARENT,
456                     mapZOrderToBarType(mZOrder),
457                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
458                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
459                             | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
460                             | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
461                     PixelFormat.TRANSLUCENT);
462             lp.setTitle(BAR_TITLE_MAP.get(mSide));
463             lp.providedInsets = new InsetsFrameProvider[] {
464                 new InsetsFrameProvider(BAR_TYPE_MAP[mBarType]),
465                 new InsetsFrameProvider(BAR_GESTURE_MAP.get(mSide))
466             };
467             lp.setFitInsetsTypes(0);
468             lp.windowAnimations = 0;
469             lp.gravity = BAR_GRAVITY_MAP.get(mSide);
470             lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
471             return lp;
472         }
473 
mapZOrderToBarType(int zOrder)474         private int mapZOrderToBarType(int zOrder) {
475             return zOrder >= HUN_ZORDER ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL
476                     : WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
477         }
478 
setPaddingBySide(@ystemBarSide int side, int padding)479         private void setPaddingBySide(@SystemBarSide int side, int padding) {
480             mPaddings[side] = padding;
481         }
482     }
483 
484     private static final class SystemBarConfigBuilder {
485         private int mSide;
486         private int mBarType;
487         private int mGirth;
488         private int mZOrder;
489         private boolean mHideForKeyboard;
490 
setSide(@ystemBarSide int side)491         private SystemBarConfigBuilder setSide(@SystemBarSide int side) {
492             mSide = side;
493             return this;
494         }
495 
setBarType(int type)496         private SystemBarConfigBuilder setBarType(int type) {
497             mBarType = type;
498             return this;
499         }
500 
setGirth(int girth)501         private SystemBarConfigBuilder setGirth(int girth) {
502             mGirth = girth;
503             return this;
504         }
505 
setZOrder(int zOrder)506         private SystemBarConfigBuilder setZOrder(int zOrder) {
507             mZOrder = zOrder;
508             return this;
509         }
510 
setHideForKeyboard(boolean hide)511         private SystemBarConfigBuilder setHideForKeyboard(boolean hide) {
512             mHideForKeyboard = hide;
513             return this;
514         }
515 
build()516         private SystemBarConfig build() {
517             return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder, mHideForKeyboard);
518         }
519     }
520 }
521