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.google.android.setupcompat.template;
18 
19 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
20 
21 import android.content.Context;
22 import android.content.res.TypedArray;
23 import android.graphics.Color;
24 import android.os.Build;
25 import android.os.Build.VERSION;
26 import android.os.Build.VERSION_CODES;
27 import androidx.annotation.AttrRes;
28 import androidx.annotation.NonNull;
29 import androidx.annotation.Nullable;
30 import androidx.annotation.VisibleForTesting;
31 import android.util.AttributeSet;
32 import android.view.View;
33 import android.view.Window;
34 import com.google.android.setupcompat.PartnerCustomizationLayout;
35 import com.google.android.setupcompat.R;
36 import com.google.android.setupcompat.internal.TemplateLayout;
37 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
38 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
39 import com.google.android.setupcompat.util.SystemBarHelper;
40 
41 /**
42  * A {@link Mixin} for setting and getting background color and window compatible with light theme
43  * of system navigation bar.
44  */
45 public class SystemNavBarMixin implements Mixin {
46 
47   private final TemplateLayout templateLayout;
48   @Nullable private final Window windowOfActivity;
49   @VisibleForTesting final boolean applyPartnerResources;
50   private int sucSystemNavBarBackgroundColor = 0;
51 
52   /**
53    * Creates a mixin for managing the system navigation bar.
54    *
55    * @param layout The layout this Mixin belongs to.
56    * @param window The window this activity of Mixin belongs to.*
57    */
SystemNavBarMixin(@onNull TemplateLayout layout, @Nullable Window window)58   public SystemNavBarMixin(@NonNull TemplateLayout layout, @Nullable Window window) {
59     this.templateLayout = layout;
60     this.windowOfActivity = window;
61     this.applyPartnerResources =
62         layout instanceof PartnerCustomizationLayout
63             && ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
64   }
65 
66   /**
67    * Creates a mixin for managing the system navigation bar.
68    *
69    * @param attrs XML attributes given to the layout.
70    * @param defStyleAttr The default style attribute as given to the constructor of the layout.
71    */
applyPartnerCustomizations(@ullable AttributeSet attrs, @AttrRes int defStyleAttr)72   public void applyPartnerCustomizations(@Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
73     // Support updating system navigation bar background color and is light system navigation bar
74     // from O.
75     if (Build.VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
76       TypedArray a =
77           templateLayout
78               .getContext()
79               .obtainStyledAttributes(attrs, R.styleable.SucSystemNavBarMixin, defStyleAttr, 0);
80       sucSystemNavBarBackgroundColor =
81           a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarBackgroundColor, 0);
82       setSystemNavBarBackground(sucSystemNavBarBackgroundColor);
83       setLightSystemNavBar(
84           a.getBoolean(
85               R.styleable.SucSystemNavBarMixin_sucLightSystemNavBar, isLightSystemNavBar()));
86       a.recycle();
87     }
88   }
89 
90   /**
91    * Sets the background color of navigation bar. The color will be overridden by partner resource
92    * if the activity is running in setup wizard flow.
93    *
94    * @param color The background color of navigation bar.
95    */
setSystemNavBarBackground(int color)96   public void setSystemNavBarBackground(int color) {
97     if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) {
98       if (applyPartnerResources) {
99         Context context = templateLayout.getContext();
100         color =
101             PartnerConfigHelper.get(context)
102                 .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR);
103       }
104       windowOfActivity.setNavigationBarColor(color);
105     }
106   }
107 
108   /** Returns the background color of navigation bar. */
getSystemNavBarBackground()109   public int getSystemNavBarBackground() {
110     if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) {
111       return windowOfActivity.getNavigationBarColor();
112     }
113     return Color.BLACK;
114   }
115 
116   /**
117    * Sets the navigation bar to draw in a mode that is compatible with light or dark navigation bar
118    * backgrounds. The navigation bar drawing mode will be overridden by partner resource if the
119    * activity is running in setup wizard flow.
120    *
121    * @param isLight true means compatible with light theme, otherwise compatible with dark theme
122    */
setLightSystemNavBar(boolean isLight)123   public void setLightSystemNavBar(boolean isLight) {
124     if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) {
125       if (applyPartnerResources) {
126         Context context = templateLayout.getContext();
127         isLight =
128             PartnerConfigHelper.get(context)
129                 .getBoolean(context, PartnerConfig.CONFIG_LIGHT_NAVIGATION_BAR, false);
130       }
131       if (isLight) {
132         windowOfActivity
133             .getDecorView()
134             .setSystemUiVisibility(
135                 windowOfActivity.getDecorView().getSystemUiVisibility()
136                     | SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
137       } else {
138         windowOfActivity
139             .getDecorView()
140             .setSystemUiVisibility(
141                 windowOfActivity.getDecorView().getSystemUiVisibility()
142                     & ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
143       }
144     }
145   }
146 
147   /**
148    * Returns true if the navigation bar icon should be drawn on light background, false if the icons
149    * should be drawn light-on-dark.
150    */
isLightSystemNavBar()151   public boolean isLightSystemNavBar() {
152     if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) {
153       return (windowOfActivity.getDecorView().getSystemUiVisibility()
154               & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
155           == SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
156     }
157     return true;
158   }
159 
160   /**
161    * Hides the navigation bar, make the color of the status and navigation bars transparent, and
162    * specify {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} flag so that the content is laid-out
163    * behind the transparent status bar. This is commonly used with {@link
164    * android.app.Activity#getWindow()} to make the navigation and status bars follow the Setup
165    * Wizard style.
166    *
167    * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
168    */
hideSystemBars(final Window window)169   public void hideSystemBars(final Window window) {
170     if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
171       SystemBarHelper.addVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
172       SystemBarHelper.addImmersiveFlagsToDecorView(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
173 
174       // Also set the navigation bar and status bar to transparent color. Note that this
175       // doesn't work if android.R.boolean.config_enableTranslucentDecor is false.
176       window.setNavigationBarColor(Color.TRANSPARENT);
177       window.setStatusBarColor(Color.TRANSPARENT);
178     }
179   }
180 
181   /**
182    * Reverts the actions of hideSystemBars. Note that this will remove the system UI visibility
183    * flags regardless of whether it is originally present. The status bar color is reset to
184    * transparent, thus it will show the status bar color set by StatusBarMixin.
185    *
186    * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
187    */
showSystemBars(final Window window, final Context context)188   public void showSystemBars(final Window window, final Context context) {
189     if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
190       SystemBarHelper.removeVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
191       SystemBarHelper.removeImmersiveFlagsFromDecorView(
192           window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS);
193 
194       if (context != null) {
195         if (applyPartnerResources) {
196           int partnerNavigationBarColor =
197               PartnerConfigHelper.get(context)
198                   .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR);
199           window.setStatusBarColor(Color.TRANSPARENT);
200           window.setNavigationBarColor(partnerNavigationBarColor);
201         } else {
202           // noinspection AndroidLintInlinedApi
203           TypedArray typedArray =
204               context.obtainStyledAttributes(
205                   new int[] {android.R.attr.statusBarColor, android.R.attr.navigationBarColor});
206           int statusBarColor = typedArray.getColor(0, 0);
207           int navigationBarColor = typedArray.getColor(1, 0);
208           if (templateLayout instanceof PartnerCustomizationLayout) {
209             if (VERSION.SDK_INT >= VERSION_CODES.M) {
210               statusBarColor = Color.TRANSPARENT;
211             }
212             if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
213               navigationBarColor = sucSystemNavBarBackgroundColor;
214             }
215           }
216           window.setStatusBarColor(statusBarColor);
217           window.setNavigationBarColor(navigationBarColor);
218           typedArray.recycle();
219         }
220       }
221     }
222   }
223 }
224