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