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