summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);