1 /* 2 * Copyright (C) 2015 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.setupdesign; 18 19 import android.annotation.TargetApi; 20 import android.content.Context; 21 import android.content.res.ColorStateList; 22 import android.content.res.TypedArray; 23 import android.graphics.drawable.ColorDrawable; 24 import android.graphics.drawable.Drawable; 25 import android.os.Build.VERSION_CODES; 26 import androidx.annotation.ColorInt; 27 import androidx.annotation.LayoutRes; 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 import android.util.AttributeSet; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.view.ViewStub; 35 import android.widget.ProgressBar; 36 import android.widget.ScrollView; 37 import android.widget.TextView; 38 import com.google.android.setupcompat.PartnerCustomizationLayout; 39 import com.google.android.setupcompat.partnerconfig.PartnerConfig; 40 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; 41 import com.google.android.setupcompat.template.StatusBarMixin; 42 import com.google.android.setupdesign.template.HeaderMixin; 43 import com.google.android.setupdesign.template.IconMixin; 44 import com.google.android.setupdesign.template.ProgressBarMixin; 45 import com.google.android.setupdesign.template.RequireScrollMixin; 46 import com.google.android.setupdesign.template.ScrollViewScrollHandlingDelegate; 47 import com.google.android.setupdesign.util.DescriptionStyler; 48 49 /** 50 * Layout for the GLIF theme used in Setup Wizard for N. 51 * 52 * <p>Example usage: 53 * 54 * <pre>{@code 55 * <com.google.android.setupdesign.GlifLayout 56 * xmlns:android="http://schemas.android.com/apk/res/android" 57 * xmlns:app="http://schemas.android.com/apk/res-auto" 58 * android:layout_width="match_parent" 59 * android:layout_height="match_parent" 60 * android:icon="@drawable/my_icon" 61 * app:sucHeaderText="@string/my_title"> 62 * 63 * <!-- Content here --> 64 * 65 * </com.google.android.setupdesign.GlifLayout> 66 * }</pre> 67 */ 68 public class GlifLayout extends PartnerCustomizationLayout { 69 70 private static final String TAG = "GlifLayout"; 71 72 private ColorStateList primaryColor; 73 74 private boolean backgroundPatterned = true; 75 76 private boolean applyPartnerHeavyThemeResource = false; 77 78 /** The color of the background. If null, the color will inherit from primaryColor. */ 79 @Nullable private ColorStateList backgroundBaseColor; 80 81 public GlifLayout(Context context) { 82 this(context, 0, 0); 83 } 84 85 public GlifLayout(Context context, int template) { 86 this(context, template, 0); 87 } 88 89 public GlifLayout(Context context, int template, int containerId) { 90 super(context, template, containerId); 91 init(null, R.attr.sudLayoutTheme); 92 } 93 94 public GlifLayout(Context context, AttributeSet attrs) { 95 super(context, attrs); 96 init(attrs, R.attr.sudLayoutTheme); 97 } 98 99 @TargetApi(VERSION_CODES.HONEYCOMB) 100 public GlifLayout(Context context, AttributeSet attrs, int defStyleAttr) { 101 super(context, attrs, defStyleAttr); 102 init(attrs, defStyleAttr); 103 } 104 105 // All the constructors delegate to this init method. The 3-argument constructor is not 106 // available in LinearLayout before v11, so call super with the exact same arguments. 107 private void init(AttributeSet attrs, int defStyleAttr) { 108 109 TypedArray a = 110 getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0); 111 boolean usePartnerHeavyTheme = 112 a.getBoolean(R.styleable.SudGlifLayout_sudUsePartnerHeavyTheme, false); 113 applyPartnerHeavyThemeResource = shouldApplyPartnerResource() && usePartnerHeavyTheme; 114 115 registerMixin( 116 HeaderMixin.class, 117 new HeaderMixin(this, attrs, defStyleAttr)); 118 registerMixin( 119 IconMixin.class, new IconMixin(this, attrs, defStyleAttr)); 120 registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this)); 121 final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this); 122 registerMixin(RequireScrollMixin.class, requireScrollMixin); 123 124 final ScrollView scrollView = getScrollView(); 125 if (scrollView != null) { 126 requireScrollMixin.setScrollHandlingDelegate( 127 new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView)); 128 } 129 130 ColorStateList primaryColor = a.getColorStateList(R.styleable.SudGlifLayout_sudColorPrimary); 131 if (primaryColor != null) { 132 setPrimaryColor(primaryColor); 133 } 134 135 if (applyPartnerHeavyThemeResource) { 136 updateContentBackgroundColorWithPartnerConfig(); 137 } 138 139 ColorStateList backgroundColor = 140 a.getColorStateList(R.styleable.SudGlifLayout_sudBackgroundBaseColor); 141 setBackgroundBaseColor(backgroundColor); 142 143 boolean backgroundPatterned = 144 a.getBoolean(R.styleable.SudGlifLayout_sudBackgroundPatterned, true); 145 setBackgroundPatterned(backgroundPatterned); 146 147 final int stickyHeader = a.getResourceId(R.styleable.SudGlifLayout_sudStickyHeader, 0); 148 if (stickyHeader != 0) { 149 inflateStickyHeader(stickyHeader); 150 } 151 a.recycle(); 152 } 153 154 @Override 155 protected void onFinishInflate() { 156 super.onFinishInflate(); 157 158 TextView description = this.findManagedViewById(R.id.sud_layout_description); 159 if (description != null) { 160 if (applyPartnerHeavyThemeResource) { 161 DescriptionStyler.applyPartnerCustomizationStyle(description); 162 } 163 } 164 getMixin(IconMixin.class).applyPartnerCustomizationStyle(); 165 getMixin(HeaderMixin.class).applyPartnerCustomizationStyle(); 166 } 167 168 @Override 169 protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) { 170 if (template == 0) { 171 template = R.layout.sud_glif_template; 172 } 173 return inflateTemplate(inflater, R.style.SudThemeGlif_Light, template); 174 } 175 176 @Override 177 protected ViewGroup findContainer(int containerId) { 178 if (containerId == 0) { 179 containerId = R.id.sud_layout_content; 180 } 181 return super.findContainer(containerId); 182 } 183 184 /** 185 * Sets the sticky header (i.e. header that doesn't scroll) of the layout, which is at the top of 186 * the content area outside of the scrolling container. The header can only be inflated once per 187 * instance of this layout. 188 * 189 * @param header The layout to be inflated as the header. 190 * @return The root of the inflated header view. 191 */ 192 public View inflateStickyHeader(@LayoutRes int header) { 193 ViewStub stickyHeaderStub = findManagedViewById(R.id.sud_layout_sticky_header); 194 stickyHeaderStub.setLayoutResource(header); 195 return stickyHeaderStub.inflate(); 196 } 197 198 public ScrollView getScrollView() { 199 final View view = findManagedViewById(R.id.sud_scroll_view); 200 return view instanceof ScrollView ? (ScrollView) view : null; 201 } 202 203 public TextView getHeaderTextView() { 204 return getMixin(HeaderMixin.class).getTextView(); 205 } 206 207 public void setHeaderText(int title) { 208 getMixin(HeaderMixin.class).setText(title); 209 } 210 211 public void setHeaderText(CharSequence title) { 212 getMixin(HeaderMixin.class).setText(title); 213 } 214 215 public CharSequence getHeaderText() { 216 return getMixin(HeaderMixin.class).getText(); 217 } 218 219 public void setHeaderColor(ColorStateList color) { 220 getMixin(HeaderMixin.class).setTextColor(color); 221 } 222 223 public ColorStateList getHeaderColor() { 224 return getMixin(HeaderMixin.class).getTextColor(); 225 } 226 227 public void setIcon(Drawable icon) { 228 getMixin(IconMixin.class).setIcon(icon); 229 } 230 231 public Drawable getIcon() { 232 return getMixin(IconMixin.class).getIcon(); 233 } 234 235 /** 236 * Sets the primary color of this layout, which will be used to determine the color of the 237 * progress bar and the background pattern. 238 */ 239 public void setPrimaryColor(@NonNull ColorStateList color) { 240 primaryColor = color; 241 updateBackground(); 242 getMixin(ProgressBarMixin.class).setColor(color); 243 } 244 245 public ColorStateList getPrimaryColor() { 246 return primaryColor; 247 } 248 249 /** 250 * Sets the base color of the background view, which is the status bar for phones and the full- 251 * screen background for tablets. If {@link #isBackgroundPatterned()} is true, the pattern will be 252 * drawn with this color. 253 * 254 * @param color The color to use as the base color of the background. If {@code null}, {@link 255 * #getPrimaryColor()} will be used. 256 */ 257 public void setBackgroundBaseColor(@Nullable ColorStateList color) { 258 backgroundBaseColor = color; 259 updateBackground(); 260 } 261 262 /** 263 * @return The base color of the background. {@code null} indicates the background will be drawn 264 * with {@link #getPrimaryColor()}. 265 */ 266 @Nullable 267 public ColorStateList getBackgroundBaseColor() { 268 return backgroundBaseColor; 269 } 270 271 /** 272 * Sets whether the background should be {@link GlifPatternDrawable}. If {@code false}, the 273 * background will be a solid color. 274 */ 275 public void setBackgroundPatterned(boolean patterned) { 276 backgroundPatterned = patterned; 277 updateBackground(); 278 } 279 280 /** @return True if this view uses {@link GlifPatternDrawable} as background. */ 281 public boolean isBackgroundPatterned() { 282 return backgroundPatterned; 283 } 284 285 private void updateBackground() { 286 final View patternBg = findManagedViewById(R.id.suc_layout_status); 287 if (patternBg != null) { 288 int backgroundColor = 0; 289 if (backgroundBaseColor != null) { 290 backgroundColor = backgroundBaseColor.getDefaultColor(); 291 } else if (primaryColor != null) { 292 backgroundColor = primaryColor.getDefaultColor(); 293 } 294 Drawable background = 295 backgroundPatterned 296 ? new GlifPatternDrawable(backgroundColor) 297 : new ColorDrawable(backgroundColor); 298 getMixin(StatusBarMixin.class).setStatusBarBackground(background); 299 } 300 } 301 302 public boolean isProgressBarShown() { 303 return getMixin(ProgressBarMixin.class).isShown(); 304 } 305 306 public void setProgressBarShown(boolean shown) { 307 getMixin(ProgressBarMixin.class).setShown(shown); 308 } 309 310 public ProgressBar peekProgressBar() { 311 return getMixin(ProgressBarMixin.class).peekProgressBar(); 312 } 313 314 /** 315 * Returns if the current layout/activity applies heavy partner customized configurations or not. 316 */ 317 public boolean shouldApplyPartnerHeavyThemeResource() { 318 return applyPartnerHeavyThemeResource; 319 } 320 321 /** Updates the background color of this layout with the partner-customizable background color. */ 322 private void updateContentBackgroundColorWithPartnerConfig() { 323 @ColorInt 324 int color = 325 PartnerConfigHelper.get(getContext()) 326 .getColor(getContext(), PartnerConfig.CONFIG_LAYOUT_BACKGROUND_COLOR); 327 this.getRootView().setBackgroundColor(color); 328 } 329 } 330