diff options
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); |