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 android.util.AttributeSet; 28 import android.view.View; 29 import android.view.Window; 30 import androidx.annotation.AttrRes; 31 import androidx.annotation.NonNull; 32 import androidx.annotation.Nullable; 33 import androidx.annotation.VisibleForTesting; 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 @VisibleForTesting final boolean useFullDynamicColor; 51 private int sucSystemNavBarBackgroundColor = 0; 52 53 /** 54 * Creates a mixin for managing the system navigation bar. 55 * 56 * @param layout The layout this Mixin belongs to. 57 * @param window The window this activity of Mixin belongs to.* 58 */ SystemNavBarMixin(@onNull TemplateLayout layout, @Nullable Window window)59 public SystemNavBarMixin(@NonNull TemplateLayout layout, @Nullable Window window) { 60 this.templateLayout = layout; 61 this.windowOfActivity = window; 62 this.applyPartnerResources = 63 layout instanceof PartnerCustomizationLayout 64 && ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource(); 65 66 this.useFullDynamicColor = 67 layout instanceof PartnerCustomizationLayout 68 && ((PartnerCustomizationLayout) layout).useFullDynamicColor(); 69 } 70 71 /** 72 * Creates a mixin for managing the system navigation bar. 73 * 74 * @param attrs XML attributes given to the layout. 75 * @param defStyleAttr The default style attribute as given to the constructor of the layout. 76 */ applyPartnerCustomizations(@ullable AttributeSet attrs, @AttrRes int defStyleAttr)77 public void applyPartnerCustomizations(@Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { 78 // Support updating system navigation bar background color and is light system navigation bar 79 // from O. 80 if (Build.VERSION.SDK_INT >= VERSION_CODES.O_MR1) { 81 TypedArray a = 82 templateLayout 83 .getContext() 84 .obtainStyledAttributes(attrs, R.styleable.SucSystemNavBarMixin, defStyleAttr, 0); 85 sucSystemNavBarBackgroundColor = 86 a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarBackgroundColor, 0); 87 setSystemNavBarBackground(sucSystemNavBarBackgroundColor); 88 setLightSystemNavBar( 89 a.getBoolean( 90 R.styleable.SucSystemNavBarMixin_sucLightSystemNavBar, isLightSystemNavBar())); 91 92 // Support updating system navigation bar divider color from P. 93 if (VERSION.SDK_INT >= VERSION_CODES.P) { 94 // get fallback value from theme 95 int[] navBarDividerColorAttr = new int[] {android.R.attr.navigationBarDividerColor}; 96 TypedArray typedArray = 97 templateLayout.getContext().obtainStyledAttributes(navBarDividerColorAttr); 98 int defaultColor = typedArray.getColor(/* index= */ 0, /* defValue= */ 0); 99 int sucSystemNavBarDividerColor = 100 a.getColor(R.styleable.SucSystemNavBarMixin_sucSystemNavBarDividerColor, defaultColor); 101 setSystemNavBarDividerColor(sucSystemNavBarDividerColor); 102 typedArray.recycle(); 103 } 104 a.recycle(); 105 } 106 } 107 108 /** 109 * Sets the background color of navigation bar. The color will be overridden by partner resource 110 * if the activity is running in setup wizard flow. 111 * 112 * @param color The background color of navigation bar. 113 */ setSystemNavBarBackground(int color)114 public void setSystemNavBarBackground(int color) { 115 if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) { 116 if (applyPartnerResources) { 117 // If full dynamic color enabled which means this activity is running outside of setup 118 // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3. 119 if (!useFullDynamicColor) { 120 Context context = templateLayout.getContext(); 121 color = 122 PartnerConfigHelper.get(context) 123 .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR); 124 } 125 } 126 windowOfActivity.setNavigationBarColor(color); 127 } 128 } 129 130 /** Returns the background color of navigation bar. */ getSystemNavBarBackground()131 public int getSystemNavBarBackground() { 132 if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) { 133 return windowOfActivity.getNavigationBarColor(); 134 } 135 return Color.BLACK; 136 } 137 138 /** 139 * Sets the navigation bar to draw in a mode that is compatible with light or dark navigation bar 140 * backgrounds. The navigation bar drawing mode will be overridden by partner resource if the 141 * activity is running in setup wizard flow. 142 * 143 * @param isLight true means compatible with light theme, otherwise compatible with dark theme 144 */ 145 setLightSystemNavBar(boolean isLight)146 public void setLightSystemNavBar(boolean isLight) { 147 if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) { 148 if (applyPartnerResources) { 149 Context context = templateLayout.getContext(); 150 isLight = 151 PartnerConfigHelper.get(context) 152 .getBoolean(context, PartnerConfig.CONFIG_LIGHT_NAVIGATION_BAR, false); 153 } 154 if (isLight) { 155 windowOfActivity 156 .getDecorView() 157 .setSystemUiVisibility( 158 windowOfActivity.getDecorView().getSystemUiVisibility() 159 | SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); 160 } else { 161 windowOfActivity 162 .getDecorView() 163 .setSystemUiVisibility( 164 windowOfActivity.getDecorView().getSystemUiVisibility() 165 & ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); 166 } 167 } 168 } 169 170 /** 171 * Returns true if the navigation bar icon should be drawn on light background, false if the icons 172 * should be drawn light-on-dark. 173 */ isLightSystemNavBar()174 public boolean isLightSystemNavBar() { 175 if (Build.VERSION.SDK_INT >= VERSION_CODES.O && windowOfActivity != null) { 176 return (windowOfActivity.getDecorView().getSystemUiVisibility() 177 & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) 178 == SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; 179 } 180 return true; 181 } 182 183 /** 184 * Sets the divider color of navigation bar. The color will be overridden by partner resource if 185 * the activity is running in setup wizard flow. 186 * 187 * @param color the default divider color of navigation bar 188 */ setSystemNavBarDividerColor(int color)189 public void setSystemNavBarDividerColor(int color) { 190 if (Build.VERSION.SDK_INT >= VERSION_CODES.P && windowOfActivity != null) { 191 if (applyPartnerResources) { 192 Context context = templateLayout.getContext(); 193 // Do nothing if the old version partner provider did not contain the new config. 194 if (PartnerConfigHelper.get(context) 195 .isPartnerConfigAvailable(PartnerConfig.CONFIG_NAVIGATION_BAR_DIVIDER_COLOR)) { 196 color = 197 PartnerConfigHelper.get(context) 198 .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_DIVIDER_COLOR); 199 } 200 } 201 windowOfActivity.setNavigationBarDividerColor(color); 202 } 203 } 204 205 /** 206 * Hides the navigation bar, make the color of the status and navigation bars transparent, and 207 * specify {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} flag so that the content is laid-out 208 * behind the transparent status bar. This is commonly used with {@link 209 * android.app.Activity#getWindow()} to make the navigation and status bars follow the Setup 210 * Wizard style. 211 * 212 * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op. 213 */ hideSystemBars(final Window window)214 public void hideSystemBars(final Window window) { 215 if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 216 SystemBarHelper.addVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS); 217 SystemBarHelper.addImmersiveFlagsToDecorView(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS); 218 219 // Also set the navigation bar and status bar to transparent color. Note that this 220 // doesn't work if android.R.boolean.config_enableTranslucentDecor is false. 221 window.setNavigationBarColor(Color.TRANSPARENT); 222 window.setStatusBarColor(Color.TRANSPARENT); 223 } 224 } 225 226 /** 227 * Reverts the actions of hideSystemBars. Note that this will remove the system UI visibility 228 * flags regardless of whether it is originally present. The status bar color is reset to 229 * transparent, thus it will show the status bar color set by StatusBarMixin. 230 * 231 * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op. 232 */ showSystemBars(final Window window, final Context context)233 public void showSystemBars(final Window window, final Context context) { 234 if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 235 SystemBarHelper.removeVisibilityFlag(window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS); 236 SystemBarHelper.removeImmersiveFlagsFromDecorView( 237 window, SystemBarHelper.DEFAULT_IMMERSIVE_FLAGS); 238 239 if (context != null) { 240 if (applyPartnerResources) { 241 int partnerNavigationBarColor = 242 PartnerConfigHelper.get(context) 243 .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR); 244 window.setStatusBarColor(Color.TRANSPARENT); 245 window.setNavigationBarColor(partnerNavigationBarColor); 246 } else { 247 // noinspection AndroidLintInlinedApi 248 TypedArray typedArray = 249 context.obtainStyledAttributes( 250 new int[] {android.R.attr.statusBarColor, android.R.attr.navigationBarColor}); 251 int statusBarColor = typedArray.getColor(0, 0); 252 int navigationBarColor = typedArray.getColor(1, 0); 253 if (templateLayout instanceof PartnerCustomizationLayout) { 254 if (VERSION.SDK_INT >= VERSION_CODES.M) { 255 statusBarColor = Color.TRANSPARENT; 256 } 257 if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) { 258 navigationBarColor = sucSystemNavBarBackgroundColor; 259 } 260 } 261 window.setStatusBarColor(statusBarColor); 262 window.setNavigationBarColor(navigationBarColor); 263 typedArray.recycle(); 264 } 265 } 266 } 267 } 268 } 269