summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorMaurice Lam <yukl@google.com>2016-12-19 11:46:55 -0800
committerMaurice Lam <yukl@google.com>2017-01-10 10:47:33 -0800
commit7514f1cee29b3feb4822ce16945c1c312057d24f (patch)
treed345c2b45f0b7641dae6301f2b2c50457c525474 /library
parent035ba6bda68e78bbb49424300bdd3f0fb305b9e7 (diff)
downloadsetupwizard-7514f1cee29b3feb4822ce16945c1c312057d24f.tar.gz
Implement Mixins for Templates
Implement Mixin functionalities, which defines part of a template layout, making it reusable in different layouts. For example, the HeaderMixin allows setting and getting the header text via the mixin, which allows for clients which uses multiple different layuots to simplify their code via something like layout.getMixin(HeaderMixin.class).setText("Foobar"); Bug: 34163318 Test: ./gradlew connectedAndroidTest Change-Id: I4348c8bb5b8e640b49c2be0c79c70aa85cf0ebc0
Diffstat (limited to 'library')
-rw-r--r--library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java2
-rw-r--r--library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java2
-rw-r--r--library/full-support/src/com/android/setupwizardlib/util/RecyclerViewRequireScrollHelper.java1
-rw-r--r--library/main/res/values/attrs.xml17
-rw-r--r--library/main/src/com/android/setupwizardlib/GlifLayout.java131
-rw-r--r--library/main/src/com/android/setupwizardlib/SetupWizardLayout.java75
-rw-r--r--library/main/src/com/android/setupwizardlib/TemplateLayout.java47
-rw-r--r--library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java73
-rw-r--r--library/main/src/com/android/setupwizardlib/template/HeaderMixin.java95
-rw-r--r--library/main/src/com/android/setupwizardlib/template/IconMixin.java81
-rw-r--r--library/main/src/com/android/setupwizardlib/template/Mixin.java26
-rw-r--r--library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java83
-rw-r--r--library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java130
-rw-r--r--library/test/res/layout/test_mixin_attributes.xml24
-rw-r--r--library/test/res/layout/test_progress_bar_template.xml28
-rw-r--r--library/test/src/com/android/setupwizardlib/TemplateLayoutTest.java (renamed from library/test/src/com/android/setupwizardlib/test/TemplateLayoutTest.java)14
-rw-r--r--library/test/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java91
-rw-r--r--library/test/src/com/android/setupwizardlib/template/HeaderMixinTest.java106
-rw-r--r--library/test/src/com/android/setupwizardlib/template/IconMixinTest.java105
-rw-r--r--library/test/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java98
-rw-r--r--library/test/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java134
-rw-r--r--library/test/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java70
-rw-r--r--library/test/src/com/android/setupwizardlib/test/GlifLayoutTest.java16
-rw-r--r--library/test/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java14
24 files changed, 1286 insertions, 177 deletions
diff --git a/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java b/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java
index 476abce..81e2b40 100644
--- a/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java
+++ b/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java
@@ -135,7 +135,7 @@ public class GlifRecyclerLayout extends GlifLayout {
}
@Override
- protected View findManagedViewById(int id) {
+ public View findManagedViewById(int id) {
if (mHeader != null) {
final View view = mHeader.findViewById(id);
if (view != null) {
diff --git a/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java b/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
index 087277f..905173a 100644
--- a/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
+++ b/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
@@ -151,7 +151,7 @@ public class SetupWizardRecyclerLayout extends SetupWizardLayout {
}
@Override
- protected View findManagedViewById(int id) {
+ public View findManagedViewById(int id) {
if (mHeader != null) {
final View view = mHeader.findViewById(id);
if (view != null) {
diff --git a/library/full-support/src/com/android/setupwizardlib/util/RecyclerViewRequireScrollHelper.java b/library/full-support/src/com/android/setupwizardlib/util/RecyclerViewRequireScrollHelper.java
index 7840175..ad9354a 100644
--- a/library/full-support/src/com/android/setupwizardlib/util/RecyclerViewRequireScrollHelper.java
+++ b/library/full-support/src/com/android/setupwizardlib/util/RecyclerViewRequireScrollHelper.java
@@ -40,6 +40,7 @@ public class RecyclerViewRequireScrollHelper extends AbstractRequireScrollHelper
mRecyclerView = recyclerView;
}
+ @Override
protected void requireScroll() {
super.requireScroll();
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
diff --git a/library/main/res/values/attrs.xml b/library/main/res/values/attrs.xml
index 1fedecc..984b6cd 100644
--- a/library/main/res/values/attrs.xml
+++ b/library/main/res/values/attrs.xml
@@ -35,7 +35,6 @@
<!-- Custom view attributes -->
<attr name="suwColorPrimary" format="color" />
<attr name="suwHeader" format="reference" />
- <attr name="suwHeaderText" format="string" localization="suggested" />
<attr name="suwDividerInset" format="dimension|reference" />
<attr name="suwItemDescriptionStyle" format="reference" />
@@ -52,10 +51,7 @@
</declare-styleable>
<declare-styleable name="SuwGlifLayout">
- <attr name="android:icon" />
<attr name="suwColorPrimary" />
- <attr name="suwHeaderColor" format="reference|color" />
- <attr name="suwHeaderText" />
</declare-styleable>
<declare-styleable name="SuwGlifListLayout">
@@ -75,7 +71,6 @@
<declare-styleable name="SuwSetupWizardLayout">
<attr name="suwBackground" format="color|reference" />
<attr name="suwBackgroundTile" format="color|reference" />
- <attr name="suwHeaderText" />
<attr name="suwDecorPaddingTop" format="dimension|reference" />
<attr name="suwIllustration" format="color|reference" />
<attr name="suwIllustrationAspectRatio" format="float|reference" />
@@ -122,4 +117,16 @@
<attr name="android:theme" />
</declare-styleable>
+ <declare-styleable name="SuwHeaderMixin">
+ <attr name="suwHeaderText" format="string" localization="suggested" />
+ </declare-styleable>
+
+ <declare-styleable name="SuwColoredHeaderMixin">
+ <attr name="suwHeaderColor" format="reference|color" />
+ </declare-styleable>
+
+ <declare-styleable name="SuwIconMixin">
+ <attr name="android:icon" />
+ </declare-styleable>
+
</resources>
diff --git a/library/main/src/com/android/setupwizardlib/GlifLayout.java b/library/main/src/com/android/setupwizardlib/GlifLayout.java
index 13d35fe..a6a852c 100644
--- a/library/main/src/com/android/setupwizardlib/GlifLayout.java
+++ b/library/main/src/com/android/setupwizardlib/GlifLayout.java
@@ -29,12 +29,14 @@ import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
+import com.android.setupwizardlib.template.ColoredHeaderMixin;
+import com.android.setupwizardlib.template.HeaderMixin;
+import com.android.setupwizardlib.template.IconMixin;
+import com.android.setupwizardlib.template.ProgressBarMixin;
import com.android.setupwizardlib.view.StatusBarBackgroundLayout;
/**
@@ -88,29 +90,13 @@ public class GlifLayout extends TemplateLayout {
// All the constructors delegate to this init method. The 3-argument constructor is not
// available in LinearLayout before v11, so call super with the exact same arguments.
private void init(AttributeSet attrs, int defStyleAttr) {
+ registerMixin(HeaderMixin.class, new ColoredHeaderMixin(this, attrs, defStyleAttr));
+ registerMixin(IconMixin.class, new IconMixin(this, attrs, defStyleAttr));
+ registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
+
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.SuwGlifLayout, defStyleAttr, 0);
- final Drawable icon = a.getDrawable(R.styleable.SuwGlifLayout_android_icon);
- if (icon != null) {
- setIcon(icon);
- }
-
- // Set the header color
- final ColorStateList headerColor =
- a.getColorStateList(R.styleable.SuwGlifLayout_suwHeaderColor);
- if (headerColor != null) {
- setHeaderColor(headerColor);
- }
-
-
- // Set the header text
- final CharSequence headerText =
- a.getText(R.styleable.SuwGlifLayout_suwHeaderText);
- if (headerText != null) {
- setHeaderText(headerText);
- }
-
ColorStateList primaryColor =
a.getColorStateList(R.styleable.SuwGlifLayout_suwColorPrimary);
@@ -149,72 +135,49 @@ public class GlifLayout extends TemplateLayout {
return super.findContainer(containerId);
}
- /**
- * Same as {@link android.view.View#findViewById(int)}, but may include views that are managed
- * by this view but not currently added to the view hierarchy. e.g. recycler view or list view
- * headers that are not currently shown.
- */
- protected View findManagedViewById(int id) {
- return findViewById(id);
- }
-
public ScrollView getScrollView() {
final View view = findManagedViewById(R.id.suw_scroll_view);
return view instanceof ScrollView ? (ScrollView) view : null;
}
public TextView getHeaderTextView() {
- return (TextView) findManagedViewById(R.id.suw_layout_title);
+ return getMixin(HeaderMixin.class).getTextView();
}
public void setHeaderText(int title) {
- setHeaderText(getContext().getResources().getText(title));
+ getMixin(HeaderMixin.class).setText(title);
}
public void setHeaderText(CharSequence title) {
- final TextView titleView = getHeaderTextView();
- if (titleView != null) {
- titleView.setText(title);
- }
+ getMixin(HeaderMixin.class).setText(title);
}
public CharSequence getHeaderText() {
- final TextView titleView = getHeaderTextView();
- return titleView != null ? titleView.getText() : null;
+ return getMixin(HeaderMixin.class).getText();
}
public void setHeaderColor(ColorStateList color) {
- final TextView titleView = getHeaderTextView();
- if (titleView != null) {
- titleView.setTextColor(color);
- }
+ final ColoredHeaderMixin mixin = (ColoredHeaderMixin) getMixin(HeaderMixin.class);
+ mixin.setColor(color);
}
public ColorStateList getHeaderColor() {
- final TextView titleView = getHeaderTextView();
- return titleView != null ? titleView.getTextColors() : null;
+ final ColoredHeaderMixin mixin = (ColoredHeaderMixin) getMixin(HeaderMixin.class);
+ return mixin.getColor();
}
public void setIcon(Drawable icon) {
- final ImageView iconView = getIconView();
- if (iconView != null) {
- iconView.setImageDrawable(icon);
- }
+ getMixin(IconMixin.class).setIcon(icon);
}
public Drawable getIcon() {
- final ImageView iconView = getIconView();
- return iconView != null ? iconView.getDrawable() : null;
- }
-
- protected ImageView getIconView() {
- return (ImageView) findManagedViewById(R.id.suw_layout_icon);
+ return getMixin(IconMixin.class).getIcon();
}
public void setPrimaryColor(ColorStateList color) {
mPrimaryColor = color;
setGlifPatternColor(color);
- setProgressBarColor(color);
+ getMixin(ProgressBarMixin.class).setColor(color);
}
public ColorStateList getPrimaryColor() {
@@ -239,64 +202,14 @@ public class GlifLayout extends TemplateLayout {
}
public boolean isProgressBarShown() {
- final View progressBar = findManagedViewById(R.id.suw_layout_progress);
- return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
+ return getMixin(ProgressBarMixin.class).isShown();
}
public void setProgressBarShown(boolean shown) {
- if (shown) {
- View progressBar = getProgressBar();
- if (progressBar != null) {
- progressBar.setVisibility(View.VISIBLE);
- }
- } else {
- View progressBar = peekProgressBar();
- if (progressBar != null) {
- progressBar.setVisibility(View.GONE);
- }
- }
+ getMixin(ProgressBarMixin.class).setShown(shown);
}
- /**
- * Gets the progress bar in the layout. If the progress bar has not been used before, it will be
- * installed (i.e. inflated from its view stub).
- *
- * @return The progress bar of this layout. May be null only if the template used doesn't have a
- * progress bar built-in.
- */
- private ProgressBar getProgressBar() {
- final View progressBar = peekProgressBar();
- if (progressBar == null) {
- final ViewStub progressBarStub =
- (ViewStub) findManagedViewById(R.id.suw_layout_progress_stub);
- if (progressBarStub != null) {
- progressBarStub.inflate();
- }
- setProgressBarColor(mPrimaryColor);
- }
- return peekProgressBar();
- }
-
- /**
- * Gets the progress bar in the layout only if it has been installed.
- * {@link #setProgressBarShown(boolean)} should be called before this to ensure the progress bar
- * is set up correctly.
- *
- * @return The progress bar of this layout, or null if the progress bar is not installed. The
- * null case can happen either if {@link #setProgressBarShown(boolean)} with true was
- * not called before this, or if the template does not contain a progress bar.
- */
public ProgressBar peekProgressBar() {
- return (ProgressBar) findManagedViewById(R.id.suw_layout_progress);
- }
-
- private void setProgressBarColor(ColorStateList color) {
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- final ProgressBar bar = peekProgressBar();
- if (bar != null) {
- bar.setIndeterminateTintList(color);
- bar.setProgressBackgroundTintList(color);
- }
- }
+ return getMixin(ProgressBarMixin.class).peekProgressBar();
}
}
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
index 79d9222..c30a12d 100644
--- a/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
+++ b/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
@@ -37,11 +37,12 @@ import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
+import com.android.setupwizardlib.template.HeaderMixin;
+import com.android.setupwizardlib.template.NavigationBarMixin;
+import com.android.setupwizardlib.template.ProgressBarMixin;
import com.android.setupwizardlib.util.RequireScrollHelper;
import com.android.setupwizardlib.view.BottomScrollView;
import com.android.setupwizardlib.view.Illustration;
@@ -51,8 +52,6 @@ public class SetupWizardLayout extends TemplateLayout {
private static final String TAG = "SetupWizardLayout";
- private ColorStateList mProgressBarColor;
-
public SetupWizardLayout(Context context) {
super(context, 0, 0);
init(null, R.attr.suwLayoutTheme);
@@ -81,6 +80,10 @@ public class SetupWizardLayout extends TemplateLayout {
// All the constructors delegate to this init method. The 3-argument constructor is not
// available in LinearLayout before v11, so call super with the exact same arguments.
private void init(AttributeSet attrs, int defStyleAttr) {
+ registerMixin(HeaderMixin.class, new HeaderMixin(this, attrs, defStyleAttr));
+ registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
+ registerMixin(NavigationBarMixin.class, new NavigationBarMixin(this));
+
final TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.SuwSetupWizardLayout, defStyleAttr, 0);
@@ -132,13 +135,6 @@ public class SetupWizardLayout extends TemplateLayout {
}
setIllustrationAspectRatio(illustrationAspectRatio);
- // Set the header text
- final CharSequence headerText =
- a.getText(R.styleable.SuwSetupWizardLayout_suwHeaderText);
- if (headerText != null) {
- setHeaderText(headerText);
- }
-
a.recycle();
}
@@ -192,8 +188,7 @@ public class SetupWizardLayout extends TemplateLayout {
}
public NavigationBar getNavigationBar() {
- final View view = findManagedViewById(R.id.suw_layout_navigation_bar);
- return view instanceof NavigationBar ? (NavigationBar) view : null;
+ return getMixin(NavigationBarMixin.class).getNavigationBar();
}
public ScrollView getScrollView() {
@@ -213,26 +208,19 @@ public class SetupWizardLayout extends TemplateLayout {
}
public void setHeaderText(int title) {
- final TextView titleView = getHeaderTextView();
- if (titleView != null) {
- titleView.setText(title);
- }
+ getMixin(HeaderMixin.class).setText(title);
}
public void setHeaderText(CharSequence title) {
- final TextView titleView = getHeaderTextView();
- if (titleView != null) {
- titleView.setText(title);
- }
+ getMixin(HeaderMixin.class).setText(title);
}
public CharSequence getHeaderText() {
- final TextView titleView = getHeaderTextView();
- return titleView != null ? titleView.getText() : null;
+ return getMixin(HeaderMixin.class).getText();
}
public TextView getHeaderTextView() {
- return (TextView) findManagedViewById(R.id.suw_layout_title);
+ return getMixin(HeaderMixin.class).getTextView();
}
/**
@@ -375,18 +363,8 @@ public class SetupWizardLayout extends TemplateLayout {
}
}
- /**
- * Same as {@link android.view.View#findViewById(int)}, but may include views that are managed
- * by this view but not currently added to the view hierarchy. e.g. recycler view or list view
- * headers that are not currently shown.
- */
- protected View findManagedViewById(int id) {
- return findViewById(id);
- }
-
public boolean isProgressBarShown() {
- final View progressBar = findManagedViewById(R.id.suw_layout_progress);
- return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
+ return getMixin(ProgressBarMixin.class).isShown();
}
/**
@@ -395,19 +373,7 @@ public class SetupWizardLayout extends TemplateLayout {
* view hierarchy until the first time this is set to {@code true}.
*/
public void setProgressBarShown(boolean shown) {
- final View progressBar = findManagedViewById(R.id.suw_layout_progress);
- if (progressBar != null) {
- progressBar.setVisibility(shown ? View.VISIBLE : View.GONE);
- } else if (shown) {
- final ViewStub progressBarStub =
- (ViewStub) findManagedViewById(R.id.suw_layout_progress_stub);
- if (progressBarStub != null) {
- progressBarStub.inflate();
- }
- if (mProgressBarColor != null) {
- setProgressBarColor(mProgressBarColor);
- }
- }
+ getMixin(ProgressBarMixin.class).setShown(shown);
}
/**
@@ -427,20 +393,11 @@ public class SetupWizardLayout extends TemplateLayout {
}
public void setProgressBarColor(ColorStateList color) {
- mProgressBarColor = color;
- if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- // Suppress lint error caused by
- // https://code.google.com/p/android/issues/detail?id=183136
- // noinspection AndroidLintWrongViewCast
- final ProgressBar bar = (ProgressBar) findViewById(R.id.suw_layout_progress);
- if (bar != null) {
- bar.setIndeterminateTintList(color);
- }
- }
+ getMixin(ProgressBarMixin.class).setColor(color);
}
public ColorStateList getProgressBarColor() {
- return mProgressBarColor;
+ return getMixin(ProgressBarMixin.class).getColor();
}
/* Misc */
diff --git a/library/main/src/com/android/setupwizardlib/TemplateLayout.java b/library/main/src/com/android/setupwizardlib/TemplateLayout.java
index 90666d2..3eae3af 100644
--- a/library/main/src/com/android/setupwizardlib/TemplateLayout.java
+++ b/library/main/src/com/android/setupwizardlib/TemplateLayout.java
@@ -28,6 +28,11 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
+import com.android.setupwizardlib.template.Mixin;
+
+import java.util.HashMap;
+import java.util.Map;
+
/**
* A generic template class that inflates a template, provided in the constructor or in
* {@code android:layout} through XML, and adds its children to a "container" in the template. When
@@ -42,6 +47,8 @@ public class TemplateLayout extends FrameLayout {
*/
private ViewGroup mContainer;
+ private Map<Class<? extends Mixin>, Mixin> mMixins = new HashMap<>();
+
public TemplateLayout(Context context, int template, int containerId) {
super(context);
init(template, containerId, null, R.attr.suwLayoutTheme);
@@ -74,6 +81,46 @@ public class TemplateLayout extends FrameLayout {
a.recycle();
}
+ /**
+ * Registers a mixin with a given class. This method should be called in the constructor.
+ *
+ * @param cls The class to register the mixin. In most cases, {@code cls} is the same as
+ * {@code mixin.getClass()}, but {@code cls} can also be a super class of that. In
+ * the latter case the the mixin must be retrieved using {@code cls} in
+ * {@link #getMixin(Class)}, not the subclass.
+ * @param mixin The mixin to be registered.
+ * @param <M> The class of the mixin to register. This is the same as {@code cls}
+ */
+ protected <M extends Mixin> void registerMixin(Class<M> cls, M mixin) {
+ mMixins.put(cls, mixin);
+ }
+
+ /**
+ * Same as {@link android.view.View#findViewById(int)}, but may include views that are managed
+ * by this view but not currently added to the view hierarchy. e.g. recycler view or list view
+ * headers that are not currently shown.
+ */
+ public View findManagedViewById(int id) {
+ return findViewById(id);
+ }
+
+ /**
+ * Get a {@link Mixin} from this template registered earlier in
+ * {@link #registerMixin(Class, Mixin)}.
+ *
+ * @param cls The class marker of Mixin being requested. The actual Mixin returned may be a
+ * subclass of this marker. Note that this must be the same class as registered in
+ * {@link #registerMixin(Class, Mixin)}, which is not necessarily the
+ * same as the concrete class of the instance returned by this method.
+ * @param <M> The type of the class marker.
+ * @return The mixin marked by {@code cls}, or null if the template does not have a matching
+ * mixin.
+ */
+ @SuppressWarnings("unchecked")
+ public <M extends Mixin> M getMixin(Class<M> cls) {
+ return (M) mMixins.get(cls);
+ }
+
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
mContainer.addView(child, index, params);
diff --git a/library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java b/library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java
new file mode 100644
index 0000000..ccc5aad
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.TemplateLayout;
+
+/**
+ * A {@link Mixin} displaying a header text that can be set to different colors. This Mixin is
+ * registered to the tempalte using HeaderMixin.class, and can be retrieved using:
+ * {@code (ColoredHeaderMixin) templateLayout.getMixin(HeaderMixin.class}.
+ */
+public class ColoredHeaderMixin extends HeaderMixin {
+
+ /**
+ * {@inheritDoc}
+ */
+ public ColoredHeaderMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
+ super(layout, attrs, defStyleAttr);
+
+ final TypedArray a = layout.getContext().obtainStyledAttributes(
+ attrs, R.styleable.SuwColoredHeaderMixin, defStyleAttr, 0);
+
+ // Set the header color
+ final ColorStateList headerColor =
+ a.getColorStateList(R.styleable.SuwColoredHeaderMixin_suwHeaderColor);
+ if (headerColor != null) {
+ setColor(headerColor);
+ }
+
+ a.recycle();
+ }
+
+ /**
+ * Sets the color of the header text. This can also be set via XML using
+ * {@code app:suwHeaderColor}.
+ *
+ * @param color The text color of the header.
+ */
+ public void setColor(ColorStateList color) {
+ final TextView titleView = getTextView();
+ if (titleView != null) {
+ titleView.setTextColor(color);
+ }
+ }
+
+ /**
+ * @return The current text color of the header.
+ */
+ public ColorStateList getColor() {
+ final TextView titleView = getTextView();
+ return titleView != null ? titleView.getTextColors() : null;
+ }
+}
diff --git a/library/main/src/com/android/setupwizardlib/template/HeaderMixin.java b/library/main/src/com/android/setupwizardlib/template/HeaderMixin.java
new file mode 100644
index 0000000..bd3f210
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/HeaderMixin.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.content.res.TypedArray;
+import android.support.annotation.AttrRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.TemplateLayout;
+
+/**
+ * A {@link Mixin} for setting and getting the header text.
+ */
+public class HeaderMixin implements Mixin {
+
+ private TemplateLayout mTemplateLayout;
+
+ /**
+ * @param layout The layout this Mixin belongs to.
+ * @param attrs XML attributes given to the layout.
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+ */
+ public HeaderMixin(@NonNull TemplateLayout layout, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ mTemplateLayout = layout;
+
+ final TypedArray a = layout.getContext().obtainStyledAttributes(
+ attrs, R.styleable.SuwHeaderMixin, defStyleAttr, 0);
+
+ // Set the header text
+ final CharSequence headerText = a.getText(R.styleable.SuwHeaderMixin_suwHeaderText);
+ if (headerText != null) {
+ setText(headerText);
+ }
+
+ a.recycle();
+ }
+
+ /**
+ * @return The TextView displaying the header.
+ */
+ public TextView getTextView() {
+ return (TextView) mTemplateLayout.findManagedViewById(R.id.suw_layout_title);
+ }
+
+ /**
+ * Sets the header text. This can also be set via the XML attribute {@code app:suwHeaderText}.
+ *
+ * @param title The resource ID of the text to be set as header.
+ */
+ public void setText(int title) {
+ final TextView titleView = getTextView();
+ if (titleView != null) {
+ titleView.setText(title);
+ }
+ }
+
+ /**
+ * Sets the header text. This can also be set via the XML attribute {@code app:suwHeaderText}.
+ *
+ * @param title The text to be set as header.
+ */
+ public void setText(CharSequence title) {
+ final TextView titleView = getTextView();
+ if (titleView != null) {
+ titleView.setText(title);
+ }
+ }
+
+ /**
+ * @return The current header text.
+ */
+ public CharSequence getText() {
+ final TextView titleView = getTextView();
+ return titleView != null ? titleView.getText() : null;
+ }
+}
diff --git a/library/main/src/com/android/setupwizardlib/template/IconMixin.java b/library/main/src/com/android/setupwizardlib/template/IconMixin.java
new file mode 100644
index 0000000..46c23f0
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/IconMixin.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.TemplateLayout;
+
+/**
+ * A {@link Mixin} for setting an icon on the template layout.
+ */
+public class IconMixin implements Mixin {
+
+ private TemplateLayout mTemplateLayout;
+
+ /**
+ * @param layout The template layout that this Mixin is a part of.
+ * @param attrs XML attributes given to the layout.
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+ */
+ public IconMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
+ mTemplateLayout = layout;
+ final Context context = layout.getContext();
+
+ final TypedArray a =
+ context.obtainStyledAttributes(attrs, R.styleable.SuwIconMixin, defStyleAttr, 0);
+
+ final Drawable icon = a.getDrawable(R.styleable.SuwIconMixin_android_icon);
+ if (icon != null) {
+ setIcon(icon);
+ }
+
+ a.recycle();
+ }
+
+ /**
+ * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
+ *
+ * @param icon A drawable icon.
+ */
+ public void setIcon(Drawable icon) {
+ final ImageView iconView = getView();
+ if (iconView != null) {
+ iconView.setImageDrawable(icon);
+ }
+ }
+
+ /**
+ * @return The icon previously set in {@link #setIcon(Drawable)} or {@code android:icon}
+ */
+ public Drawable getIcon() {
+ final ImageView iconView = getView();
+ return iconView != null ? iconView.getDrawable() : null;
+ }
+
+ /**
+ * @return The ImageView responsible for displaying the icon.
+ */
+ protected ImageView getView() {
+ return (ImageView) mTemplateLayout.findManagedViewById(R.id.suw_layout_icon);
+ }
+}
diff --git a/library/main/src/com/android/setupwizardlib/template/Mixin.java b/library/main/src/com/android/setupwizardlib/template/Mixin.java
new file mode 100644
index 0000000..285ea31
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/Mixin.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+/**
+ * Marker interface to indicate Mixin classes.
+ *
+ * @see TemplateLayout#registerMixin(Class, Mixin)
+ * @see TemplateLayout#getMixin(Class)
+ */
+public interface Mixin {
+}
diff --git a/library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java b/library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java
new file mode 100644
index 0000000..df35017
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.view.View;
+
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.view.NavigationBar;
+import com.android.setupwizardlib.view.NavigationBar.NavigationBarListener;
+
+/**
+ * A {@link Mixin} for interacting with a {@link NavigationBar}.
+ */
+public class NavigationBarMixin implements Mixin {
+
+ private TemplateLayout mTemplateLayout;
+
+ /**
+ * @param layout The layout this mixin belongs to.
+ */
+ public NavigationBarMixin(TemplateLayout layout) {
+ mTemplateLayout = layout;
+ }
+
+ /**
+ * @return The navigation bar instance in the layout, or null if the layout does not have a
+ * navigation bar.
+ */
+ public NavigationBar getNavigationBar() {
+ final View view = mTemplateLayout.findManagedViewById(R.id.suw_layout_navigation_bar);
+ return view instanceof NavigationBar ? (NavigationBar) view : null;
+ }
+
+ /**
+ * Sets the label of the next button.
+ *
+ * @param text Label of the next button.
+ */
+ public void setNextButtonText(int text) {
+ getNavigationBar().getNextButton().setText(text);
+ }
+
+ /**
+ * Sets the label of the next button.
+ *
+ * @param text Label of the next button.
+ */
+ public void setNextButtonText(CharSequence text) {
+ getNavigationBar().getNextButton().setText(text);
+ }
+
+ /**
+ * @return The current label of the next button.
+ */
+ public CharSequence getNextButtonText() {
+ return getNavigationBar().getNextButton().getText();
+ }
+
+ /**
+ * Sets the listener to handle back and next button clicks in the navigation bar.
+ *
+ * @see NavigationBar#setNavigationBarListener(NavigationBarListener)
+ * @see NavigationBarListener
+ */
+ public void setNavigationBarListener(NavigationBarListener listener) {
+ getNavigationBar().setNavigationBarListener(listener);
+ }
+}
diff --git a/library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java b/library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java
new file mode 100644
index 0000000..d5c038e
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.content.res.ColorStateList;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.ProgressBar;
+
+import com.android.setupwizardlib.R;
+import com.android.setupwizardlib.TemplateLayout;
+
+/**
+ * A {@link Mixin} for showing a progress bar.
+ */
+public class ProgressBarMixin implements Mixin {
+
+ private TemplateLayout mTemplateLayout;
+
+ @Nullable
+ private ColorStateList mColor;
+
+ /**
+ * @param layout The layout this mixin belongs to.
+ */
+ public ProgressBarMixin(TemplateLayout layout) {
+ mTemplateLayout = layout;
+ }
+
+ /**
+ * @return True if the progress bar is currently shown.
+ */
+ public boolean isShown() {
+ final View progressBar = mTemplateLayout.findManagedViewById(R.id.suw_layout_progress);
+ return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Sets whether the progress bar is shown. If the progress bar has not been inflated from the
+ * stub, this method will inflate the progress bar.
+ *
+ * @param shown True to show the progress bar, false to hide it.
+ */
+ public void setShown(boolean shown) {
+ if (shown) {
+ View progressBar = getProgressBar();
+ if (progressBar != null) {
+ progressBar.setVisibility(View.VISIBLE);
+ }
+ } else {
+ View progressBar = peekProgressBar();
+ if (progressBar != null) {
+ progressBar.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ /**
+ * Gets the progress bar in the layout. If the progress bar has not been used before, it will be
+ * installed (i.e. inflated from its view stub).
+ *
+ * @return The progress bar of this layout. May be null only if the template used doesn't have a
+ * progress bar built-in.
+ */
+ private ProgressBar getProgressBar() {
+ final View progressBar = peekProgressBar();
+ if (progressBar == null) {
+ final ViewStub progressBarStub =
+ (ViewStub) mTemplateLayout.findManagedViewById(R.id.suw_layout_progress_stub);
+ if (progressBarStub != null) {
+ progressBarStub.inflate();
+ }
+ setColor(mColor);
+ }
+ return peekProgressBar();
+ }
+
+ /**
+ * Gets the progress bar in the layout only if it has been installed.
+ * {@link #setShown(boolean)} should be called before this to ensure the progress bar
+ * is set up correctly.
+ *
+ * @return The progress bar of this layout, or null if the progress bar is not installed. The
+ * null case can happen either if {@link #setShown(boolean)} with true was
+ * not called before this, or if the template does not contain a progress bar.
+ */
+ public ProgressBar peekProgressBar() {
+ return (ProgressBar) mTemplateLayout.findManagedViewById(R.id.suw_layout_progress);
+ }
+
+ /**
+ * Sets the color of the indeterminate progress bar. This method is a no-op on SDK < 21.
+ */
+ public void setColor(@Nullable ColorStateList color) {
+ mColor = color;
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ final ProgressBar bar = peekProgressBar();
+ if (bar != null) {
+ bar.setIndeterminateTintList(color);
+ bar.setProgressBackgroundTintList(color);
+ }
+ }
+ }
+
+ /**
+ * @return The color previously set in {@link #setColor(ColorStateList)}, or null if the color
+ * is not set. In case of null, the color of the progress bar will be inherited from the theme.
+ */
+ @Nullable
+ public ColorStateList getColor() {
+ return mColor;
+ }
+}
diff --git a/library/test/res/layout/test_mixin_attributes.xml b/library/test/res/layout/test_mixin_attributes.xml
new file mode 100644
index 0000000..dad3be7
--- /dev/null
+++ b/library/test/res/layout/test_mixin_attributes.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.setupwizardlib.TemplateLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:icon="@android:drawable/ic_menu_add"
+ app:suwHeaderColor="#ffff0000"
+ app:suwHeaderText="lorem ipsum" />
diff --git a/library/test/res/layout/test_progress_bar_template.xml b/library/test/res/layout/test_progress_bar_template.xml
new file mode 100644
index 0000000..ffe85f4
--- /dev/null
+++ b/library/test/res/layout/test_progress_bar_template.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/suw_progress_bar_stub" />
+
+ <FrameLayout
+ android:id="@+id/suw_layout_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</FrameLayout>
diff --git a/library/test/src/com/android/setupwizardlib/test/TemplateLayoutTest.java b/library/test/src/com/android/setupwizardlib/TemplateLayoutTest.java
index 8cec897..ddce677 100644
--- a/library/test/src/com/android/setupwizardlib/test/TemplateLayoutTest.java
+++ b/library/test/src/com/android/setupwizardlib/TemplateLayoutTest.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.setupwizardlib.test;
+package com.android.setupwizardlib;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -29,7 +30,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
-import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.template.HeaderMixin;
+import com.android.setupwizardlib.test.R;
import org.junit.Before;
import org.junit.Test;
@@ -92,4 +94,12 @@ public class TemplateLayoutTest {
// Expected IllegalArgumentException
}
}
+
+ @Test
+ public void testGetMixin() {
+ TemplateLayout layout = new TemplateLayout(mContext, R.layout.test_template,
+ R.id.suw_layout_content);
+ final HeaderMixin mixin = layout.getMixin(HeaderMixin.class);
+ assertNull("getMixin for a mixin that doesn't exist should return null", mixin);
+ }
}
diff --git a/library/test/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java b/library/test/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java
new file mode 100644
index 0000000..1c86af1
--- /dev/null
+++ b/library/test/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Xml;
+import android.widget.TextView;
+
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.test.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ColoredHeaderMixinTest {
+
+ private Context mContext;
+ private TemplateLayout mTemplateLayout;
+ private TextView mHeaderTextView;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
+ R.id.suw_layout_content));
+
+ mHeaderTextView = new TextView(mContext);
+ doReturn(mHeaderTextView).when(mTemplateLayout)
+ .findManagedViewById(eq(R.id.suw_layout_title));
+ }
+
+ @Test
+ public void testSetColor() {
+ ColoredHeaderMixin mixin = new ColoredHeaderMixin(mTemplateLayout, null, 0);
+ mixin.setColor(ColorStateList.valueOf(Color.MAGENTA));
+
+ assertEquals(ColorStateList.valueOf(Color.MAGENTA), mHeaderTextView.getTextColors());
+ }
+
+ @Test
+ public void testGetColor() {
+ ColoredHeaderMixin mixin = new ColoredHeaderMixin(mTemplateLayout, null, 0);
+ mHeaderTextView.setTextColor(ColorStateList.valueOf(Color.GREEN));
+
+ assertEquals(ColorStateList.valueOf(Color.GREEN), mixin.getColor());
+ }
+
+ @SuppressWarnings("ResourceType") // Needed to create attribute set from layout XML.
+ @Test
+ public void testSetColorFromXml() throws IOException, XmlPullParserException {
+ final XmlResourceParser parser =
+ mContext.getResources().getXml(R.layout.test_mixin_attributes);
+ while (!TemplateLayout.class.getName().equals(parser.getName())) {
+ parser.next();
+ }
+ new ColoredHeaderMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
+
+ assertEquals(ColorStateList.valueOf(Color.RED), mHeaderTextView.getTextColors());
+ }
+}
diff --git a/library/test/src/com/android/setupwizardlib/template/HeaderMixinTest.java b/library/test/src/com/android/setupwizardlib/template/HeaderMixinTest.java
new file mode 100644
index 0000000..a1b4b59
--- /dev/null
+++ b/library/test/src/com/android/setupwizardlib/template/HeaderMixinTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Xml;
+import android.widget.TextView;
+
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.test.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class HeaderMixinTest {
+
+ private Context mContext;
+ private TemplateLayout mTemplateLayout;
+ private TextView mHeaderTextView;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
+ R.id.suw_layout_content));
+
+ mHeaderTextView = new TextView(mContext);
+ doReturn(mHeaderTextView).when(mTemplateLayout)
+ .findManagedViewById(eq(R.id.suw_layout_title));
+ }
+
+ @Test
+ public void testGetTextView() {
+ HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+ assertSame(mHeaderTextView, mixin.getTextView());
+ }
+
+ @Test
+ public void testSetTextId() {
+ HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+ mixin.setText(R.string.suw_next_button_label);
+
+ assertEquals("Next", mHeaderTextView.getText());
+ }
+
+ @Test
+ public void testSetText() {
+ HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+ mixin.setText("Foobar");
+
+ assertEquals("Foobar", mHeaderTextView.getText());
+ }
+
+ @SuppressLint("SetTextI18n") // It's OK, this is a test
+ @Test
+ public void testGetText() {
+ mHeaderTextView.setText("Lorem ipsum");
+
+ HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+ assertEquals("Lorem ipsum", mixin.getText());
+ }
+
+ @SuppressWarnings("ResourceType") // Needed to create attribute set from layout XML.
+ @Test
+ public void testSetTextFromXml() throws IOException, XmlPullParserException {
+ final XmlResourceParser parser =
+ mContext.getResources().getXml(R.layout.test_mixin_attributes);
+ while (!TemplateLayout.class.getName().equals(parser.getName())) {
+ parser.next();
+ }
+ new HeaderMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
+
+ assertEquals("lorem ipsum", mHeaderTextView.getText());
+ }
+}
diff --git a/library/test/src/com/android/setupwizardlib/template/IconMixinTest.java b/library/test/src/com/android/setupwizardlib/template/IconMixinTest.java
new file mode 100644
index 0000000..a1f2b54
--- /dev/null
+++ b/library/test/src/com/android/setupwizardlib/template/IconMixinTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Xml;
+import android.widget.ImageView;
+
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.test.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IconMixinTest {
+
+ private Context mContext;
+ private TemplateLayout mTemplateLayout;
+ private ImageView mIconView;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
+ R.id.suw_layout_content));
+
+ mIconView = new ImageView(mContext);
+ doReturn(mIconView).when(mTemplateLayout).findManagedViewById(eq(R.id.suw_layout_icon));
+ }
+
+ @Test
+ public void testGetIconView() {
+ IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+ assertSame(mIconView, mixin.getView());
+ }
+
+ @Test
+ public void testSetIcon() {
+ final ColorDrawable drawable = new ColorDrawable(Color.CYAN);
+ IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+ mixin.setIcon(drawable);
+
+ assertSame(drawable, mIconView.getDrawable());
+ }
+
+ @Test
+ public void testGetIcon() {
+ final ColorDrawable drawable = new ColorDrawable(Color.BLUE);
+ mIconView.setImageDrawable(drawable);
+
+ IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+ assertSame(drawable, mixin.getIcon());
+ }
+
+ @SuppressWarnings("ResourceType") // Needed to create attribute set from layout XML.
+ @Test
+ public void testSetIconFromXml() throws IOException, XmlPullParserException {
+ final XmlResourceParser parser =
+ mContext.getResources().getXml(R.layout.test_mixin_attributes);
+ while (!TemplateLayout.class.getName().equals(parser.getName())) {
+ parser.next();
+ }
+ new IconMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
+
+ // Check that the bitmaps themselves are equal because BitmapDrawable does not implement
+ // equals()
+ final BitmapDrawable expected = (BitmapDrawable) mContext.getResources()
+ .getDrawable(android.R.drawable.ic_menu_add);
+ final BitmapDrawable actual = (BitmapDrawable) mIconView.getDrawable();
+ assertEquals(expected.getBitmap(), actual.getBitmap());
+ }
+}
diff --git a/library/test/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java b/library/test/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java
new file mode 100644
index 0000000..aca6084
--- /dev/null
+++ b/library/test/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.test.R;
+import com.android.setupwizardlib.view.NavigationBar;
+import com.android.setupwizardlib.view.NavigationBar.NavigationBarListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NavigationBarMixinTest {
+
+ private Context mContext;
+ private TemplateLayout mTemplateLayout;
+ private NavigationBar mNavigationBar;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
+ R.id.suw_layout_content));
+
+ mNavigationBar = new NavigationBar(mContext);
+ doReturn(mNavigationBar).when(mTemplateLayout)
+ .findManagedViewById(eq(R.id.suw_layout_navigation_bar));
+ }
+
+ @Test
+ public void testGetNavigationBar() {
+ NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+ assertSame(mNavigationBar, mixin.getNavigationBar());
+ }
+
+ @Test
+ public void testSetNextButtonText() {
+ NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+ mixin.setNextButtonText(R.string.suw_more_button_label);
+ assertEquals("More", mNavigationBar.getNextButton().getText());
+
+ mixin.setNextButtonText("Foobar");
+ assertEquals("Foobar", mNavigationBar.getNextButton().getText());
+ }
+
+ @SuppressLint("SetTextI18n") // It's OK, this is just a test
+ @Test
+ public void testGetNextButtonText() {
+ mNavigationBar.getNextButton().setText("lorem ipsum");
+
+ NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+ assertSame("lorem ipsum", mixin.getNextButtonText());
+ }
+
+ @Test
+ public void testSetNavigationBarListener() {
+ final NavigationBarListener listener = mock(NavigationBarListener.class);
+ NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+ mixin.setNavigationBarListener(listener);
+
+ mNavigationBar.getNextButton().performClick();
+ verify(listener).onNavigateNext();
+
+ mNavigationBar.getBackButton().performClick();
+ verify(listener).onNavigateBack();
+ }
+}
diff --git a/library/test/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java b/library/test/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java
new file mode 100644
index 0000000..b444632
--- /dev/null
+++ b/library/test/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.test.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ProgressBarMixinTest {
+
+ private TemplateLayout mTemplateLayout;
+
+ @Before
+ public void setUp() {
+ mTemplateLayout = new TemplateLayout(InstrumentationRegistry.getContext(),
+ R.layout.test_progress_bar_template, R.id.suw_layout_content);
+ }
+
+ @Test
+ public void testSetShown() {
+ ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+ mixin.setShown(true);
+
+ ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
+ R.id.suw_layout_progress);
+ assertNotNull("Progress bar should be available after setting to shown", progressBar);
+ assertEquals(View.VISIBLE, progressBar.getVisibility());
+ }
+
+ @Test
+ public void testNotShown() {
+ ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+ mixin.setShown(true);
+ mixin.setShown(false);
+
+ ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
+ R.id.suw_layout_progress);
+ assertNotEquals(View.VISIBLE, progressBar.getVisibility());
+ }
+
+ @Test
+ public void testIsShown() {
+ ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+
+ mixin.setShown(true);
+ assertTrue(mixin.isShown());
+
+ mixin.setShown(false);
+ assertFalse(mixin.isShown());
+ }
+
+ @Test
+ public void testPeekProgressBar() {
+ ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+ assertNull("PeekProgressBar should return null when stub not inflated yet",
+ mixin.peekProgressBar());
+
+ mixin.setShown(true);
+ assertNotNull("PeekProgressBar should be available after setting to shown",
+ mixin.peekProgressBar());
+ }
+
+ @Test
+ public void testSetColorBeforeSetShown() {
+ ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+ mixin.setColor(ColorStateList.valueOf(Color.MAGENTA));
+
+ mixin.setShown(true);
+
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
+ R.id.suw_layout_progress);
+ assertEquals(ColorStateList.valueOf(Color.MAGENTA),
+ progressBar.getIndeterminateTintList());
+ assertEquals(ColorStateList.valueOf(Color.MAGENTA),
+ progressBar.getProgressBackgroundTintList());
+ }
+ // this method is a no-op on versions < lollipop. Just check that it doesn't crash.
+ }
+
+ @Test
+ public void testSetColorAfterSetShown() {
+ ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+ mixin.setShown(true);
+
+ mixin.setColor(ColorStateList.valueOf(Color.YELLOW));
+
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
+ R.id.suw_layout_progress);
+ assertEquals(ColorStateList.valueOf(Color.YELLOW),
+ progressBar.getIndeterminateTintList());
+ assertEquals(ColorStateList.valueOf(Color.YELLOW),
+ progressBar.getProgressBackgroundTintList());
+ }
+ // this method is a no-op on versions < lollipop. Just check that it doesn't crash.
+ }
+}
diff --git a/library/test/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java b/library/test/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java
new file mode 100644
index 0000000..7cc934a
--- /dev/null
+++ b/library/test/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.test.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TemplateLayoutMixinTest {
+
+ private TestTemplateLayout mLayout;
+
+ @Before
+ public void setUp() throws Exception {
+ mLayout = new TestTemplateLayout(InstrumentationRegistry.getContext());
+ }
+
+ @Test
+ public void testGetMixin() {
+ final TestMixin mixin = mLayout.getMixin(TestMixin.class);
+ assertNotNull("TestMixin should not be null", mixin);
+ assertTrue("TestMixin should be an instance of TestMixinSubclass. "
+ + "Found " + mixin.getClass() + " instead.",
+ mixin instanceof TestMixinSubclass);
+
+ // Mixin must be retrieved using the interface it's registered with, not the concrete class,
+ // although they are often the same.
+ assertNull("TestMixinSubclass should be null", mLayout.getMixin(TestMixinSubclass.class));
+ }
+
+ private static class TestTemplateLayout extends TemplateLayout {
+
+ TestTemplateLayout(Context context) {
+ super(context, R.layout.test_template, R.id.suw_layout_content);
+ registerMixin(TestMixin.class, new TestMixinSubclass());
+ }
+ }
+
+ private static class TestMixin implements Mixin {}
+
+ private static class TestMixinSubclass extends TestMixin {}
+}
diff --git a/library/test/src/com/android/setupwizardlib/test/GlifLayoutTest.java b/library/test/src/com/android/setupwizardlib/test/GlifLayoutTest.java
index 3a08f2a..f3a39fc 100644
--- a/library/test/src/com/android/setupwizardlib/test/GlifLayoutTest.java
+++ b/library/test/src/com/android/setupwizardlib/test/GlifLayoutTest.java
@@ -39,6 +39,10 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.android.setupwizardlib.GlifLayout;
+import com.android.setupwizardlib.template.ColoredHeaderMixin;
+import com.android.setupwizardlib.template.HeaderMixin;
+import com.android.setupwizardlib.template.IconMixin;
+import com.android.setupwizardlib.template.ProgressBarMixin;
import org.junit.Before;
import org.junit.Test;
@@ -160,6 +164,18 @@ public class GlifLayoutTest {
// This is a no-op because there is no progress bar stub
}
+ @Test
+ public void testMixins() {
+ GlifLayout layout = new GlifLayout(mContext);
+ final HeaderMixin header = layout.getMixin(HeaderMixin.class);
+ assertTrue("Header should be instance of ColoredHeaderMixin. "
+ + "Found " + header.getClass() + " instead.", header instanceof ColoredHeaderMixin);
+
+ assertNotNull("GlifLayout should have icon mixin", layout.getMixin(IconMixin.class));
+ assertNotNull("GlifLayout should have progress bar mixin",
+ layout.getMixin(ProgressBarMixin.class));
+ }
+
private void assertDefaultTemplateInflated(GlifLayout layout) {
View title = layout.findViewById(R.id.suw_layout_title);
assertNotNull("@id/suw_layout_title should not be null", title);
diff --git a/library/test/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java b/library/test/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java
index 1e74bad..5da6166 100644
--- a/library/test/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java
+++ b/library/test/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java
@@ -42,6 +42,9 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.setupwizardlib.SetupWizardLayout;
+import com.android.setupwizardlib.template.HeaderMixin;
+import com.android.setupwizardlib.template.NavigationBarMixin;
+import com.android.setupwizardlib.template.ProgressBarMixin;
import com.android.setupwizardlib.view.NavigationBar;
import org.junit.Before;
@@ -226,6 +229,17 @@ public class SetupWizardLayoutTest {
assertFalse("Progress bar should not be shown", layout.isProgressBarShown());
}
+ @Test
+ public void testGetMixins() {
+ final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+ assertNotNull("SetupWizardLayout should have header mixin",
+ layout.getMixin(HeaderMixin.class));
+ assertNotNull("SetupWizardLayout should have progress bar mixin",
+ layout.getMixin(ProgressBarMixin.class));
+ assertNotNull("SetupWizardLayout should have navigation bar mixin",
+ layout.getMixin(NavigationBarMixin.class));
+ }
+
private void assertDefaultTemplateInflated(SetupWizardLayout layout) {
View decorView = layout.findViewById(R.id.suw_layout_decor);
View navbar = layout.findViewById(R.id.suw_layout_navigation_bar);