From 4860e4ee4876743b52cb03f2fce3c213ad7a2f6a Mon Sep 17 00:00:00 2001 From: Aurimas Liutikas Date: Mon, 16 Apr 2018 16:55:01 -0700 Subject: Migrate setup-wizard-lib to androidx. Test: make setup-wizard-lib Bug: 76692459 Change-Id: I40171e973d442b1a1815e9e7d7c2cc984cb38bac --- .../res/layout/suw_preference_recycler_view_normal.xml | 2 +- .../recyclerview/res/layout/suw_recycler_template_card.xml | 2 +- .../res/layout/suw_recycler_template_card_wide.xml | 2 +- .../res/layout/suw_recycler_template_header_collapsed.xml | 2 +- .../com/android/setupwizardlib/DividerItemDecoration.java | 9 +++++---- .../com/android/setupwizardlib/GlifPreferenceLayout.java | 3 ++- .../src/com/android/setupwizardlib/GlifRecyclerLayout.java | 7 ++++--- .../android/setupwizardlib/SetupWizardPreferenceLayout.java | 3 ++- .../android/setupwizardlib/SetupWizardRecyclerLayout.java | 9 +++++---- .../com/android/setupwizardlib/items/ItemViewHolder.java | 3 ++- .../android/setupwizardlib/items/RecyclerItemAdapter.java | 5 +++-- .../com/android/setupwizardlib/template/RecyclerMixin.java | 13 +++++++------ .../template/RecyclerViewScrollHandlingDelegate.java | 7 ++++--- .../com/android/setupwizardlib/view/HeaderRecyclerView.java | 3 ++- .../setupwizardlib/items/RecyclerItemAdapterTest.java | 3 ++- .../android/setupwizardlib/template/RecyclerMixinTest.java | 5 +++-- .../setupwizardlib/test/DividerItemDecorationTest.java | 5 +++-- .../setupwizardlib/test/GlifPreferenceLayoutTest.java | 3 ++- .../android/setupwizardlib/test/GlifRecyclerLayoutTest.java | 5 +++-- .../android/setupwizardlib/test/HeaderRecyclerViewTest.java | 3 ++- .../test/SetupWizardPreferenceLayoutTest.java | 3 ++- .../setupwizardlib/test/SetupWizardRecyclerLayoutTest.java | 7 ++++--- .../template/RecyclerViewScrollHandlingDelegateTest.java | 4 ++-- 23 files changed, 63 insertions(+), 45 deletions(-) (limited to 'library/recyclerview') diff --git a/library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml b/library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml index 0979d91..088a35c 100644 --- a/library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml +++ b/library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml @@ -15,7 +15,7 @@ limitations under the License. --> - - - - Date: Fri, 24 Aug 2018 14:16:17 -0700 Subject: Import updated Android Setup Wizard Library 210155157 PiperOrigin-RevId: 210155157 Change-Id: I707512aab6ac57aaebb93275669ae16b92b0e66d --- .../setupwizardlib/DividerItemDecoration.java | 356 +++++++++--------- .../setupwizardlib/GlifPreferenceLayout.java | 103 +++--- .../android/setupwizardlib/GlifRecyclerLayout.java | 294 +++++++-------- .../SetupWizardPreferenceLayout.java | 98 +++-- .../setupwizardlib/SetupWizardRecyclerLayout.java | 298 +++++++-------- .../setupwizardlib/items/ItemViewHolder.java | 70 ++-- .../setupwizardlib/items/RecyclerItemAdapter.java | 401 ++++++++++---------- .../setupwizardlib/template/RecyclerMixin.java | 393 ++++++++++---------- .../RecyclerViewScrollHandlingDelegate.java | 84 ++--- .../setupwizardlib/view/HeaderRecyclerView.java | 408 ++++++++++----------- .../view/StickyHeaderRecyclerView.java | 174 ++++----- .../items/RecyclerItemAdapterTest.java | 218 ++++++----- .../setupwizardlib/template/RecyclerMixinTest.java | 176 +++++---- .../test/DividerItemDecorationTest.java | 334 ++++++++--------- .../test/GlifPreferenceLayoutTest.java | 116 +++--- .../test/GlifRecyclerLayoutTest.java | 250 ++++++------- .../test/HeaderRecyclerViewTest.java | 259 ++++++------- .../test/SetupWizardPreferenceLayoutTest.java | 118 +++--- .../test/SetupWizardRecyclerLayoutTest.java | 257 +++++++------ .../RecyclerViewScrollHandlingDelegateTest.java | 91 +++-- 20 files changed, 2181 insertions(+), 2317 deletions(-) (limited to 'library/recyclerview') diff --git a/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java index 2db17f8..128ed6b 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java +++ b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java @@ -21,223 +21,207 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.view.View; - import androidx.annotation.IntDef; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; - +import android.view.View; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * An {@link androidx.recyclerview.widget.RecyclerView.ItemDecoration} for RecyclerView to draw - * dividers between items. This ItemDecoration will draw the drawable specified by - * {@link #setDivider(android.graphics.drawable.Drawable)} as the divider in between each item by - * default, and the behavior of whether the divider is shown can be customized by subclassing - * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}. + * dividers between items. This ItemDecoration will draw the drawable specified by {@link + * #setDivider(android.graphics.drawable.Drawable)} as the divider in between each item by default, + * and the behavior of whether the divider is shown can be customized by subclassing {@link + * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}. * *

Modified from v14 PreferenceFragment.DividerDecoration. */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { - /* static section */ + /* static section */ + + /** + * An interface to be implemented by a {@link RecyclerView.ViewHolder} which controls whether + * dividers should be shown above and below that item. + */ + public interface DividedViewHolder { /** - * An interface to be implemented by a {@link RecyclerView.ViewHolder} which controls whether - * dividers should be shown above and below that item. + * Returns whether divider is allowed above this item. A divider will be shown only if both + * items immediately above and below it allows this divider. */ - public interface DividedViewHolder { - - /** - * Returns whether divider is allowed above this item. A divider will be shown only if both - * items immediately above and below it allows this divider. - */ - boolean isDividerAllowedAbove(); - - /** - * Returns whether divider is allowed below this item. A divider will be shown only if both - * items immediately above and below it allows this divider. - */ - boolean isDividerAllowedBelow(); - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - DIVIDER_CONDITION_EITHER, - DIVIDER_CONDITION_BOTH}) - public @interface DividerCondition {} - - public static final int DIVIDER_CONDITION_EITHER = 0; - public static final int DIVIDER_CONDITION_BOTH = 1; + boolean isDividerAllowedAbove(); /** - * @deprecated Use {@link #DividerItemDecoration(android.content.Context)} + * Returns whether divider is allowed below this item. A divider will be shown only if both + * items immediately above and below it allows this divider. */ - @Deprecated - public static DividerItemDecoration getDefault(Context context) { - return new DividerItemDecoration(context); - } - - /* non-static section */ - - private Drawable mDivider; - private int mDividerHeight; - private int mDividerIntrinsicHeight; - @DividerCondition - private int mDividerCondition; + boolean isDividerAllowedBelow(); + } - public DividerItemDecoration() { - } + @Retention(RetentionPolicy.SOURCE) + @IntDef({DIVIDER_CONDITION_EITHER, DIVIDER_CONDITION_BOTH}) + public @interface DividerCondition {} - public DividerItemDecoration(Context context) { - final TypedArray a = context.obtainStyledAttributes(R.styleable.SuwDividerItemDecoration); - final Drawable divider = a.getDrawable( - R.styleable.SuwDividerItemDecoration_android_listDivider); - final int dividerHeight = a.getDimensionPixelSize( - R.styleable.SuwDividerItemDecoration_android_dividerHeight, 0); - @DividerCondition final int dividerCondition = a.getInt( - R.styleable.SuwDividerItemDecoration_suwDividerCondition, - DIVIDER_CONDITION_EITHER); - a.recycle(); - - setDivider(divider); - setDividerHeight(dividerHeight); - setDividerCondition(dividerCondition); - } + public static final int DIVIDER_CONDITION_EITHER = 0; + public static final int DIVIDER_CONDITION_BOTH = 1; - @Override - public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - if (mDivider == null) { - return; - } - final int childCount = parent.getChildCount(); - final int width = parent.getWidth(); - final int dividerHeight = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight; - for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) { - final View view = parent.getChildAt(childViewIndex); - if (shouldDrawDividerBelow(view, parent)) { - final int top = (int) ViewCompat.getY(view) + view.getHeight(); - mDivider.setBounds(0, top, width, top + dividerHeight); - mDivider.draw(c); - } - } - } + /** @deprecated Use {@link #DividerItemDecoration(android.content.Context)} */ + @Deprecated + public static DividerItemDecoration getDefault(Context context) { + return new DividerItemDecoration(context); + } - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, - RecyclerView.State state) { - if (shouldDrawDividerBelow(view, parent)) { - outRect.bottom = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight; - } - } + /* non-static section */ - private boolean shouldDrawDividerBelow(View view, RecyclerView parent) { - final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view); - final int index = holder.getLayoutPosition(); - final int lastItemIndex = parent.getAdapter().getItemCount() - 1; - if (isDividerAllowedBelow(holder)) { - if (mDividerCondition == DIVIDER_CONDITION_EITHER) { - // Draw the divider without consulting the next item if we only - // need permission for either above or below. - return true; - } - } else if (mDividerCondition == DIVIDER_CONDITION_BOTH || index == lastItemIndex) { - // Don't draw if the current view holder doesn't allow drawing below - // and the current theme requires permission for both the item below and above. - // Also, if this is the last item, there is no item below to ask permission - // for whether to draw a divider above, so don't draw it. - return false; - } - // Require permission from index below to draw the divider. - if (index < lastItemIndex) { - final RecyclerView.ViewHolder nextHolder = - parent.findViewHolderForLayoutPosition(index + 1); - if (!isDividerAllowedAbove(nextHolder)) { - // Don't draw if the next view holder doesn't allow drawing above - return false; - } - } - return true; - } - - /** - * Whether a divider is allowed above the view holder. The allowed values will be combined - * according to {@link #getDividerCondition()}. The default implementation delegates to - * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows - * the divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can - * override this to give more information to decide whether a divider should be drawn. - * - * @return True if divider is allowed above this view holder. - */ - protected boolean isDividerAllowedAbove(RecyclerView.ViewHolder viewHolder) { - return !(viewHolder instanceof DividedViewHolder) - || ((DividedViewHolder) viewHolder).isDividerAllowedAbove(); - } + private Drawable divider; + private int dividerHeight; + private int dividerIntrinsicHeight; + @DividerCondition private int dividerCondition; - /** - * Whether a divider is allowed below the view holder. The allowed values will be combined - * according to {@link #getDividerCondition()}. The default implementation delegates to - * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows - * the divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can - * override this to give more information to decide whether a divider should be drawn. - * - * @return True if divider is allowed below this view holder. - */ - protected boolean isDividerAllowedBelow(RecyclerView.ViewHolder viewHolder) { - return !(viewHolder instanceof DividedViewHolder) - || ((DividedViewHolder) viewHolder).isDividerAllowedBelow(); - } + public DividerItemDecoration() {} - /** - * Sets the drawable to be used as the divider. - */ - public void setDivider(Drawable divider) { - if (divider != null) { - mDividerIntrinsicHeight = divider.getIntrinsicHeight(); - } else { - mDividerIntrinsicHeight = 0; - } - mDivider = divider; + public DividerItemDecoration(Context context) { + final TypedArray a = context.obtainStyledAttributes(R.styleable.SuwDividerItemDecoration); + final Drawable divider = + a.getDrawable(R.styleable.SuwDividerItemDecoration_android_listDivider); + final int dividerHeight = + a.getDimensionPixelSize(R.styleable.SuwDividerItemDecoration_android_dividerHeight, 0); + @DividerCondition + final int dividerCondition = + a.getInt( + R.styleable.SuwDividerItemDecoration_suwDividerCondition, DIVIDER_CONDITION_EITHER); + a.recycle(); + + setDivider(divider); + setDividerHeight(dividerHeight); + setDividerCondition(dividerCondition); + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + if (divider == null) { + return; } - - /** - * Gets the drawable currently used as the divider. - */ - public Drawable getDivider() { - return mDivider; + final int childCount = parent.getChildCount(); + final int width = parent.getWidth(); + final int dividerHeight = this.dividerHeight != 0 ? this.dividerHeight : dividerIntrinsicHeight; + for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) { + final View view = parent.getChildAt(childViewIndex); + if (shouldDrawDividerBelow(view, parent)) { + final int top = (int) ViewCompat.getY(view) + view.getHeight(); + divider.setBounds(0, top, width, top + dividerHeight); + divider.draw(c); + } } + } - /** - * Sets the divider height, in pixels. - */ - public void setDividerHeight(int dividerHeight) { - mDividerHeight = dividerHeight; + @Override + public void getItemOffsets( + Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + if (shouldDrawDividerBelow(view, parent)) { + outRect.bottom = dividerHeight != 0 ? dividerHeight : dividerIntrinsicHeight; } - - /** - * Gets the divider height, in pixels. - */ - public int getDividerHeight() { - return mDividerHeight; + } + + private boolean shouldDrawDividerBelow(View view, RecyclerView parent) { + final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view); + final int index = holder.getLayoutPosition(); + final int lastItemIndex = parent.getAdapter().getItemCount() - 1; + if (isDividerAllowedBelow(holder)) { + if (dividerCondition == DIVIDER_CONDITION_EITHER) { + // Draw the divider without consulting the next item if we only + // need permission for either above or below. + return true; + } + } else if (dividerCondition == DIVIDER_CONDITION_BOTH || index == lastItemIndex) { + // Don't draw if the current view holder doesn't allow drawing below + // and the current theme requires permission for both the item below and above. + // Also, if this is the last item, there is no item below to ask permission + // for whether to draw a divider above, so don't draw it. + return false; } - - /** - * Sets whether the divider needs permission from both the item view holder below - * and above from where the divider would draw itself or just needs permission from - * one or the other before drawing itself. - */ - public void setDividerCondition(@DividerCondition int dividerCondition) { - mDividerCondition = dividerCondition; + // Require permission from index below to draw the divider. + if (index < lastItemIndex) { + final RecyclerView.ViewHolder nextHolder = parent.findViewHolderForLayoutPosition(index + 1); + if (!isDividerAllowedAbove(nextHolder)) { + // Don't draw if the next view holder doesn't allow drawing above + return false; + } } - - /** - * Gets whether the divider needs permission from both the item view holder below - * and above from where the divider would draw itself or just needs permission from - * one or the other before drawing itself. - */ - @DividerCondition - public int getDividerCondition() { - return mDividerCondition; + return true; + } + + /** + * Whether a divider is allowed above the view holder. The allowed values will be combined + * according to {@link #getDividerCondition()}. The default implementation delegates to {@link + * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows the + * divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can override + * this to give more information to decide whether a divider should be drawn. + * + * @return True if divider is allowed above this view holder. + */ + protected boolean isDividerAllowedAbove(RecyclerView.ViewHolder viewHolder) { + return !(viewHolder instanceof DividedViewHolder) + || ((DividedViewHolder) viewHolder).isDividerAllowedAbove(); + } + + /** + * Whether a divider is allowed below the view holder. The allowed values will be combined + * according to {@link #getDividerCondition()}. The default implementation delegates to {@link + * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows the + * divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can override + * this to give more information to decide whether a divider should be drawn. + * + * @return True if divider is allowed below this view holder. + */ + protected boolean isDividerAllowedBelow(RecyclerView.ViewHolder viewHolder) { + return !(viewHolder instanceof DividedViewHolder) + || ((DividedViewHolder) viewHolder).isDividerAllowedBelow(); + } + + /** Sets the drawable to be used as the divider. */ + public void setDivider(Drawable divider) { + if (divider != null) { + dividerIntrinsicHeight = divider.getIntrinsicHeight(); + } else { + dividerIntrinsicHeight = 0; } + this.divider = divider; + } + + /** Gets the drawable currently used as the divider. */ + public Drawable getDivider() { + return divider; + } + + /** Sets the divider height, in pixels. */ + public void setDividerHeight(int dividerHeight) { + this.dividerHeight = dividerHeight; + } + + /** Gets the divider height, in pixels. */ + public int getDividerHeight() { + return dividerHeight; + } + + /** + * Sets whether the divider needs permission from both the item view holder below and above from + * where the divider would draw itself or just needs permission from one or the other before + * drawing itself. + */ + public void setDividerCondition(@DividerCondition int dividerCondition) { + this.dividerCondition = dividerCondition; + } + + /** + * Gets whether the divider needs permission from both the item view holder below and above from + * where the divider would draw itself or just needs permission from one or the other before + * drawing itself. + */ + @DividerCondition + public int getDividerCondition() { + return dividerCondition; + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java index af1a739..795fdad 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java @@ -18,21 +18,20 @@ package com.android.setupwizardlib; import android.content.Context; import android.os.Bundle; +import androidx.recyclerview.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; - import com.android.setupwizardlib.template.RecyclerMixin; /** * A layout to be used with {@code PreferenceFragment} in v14 support library. This can be specified - * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in - * {@code app:preferenceTheme}. + * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in {@code + * app:preferenceTheme}. + * + *

Example: * - *

Example: *

{@code
  * <style android:name="MyActivityTheme">
  *     <item android:name="preferenceTheme">@style/MyPreferenceTheme</item>
@@ -47,10 +46,11 @@ import com.android.setupwizardlib.template.RecyclerMixin;
  * </style>
  * }
* - * where {@code my_preference_layout} is a layout that contains - * {@link com.android.setupwizardlib.GlifPreferenceLayout}. + * where {@code my_preference_layout} is a layout that contains {@link + * com.android.setupwizardlib.GlifPreferenceLayout}. + * + *

Example: * - *

Example: *

{@code
  * <com.android.setupwizardlib.GlifPreferenceLayout
  *     xmlns:android="http://schemas.android.com/apk/res/android"
@@ -59,60 +59,57 @@ import com.android.setupwizardlib.template.RecyclerMixin;
  *     android:layout_height="match_parent" />
  * }
* - *

Fragments using this layout must delegate {@code onCreateRecyclerView} to the - * implementation in this class: - * {@link #onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, - * android.os.Bundle)} + *

Fragments using this layout must delegate {@code onCreateRecyclerView} to the + * implementation in this class: {@link #onCreateRecyclerView(android.view.LayoutInflater, + * android.view.ViewGroup, android.os.Bundle)} */ public class GlifPreferenceLayout extends GlifRecyclerLayout { - public GlifPreferenceLayout(Context context) { - super(context); - } + public GlifPreferenceLayout(Context context) { + super(context); + } - public GlifPreferenceLayout(Context context, int template, int containerId) { - super(context, template, containerId); - } + public GlifPreferenceLayout(Context context, int template, int containerId) { + super(context, template, containerId); + } - public GlifPreferenceLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } + public GlifPreferenceLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } - public GlifPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } + public GlifPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } - @Override - protected ViewGroup findContainer(int containerId) { - if (containerId == 0) { - containerId = R.id.suw_layout_content; - } - return super.findContainer(containerId); + @Override + protected ViewGroup findContainer(int containerId) { + if (containerId == 0) { + containerId = R.id.suw_layout_content; } + return super.findContainer(containerId); + } - /** - * This method must be called in {@code PreferenceFragment#onCreateRecyclerView}. - */ - public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, - Bundle savedInstanceState) { - return mRecyclerMixin.getRecyclerView(); - } + /** This method must be called in {@code PreferenceFragment#onCreateRecyclerView}. */ + public RecyclerView onCreateRecyclerView( + LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + return mRecyclerMixin.getRecyclerView(); + } - @Override - protected View onInflateTemplate(LayoutInflater inflater, int template) { - if (template == 0) { - template = R.layout.suw_glif_preference_template; - } - return super.onInflateTemplate(inflater, template); + @Override + protected View onInflateTemplate(LayoutInflater inflater, int template) { + if (template == 0) { + template = R.layout.suw_glif_preference_template; } + return super.onInflateTemplate(inflater, template); + } - @Override - protected void onTemplateInflated() { - // Inflate the recycler view here, so attributes on the decoration views can be applied - // immediately. - final LayoutInflater inflater = LayoutInflater.from(getContext()); - RecyclerView recyclerView = (RecyclerView) inflater.inflate( - R.layout.suw_glif_preference_recycler_view, this, false); - mRecyclerMixin = new RecyclerMixin(this, recyclerView); - } + @Override + protected void onTemplateInflated() { + // Inflate the recycler view here, so attributes on the decoration views can be applied + // immediately. + final LayoutInflater inflater = LayoutInflater.from(getContext()); + RecyclerView recyclerView = + (RecyclerView) inflater.inflate(R.layout.suw_glif_preference_recycler_view, this, false); + mRecyclerMixin = new RecyclerMixin(this, recyclerView); + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java index 7e0b1b7..2595b79 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java @@ -20,15 +20,13 @@ import android.annotation.TargetApi; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Build.VERSION_CODES; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.Adapter; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.Adapter; -import androidx.recyclerview.widget.RecyclerView.ViewHolder; - import com.android.setupwizardlib.template.RecyclerMixin; import com.android.setupwizardlib.template.RecyclerViewScrollHandlingDelegate; import com.android.setupwizardlib.template.RequireScrollMixin; @@ -39,157 +37,137 @@ import com.android.setupwizardlib.template.RequireScrollMixin; */ public class GlifRecyclerLayout extends GlifLayout { - protected RecyclerMixin mRecyclerMixin; - - public GlifRecyclerLayout(Context context) { - this(context, 0, 0); - } - - public GlifRecyclerLayout(Context context, int template) { - this(context, template, 0); - } - - public GlifRecyclerLayout(Context context, int template, int containerId) { - super(context, template, containerId); - init(context, null, 0); - } - - public GlifRecyclerLayout(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs, 0); - } - - @TargetApi(VERSION_CODES.HONEYCOMB) - public GlifRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context, attrs, defStyleAttr); - } - - private void init(Context context, AttributeSet attrs, int defStyleAttr) { - mRecyclerMixin.parseAttributes(attrs, defStyleAttr); - registerMixin(RecyclerMixin.class, mRecyclerMixin); - - final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class); - requireScrollMixin.setScrollHandlingDelegate( - new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView())); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mRecyclerMixin.onLayout(); - } - - @Override - protected View onInflateTemplate(LayoutInflater inflater, int template) { - if (template == 0) { - template = R.layout.suw_glif_recycler_template; - } - return super.onInflateTemplate(inflater, template); - } - - @Override - protected void onTemplateInflated() { - final View recyclerView = findViewById(R.id.suw_recycler_view); - if (recyclerView instanceof RecyclerView) { - mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView); - } else { - throw new IllegalStateException( - "GlifRecyclerLayout should use a template with recycler view"); - } - } - - @Override - protected ViewGroup findContainer(int containerId) { - if (containerId == 0) { - containerId = R.id.suw_recycler_view; - } - return super.findContainer(containerId); - } - - @Override - // Returning generic type is the common pattern used for findViewBy* methods - @SuppressWarnings("TypeParameterUnusedInFormals") - public T findManagedViewById(int id) { - final View header = mRecyclerMixin.getHeader(); - if (header != null) { - final T view = header.findViewById(id); - if (view != null) { - return view; - } - } - return super.findViewById(id); - } - - /** - * @see RecyclerMixin#setDividerItemDecoration(DividerItemDecoration) - */ - public void setDividerItemDecoration(DividerItemDecoration decoration) { - mRecyclerMixin.setDividerItemDecoration(decoration); - } - - /** - * @see RecyclerMixin#getRecyclerView() - */ - public RecyclerView getRecyclerView() { - return mRecyclerMixin.getRecyclerView(); - } - - /** - * @see RecyclerMixin#setAdapter(Adapter) - */ - public void setAdapter(Adapter adapter) { - mRecyclerMixin.setAdapter(adapter); - } - - /** - * @see RecyclerMixin#getAdapter() - */ - public Adapter getAdapter() { - return mRecyclerMixin.getAdapter(); - } - - /** - * @deprecated Use {@link #setDividerInsets(int, int)} instead. - */ - @Deprecated - public void setDividerInset(int inset) { - mRecyclerMixin.setDividerInset(inset); - } - - /** - * @see RecyclerMixin#setDividerInset(int) - */ - public void setDividerInsets(int start, int end) { - mRecyclerMixin.setDividerInsets(start, end); - } - - /** - * @deprecated Use {@link #getDividerInsetStart()} instead. - */ - @Deprecated - public int getDividerInset() { - return mRecyclerMixin.getDividerInset(); - } - - /** - * @see RecyclerMixin#getDividerInsetStart() - */ - public int getDividerInsetStart() { - return mRecyclerMixin.getDividerInsetStart(); - } - - /** - * @see RecyclerMixin#getDividerInsetEnd() - */ - public int getDividerInsetEnd() { - return mRecyclerMixin.getDividerInsetEnd(); - } - - /** - * @see RecyclerMixin#getDivider() - */ - public Drawable getDivider() { - return mRecyclerMixin.getDivider(); - } + protected RecyclerMixin mRecyclerMixin; + + public GlifRecyclerLayout(Context context) { + this(context, 0, 0); + } + + public GlifRecyclerLayout(Context context, int template) { + this(context, template, 0); + } + + public GlifRecyclerLayout(Context context, int template, int containerId) { + super(context, template, containerId); + init(null, 0); + } + + public GlifRecyclerLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + @TargetApi(VERSION_CODES.HONEYCOMB) + public GlifRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs, defStyleAttr); + } + + private void init(AttributeSet attrs, int defStyleAttr) { + mRecyclerMixin.parseAttributes(attrs, defStyleAttr); + registerMixin(RecyclerMixin.class, mRecyclerMixin); + + final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class); + requireScrollMixin.setScrollHandlingDelegate( + new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView())); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mRecyclerMixin.onLayout(); + } + + @Override + protected View onInflateTemplate(LayoutInflater inflater, int template) { + if (template == 0) { + template = R.layout.suw_glif_recycler_template; + } + return super.onInflateTemplate(inflater, template); + } + + @Override + protected void onTemplateInflated() { + final View recyclerView = findViewById(R.id.suw_recycler_view); + if (recyclerView instanceof RecyclerView) { + mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView); + } else { + throw new IllegalStateException( + "GlifRecyclerLayout should use a template with recycler view"); + } + } + + @Override + protected ViewGroup findContainer(int containerId) { + if (containerId == 0) { + containerId = R.id.suw_recycler_view; + } + return super.findContainer(containerId); + } + + @Override + // Returning generic type is the common pattern used for findViewBy* methods + @SuppressWarnings("TypeParameterUnusedInFormals") + public T findManagedViewById(int id) { + final View header = mRecyclerMixin.getHeader(); + if (header != null) { + final T view = header.findViewById(id); + if (view != null) { + return view; + } + } + return super.findViewById(id); + } + + /** @see RecyclerMixin#setDividerItemDecoration(DividerItemDecoration) */ + public void setDividerItemDecoration(DividerItemDecoration decoration) { + mRecyclerMixin.setDividerItemDecoration(decoration); + } + + /** @see RecyclerMixin#getRecyclerView() */ + public RecyclerView getRecyclerView() { + return mRecyclerMixin.getRecyclerView(); + } + + /** @see RecyclerMixin#setAdapter(Adapter) */ + public void setAdapter(Adapter adapter) { + mRecyclerMixin.setAdapter(adapter); + } + + /** @see RecyclerMixin#getAdapter() */ + public Adapter getAdapter() { + return mRecyclerMixin.getAdapter(); + } + + /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */ + @Deprecated + public void setDividerInset(int inset) { + mRecyclerMixin.setDividerInset(inset); + } + + /** @see RecyclerMixin#setDividerInset(int) */ + public void setDividerInsets(int start, int end) { + mRecyclerMixin.setDividerInsets(start, end); + } + + /** @deprecated Use {@link #getDividerInsetStart()} instead. */ + @Deprecated + public int getDividerInset() { + return mRecyclerMixin.getDividerInset(); + } + + /** @see RecyclerMixin#getDividerInsetStart() */ + public int getDividerInsetStart() { + return mRecyclerMixin.getDividerInsetStart(); + } + + /** @see RecyclerMixin#getDividerInsetEnd() */ + public int getDividerInsetEnd() { + return mRecyclerMixin.getDividerInsetEnd(); + } + + /** @see RecyclerMixin#getDivider() */ + public Drawable getDivider() { + return mRecyclerMixin.getDivider(); + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java index 670c309..e9aa329 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java @@ -18,21 +18,20 @@ package com.android.setupwizardlib; import android.content.Context; import android.os.Bundle; +import androidx.recyclerview.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; - import com.android.setupwizardlib.template.RecyclerMixin; /** * A layout to be used with {@code PreferenceFragment} in v14 support library. This can be specified - * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in - * {@code app:preferenceTheme}. + * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in {@code + * app:preferenceTheme}. + * + *

Example: * - *

Example: *

{@code
  * <style android:name="MyActivityTheme">
  *     <item android:name="preferenceTheme">@style/MyPreferenceTheme</item>
@@ -47,10 +46,11 @@ import com.android.setupwizardlib.template.RecyclerMixin;
  * </style>
  * }
* - * where {@code my_preference_layout} is a layout that contains - * {@link com.android.setupwizardlib.SetupWizardPreferenceLayout}. + * where {@code my_preference_layout} is a layout that contains {@link + * com.android.setupwizardlib.SetupWizardPreferenceLayout}. + * + *

Example: * - *

Example: *

{@code
  * <com.android.setupwizardlib.SetupWizardPreferenceLayout
  *     xmlns:android="http://schemas.android.com/apk/res/android"
@@ -59,58 +59,56 @@ import com.android.setupwizardlib.template.RecyclerMixin;
  *     android:layout_height="match_parent" />
  * }
* - *

Fragments using this layout must delegate {@code onCreateRecyclerView} to the + *

Fragments using this layout must delegate {@code onCreateRecyclerView} to the * implementation in this class: {@link #onCreateRecyclerView} */ public class SetupWizardPreferenceLayout extends SetupWizardRecyclerLayout { - public SetupWizardPreferenceLayout(Context context) { - super(context); - } + public SetupWizardPreferenceLayout(Context context) { + super(context); + } - public SetupWizardPreferenceLayout(Context context, int template, int containerId) { - super(context, template, containerId); - } + public SetupWizardPreferenceLayout(Context context, int template, int containerId) { + super(context, template, containerId); + } - public SetupWizardPreferenceLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } + public SetupWizardPreferenceLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } - public SetupWizardPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } + public SetupWizardPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } - @Override - protected ViewGroup findContainer(int containerId) { - if (containerId == 0) { - containerId = R.id.suw_layout_content; - } - return super.findContainer(containerId); + @Override + protected ViewGroup findContainer(int containerId) { + if (containerId == 0) { + containerId = R.id.suw_layout_content; } + return super.findContainer(containerId); + } - /** - * This method must be called in {@code PreferenceFragment#onCreateRecyclerView}. - */ - public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, - Bundle savedInstanceState) { - return mRecyclerMixin.getRecyclerView(); - } + /** This method must be called in {@code PreferenceFragment#onCreateRecyclerView}. */ + public RecyclerView onCreateRecyclerView( + LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + return mRecyclerMixin.getRecyclerView(); + } - @Override - protected View onInflateTemplate(LayoutInflater inflater, int template) { - if (template == 0) { - template = R.layout.suw_preference_template; - } - return super.onInflateTemplate(inflater, template); + @Override + protected View onInflateTemplate(LayoutInflater inflater, int template) { + if (template == 0) { + template = R.layout.suw_preference_template; } + return super.onInflateTemplate(inflater, template); + } - @Override - protected void onTemplateInflated() { - // Inflate the recycler view here, so attributes on the decoration views can be applied - // immediately. - final LayoutInflater inflater = LayoutInflater.from(getContext()); - RecyclerView recyclerView = (RecyclerView) inflater.inflate( - R.layout.suw_preference_recycler_view, this, false); - mRecyclerMixin = new RecyclerMixin(this, recyclerView); - } + @Override + protected void onTemplateInflated() { + // Inflate the recycler view here, so attributes on the decoration views can be applied + // immediately. + final LayoutInflater inflater = LayoutInflater.from(getContext()); + RecyclerView recyclerView = + (RecyclerView) inflater.inflate(R.layout.suw_preference_recycler_view, this, false); + mRecyclerMixin = new RecyclerMixin(this, recyclerView); + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java index 5d3f1a5..ba0b598 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java @@ -18,178 +18,156 @@ package com.android.setupwizardlib; import android.content.Context; import android.graphics.drawable.Drawable; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.Adapter; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.Adapter; -import androidx.recyclerview.widget.RecyclerView.ViewHolder; - import com.android.setupwizardlib.template.RecyclerMixin; import com.android.setupwizardlib.template.RecyclerViewScrollHandlingDelegate; import com.android.setupwizardlib.template.RequireScrollMixin; /** - * A setup wizard layout for use with {@link androidx.recyclerview.widget.RecyclerView}. - * {@code android:entries} can also be used to specify an - * {@link com.android.setupwizardlib.items.ItemHierarchy} to be used with this layout in XML. + * A setup wizard layout for use with {@link androidx.recyclerview.widget.RecyclerView}. {@code + * android:entries} can also be used to specify an {@link + * com.android.setupwizardlib.items.ItemHierarchy} to be used with this layout in XML. * * @see SetupWizardListLayout */ public class SetupWizardRecyclerLayout extends SetupWizardLayout { - private static final String TAG = "RecyclerLayout"; - - protected RecyclerMixin mRecyclerMixin; - - public SetupWizardRecyclerLayout(Context context) { - this(context, 0, 0); - } - - public SetupWizardRecyclerLayout(Context context, int template, int containerId) { - super(context, template, containerId); - init(context, null, 0); - } - - public SetupWizardRecyclerLayout(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs, 0); - } - - public SetupWizardRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context, attrs, defStyleAttr); - } - - private void init(Context context, AttributeSet attrs, int defStyleAttr) { - mRecyclerMixin.parseAttributes(attrs, defStyleAttr); - registerMixin(RecyclerMixin.class, mRecyclerMixin); - - - final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class); - requireScrollMixin.setScrollHandlingDelegate( - new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView())); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mRecyclerMixin.onLayout(); - } - - /** - * @see RecyclerMixin#getAdapter() - */ - public Adapter getAdapter() { - return mRecyclerMixin.getAdapter(); - } - - /** - * @see RecyclerMixin#setAdapter(Adapter) - */ - public void setAdapter(Adapter adapter) { - mRecyclerMixin.setAdapter(adapter); - } - - /** - * @see RecyclerMixin#getRecyclerView() - */ - public RecyclerView getRecyclerView() { - return mRecyclerMixin.getRecyclerView(); - } - - @Override - protected ViewGroup findContainer(int containerId) { - if (containerId == 0) { - containerId = R.id.suw_recycler_view; - } - return super.findContainer(containerId); - } - - @Override - protected View onInflateTemplate(LayoutInflater inflater, int template) { - if (template == 0) { - template = R.layout.suw_recycler_template; - } - return super.onInflateTemplate(inflater, template); - } - - @Override - protected void onTemplateInflated() { - final View recyclerView = findViewById(R.id.suw_recycler_view); - if (recyclerView instanceof RecyclerView) { - mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView); - } else { - throw new IllegalStateException( - "SetupWizardRecyclerLayout should use a template with recycler view"); - } - } - - @Override - // Returning generic type is the common pattern used for findViewBy* methods - @SuppressWarnings("TypeParameterUnusedInFormals") - public T findManagedViewById(int id) { - final View header = mRecyclerMixin.getHeader(); - if (header != null) { - final T view = header.findViewById(id); - if (view != null) { - return view; - } - } - return super.findViewById(id); - } - - /** - * @deprecated Use {@link #setDividerInsets(int, int)} instead. - */ - @Deprecated - public void setDividerInset(int inset) { - mRecyclerMixin.setDividerInset(inset); - } - - /** - * Sets the start inset of the divider. This will use the default divider drawable set in the - * theme and apply insets to it. - * - * @param start The number of pixels to inset on the "start" side of the list divider. Typically - * this will be either {@code @dimen/suw_items_icon_divider_inset} or - * {@code @dimen/suw_items_text_divider_inset}. - * @param end The number of pixels to inset on the "end" side of the list divider. - * - * @see RecyclerMixin#setDividerInsets(int, int) - */ - public void setDividerInsets(int start, int end) { - mRecyclerMixin.setDividerInsets(start, end); - } - - /** - * @deprecated Use {@link #getDividerInsetStart()} instead. - */ - @Deprecated - public int getDividerInset() { - return mRecyclerMixin.getDividerInset(); - } - - /** - * @see RecyclerMixin#getDividerInsetStart() - */ - public int getDividerInsetStart() { - return mRecyclerMixin.getDividerInsetStart(); - } - - /** - * @see RecyclerMixin#getDividerInsetEnd() - */ - public int getDividerInsetEnd() { - return mRecyclerMixin.getDividerInsetEnd(); - } - - /** - * @see RecyclerMixin#getDivider() - */ - public Drawable getDivider() { - return mRecyclerMixin.getDivider(); - } + protected RecyclerMixin mRecyclerMixin; + + public SetupWizardRecyclerLayout(Context context) { + this(context, 0, 0); + } + + public SetupWizardRecyclerLayout(Context context, int template, int containerId) { + super(context, template, containerId); + init(null, 0); + } + + public SetupWizardRecyclerLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public SetupWizardRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs, defStyleAttr); + } + + private void init(AttributeSet attrs, int defStyleAttr) { + mRecyclerMixin.parseAttributes(attrs, defStyleAttr); + registerMixin(RecyclerMixin.class, mRecyclerMixin); + + final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class); + requireScrollMixin.setScrollHandlingDelegate( + new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView())); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mRecyclerMixin.onLayout(); + } + + /** @see RecyclerMixin#getAdapter() */ + public Adapter getAdapter() { + return mRecyclerMixin.getAdapter(); + } + + /** @see RecyclerMixin#setAdapter(Adapter) */ + public void setAdapter(Adapter adapter) { + mRecyclerMixin.setAdapter(adapter); + } + + /** @see RecyclerMixin#getRecyclerView() */ + public RecyclerView getRecyclerView() { + return mRecyclerMixin.getRecyclerView(); + } + + @Override + protected ViewGroup findContainer(int containerId) { + if (containerId == 0) { + containerId = R.id.suw_recycler_view; + } + return super.findContainer(containerId); + } + + @Override + protected View onInflateTemplate(LayoutInflater inflater, int template) { + if (template == 0) { + template = R.layout.suw_recycler_template; + } + return super.onInflateTemplate(inflater, template); + } + + @Override + protected void onTemplateInflated() { + final View recyclerView = findViewById(R.id.suw_recycler_view); + if (recyclerView instanceof RecyclerView) { + mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView); + } else { + throw new IllegalStateException( + "SetupWizardRecyclerLayout should use a template with recycler view"); + } + } + + @Override + // Returning generic type is the common pattern used for findViewBy* methods + @SuppressWarnings("TypeParameterUnusedInFormals") + public T findManagedViewById(int id) { + final View header = mRecyclerMixin.getHeader(); + if (header != null) { + final T view = header.findViewById(id); + if (view != null) { + return view; + } + } + return super.findViewById(id); + } + + /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */ + @Deprecated + public void setDividerInset(int inset) { + mRecyclerMixin.setDividerInset(inset); + } + + /** + * Sets the start inset of the divider. This will use the default divider drawable set in the + * theme and apply insets to it. + * + * @param start The number of pixels to inset on the "start" side of the list divider. Typically + * this will be either {@code @dimen/suw_items_icon_divider_inset} or + * {@code @dimen/suw_items_text_divider_inset}. + * @param end The number of pixels to inset on the "end" side of the list divider. + * @see RecyclerMixin#setDividerInsets(int, int) + */ + public void setDividerInsets(int start, int end) { + mRecyclerMixin.setDividerInsets(start, end); + } + + /** @deprecated Use {@link #getDividerInsetStart()} instead. */ + @Deprecated + public int getDividerInset() { + return mRecyclerMixin.getDividerInset(); + } + + /** @see RecyclerMixin#getDividerInsetStart() */ + public int getDividerInsetStart() { + return mRecyclerMixin.getDividerInsetStart(); + } + + /** @see RecyclerMixin#getDividerInsetEnd() */ + public int getDividerInsetEnd() { + return mRecyclerMixin.getDividerInsetEnd(); + } + + /** @see RecyclerMixin#getDivider() */ + public Drawable getDivider() { + return mRecyclerMixin.getDivider(); + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java index aeaba68..419e2aa 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java +++ b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java @@ -16,44 +16,42 @@ package com.android.setupwizardlib.items; -import android.view.View; - import androidx.recyclerview.widget.RecyclerView; - +import android.view.View; import com.android.setupwizardlib.DividerItemDecoration; class ItemViewHolder extends RecyclerView.ViewHolder - implements DividerItemDecoration.DividedViewHolder { - - private boolean mIsEnabled; - private IItem mItem; - - ItemViewHolder(View itemView) { - super(itemView); - } - - @Override - public boolean isDividerAllowedAbove() { - return mIsEnabled; - } - - @Override - public boolean isDividerAllowedBelow() { - return mIsEnabled; - } - - public void setEnabled(boolean isEnabled) { - mIsEnabled = isEnabled; - itemView.setClickable(isEnabled); - itemView.setEnabled(isEnabled); - itemView.setFocusable(isEnabled); - } - - public void setItem(IItem item) { - mItem = item; - } - - public IItem getItem() { - return mItem; - } + implements DividerItemDecoration.DividedViewHolder { + + private boolean isEnabled; + private IItem item; + + ItemViewHolder(View itemView) { + super(itemView); + } + + @Override + public boolean isDividerAllowedAbove() { + return isEnabled; + } + + @Override + public boolean isDividerAllowedBelow() { + return isEnabled; + } + + public void setEnabled(boolean isEnabled) { + this.isEnabled = isEnabled; + itemView.setClickable(isEnabled); + itemView.setEnabled(isEnabled); + itemView.setFocusable(isEnabled); + } + + public void setItem(IItem item) { + this.item = item; + } + + public IItem getItem() { + return item; + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java index 56c60e7..ee753b8 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java +++ b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java @@ -20,14 +20,12 @@ import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import androidx.annotation.VisibleForTesting; +import androidx.recyclerview.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import androidx.annotation.VisibleForTesting; -import androidx.recyclerview.widget.RecyclerView; - import com.android.setupwizardlib.R; /** @@ -36,217 +34,214 @@ import com.android.setupwizardlib.R; * XML. */ public class RecyclerItemAdapter extends RecyclerView.Adapter - implements ItemHierarchy.Observer { + implements ItemHierarchy.Observer { - private static final String TAG = "RecyclerItemAdapter"; + private static final String TAG = "RecyclerItemAdapter"; - /** - * A view tag set by {@link View#setTag(Object)}. If set on the root view of a layout, it will - * not create the default background for the list item. This means the item will not have ripple - * touch feedback by default. - */ - public static final String TAG_NO_BACKGROUND = "noBackground"; + /** + * A view tag set by {@link View#setTag(Object)}. If set on the root view of a layout, it will not + * create the default background for the list item. This means the item will not have ripple touch + * feedback by default. + */ + public static final String TAG_NO_BACKGROUND = "noBackground"; - /** - * Listener for item selection in this adapter. - */ - public interface OnItemSelectedListener { - - /** - * Called when an item in this adapter is clicked. - * - * @param item The Item corresponding to the position being clicked. - */ - void onItemSelected(IItem item); - } - - private final ItemHierarchy mItemHierarchy; - private OnItemSelectedListener mListener; - - public RecyclerItemAdapter(ItemHierarchy hierarchy) { - mItemHierarchy = hierarchy; - mItemHierarchy.registerObserver(this); - } + /** Listener for item selection in this adapter. */ + public interface OnItemSelectedListener { /** - * Gets the item at the given position. + * Called when an item in this adapter is clicked. * - * @see ItemHierarchy#getItemAt(int) + * @param item The Item corresponding to the position being clicked. */ - public IItem getItem(int position) { - return mItemHierarchy.getItemAt(position); - } - - @Override - public long getItemId(int position) { - IItem mItem = getItem(position); - if (mItem instanceof AbstractItem) { - final int id = ((AbstractItem) mItem).getId(); - return id > 0 ? id : RecyclerView.NO_ID; - } else { - return RecyclerView.NO_ID; - } - } - - @Override - public int getItemCount() { - return mItemHierarchy.getCount(); - } - - @Override - public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - final View view = inflater.inflate(viewType, parent, false); - final ItemViewHolder viewHolder = new ItemViewHolder(view); - - final Object viewTag = view.getTag(); - if (!TAG_NO_BACKGROUND.equals(viewTag)) { - final TypedArray typedArray = parent.getContext() - .obtainStyledAttributes(R.styleable.SuwRecyclerItemAdapter); - Drawable selectableItemBackground = typedArray.getDrawable( - R.styleable.SuwRecyclerItemAdapter_android_selectableItemBackground); - if (selectableItemBackground == null) { - selectableItemBackground = typedArray.getDrawable( - R.styleable.SuwRecyclerItemAdapter_selectableItemBackground); - } - - Drawable background = view.getBackground(); - if (background == null) { - background = typedArray.getDrawable( - R.styleable.SuwRecyclerItemAdapter_android_colorBackground); - } - - if (selectableItemBackground == null || background == null) { - Log.e(TAG, "Cannot resolve required attributes." - + " selectableItemBackground=" + selectableItemBackground - + " background=" + background); - } else { - final Drawable[] layers = {background, selectableItemBackground}; - view.setBackgroundDrawable(new PatchedLayerDrawable(layers)); - } - - typedArray.recycle(); - } - - view.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - final IItem item = viewHolder.getItem(); - if (mListener != null && item != null && item.isEnabled()) { - mListener.onItemSelected(item); - } + void onItemSelected(IItem item); + } + + private final ItemHierarchy itemHierarchy; + private OnItemSelectedListener listener; + + public RecyclerItemAdapter(ItemHierarchy hierarchy) { + itemHierarchy = hierarchy; + itemHierarchy.registerObserver(this); + } + + /** + * Gets the item at the given position. + * + * @see ItemHierarchy#getItemAt(int) + */ + public IItem getItem(int position) { + return itemHierarchy.getItemAt(position); + } + + @Override + public long getItemId(int position) { + IItem mItem = getItem(position); + if (mItem instanceof AbstractItem) { + final int id = ((AbstractItem) mItem).getId(); + return id > 0 ? id : RecyclerView.NO_ID; + } else { + return RecyclerView.NO_ID; + } + } + + @Override + public int getItemCount() { + return itemHierarchy.getCount(); + } + + @Override + public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + final View view = inflater.inflate(viewType, parent, false); + final ItemViewHolder viewHolder = new ItemViewHolder(view); + + final Object viewTag = view.getTag(); + if (!TAG_NO_BACKGROUND.equals(viewTag)) { + final TypedArray typedArray = + parent.getContext().obtainStyledAttributes(R.styleable.SuwRecyclerItemAdapter); + Drawable selectableItemBackground = + typedArray.getDrawable( + R.styleable.SuwRecyclerItemAdapter_android_selectableItemBackground); + if (selectableItemBackground == null) { + selectableItemBackground = + typedArray.getDrawable(R.styleable.SuwRecyclerItemAdapter_selectableItemBackground); + } + + Drawable background = view.getBackground(); + if (background == null) { + background = + typedArray.getDrawable(R.styleable.SuwRecyclerItemAdapter_android_colorBackground); + } + + if (selectableItemBackground == null || background == null) { + Log.e( + TAG, + "Cannot resolve required attributes." + + " selectableItemBackground=" + + selectableItemBackground + + " background=" + + background); + } else { + final Drawable[] layers = {background, selectableItemBackground}; + view.setBackgroundDrawable(new PatchedLayerDrawable(layers)); + } + + typedArray.recycle(); + } + + view.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + final IItem item = viewHolder.getItem(); + if (listener != null && item != null && item.isEnabled()) { + listener.onItemSelected(item); } + } }); - return viewHolder; + return viewHolder; + } + + @Override + public void onBindViewHolder(ItemViewHolder holder, int position) { + final IItem item = getItem(position); + holder.setEnabled(item.isEnabled()); + holder.setItem(item); + item.onBindView(holder.itemView); + } + + @Override + public int getItemViewType(int position) { + // Use layout resource as item view type. RecyclerView item type does not have to be + // contiguous. + IItem item = getItem(position); + return item.getLayoutResource(); + } + + @Override + public void onChanged(ItemHierarchy hierarchy) { + notifyDataSetChanged(); + } + + @Override + public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) { + notifyItemRangeChanged(positionStart, itemCount); + } + + @Override + public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) { + notifyItemRangeInserted(positionStart, itemCount); + } + + @Override + public void onItemRangeMoved( + ItemHierarchy itemHierarchy, int fromPosition, int toPosition, int itemCount) { + // There is no notifyItemRangeMoved + // https://code.google.com/p/android/issues/detail?id=125984 + if (itemCount == 1) { + notifyItemMoved(fromPosition, toPosition); + } else { + // If more than one, degenerate into the catch-all data set changed callback, since I'm + // not sure how recycler view handles multiple calls to notifyItemMoved (if the result + // is committed after every notification then naively calling + // notifyItemMoved(from + i, to + i) is wrong). + // Logging this in case this is a more common occurrence than expected. + Log.i(TAG, "onItemRangeMoved with more than one item"); + notifyDataSetChanged(); + } + } + + @Override + public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) { + notifyItemRangeRemoved(positionStart, itemCount); + } + + /** + * Find an item hierarchy within the root hierarchy. + * + * @see ItemHierarchy#findItemById(int) + */ + public ItemHierarchy findItemById(int id) { + return itemHierarchy.findItemById(id); + } + + /** Gets the root item hierarchy in this adapter. */ + public ItemHierarchy getRootItemHierarchy() { + return itemHierarchy; + } + + /** + * Sets the listener to listen for when user clicks on a item. + * + * @see OnItemSelectedListener + */ + public void setOnItemSelectedListener(OnItemSelectedListener listener) { + this.listener = listener; + } + + /** + * Before Lollipop, LayerDrawable always return true in getPadding, even if the children layers do + * not have any padding. Patch the implementation so that getPadding returns false if the padding + * is empty. + * + *

When getPadding is true, the padding of the view will be replaced by the padding of the + * drawable when {@link View#setBackgroundDrawable(Drawable)} is called. This patched class makes + * sure layer drawables without padding does not clear out original padding on the view. + */ + @VisibleForTesting + static class PatchedLayerDrawable extends LayerDrawable { + + /** {@inheritDoc} */ + PatchedLayerDrawable(Drawable[] layers) { + super(layers); } @Override - public void onBindViewHolder(ItemViewHolder holder, int position) { - final IItem item = getItem(position); - holder.setEnabled(item.isEnabled()); - holder.setItem(item); - item.onBindView(holder.itemView); - } - - @Override - public int getItemViewType(int position) { - // Use layout resource as item view type. RecyclerView item type does not have to be - // contiguous. - IItem item = getItem(position); - return item.getLayoutResource(); - } - - @Override - public void onChanged(ItemHierarchy hierarchy) { - notifyDataSetChanged(); - } - - @Override - public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) { - notifyItemRangeChanged(positionStart, itemCount); - } - - @Override - public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) { - notifyItemRangeInserted(positionStart, itemCount); - } - - @Override - public void onItemRangeMoved(ItemHierarchy itemHierarchy, int fromPosition, int toPosition, - int itemCount) { - // There is no notifyItemRangeMoved - // https://code.google.com/p/android/issues/detail?id=125984 - if (itemCount == 1) { - notifyItemMoved(fromPosition, toPosition); - } else { - // If more than one, degenerate into the catch-all data set changed callback, since I'm - // not sure how recycler view handles multiple calls to notifyItemMoved (if the result - // is committed after every notification then naively calling - // notifyItemMoved(from + i, to + i) is wrong). - // Logging this in case this is a more common occurrence than expected. - Log.i(TAG, "onItemRangeMoved with more than one item"); - notifyDataSetChanged(); - } - } - - @Override - public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) { - notifyItemRangeRemoved(positionStart, itemCount); - } - - /** - * Find an item hierarchy within the root hierarchy. - * - * @see ItemHierarchy#findItemById(int) - */ - public ItemHierarchy findItemById(int id) { - return mItemHierarchy.findItemById(id); - } - - /** - * Gets the root item hierarchy in this adapter. - */ - public ItemHierarchy getRootItemHierarchy() { - return mItemHierarchy; - } - - /** - * Sets the listener to listen for when user clicks on a item. - * - * @see OnItemSelectedListener - */ - public void setOnItemSelectedListener(OnItemSelectedListener listener) { - mListener = listener; - } - - /** - * Before Lollipop, LayerDrawable always return true in getPadding, even if the children layers - * do not have any padding. Patch the implementation so that getPadding returns false if the - * padding is empty. - * - * When getPadding is true, the padding of the view will be replaced by the padding of the - * drawable when {@link View#setBackgroundDrawable(Drawable)} is called. This patched class - * makes sure layer drawables without padding does not clear out original padding on the view. - */ - @VisibleForTesting - static class PatchedLayerDrawable extends LayerDrawable { - - /** - * {@inheritDoc} - */ - PatchedLayerDrawable(Drawable[] layers) { - super(layers); - } - - @Override - public boolean getPadding(Rect padding) { - final boolean superHasPadding = super.getPadding(padding); - return superHasPadding - && !(padding.left == 0 - && padding.top == 0 - && padding.right == 0 - && padding.bottom == 0); - } + public boolean getPadding(Rect padding) { + final boolean superHasPadding = super.getPadding(padding); + return superHasPadding + && !(padding.left == 0 && padding.top == 0 && padding.right == 0 && padding.bottom == 0); } + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java index 32e7bd8..a6c6526 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java +++ b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java @@ -21,16 +21,14 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Build.VERSION_CODES; -import android.util.AttributeSet; -import android.view.View; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.ViewHolder; - +import android.util.AttributeSet; +import android.view.View; import com.android.setupwizardlib.DividerItemDecoration; import com.android.setupwizardlib.R; import com.android.setupwizardlib.TemplateLayout; @@ -51,223 +49,208 @@ import com.android.setupwizardlib.view.HeaderRecyclerView.HeaderAdapter; */ public class RecyclerMixin implements Mixin { - private TemplateLayout mTemplateLayout; + private final TemplateLayout templateLayout; - @NonNull - private final RecyclerView mRecyclerView; + @NonNull private final RecyclerView recyclerView; - @Nullable - private View mHeader; + @Nullable private View header; - @NonNull - private DividerItemDecoration mDividerDecoration; + @NonNull private DividerItemDecoration dividerDecoration; - private Drawable mDefaultDivider; - private Drawable mDivider; + private Drawable defaultDivider; + private Drawable divider; - private int mDividerInsetStart; - private int mDividerInsetEnd; + private int dividerInsetStart; + private int dividerInsetEnd; - /** - * Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this - * mixin should be called in {@link TemplateLayout#onTemplateInflated()}, which is called by - * the super constructor, because the recycler view and the header needs to be made available - * before other mixins from the super class. - * - * @param layout The layout this mixin belongs to. - */ - public RecyclerMixin(@NonNull TemplateLayout layout, @NonNull RecyclerView recyclerView) { - mTemplateLayout = layout; + /** + * Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this + * mixin should be called in {@link TemplateLayout#onTemplateInflated()}, which is called by the + * super constructor, because the recycler view and the header needs to be made available before + * other mixins from the super class. + * + * @param layout The layout this mixin belongs to. + */ + public RecyclerMixin(@NonNull TemplateLayout layout, @NonNull RecyclerView recyclerView) { + templateLayout = layout; - mDividerDecoration = new DividerItemDecoration(mTemplateLayout.getContext()); + dividerDecoration = new DividerItemDecoration(templateLayout.getContext()); - // The recycler view needs to be available - mRecyclerView = recyclerView; - mRecyclerView.setLayoutManager(new LinearLayoutManager(mTemplateLayout.getContext())); + // The recycler view needs to be available + this.recyclerView = recyclerView; + this.recyclerView.setLayoutManager(new LinearLayoutManager(templateLayout.getContext())); - if (recyclerView instanceof HeaderRecyclerView) { - mHeader = ((HeaderRecyclerView) recyclerView).getHeader(); - } - - mRecyclerView.addItemDecoration(mDividerDecoration); + if (recyclerView instanceof HeaderRecyclerView) { + header = ((HeaderRecyclerView) recyclerView).getHeader(); } - /** - * Parse XML attributes and configures this mixin and the recycler view accordingly. This should - * be called from the constructor of the layout. - * - * @param attrs The {@link AttributeSet} as passed into the constructor. Can be null if the - * layout was not created from XML. - * @param defStyleAttr The default style attribute as passed into the layout constructor. Can be - * 0 if it is not needed. - */ - public void parseAttributes(@Nullable AttributeSet attrs, int defStyleAttr) { - final Context context = mTemplateLayout.getContext(); - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.SuwRecyclerMixin, defStyleAttr, 0); - - final int entries = a.getResourceId(R.styleable.SuwRecyclerMixin_android_entries, 0); - if (entries != 0) { - final ItemHierarchy inflated = new ItemInflater(context).inflate(entries); - final RecyclerItemAdapter adapter = new RecyclerItemAdapter(inflated); - adapter.setHasStableIds(a.getBoolean( - R.styleable.SuwRecyclerMixin_suwHasStableIds, false)); - setAdapter(adapter); - } - int dividerInset = - a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInset, -1); - if (dividerInset != -1) { - setDividerInset(dividerInset); - } else { - int dividerInsetStart = - a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInsetStart, 0); - int dividerInsetEnd = - a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInsetEnd, 0); - setDividerInsets(dividerInsetStart, dividerInsetEnd); - } - - a.recycle(); + this.recyclerView.addItemDecoration(dividerDecoration); + } + + /** + * Parse XML attributes and configures this mixin and the recycler view accordingly. This should + * be called from the constructor of the layout. + * + * @param attrs The {@link AttributeSet} as passed into the constructor. Can be null if the layout + * was not created from XML. + * @param defStyleAttr The default style attribute as passed into the layout constructor. Can be 0 + * if it is not needed. + */ + public void parseAttributes(@Nullable AttributeSet attrs, int defStyleAttr) { + final Context context = templateLayout.getContext(); + final TypedArray a = + context.obtainStyledAttributes(attrs, R.styleable.SuwRecyclerMixin, defStyleAttr, 0); + + final int entries = a.getResourceId(R.styleable.SuwRecyclerMixin_android_entries, 0); + if (entries != 0) { + final ItemHierarchy inflated = new ItemInflater(context).inflate(entries); + final RecyclerItemAdapter adapter = new RecyclerItemAdapter(inflated); + adapter.setHasStableIds(a.getBoolean(R.styleable.SuwRecyclerMixin_suwHasStableIds, false)); + setAdapter(adapter); } - - /** - * @return The recycler view contained in the layout, as marked by - * {@code @id/suw_recycler_view}. This will return {@code null} if the recycler view - * doesn't exist in the layout. - */ - @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a recycler - // view, and call this after the template is inflated, - // this will not return null. - public RecyclerView getRecyclerView() { - return mRecyclerView; + int dividerInset = a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInset, -1); + if (dividerInset != -1) { + setDividerInset(dividerInset); + } else { + int dividerInsetStart = + a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInsetStart, 0); + int dividerInsetEnd = + a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInsetEnd, 0); + setDividerInsets(dividerInsetStart, dividerInsetEnd); } - /** - * Gets the header view of the recycler layout. This is useful for other mixins if they need to - * access views within the header, usually via {@link TemplateLayout#findManagedViewById(int)}. - */ - @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a header, - // this call will not return null. - public View getHeader() { - return mHeader; + a.recycle(); + } + + /** + * @return The recycler view contained in the layout, as marked by {@code @id/suw_recycler_view}. + * This will return {@code null} if the recycler view doesn't exist in the layout. + */ + @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a recycler + // view, and call this after the template is inflated, + // this will not return null. + public RecyclerView getRecyclerView() { + return recyclerView; + } + + /** + * Gets the header view of the recycler layout. This is useful for other mixins if they need to + * access views within the header, usually via {@link TemplateLayout#findManagedViewById(int)}. + */ + @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a header, + // this call will not return null. + public View getHeader() { + return header; + } + + /** + * Recycler mixin needs to update the dividers if the layout direction has changed. This method + * should be called when {@link View#onLayout(boolean, int, int, int, int)} of the template is + * called. + */ + public void onLayout() { + if (divider == null) { + // Update divider in case layout direction has just been resolved + updateDivider(); } - - /** - * Recycler mixin needs to update the dividers if the layout direction has changed. This method - * should be called when {@link View#onLayout(boolean, int, int, int, int)} of the template - * is called. - */ - public void onLayout() { - if (mDivider == null) { - // Update divider in case layout direction has just been resolved - updateDivider(); - } + } + + /** + * Gets the adapter of the recycler view in this layout. If the adapter includes a header, this + * method will unwrap it and return the underlying adapter. + * + * @return The adapter, or {@code null} if the recycler view has no adapter. + */ + public Adapter getAdapter() { + @SuppressWarnings("unchecked") // RecyclerView.getAdapter returns raw type :( + final RecyclerView.Adapter adapter = recyclerView.getAdapter(); + if (adapter instanceof HeaderAdapter) { + return ((HeaderAdapter) adapter).getWrappedAdapter(); } - - /** - * Gets the adapter of the recycler view in this layout. If the adapter includes a header, - * this method will unwrap it and return the underlying adapter. - * - * @return The adapter, or {@code null} if the recycler view has no adapter. - */ - public Adapter getAdapter() { - @SuppressWarnings("unchecked") // RecyclerView.getAdapter returns raw type :( - final RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); - if (adapter instanceof HeaderAdapter) { - return ((HeaderAdapter) adapter).getWrappedAdapter(); - } - return adapter; + return adapter; + } + + /** Sets the adapter on the recycler view in this layout. */ + public void setAdapter(Adapter adapter) { + recyclerView.setAdapter(adapter); + } + + /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */ + @Deprecated + public void setDividerInset(int inset) { + setDividerInsets(inset, 0); + } + + /** + * Sets the start inset of the divider. This will use the default divider drawable set in the + * theme and apply insets to it. + * + * @param start The number of pixels to inset on the "start" side of the list divider. Typically + * this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or + * {@code @dimen/suw_items_glif_text_divider_inset}. + * @param end The number of pixels to inset on the "end" side of the list divider. + */ + public void setDividerInsets(int start, int end) { + dividerInsetStart = start; + dividerInsetEnd = end; + updateDivider(); + } + + /** + * @return The number of pixels inset on the start side of the divider. + * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead. + */ + @Deprecated + public int getDividerInset() { + return getDividerInsetStart(); + } + + /** @return The number of pixels inset on the start side of the divider. */ + public int getDividerInsetStart() { + return dividerInsetStart; + } + + /** @return The number of pixels inset on the end side of the divider. */ + public int getDividerInsetEnd() { + return dividerInsetEnd; + } + + private void updateDivider() { + boolean shouldUpdate = true; + if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) { + shouldUpdate = templateLayout.isLayoutDirectionResolved(); } - - /** - * Sets the adapter on the recycler view in this layout. - */ - public void setAdapter(Adapter adapter) { - mRecyclerView.setAdapter(adapter); - } - - /** - * @deprecated Use {@link #setDividerInsets(int, int)} instead. - */ - @Deprecated - public void setDividerInset(int inset) { - setDividerInsets(inset, 0); - } - - /** - * Sets the start inset of the divider. This will use the default divider drawable set in the - * theme and apply insets to it. - * - * @param start The number of pixels to inset on the "start" side of the list divider. Typically - * this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or - * {@code @dimen/suw_items_glif_text_divider_inset}. - * @param end The number of pixels to inset on the "end" side of the list divider. - */ - public void setDividerInsets(int start, int end) { - mDividerInsetStart = start; - mDividerInsetEnd = end; - updateDivider(); - } - - /** - * @return The number of pixels inset on the start side of the divider. - * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead. - */ - @Deprecated - public int getDividerInset() { - return getDividerInsetStart(); - } - - /** - * @return The number of pixels inset on the start side of the divider. - */ - public int getDividerInsetStart() { - return mDividerInsetStart; - } - - /** - * @return The number of pixels inset on the end side of the divider. - */ - public int getDividerInsetEnd() { - return mDividerInsetEnd; - } - - private void updateDivider() { - boolean shouldUpdate = true; - if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) { - shouldUpdate = mTemplateLayout.isLayoutDirectionResolved(); - } - if (shouldUpdate) { - if (mDefaultDivider == null) { - mDefaultDivider = mDividerDecoration.getDivider(); - } - mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable( - mDefaultDivider, - mDividerInsetStart /* start */, - 0 /* top */, - mDividerInsetEnd /* end */, - 0 /* bottom */, - mTemplateLayout); - mDividerDecoration.setDivider(mDivider); - } - } - - /** - * @return The drawable used as the divider. - */ - public Drawable getDivider() { - return mDivider; - } - - /** - * Sets the divider item decoration directly. This is a low level method which should be used - * only if custom divider behavior is needed, for example if the divider should be shown / - * hidden in some specific cases for view holders that cannot implement - * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}. - */ - public void setDividerItemDecoration(@NonNull DividerItemDecoration decoration) { - mRecyclerView.removeItemDecoration(mDividerDecoration); - mDividerDecoration = decoration; - mRecyclerView.addItemDecoration(mDividerDecoration); - updateDivider(); + if (shouldUpdate) { + if (defaultDivider == null) { + defaultDivider = dividerDecoration.getDivider(); + } + divider = + DrawableLayoutDirectionHelper.createRelativeInsetDrawable( + defaultDivider, + dividerInsetStart /* start */, + 0 /* top */, + dividerInsetEnd /* end */, + 0 /* bottom */, + templateLayout); + dividerDecoration.setDivider(divider); } + } + + /** @return The drawable used as the divider. */ + public Drawable getDivider() { + return divider; + } + + /** + * Sets the divider item decoration directly. This is a low level method which should be used only + * if custom divider behavior is needed, for example if the divider should be shown / hidden in + * some specific cases for view holders that cannot implement {@link + * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}. + */ + public void setDividerItemDecoration(@NonNull DividerItemDecoration decoration) { + recyclerView.removeItemDecoration(dividerDecoration); + dividerDecoration = decoration; + recyclerView.addItemDecoration(dividerDecoration); + updateDivider(); + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java index bfe8df2..8838c44 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java +++ b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java @@ -16,12 +16,10 @@ package com.android.setupwizardlib.template; -import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; - +import android.util.Log; import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDelegate; /** @@ -30,55 +28,53 @@ import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDele */ public class RecyclerViewScrollHandlingDelegate implements ScrollHandlingDelegate { - private static final String TAG = "RVRequireScrollMixin"; + private static final String TAG = "RVRequireScrollMixin"; - @Nullable - private final RecyclerView mRecyclerView; + @Nullable private final RecyclerView recyclerView; - @NonNull - private final RequireScrollMixin mRequireScrollMixin; + @NonNull private final RequireScrollMixin requireScrollMixin; - public RecyclerViewScrollHandlingDelegate( - @NonNull RequireScrollMixin requireScrollMixin, - @Nullable RecyclerView recyclerView) { - mRequireScrollMixin = requireScrollMixin; - mRecyclerView = recyclerView; - } + public RecyclerViewScrollHandlingDelegate( + @NonNull RequireScrollMixin requireScrollMixin, @Nullable RecyclerView recyclerView) { + this.requireScrollMixin = requireScrollMixin; + this.recyclerView = recyclerView; + } - private boolean canScrollDown() { - if (mRecyclerView != null) { - // Compatibility implementation of View#canScrollVertically - final int offset = mRecyclerView.computeVerticalScrollOffset(); - final int range = mRecyclerView.computeVerticalScrollRange() - - mRecyclerView.computeVerticalScrollExtent(); - return range != 0 && offset < range - 1; - } - return false; + private boolean canScrollDown() { + if (recyclerView != null) { + // Compatibility implementation of View#canScrollVertically + final int offset = recyclerView.computeVerticalScrollOffset(); + final int range = + recyclerView.computeVerticalScrollRange() - recyclerView.computeVerticalScrollExtent(); + return range != 0 && offset < range - 1; } + return false; + } - @Override - public void startListening() { - if (mRecyclerView != null) { - mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - mRequireScrollMixin.notifyScrollabilityChange(canScrollDown()); - } - }); - - if (canScrollDown()) { - mRequireScrollMixin.notifyScrollabilityChange(true); + @Override + public void startListening() { + if (this.recyclerView != null) { + this.recyclerView.addOnScrollListener( + new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + requireScrollMixin.notifyScrollabilityChange(canScrollDown()); } - } else { - Log.w(TAG, "Cannot require scroll. Recycler view is null."); - } + }); + + if (canScrollDown()) { + requireScrollMixin.notifyScrollabilityChange(true); + } + } else { + Log.w(TAG, "Cannot require scroll. Recycler view is null."); } + } - @Override - public void pageScrollDown() { - if (mRecyclerView != null) { - final int height = mRecyclerView.getHeight(); - mRecyclerView.smoothScrollBy(0, height); - } + @Override + public void pageScrollDown() { + if (recyclerView != null) { + final int height = recyclerView.getHeight(); + recyclerView.smoothScrollBy(0, height); } + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java index 0304b65..3808e11 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java +++ b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java @@ -19,259 +19,257 @@ package com.android.setupwizardlib.view; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; +import androidx.recyclerview.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; - -import androidx.recyclerview.widget.RecyclerView; - import com.android.setupwizardlib.DividerItemDecoration; import com.android.setupwizardlib.R; /** * A RecyclerView that can display a header item at the start of the list. The header can be set by - * {@code app:suwHeader} in XML. Note that the header will not be inflated until a layout manager - * is set. + * {@code app:suwHeader} in XML. Note that the header will not be inflated until a layout manager is + * set. */ public class HeaderRecyclerView extends RecyclerView { - private static class HeaderViewHolder extends ViewHolder - implements DividerItemDecoration.DividedViewHolder { - - HeaderViewHolder(View itemView) { - super(itemView); - } - - @Override - public boolean isDividerAllowedAbove() { - return false; - } + private static class HeaderViewHolder extends ViewHolder + implements DividerItemDecoration.DividedViewHolder { - @Override - public boolean isDividerAllowedBelow() { - return false; - } + HeaderViewHolder(View itemView) { + super(itemView); } - /** - * An adapter that can optionally add one header item to the RecyclerView. - * - * @param Type of the content view holder. i.e. view holder type of the wrapped adapter. - */ - public static class HeaderAdapter - extends RecyclerView.Adapter { - - private static final int HEADER_VIEW_TYPE = Integer.MAX_VALUE; + @Override + public boolean isDividerAllowedAbove() { + return false; + } - private RecyclerView.Adapter mAdapter; - private View mHeader; + @Override + public boolean isDividerAllowedBelow() { + return false; + } + } - private final AdapterDataObserver mObserver = new AdapterDataObserver() { + /** + * An adapter that can optionally add one header item to the RecyclerView. + * + * @param Type of the content view holder. i.e. view holder type of the wrapped adapter. + */ + public static class HeaderAdapter + extends RecyclerView.Adapter { - @Override - public void onChanged() { - notifyDataSetChanged(); - } + private static final int HEADER_VIEW_TYPE = Integer.MAX_VALUE; - @Override - public void onItemRangeChanged(int positionStart, int itemCount) { - if (mHeader != null) { - positionStart++; - } - notifyItemRangeChanged(positionStart, itemCount); - } + private final RecyclerView.Adapter adapter; + private View header; - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - if (mHeader != null) { - positionStart++; - } - notifyItemRangeInserted(positionStart, itemCount); - } + private final AdapterDataObserver observer = + new AdapterDataObserver() { - @Override - public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { - if (mHeader != null) { - fromPosition++; - toPosition++; - } - // Why is there no notifyItemRangeMoved? - for (int i = 0; i < itemCount; i++) { - notifyItemMoved(fromPosition + i, toPosition + i); - } - } + @Override + public void onChanged() { + notifyDataSetChanged(); + } - @Override - public void onItemRangeRemoved(int positionStart, int itemCount) { - if (mHeader != null) { - positionStart++; - } - notifyItemRangeRemoved(positionStart, itemCount); + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + if (header != null) { + positionStart++; } - }; - - public HeaderAdapter(RecyclerView.Adapter adapter) { - mAdapter = adapter; - mAdapter.registerAdapterDataObserver(mObserver); - setHasStableIds(mAdapter.hasStableIds()); - } + notifyItemRangeChanged(positionStart, itemCount); + } - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - // Returning the same view (mHeader) results in crash ".. but view is not a real child." - // The framework creates more than one instance of header because of "disappear" - // animations applied on the header and this necessitates creation of another header - // view to use after the animation. We work around this restriction by returning an - // empty FrameLayout to which the header is attached using #onBindViewHolder method. - if (viewType == HEADER_VIEW_TYPE) { - FrameLayout frameLayout = new FrameLayout(parent.getContext()); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.WRAP_CONTENT); - frameLayout.setLayoutParams(params); - return new HeaderViewHolder(frameLayout); - } else { - return mAdapter.onCreateViewHolder(parent, viewType); + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + if (header != null) { + positionStart++; } - } - - @Override - @SuppressWarnings("unchecked") // Non-header position always return type CVH - public void onBindViewHolder(ViewHolder holder, int position) { - if (mHeader != null) { - position--; + notifyItemRangeInserted(positionStart, itemCount); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + if (header != null) { + fromPosition++; + toPosition++; } - - if (holder instanceof HeaderViewHolder) { - if (mHeader == null) { - throw new IllegalStateException("HeaderViewHolder cannot find mHeader"); - } - if (mHeader.getParent() != null) { - ((ViewGroup) mHeader.getParent()).removeView(mHeader); - } - FrameLayout mHeaderParent = (FrameLayout) holder.itemView; - mHeaderParent.addView(mHeader); - } else { - mAdapter.onBindViewHolder((CVH) holder, position); + // Why is there no notifyItemRangeMoved? + for (int i = 0; i < itemCount; i++) { + notifyItemMoved(fromPosition + i, toPosition + i); } - } + } - @Override - public int getItemViewType(int position) { - if (mHeader != null) { - position--; + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + if (header != null) { + positionStart++; } - if (position < 0) { - return HEADER_VIEW_TYPE; - } - return mAdapter.getItemViewType(position); - } - - @Override - public int getItemCount() { - int count = mAdapter.getItemCount(); - if (mHeader != null) { - count++; - } - return count; - } - - @Override - public long getItemId(int position) { - if (mHeader != null) { - position--; - } - if (position < 0) { - return Long.MAX_VALUE; - } - return mAdapter.getItemId(position); - } - - public void setHeader(View header) { - mHeader = header; - } - - public RecyclerView.Adapter getWrappedAdapter() { - return mAdapter; - } - } - - private View mHeader; - private int mHeaderRes; + notifyItemRangeRemoved(positionStart, itemCount); + } + }; - public HeaderRecyclerView(Context context) { - super(context); - init(null, 0); + public HeaderAdapter(RecyclerView.Adapter adapter) { + this.adapter = adapter; + this.adapter.registerAdapterDataObserver(observer); + setHasStableIds(this.adapter.hasStableIds()); } - public HeaderRecyclerView(Context context, AttributeSet attrs) { - super(context, attrs); - init(attrs, 0); + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + // Returning the same view (header) results in crash ".. but view is not a real child." + // The framework creates more than one instance of header because of "disappear" + // animations applied on the header and this necessitates creation of another header + // view to use after the animation. We work around this restriction by returning an + // empty FrameLayout to which the header is attached using #onBindViewHolder method. + if (viewType == HEADER_VIEW_TYPE) { + FrameLayout frameLayout = new FrameLayout(parent.getContext()); + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); + frameLayout.setLayoutParams(params); + return new HeaderViewHolder(frameLayout); + } else { + return adapter.onCreateViewHolder(parent, viewType); + } } - public HeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(attrs, defStyleAttr); + @Override + @SuppressWarnings("unchecked") // Non-header position always return type CVH + public void onBindViewHolder(ViewHolder holder, int position) { + if (header != null) { + position--; + } + + if (holder instanceof HeaderViewHolder) { + if (header == null) { + throw new IllegalStateException("HeaderViewHolder cannot find mHeader"); + } + if (header.getParent() != null) { + ((ViewGroup) header.getParent()).removeView(header); + } + FrameLayout mHeaderParent = (FrameLayout) holder.itemView; + mHeaderParent.addView(header); + } else { + adapter.onBindViewHolder((CVH) holder, position); + } } - private void init(AttributeSet attrs, int defStyleAttr) { - final TypedArray a = getContext().obtainStyledAttributes(attrs, - R.styleable.SuwHeaderRecyclerView, defStyleAttr, 0); - mHeaderRes = a.getResourceId(R.styleable.SuwHeaderRecyclerView_suwHeader, 0); - a.recycle(); + @Override + public int getItemViewType(int position) { + if (header != null) { + position--; + } + if (position < 0) { + return HEADER_VIEW_TYPE; + } + return adapter.getItemViewType(position); } @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - - // Decoration-only headers should not count as an item for accessibility, adjust the - // accessibility event to account for that. - final int numberOfHeaders = mHeader != null ? 1 : 0; - event.setItemCount(event.getItemCount() - numberOfHeaders); - event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0)); - } + public int getItemCount() { + int count = adapter.getItemCount(); + if (header != null) { + count++; + } + return count; } - /** - * Gets the header view of this RecyclerView, or {@code null} if there are no headers. - */ - public View getHeader() { - return mHeader; + @Override + public long getItemId(int position) { + if (header != null) { + position--; + } + if (position < 0) { + return Long.MAX_VALUE; + } + return adapter.getItemId(position); } - /** - * Set the view to use as the header of this recycler view. - * Note: This must be called before setAdapter. - */ public void setHeader(View header) { - mHeader = header; + this.header = header; } - @Override - public void setLayoutManager(LayoutManager layout) { - super.setLayoutManager(layout); - if (layout != null && mHeader == null && mHeaderRes != 0) { - // Inflating a child view requires the layout manager to be set. Check here to see if - // any header item is specified in XML and inflate them. - final LayoutInflater inflater = LayoutInflater.from(getContext()); - mHeader = inflater.inflate(mHeaderRes, this, false); - } + public RecyclerView.Adapter getWrappedAdapter() { + return adapter; } - - @Override - @SuppressWarnings("rawtypes,unchecked") // RecyclerView.setAdapter uses raw type :( - public void setAdapter(Adapter adapter) { - if (mHeader != null && adapter != null) { - final HeaderAdapter headerAdapter = new HeaderAdapter(adapter); - headerAdapter.setHeader(mHeader); - adapter = headerAdapter; - } - super.setAdapter(adapter); + } + + private View header; + private int headerRes; + + public HeaderRecyclerView(Context context) { + super(context); + init(null, 0); + } + + public HeaderRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public HeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs, defStyleAttr); + } + + private void init(AttributeSet attrs, int defStyleAttr) { + final TypedArray a = + getContext() + .obtainStyledAttributes(attrs, R.styleable.SuwHeaderRecyclerView, defStyleAttr, 0); + headerRes = a.getResourceId(R.styleable.SuwHeaderRecyclerView_suwHeader, 0); + a.recycle(); + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + + // Decoration-only headers should not count as an item for accessibility, adjust the + // accessibility event to account for that. + final int numberOfHeaders = header != null ? 1 : 0; + event.setItemCount(event.getItemCount() - numberOfHeaders); + event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0)); + } + } + + /** Gets the header view of this RecyclerView, or {@code null} if there are no headers. */ + public View getHeader() { + return header; + } + + /** + * Set the view to use as the header of this recycler view. Note: This must be called before + * setAdapter. + */ + public void setHeader(View header) { + this.header = header; + } + + @Override + public void setLayoutManager(LayoutManager layout) { + super.setLayoutManager(layout); + if (layout != null && header == null && headerRes != 0) { + // Inflating a child view requires the layout manager to be set. Check here to see if + // any header item is specified in XML and inflate them. + final LayoutInflater inflater = LayoutInflater.from(getContext()); + header = inflater.inflate(headerRes, this, false); + } + } + + @Override + @SuppressWarnings("rawtypes,unchecked") // RecyclerView.setAdapter uses raw type :( + public void setAdapter(Adapter adapter) { + if (header != null && adapter != null) { + final HeaderAdapter headerAdapter = new HeaderAdapter(adapter); + headerAdapter.setHeader(header); + adapter = headerAdapter; } + super.setAdapter(adapter); + } } diff --git a/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java index d51ea56..a5fa69c 100644 --- a/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java +++ b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java @@ -32,112 +32,114 @@ import android.view.WindowInsets; * to be drawn when the sticky element hits the top of the view. * *

There are a few things to note: + * *

    *
  1. The view does not work well with padding. b/16190933 *
  2. If fitsSystemWindows is true, then this will offset the sticking position by the height of - * the system decorations at the top of the screen. + * the system decorations at the top of the screen. *
*/ public class StickyHeaderRecyclerView extends HeaderRecyclerView { - private View mSticky; - private int mStatusBarInset = 0; - private RectF mStickyRect = new RectF(); + private View sticky; + private int statusBarInset = 0; + private final RectF stickyRect = new RectF(); - public StickyHeaderRecyclerView(Context context) { - super(context); - } + public StickyHeaderRecyclerView(Context context) { + super(context); + } - public StickyHeaderRecyclerView(Context context, AttributeSet attrs) { - super(context, attrs); - } + public StickyHeaderRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + } - public StickyHeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } + public StickyHeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - if (mSticky == null) { - updateStickyView(); - } - if (mSticky != null) { - final View headerView = getHeader(); - if (headerView != null && headerView.getHeight() == 0) { - headerView.layout(0, -headerView.getMeasuredHeight(), - headerView.getMeasuredWidth(), 0); - } - } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (sticky == null) { + updateStickyView(); + } + if (sticky != null) { + final View headerView = getHeader(); + if (headerView != null && headerView.getHeight() == 0) { + headerView.layout(0, -headerView.getMeasuredHeight(), headerView.getMeasuredWidth(), 0); + } } + } - @Override - protected void onMeasure(int widthSpec, int heightSpec) { - super.onMeasure(widthSpec, heightSpec); - if (mSticky != null) { - measureChild(getHeader(), widthSpec, heightSpec); - } + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + super.onMeasure(widthSpec, heightSpec); + if (sticky != null) { + measureChild(getHeader(), widthSpec, heightSpec); } + } - /** - * Call this method when the "sticky" view has changed, so this view can update its internal - * states as well. - */ - public void updateStickyView() { - final View header = getHeader(); - if (header != null) { - mSticky = header.findViewWithTag("sticky"); - } + /** + * Call this method when the "sticky" view has changed, so this view can update its internal + * states as well. + */ + public void updateStickyView() { + final View header = getHeader(); + if (header != null) { + sticky = header.findViewWithTag("sticky"); } + } - @Override - public void draw(Canvas canvas) { - super.draw(canvas); - if (mSticky != null) { - final View headerView = getHeader(); - final int saveCount = canvas.save(); - // The view to draw when sticking to the top - final View drawTarget = headerView != null ? headerView : mSticky; - // The offset to draw the view at when sticky - final int drawOffset = headerView != null ? mSticky.getTop() : 0; - // Position of the draw target, relative to the outside of the scrollView - final int drawTop = drawTarget.getTop(); - if (drawTop + drawOffset < mStatusBarInset || !drawTarget.isShown()) { - // RecyclerView does not translate the canvas, so we can simply draw at the top - mStickyRect.set(0, -drawOffset + mStatusBarInset, drawTarget.getWidth(), - drawTarget.getHeight() - drawOffset + mStatusBarInset); - canvas.translate(0, mStickyRect.top); - canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight()); - drawTarget.draw(canvas); - } else { - mStickyRect.setEmpty(); - } - canvas.restoreToCount(saveCount); - } + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (sticky != null) { + final View headerView = getHeader(); + final int saveCount = canvas.save(); + // The view to draw when sticking to the top + final View drawTarget = headerView != null ? headerView : sticky; + // The offset to draw the view at when sticky + final int drawOffset = headerView != null ? sticky.getTop() : 0; + // Position of the draw target, relative to the outside of the scrollView + final int drawTop = drawTarget.getTop(); + if (drawTop + drawOffset < statusBarInset || !drawTarget.isShown()) { + // RecyclerView does not translate the canvas, so we can simply draw at the top + stickyRect.set( + 0, + -drawOffset + statusBarInset, + drawTarget.getWidth(), + drawTarget.getHeight() - drawOffset + statusBarInset); + canvas.translate(0, stickyRect.top); + canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight()); + drawTarget.draw(canvas); + } else { + stickyRect.setEmpty(); + } + canvas.restoreToCount(saveCount); } + } - @Override - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public WindowInsets onApplyWindowInsets(WindowInsets insets) { - if (getFitsSystemWindows()) { - mStatusBarInset = insets.getSystemWindowInsetTop(); - insets.replaceSystemWindowInsets( - insets.getSystemWindowInsetLeft(), - 0, /* top */ - insets.getSystemWindowInsetRight(), - insets.getSystemWindowInsetBottom() - ); - } - return insets; + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + if (getFitsSystemWindows()) { + statusBarInset = insets.getSystemWindowInsetTop(); + insets.replaceSystemWindowInsets( + insets.getSystemWindowInsetLeft(), + 0, /* top */ + insets.getSystemWindowInsetRight(), + insets.getSystemWindowInsetBottom()); } + return insets; + } - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - if (mStickyRect.contains(ev.getX(), ev.getY())) { - ev.offsetLocation(-mStickyRect.left, -mStickyRect.top); - return getHeader().dispatchTouchEvent(ev); - } else { - return super.dispatchTouchEvent(ev); - } + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (stickyRect.contains(ev.getX(), ev.getY())) { + ev.offsetLocation(-stickyRect.left, -stickyRect.top); + return getHeader().dispatchTouchEvent(ev); + } else { + return super.dispatchTouchEvent(ev); } + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java index 6f42e84..bed736e 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java @@ -32,16 +32,13 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RectShape; +import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver; +import android.widget.FrameLayout; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.widget.FrameLayout; - -import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver; - import com.android.setupwizardlib.items.RecyclerItemAdapter.PatchedLayerDrawable; import com.android.setupwizardlib.test.R; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -50,111 +47,110 @@ import org.junit.runner.RunWith; @SmallTest public class RecyclerItemAdapterTest { - private Item[] mItems = new Item[5]; - private ItemGroup mItemGroup = new ItemGroup(); - - @Before - public void setUp() throws Exception { - for (int i = 0; i < 5; i++) { - Item item = new Item(); - item.setTitle("TestTitle" + i); - item.setId(i); - // Layout resource: 0 -> 1, 1 -> 11, 2 -> 21, 3 -> 1, 4 -> 11. - // (Resource IDs cannot be 0) - item.setLayoutResource((i % 3) * 10 + 1); - mItems[i] = item; - mItemGroup.addChild(item); - } - } - - @Test - public void testAdapter() { - RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); - assertEquals("Adapter should have 5 items", 5, adapter.getItemCount()); - assertEquals("Adapter should return the first item", mItems[0], adapter.getItem(0)); - assertEquals("ID should be same as position", 2, adapter.getItemId(2)); - - // ViewType is same as layout resource for RecyclerItemAdapter - assertEquals("Second item should have view type 21", 21, adapter.getItemViewType(2)); - } - - @Test - public void testGetRootItemHierarchy() { - RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); - ItemHierarchy root = adapter.getRootItemHierarchy(); - assertSame("Root item hierarchy should be mItemGroup", mItemGroup, root); - } - - @Test - public void testPatchedLayerDrawableNoPadding() { - ShapeDrawable child = new ShapeDrawable(new RectShape()); - child.setPadding(0, 0, 0, 0); - PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] { child }); - - Rect padding = new Rect(); - assertFalse("Patched layer drawable should not have padding", drawable.getPadding(padding)); - assertEquals(new Rect(0, 0, 0, 0), padding); - } - - @Test - public void testPatchedLayerDrawableWithPadding() { - ShapeDrawable child = new ShapeDrawable(new RectShape()); - child.setPadding(10, 10, 10, 10); - PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] { child }); - - Rect padding = new Rect(); - assertTrue("Patched layer drawable should have padding", drawable.getPadding(padding)); - assertEquals(new Rect(10, 10, 10, 10), padding); - } - - @Test - public void testAdapterNotifications() { - RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); - final AdapterDataObserver observer = mock(AdapterDataObserver.class); - adapter.registerAdapterDataObserver(observer); - - mItems[0].setTitle("Child 1"); - verify(observer).onItemRangeChanged(eq(0), eq(1), anyObject()); - - mItemGroup.removeChild(mItems[1]); - verify(observer).onItemRangeRemoved(eq(1), eq(1)); - - mItemGroup.addChild(mItems[1]); - verify(observer).onItemRangeInserted(eq(4), eq(1)); - } - - @Test - public void testCreateViewHolder() { - RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); - FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext()); - - final ItemViewHolder viewHolder = - adapter.onCreateViewHolder(parent, R.layout.test_list_item); - assertNotNull("Background should be set", viewHolder.itemView.getBackground()); - assertEquals("foobar", viewHolder.itemView.getTag()); - } - - @Test - public void testCreateViewHolderNoBackground() { - RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); - FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext()); - - final ItemViewHolder viewHolder = - adapter.onCreateViewHolder(parent, R.layout.test_list_item_no_background); - assertNull("Background should be null", viewHolder.itemView.getBackground()); - } - - @Test - public void testCreateViewHolderWithExistingBackground() { - RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); - FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext()); - - final ItemViewHolder viewHolder = - adapter.onCreateViewHolder(parent, R.layout.test_existing_background); - Drawable background = viewHolder.itemView.getBackground(); - assertTrue(background instanceof PatchedLayerDrawable); - - PatchedLayerDrawable layerDrawable = (PatchedLayerDrawable) background; - assertTrue(layerDrawable.getDrawable(0) instanceof GradientDrawable); + private Item[] mItems = new Item[5]; + private ItemGroup mItemGroup = new ItemGroup(); + + @Before + public void setUp() throws Exception { + for (int i = 0; i < 5; i++) { + Item item = new Item(); + item.setTitle("TestTitle" + i); + item.setId(i); + // Layout resource: 0 -> 1, 1 -> 11, 2 -> 21, 3 -> 1, 4 -> 11. + // (Resource IDs cannot be 0) + item.setLayoutResource((i % 3) * 10 + 1); + mItems[i] = item; + mItemGroup.addChild(item); } + } + + @Test + public void testAdapter() { + RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); + assertEquals("Adapter should have 5 items", 5, adapter.getItemCount()); + assertEquals("Adapter should return the first item", mItems[0], adapter.getItem(0)); + assertEquals("ID should be same as position", 2, adapter.getItemId(2)); + + // ViewType is same as layout resource for RecyclerItemAdapter + assertEquals("Second item should have view type 21", 21, adapter.getItemViewType(2)); + } + + @Test + public void testGetRootItemHierarchy() { + RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); + ItemHierarchy root = adapter.getRootItemHierarchy(); + assertSame("Root item hierarchy should be mItemGroup", mItemGroup, root); + } + + @Test + public void testPatchedLayerDrawableNoPadding() { + ShapeDrawable child = new ShapeDrawable(new RectShape()); + child.setPadding(0, 0, 0, 0); + PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] {child}); + + Rect padding = new Rect(); + assertFalse("Patched layer drawable should not have padding", drawable.getPadding(padding)); + assertEquals(new Rect(0, 0, 0, 0), padding); + } + + @Test + public void testPatchedLayerDrawableWithPadding() { + ShapeDrawable child = new ShapeDrawable(new RectShape()); + child.setPadding(10, 10, 10, 10); + PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] {child}); + + Rect padding = new Rect(); + assertTrue("Patched layer drawable should have padding", drawable.getPadding(padding)); + assertEquals(new Rect(10, 10, 10, 10), padding); + } + + @Test + public void testAdapterNotifications() { + RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); + final AdapterDataObserver observer = mock(AdapterDataObserver.class); + adapter.registerAdapterDataObserver(observer); + + mItems[0].setTitle("Child 1"); + verify(observer).onItemRangeChanged(eq(0), eq(1), anyObject()); + + mItemGroup.removeChild(mItems[1]); + verify(observer).onItemRangeRemoved(eq(1), eq(1)); + + mItemGroup.addChild(mItems[1]); + verify(observer).onItemRangeInserted(eq(4), eq(1)); + } + + @Test + public void testCreateViewHolder() { + RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); + FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext()); + + final ItemViewHolder viewHolder = adapter.onCreateViewHolder(parent, R.layout.test_list_item); + assertNotNull("Background should be set", viewHolder.itemView.getBackground()); + assertEquals("foobar", viewHolder.itemView.getTag()); + } + + @Test + public void testCreateViewHolderNoBackground() { + RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); + FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext()); + + final ItemViewHolder viewHolder = + adapter.onCreateViewHolder(parent, R.layout.test_list_item_no_background); + assertNull("Background should be null", viewHolder.itemView.getBackground()); + } + + @Test + public void testCreateViewHolderWithExistingBackground() { + RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); + FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext()); + + final ItemViewHolder viewHolder = + adapter.onCreateViewHolder(parent, R.layout.test_existing_background); + Drawable background = viewHolder.itemView.getBackground(); + assertTrue(background instanceof PatchedLayerDrawable); + + PatchedLayerDrawable layerDrawable = (PatchedLayerDrawable) background; + assertTrue(layerDrawable.getDrawable(0) instanceof GradientDrawable); + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java index ece4bf9..f295b91 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java @@ -30,17 +30,14 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.Adapter; +import android.view.View; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.View; - -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.Adapter; - import com.android.setupwizardlib.TemplateLayout; import com.android.setupwizardlib.test.R; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,120 +48,119 @@ import org.mockito.MockitoAnnotations; @SmallTest public class RecyclerMixinTest { - private Context mContext; - private TemplateLayout mTemplateLayout; + private Context mContext; + private TemplateLayout mTemplateLayout; - private RecyclerView mRecyclerView; + private RecyclerView mRecyclerView; - @Mock - private Adapter mAdapter; + @Mock private Adapter mAdapter; - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getTargetContext(); - mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template, - R.id.suw_layout_content)); + mContext = InstrumentationRegistry.getTargetContext(); + mTemplateLayout = + spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content)); - mRecyclerView = mock(RecyclerView.class, delegatesTo(new RecyclerView(mContext))); + mRecyclerView = mock(RecyclerView.class, delegatesTo(new RecyclerView(mContext))); - doReturn(true).when(mTemplateLayout).isLayoutDirectionResolved(); - } + doReturn(true).when(mTemplateLayout).isLayoutDirectionResolved(); + } - @Test - public void testGetRecyclerView() { - RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); - assertSame(mRecyclerView, mixin.getRecyclerView()); - } + @Test + public void testGetRecyclerView() { + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + assertSame(mRecyclerView, mixin.getRecyclerView()); + } - @Test - public void testGetAdapter() { - mRecyclerView.setAdapter(mAdapter); + @Test + public void testGetAdapter() { + mRecyclerView.setAdapter(mAdapter); - RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); - assertSame(mAdapter, mixin.getAdapter()); - } + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + assertSame(mAdapter, mixin.getAdapter()); + } - @Test - public void testSetAdapter() { - assertNull(mRecyclerView.getAdapter()); + @Test + public void testSetAdapter() { + assertNull(mRecyclerView.getAdapter()); - RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); - mixin.setAdapter(mAdapter); + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + mixin.setAdapter(mAdapter); - assertSame(mAdapter, mRecyclerView.getAdapter()); - } + assertSame(mAdapter, mRecyclerView.getAdapter()); + } - @Test - public void testDividerLegacyInset() { - RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); - mixin.setDividerInset(123); + @Test + public void testDividerLegacyInset() { + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + mixin.setDividerInset(123); - assertEquals(123, mixin.getDividerInset()); + assertEquals(123, mixin.getDividerInset()); - final Drawable divider = mixin.getDivider(); - InsetDrawable insetDrawable = (InsetDrawable) divider; - Rect rect = new Rect(); - insetDrawable.getPadding(rect); + final Drawable divider = mixin.getDivider(); + InsetDrawable insetDrawable = (InsetDrawable) divider; + Rect rect = new Rect(); + insetDrawable.getPadding(rect); - assertEquals(new Rect(123, 0, 0, 0), rect); - } + assertEquals(new Rect(123, 0, 0, 0), rect); + } - @Test - public void testDividerInsets() { - RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); - mixin.setDividerInsets(123, 456); + @Test + public void testDividerInsets() { + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + mixin.setDividerInsets(123, 456); - assertEquals(123, mixin.getDividerInsetStart()); - assertEquals(456, mixin.getDividerInsetEnd()); + assertEquals(123, mixin.getDividerInsetStart()); + assertEquals(456, mixin.getDividerInsetEnd()); - final Drawable divider = mixin.getDivider(); - InsetDrawable insetDrawable = (InsetDrawable) divider; - Rect rect = new Rect(); - insetDrawable.getPadding(rect); + final Drawable divider = mixin.getDivider(); + InsetDrawable insetDrawable = (InsetDrawable) divider; + Rect rect = new Rect(); + insetDrawable.getPadding(rect); - assertEquals(new Rect(123, 0, 456, 0), rect); - } + assertEquals(new Rect(123, 0, 456, 0), rect); + } - @Test - public void testDividerInsetLegacyRtl() { - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { - doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection(); + @Test + public void testDividerInsetLegacyRtl() { + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { + doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection(); - RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); - mixin.setDividerInset(123); + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + mixin.setDividerInset(123); - assertEquals(123, mixin.getDividerInset()); + assertEquals(123, mixin.getDividerInset()); - final Drawable divider = mixin.getDivider(); - InsetDrawable insetDrawable = (InsetDrawable) divider; - Rect rect = new Rect(); - insetDrawable.getPadding(rect); + final Drawable divider = mixin.getDivider(); + InsetDrawable insetDrawable = (InsetDrawable) divider; + Rect rect = new Rect(); + insetDrawable.getPadding(rect); - assertEquals(new Rect(0, 0, 123, 0), rect); - } - // else the test passes + assertEquals(new Rect(0, 0, 123, 0), rect); } + // else the test passes + } - @Test - public void testDividerInsetsRtl() { - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { - doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection(); + @Test + public void testDividerInsetsRtl() { + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { + doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection(); - RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); - mixin.setDividerInsets(123, 456); + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + mixin.setDividerInsets(123, 456); - assertEquals(123, mixin.getDividerInsetStart()); - assertEquals(456, mixin.getDividerInsetEnd()); + assertEquals(123, mixin.getDividerInsetStart()); + assertEquals(456, mixin.getDividerInsetEnd()); - final Drawable divider = mixin.getDivider(); - InsetDrawable insetDrawable = (InsetDrawable) divider; - Rect rect = new Rect(); - insetDrawable.getPadding(rect); + final Drawable divider = mixin.getDivider(); + InsetDrawable insetDrawable = (InsetDrawable) divider; + Rect rect = new Rect(); + insetDrawable.getPadding(rect); - assertEquals(new Rect(456, 0, 123, 0), rect); - } - // else the test passes + assertEquals(new Rect(456, 0, 123, 0), rect); } + // else the test passes + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java index 9cf33b9..a3a0cfe 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java @@ -28,17 +28,14 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.View; -import android.view.ViewGroup; - -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - import com.android.setupwizardlib.DividerItemDecoration; - import org.junit.Test; import org.junit.runner.RunWith; @@ -46,176 +43,179 @@ import org.junit.runner.RunWith; @SmallTest public class DividerItemDecorationTest { - @Test - public void testDivider() { - final DividerItemDecoration decoration = new DividerItemDecoration(); - Drawable divider = new ColorDrawable(); - decoration.setDivider(divider); - assertSame("Divider should be same as set", divider, decoration.getDivider()); - } + @Test + public void testDivider() { + final DividerItemDecoration decoration = new DividerItemDecoration(); + Drawable divider = new ColorDrawable(); + decoration.setDivider(divider); + assertSame("Divider should be same as set", divider, decoration.getDivider()); + } + + @Test + public void testDividerHeight() { + final DividerItemDecoration decoration = new DividerItemDecoration(); + decoration.setDividerHeight(123); + assertEquals("Divider height should be 123", 123, decoration.getDividerHeight()); + } + + @Test + public void testShouldDrawDividerBelowWithEitherCondition() { + // Set up the item decoration, with 1px red divider line + final DividerItemDecoration decoration = new DividerItemDecoration(); + Drawable divider = new ColorDrawable(Color.RED); + decoration.setDivider(divider); + decoration.setDividerHeight(1); + + Bitmap bitmap = drawDecoration(decoration, true, true); + + // Draw the expected result on a bitmap + Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444); + Canvas expectedCanvas = new Canvas(expectedBitmap); + Paint paint = new Paint(); + paint.setColor(Color.RED); + expectedCanvas.drawRect(0, 5, 20, 6, paint); + expectedCanvas.drawRect(0, 10, 20, 11, paint); + expectedCanvas.drawRect(0, 15, 20, 16, paint); + // Compare the two bitmaps + assertBitmapEquals(expectedBitmap, bitmap); + + bitmap.recycle(); + bitmap = drawDecoration(decoration, false, true); + // should still be the same. + assertBitmapEquals(expectedBitmap, bitmap); + + bitmap.recycle(); + bitmap = drawDecoration(decoration, true, false); + // last item should not have a divider below it now + paint.setColor(Color.TRANSPARENT); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + expectedCanvas.drawRect(0, 15, 20, 16, paint); + assertBitmapEquals(expectedBitmap, bitmap); + + bitmap.recycle(); + bitmap = drawDecoration(decoration, false, false); + // everything should be transparent now + expectedCanvas.drawRect(0, 5, 20, 6, paint); + expectedCanvas.drawRect(0, 10, 20, 11, paint); + assertBitmapEquals(expectedBitmap, bitmap); + } + + @Test + public void testShouldDrawDividerBelowWithBothCondition() { + // Set up the item decoration, with 1px green divider line + final DividerItemDecoration decoration = new DividerItemDecoration(); + Drawable divider = new ColorDrawable(Color.GREEN); + decoration.setDivider(divider); + decoration.setDividerHeight(1); + decoration.setDividerCondition(DividerItemDecoration.DIVIDER_CONDITION_BOTH); + + Bitmap bitmap = drawDecoration(decoration, true, true); + Paint paint = new Paint(); + paint.setColor(Color.GREEN); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); + Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444); + Canvas expectedCanvas = new Canvas(expectedBitmap); + expectedCanvas.drawRect(0, 5, 20, 6, paint); + expectedCanvas.drawRect(0, 10, 20, 11, paint); + expectedCanvas.drawRect(0, 15, 20, 16, paint); + // Should have all the dividers + assertBitmapEquals(expectedBitmap, bitmap); + + bitmap.recycle(); + bitmap = drawDecoration(decoration, false, true); + paint.setColor(Color.TRANSPARENT); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + expectedCanvas.drawRect(0, 5, 20, 6, paint); + expectedCanvas.drawRect(0, 10, 20, 11, paint); + assertBitmapEquals(expectedBitmap, bitmap); + + bitmap.recycle(); + bitmap = drawDecoration(decoration, true, false); + // nothing should be drawn now. + expectedCanvas.drawRect(0, 15, 20, 16, paint); + assertBitmapEquals(expectedBitmap, bitmap); + + bitmap.recycle(); + bitmap = drawDecoration(decoration, false, false); + assertBitmapEquals(expectedBitmap, bitmap); + } + + private Bitmap drawDecoration( + DividerItemDecoration decoration, + final boolean allowDividerAbove, + final boolean allowDividerBelow) { + // Set up the canvas to be drawn + Bitmap bitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444); + Canvas canvas = new Canvas(bitmap); + + final Context context = InstrumentationRegistry.getContext(); + // Set up recycler view with vertical linear layout manager + RecyclerView testRecyclerView = new RecyclerView(context); + testRecyclerView.setLayoutManager(new LinearLayoutManager(context)); + + // Set up adapter with 3 items, each 5px tall + testRecyclerView.setAdapter( + new RecyclerView.Adapter() { + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { + final View itemView = new View(context); + itemView.setMinimumWidth(20); + itemView.setMinimumHeight(5); + return ViewHolder.createInstance(itemView, allowDividerAbove, allowDividerBelow); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {} + + @Override + public int getItemCount() { + return 3; + } + }); - @Test - public void testDividerHeight() { - final DividerItemDecoration decoration = new DividerItemDecoration(); - decoration.setDividerHeight(123); - assertEquals("Divider height should be 123", 123, decoration.getDividerHeight()); + testRecyclerView.layout(0, 0, 20, 20); + decoration.onDraw(canvas, testRecyclerView, null); + return bitmap; + } + + private void assertBitmapEquals(Bitmap expected, Bitmap actual) { + assertEquals("Width should be the same", expected.getWidth(), actual.getWidth()); + assertEquals("Height should be the same", expected.getHeight(), actual.getHeight()); + for (int x = 0; x < expected.getWidth(); x++) { + for (int y = 0; y < expected.getHeight(); y++) { + assertEquals( + "Pixel at (" + x + ", " + y + ") should be the same", + expected.getPixel(x, y), + actual.getPixel(x, y)); + } } + } - @Test - public void testShouldDrawDividerBelowWithEitherCondition() { - // Set up the item decoration, with 1px red divider line - final DividerItemDecoration decoration = new DividerItemDecoration(); - Drawable divider = new ColorDrawable(Color.RED); - decoration.setDivider(divider); - decoration.setDividerHeight(1); - - Bitmap bitmap = drawDecoration(decoration, true, true); - - // Draw the expected result on a bitmap - Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444); - Canvas expectedCanvas = new Canvas(expectedBitmap); - Paint paint = new Paint(); - paint.setColor(Color.RED); - expectedCanvas.drawRect(0, 5, 20, 6, paint); - expectedCanvas.drawRect(0, 10, 20, 11, paint); - expectedCanvas.drawRect(0, 15, 20, 16, paint); - // Compare the two bitmaps - assertBitmapEquals(expectedBitmap, bitmap); - - bitmap.recycle(); - bitmap = drawDecoration(decoration, false, true); - // should still be the same. - assertBitmapEquals(expectedBitmap, bitmap); - - bitmap.recycle(); - bitmap = drawDecoration(decoration, true, false); - // last item should not have a divider below it now - paint.setColor(Color.TRANSPARENT); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - expectedCanvas.drawRect(0, 15, 20, 16, paint); - assertBitmapEquals(expectedBitmap, bitmap); - - bitmap.recycle(); - bitmap = drawDecoration(decoration, false, false); - // everything should be transparent now - expectedCanvas.drawRect(0, 5, 20, 6, paint); - expectedCanvas.drawRect(0, 10, 20, 11, paint); - assertBitmapEquals(expectedBitmap, bitmap); + private static class ViewHolder extends RecyclerView.ViewHolder + implements DividerItemDecoration.DividedViewHolder { - } + private boolean mAllowDividerAbove; + private boolean mAllowDividerBelow; - @Test - public void testShouldDrawDividerBelowWithBothCondition() { - // Set up the item decoration, with 1px green divider line - final DividerItemDecoration decoration = new DividerItemDecoration(); - Drawable divider = new ColorDrawable(Color.GREEN); - decoration.setDivider(divider); - decoration.setDividerHeight(1); - decoration.setDividerCondition(DividerItemDecoration.DIVIDER_CONDITION_BOTH); - - Bitmap bitmap = drawDecoration(decoration, true, true); - Paint paint = new Paint(); - paint.setColor(Color.GREEN); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); - Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444); - Canvas expectedCanvas = new Canvas(expectedBitmap); - expectedCanvas.drawRect(0, 5, 20, 6, paint); - expectedCanvas.drawRect(0, 10, 20, 11, paint); - expectedCanvas.drawRect(0, 15, 20, 16, paint); - // Should have all the dividers - assertBitmapEquals(expectedBitmap, bitmap); - - bitmap.recycle(); - bitmap = drawDecoration(decoration, false, true); - paint.setColor(Color.TRANSPARENT); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - expectedCanvas.drawRect(0, 5, 20, 6, paint); - expectedCanvas.drawRect(0, 10, 20, 11, paint); - assertBitmapEquals(expectedBitmap, bitmap); - - bitmap.recycle(); - bitmap = drawDecoration(decoration, true, false); - // nothing should be drawn now. - expectedCanvas.drawRect(0, 15, 20, 16, paint); - assertBitmapEquals(expectedBitmap, bitmap); - - bitmap.recycle(); - bitmap = drawDecoration(decoration, false, false); - assertBitmapEquals(expectedBitmap, bitmap); + public static ViewHolder createInstance( + View itemView, boolean allowDividerAbove, boolean allowDividerBelow) { + return new ViewHolder(itemView, allowDividerAbove, allowDividerBelow); } - private Bitmap drawDecoration(DividerItemDecoration decoration, final boolean allowDividerAbove, - final boolean allowDividerBelow) { - // Set up the canvas to be drawn - Bitmap bitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444); - Canvas canvas = new Canvas(bitmap); - - final Context context = InstrumentationRegistry.getContext(); - // Set up recycler view with vertical linear layout manager - RecyclerView testRecyclerView = new RecyclerView(context); - testRecyclerView.setLayoutManager(new LinearLayoutManager(context)); - - // Set up adapter with 3 items, each 5px tall - testRecyclerView.setAdapter(new RecyclerView.Adapter() { - @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { - final View itemView = new View(context); - itemView.setMinimumWidth(20); - itemView.setMinimumHeight(5); - return ViewHolder.createInstance(itemView, allowDividerAbove, allowDividerBelow); - } - - @Override - public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { - } - - @Override - public int getItemCount() { - return 3; - } - }); - - testRecyclerView.layout(0, 0, 20, 20); - decoration.onDraw(canvas, testRecyclerView, null); - return bitmap; + private ViewHolder(View itemView, boolean allowDividerAbove, boolean allowDividerBelow) { + super(itemView); + mAllowDividerAbove = allowDividerAbove; + mAllowDividerBelow = allowDividerBelow; } - private void assertBitmapEquals(Bitmap expected, Bitmap actual) { - assertEquals("Width should be the same", expected.getWidth(), actual.getWidth()); - assertEquals("Height should be the same", expected.getHeight(), actual.getHeight()); - for (int x = 0; x < expected.getWidth(); x++) { - for (int y = 0; y < expected.getHeight(); y++) { - assertEquals("Pixel at (" + x + ", " + y + ") should be the same", - expected.getPixel(x, y), actual.getPixel(x, y)); - } - } + @Override + public boolean isDividerAllowedAbove() { + return mAllowDividerAbove; } - private static class ViewHolder extends RecyclerView.ViewHolder - implements DividerItemDecoration.DividedViewHolder { - - private boolean mAllowDividerAbove; - private boolean mAllowDividerBelow; - - public static ViewHolder createInstance(View itemView, boolean allowDividerAbove, - boolean allowDividerBelow) { - return new ViewHolder(itemView, allowDividerAbove, allowDividerBelow); - } - - private ViewHolder(View itemView, boolean allowDividerAbove, boolean allowDividerBelow) { - super(itemView); - mAllowDividerAbove = allowDividerAbove; - mAllowDividerBelow = allowDividerBelow; - } - - @Override - public boolean isDividerAllowedAbove() { - return mAllowDividerAbove; - } - - @Override - public boolean isDividerAllowedBelow() { - return mAllowDividerBelow; - } + @Override + public boolean isDividerAllowedBelow() { + return mAllowDividerBelow; } + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java index 4d2876d..d55ba23 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java @@ -24,18 +24,15 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.recyclerview.widget.RecyclerView; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; - +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import com.android.setupwizardlib.GlifPreferenceLayout; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,62 +41,63 @@ import org.junit.runner.RunWith; @SmallTest public class GlifPreferenceLayoutTest { - private Context mContext; - - @Before - public void setUp() throws Exception { - mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(), - R.style.SuwThemeGlif_Light); - } - - @Test - public void testDefaultTemplate() { - GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); - assertPreferenceTemplateInflated(layout); - } - - @Test - public void testGetRecyclerView() { - GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); - assertPreferenceTemplateInflated(layout); - assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = + new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.SuwThemeGlif_Light); + } + + @Test + public void testDefaultTemplate() { + GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); + assertPreferenceTemplateInflated(layout); + } + + @Test + public void testGetRecyclerView() { + GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); + assertPreferenceTemplateInflated(layout); + assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); + } + + @Test + public void testOnCreateRecyclerView() { + GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); + assertPreferenceTemplateInflated(layout); + final RecyclerView recyclerView = + layout.onCreateRecyclerView( + LayoutInflater.from(mContext), layout, null /* savedInstanceState */); + assertNotNull("RecyclerView created should not be null", recyclerView); + } + + @Test + public void testDividerInset() { + GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); } + assertPreferenceTemplateInflated(layout); - @Test - public void testOnCreateRecyclerView() { - GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); - assertPreferenceTemplateInflated(layout); - final RecyclerView recyclerView = layout.onCreateRecyclerView(LayoutInflater.from(mContext), - layout, null /* savedInstanceState */); - assertNotNull("RecyclerView created should not be null", recyclerView); - } - - @Test - public void testDividerInset() { - GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); - } - assertPreferenceTemplateInflated(layout); + layout.addView( + layout.onCreateRecyclerView( + LayoutInflater.from(mContext), layout, null /* savedInstanceState */)); - layout.addView(layout.onCreateRecyclerView(LayoutInflater.from(mContext), layout, - null /* savedInstanceState */)); + layout.setDividerInset(10); + assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); - layout.setDividerInset(10); - assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); + final Drawable divider = layout.getDivider(); + assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + } - final Drawable divider = layout.getDivider(); - assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); - } + private void assertPreferenceTemplateInflated(GlifPreferenceLayout layout) { + View contentContainer = layout.findViewById(R.id.suw_layout_content); + assertTrue( + "@id/suw_layout_content should be a ViewGroup", contentContainer instanceof ViewGroup); - private void assertPreferenceTemplateInflated(GlifPreferenceLayout layout) { - View contentContainer = layout.findViewById(R.id.suw_layout_content); - assertTrue("@id/suw_layout_content should be a ViewGroup", - contentContainer instanceof ViewGroup); - - assertNotNull("Header text view should not be null", - layout.findManagedViewById(R.id.suw_layout_title)); - assertNotNull("Icon view should not be null", - layout.findManagedViewById(R.id.suw_layout_icon)); - } + assertNotNull( + "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title)); + assertNotNull("Icon view should not be null", layout.findManagedViewById(R.id.suw_layout_icon)); + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java index a68faf0..5db7db5 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java @@ -26,20 +26,17 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.Adapter; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.Adapter; - +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import com.android.setupwizardlib.GlifRecyclerLayout; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,130 +45,127 @@ import org.junit.runner.RunWith; @SmallTest public class GlifRecyclerLayoutTest { - private Context mContext; - - @Before - public void setUp() throws Exception { - mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(), - R.style.SuwThemeGlif_Light); - } - - @Test - public void testDefaultTemplate() { - GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); - } - - @Test - public void testInflateFromXml() { - LayoutInflater inflater = LayoutInflater.from(mContext); - GlifRecyclerLayout layout = (GlifRecyclerLayout) - inflater.inflate(R.layout.test_glif_recycler_layout, null); - assertRecyclerTemplateInflated(layout); - } - - @Test - public void testGetRecyclerView() { - GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); - assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); - } - - @Test - public void testAdapter() { - GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); - - final RecyclerView.Adapter adapter = createTestAdapter(1); - layout.setAdapter(adapter); - - final RecyclerView.Adapter gotAdapter = layout.getAdapter(); - // Note: The wrapped adapter should be returned, not the HeaderAdapter. - assertSame("Adapter got from GlifRecyclerLayout should be same as set", - adapter, gotAdapter); + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = + new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.SuwThemeGlif_Light); + } + + @Test + public void testDefaultTemplate() { + GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + } + + @Test + public void testInflateFromXml() { + LayoutInflater inflater = LayoutInflater.from(mContext); + GlifRecyclerLayout layout = + (GlifRecyclerLayout) inflater.inflate(R.layout.test_glif_recycler_layout, null); + assertRecyclerTemplateInflated(layout); + } + + @Test + public void testGetRecyclerView() { + GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); + } + + @Test + public void testAdapter() { + GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + + final RecyclerView.Adapter adapter = createTestAdapter(1); + layout.setAdapter(adapter); + + final RecyclerView.Adapter gotAdapter = layout.getAdapter(); + // Note: The wrapped adapter should be returned, not the HeaderAdapter. + assertSame("Adapter got from GlifRecyclerLayout should be same as set", adapter, gotAdapter); + } + + @Test + public void testLayout() { + GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + + layout.setAdapter(createTestAdapter(3)); + + layout.measure( + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); + layout.layout(0, 0, 500, 500); + // Test that the layout code doesn't crash. + } + + @Test + public void testDividerInsetLegacy() { + GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); } + assertRecyclerTemplateInflated(layout); - @Test - public void testLayout() { - GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); + layout.setDividerInset(10); + assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); - layout.setAdapter(createTestAdapter(3)); + final Drawable divider = layout.getDivider(); + assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + } - layout.measure( - MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); - layout.layout(0, 0, 500, 500); - // Test that the layout code doesn't crash. + @Test + public void testDividerInsets() { + GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); } - - @Test - public void testDividerInsetLegacy() { - GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); - } - assertRecyclerTemplateInflated(layout); - - layout.setDividerInset(10); - assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); - - final Drawable divider = layout.getDivider(); - assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); - } - - @Test - public void testDividerInsets() { - GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); - } - assertRecyclerTemplateInflated(layout); - - layout.setDividerInsets(10, 15); - assertEquals("Divider inset start should be 10", 10, layout.getDividerInsetStart()); - assertEquals("Divider inset end should be 15", 15, layout.getDividerInsetEnd()); - - final Drawable divider = layout.getDivider(); - assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); - } - - @Test - public void testTemplateWithNoRecyclerView() { - try { - new GlifRecyclerLayout(mContext, R.layout.suw_glif_template); - fail("Creating GlifRecyclerLayout with no recycler view should throw exception"); - } catch (Exception e) { - // pass - } - } - - private void assertRecyclerTemplateInflated(GlifRecyclerLayout layout) { - View recyclerView = layout.findViewById(R.id.suw_recycler_view); - assertTrue("@id/suw_recycler_view should be a RecyclerView", - recyclerView instanceof RecyclerView); - - assertNotNull("Header text view should not be null", - layout.findManagedViewById(R.id.suw_layout_title)); - assertNotNull("Icon view should not be null", - layout.findManagedViewById(R.id.suw_layout_icon)); - } - - private Adapter createTestAdapter(final int itemCount) { - return new RecyclerView.Adapter() { - @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) { - return new RecyclerView.ViewHolder(new View(parent.getContext())) {}; - } - - @Override - public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { - } - - @Override - public int getItemCount() { - return itemCount; - } - }; + assertRecyclerTemplateInflated(layout); + + layout.setDividerInsets(10, 15); + assertEquals("Divider inset start should be 10", 10, layout.getDividerInsetStart()); + assertEquals("Divider inset end should be 15", 15, layout.getDividerInsetEnd()); + + final Drawable divider = layout.getDivider(); + assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + } + + @Test + public void testTemplateWithNoRecyclerView() { + try { + new GlifRecyclerLayout(mContext, R.layout.suw_glif_template); + fail("Creating GlifRecyclerLayout with no recycler view should throw exception"); + } catch (Exception e) { + // pass } + } + + private void assertRecyclerTemplateInflated(GlifRecyclerLayout layout) { + View recyclerView = layout.findViewById(R.id.suw_recycler_view); + assertTrue( + "@id/suw_recycler_view should be a RecyclerView", recyclerView instanceof RecyclerView); + + assertNotNull( + "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title)); + assertNotNull("Icon view should not be null", layout.findManagedViewById(R.id.suw_layout_icon)); + } + + private Adapter createTestAdapter(final int itemCount) { + return new RecyclerView.Adapter() { + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) { + return new RecyclerView.ViewHolder(new View(parent.getContext())) {}; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {} + + @Override + public int getItemCount() { + return itemCount; + } + }; + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java index 9af68a7..3d0f9da 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java @@ -19,160 +19,143 @@ package com.android.setupwizardlib.test; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; +import androidx.recyclerview.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.View; -import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; - import com.android.setupwizardlib.view.HeaderRecyclerView.HeaderAdapter; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -/** - * Test for {@link com.android.setupwizardlib.view.HeaderRecyclerView} - */ +/** Test for {@link com.android.setupwizardlib.view.HeaderRecyclerView} */ @RunWith(AndroidJUnit4.class) @SmallTest public class HeaderRecyclerViewTest { - private TestAdapter mWrappedAdapter; - private HeaderAdapter mHeaderAdapter; - - @Mock - private RecyclerView.AdapterDataObserver mObserver; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mWrappedAdapter = new TestAdapter(); - - mHeaderAdapter = new HeaderAdapter(mWrappedAdapter); - mHeaderAdapter.registerAdapterDataObserver(mObserver); - } - - /** - * Test that notifyDataSetChanged gets propagated by HeaderRecyclerView's adapter. - */ - @Test - public void testNotifyChanged() { - mWrappedAdapter.notifyDataSetChanged(); - - verify(mObserver).onChanged(); + private TestAdapter mWrappedAdapter; + private HeaderAdapter mHeaderAdapter; + + @Mock private RecyclerView.AdapterDataObserver mObserver; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mWrappedAdapter = new TestAdapter(); + + mHeaderAdapter = new HeaderAdapter(mWrappedAdapter); + mHeaderAdapter.registerAdapterDataObserver(mObserver); + } + + /** Test that notifyDataSetChanged gets propagated by HeaderRecyclerView's adapter. */ + @Test + public void testNotifyChanged() { + mWrappedAdapter.notifyDataSetChanged(); + + verify(mObserver).onChanged(); + } + + /** Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter. */ + @Test + public void testNotifyItemChangedNoHeader() { + mWrappedAdapter.notifyItemChanged(12); + + verify(mObserver).onItemRangeChanged(eq(12), eq(1), eq(null)); + } + + /** + * Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter and adds 1 to the + * position for the extra header items. + */ + @Test + public void testNotifyItemChangedWithHeader() { + mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); + mWrappedAdapter.notifyItemChanged(12); + + verify(mObserver).onItemRangeChanged(eq(13), eq(1), eq(null)); + } + + /** Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter. */ + @Test + public void testNotifyItemInsertedNoHeader() { + mWrappedAdapter.notifyItemInserted(12); + + verify(mObserver).onItemRangeInserted(eq(12), eq(1)); + } + + /** + * Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter and adds 1 to the + * position for the extra header item. + */ + @Test + public void testNotifyItemInsertedWithHeader() { + mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); + mWrappedAdapter.notifyItemInserted(12); + + verify(mObserver).onItemRangeInserted(eq(13), eq(1)); + } + + /** Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter. */ + @Test + public void testNotifyItemRemovedNoHeader() { + mWrappedAdapter.notifyItemRemoved(12); + + verify(mObserver).onItemRangeRemoved(eq(12), eq(1)); + } + + /** + * Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter and adds 1 to the + * position for the extra header item. + */ + @Test + public void testNotifyItemRemovedWithHeader() { + mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); + mWrappedAdapter.notifyItemRemoved(12); + + verify(mObserver).onItemRangeRemoved(eq(13), eq(1)); + } + + /** Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter. */ + @Test + public void testNotifyItemMovedNoHeader() { + mWrappedAdapter.notifyItemMoved(12, 18); + + verify(mObserver).onItemRangeMoved(eq(12), eq(18), eq(1)); + } + + /** + * Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter and adds 1 to the + * position for the extra header item. + */ + @Test + public void testNotifyItemMovedWithHeader() { + mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); + mWrappedAdapter.notifyItemMoved(12, 18); + + verify(mObserver).onItemRangeMoved(eq(13), eq(19), eq(1)); + } + + /** + * Test adapter to be wrapped inside {@link HeaderAdapter} to to send item change notifications. + */ + public static class TestAdapter extends RecyclerView.Adapter { + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { + return null; } - /** - * Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter. - */ - @Test - public void testNotifyItemChangedNoHeader() { - mWrappedAdapter.notifyItemChanged(12); - - verify(mObserver).onItemRangeChanged(eq(12), eq(1), eq(null)); - } - - /** - * Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter and adds 1 to the - * position for the extra header items. - */ - @Test - public void testNotifyItemChangedWithHeader() { - mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); - mWrappedAdapter.notifyItemChanged(12); - - verify(mObserver).onItemRangeChanged(eq(13), eq(1), eq(null)); - } - - /** - * Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter. - */ - @Test - public void testNotifyItemInsertedNoHeader() { - mWrappedAdapter.notifyItemInserted(12); - - verify(mObserver).onItemRangeInserted(eq(12), eq(1)); - } - - /** - * Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter and adds 1 to - * the position for the extra header item. - */ - @Test - public void testNotifyItemInsertedWithHeader() { - mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); - mWrappedAdapter.notifyItemInserted(12); - - verify(mObserver).onItemRangeInserted(eq(13), eq(1)); - } - - /** - * Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter. - */ - @Test - public void testNotifyItemRemovedNoHeader() { - mWrappedAdapter.notifyItemRemoved(12); - - verify(mObserver).onItemRangeRemoved(eq(12), eq(1)); - } - - /** - * Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter and adds 1 to - * the position for the extra header item. - */ - @Test - public void testNotifyItemRemovedWithHeader() { - mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); - mWrappedAdapter.notifyItemRemoved(12); - - verify(mObserver).onItemRangeRemoved(eq(13), eq(1)); - } - - /** - * Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter. - */ - @Test - public void testNotifyItemMovedNoHeader() { - mWrappedAdapter.notifyItemMoved(12, 18); - - verify(mObserver).onItemRangeMoved(eq(12), eq(18), eq(1)); - } - - /** - * Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter and adds 1 to - * the position for the extra header item. - */ - @Test - public void testNotifyItemMovedWithHeader() { - mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext())); - mWrappedAdapter.notifyItemMoved(12, 18); - - verify(mObserver).onItemRangeMoved(eq(13), eq(19), eq(1)); - } - - /** - * Test adapter to be wrapped inside {@link HeaderAdapter} to to send item change notifications. - */ - public static class TestAdapter extends RecyclerView.Adapter { - - @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { - return null; - } - - @Override - public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { - } + @Override + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {} - @Override - public int getItemCount() { - return 0; - } + @Override + public int getItemCount() { + return 0; } + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java index 316793f..39929dc 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java @@ -24,18 +24,15 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.recyclerview.widget.RecyclerView; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; - +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import com.android.setupwizardlib.SetupWizardPreferenceLayout; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,62 +41,65 @@ import org.junit.runner.RunWith; @SmallTest public class SetupWizardPreferenceLayoutTest { - private Context mContext; - - @Before - public void setUp() throws Exception { - mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(), - R.style.SuwThemeMaterial_Light); - } - - @Test - public void testDefaultTemplate() { - SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); - assertPreferenceTemplateInflated(layout); - } - - @Test - public void testGetRecyclerView() { - SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); - assertPreferenceTemplateInflated(layout); - assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = + new ContextThemeWrapper( + InstrumentationRegistry.getContext(), R.style.SuwThemeMaterial_Light); + } + + @Test + public void testDefaultTemplate() { + SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); + assertPreferenceTemplateInflated(layout); + } + + @Test + public void testGetRecyclerView() { + SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); + assertPreferenceTemplateInflated(layout); + assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); + } + + @Test + public void testOnCreateRecyclerView() { + SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); + assertPreferenceTemplateInflated(layout); + final RecyclerView recyclerView = + layout.onCreateRecyclerView( + LayoutInflater.from(mContext), layout, null /* savedInstanceState */); + assertNotNull("RecyclerView created should not be null", recyclerView); + } + + @Test + public void testDividerInset() { + SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); } + assertPreferenceTemplateInflated(layout); - @Test - public void testOnCreateRecyclerView() { - SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); - assertPreferenceTemplateInflated(layout); - final RecyclerView recyclerView = layout.onCreateRecyclerView(LayoutInflater.from(mContext), - layout, null /* savedInstanceState */); - assertNotNull("RecyclerView created should not be null", recyclerView); - } - - @Test - public void testDividerInset() { - SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); - } - assertPreferenceTemplateInflated(layout); + layout.addView( + layout.onCreateRecyclerView( + LayoutInflater.from(mContext), layout, null /* savedInstanceState */)); - layout.addView(layout.onCreateRecyclerView(LayoutInflater.from(mContext), layout, - null /* savedInstanceState */)); + layout.setDividerInset(10); + assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); - layout.setDividerInset(10); - assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); + final Drawable divider = layout.getDivider(); + assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + } - final Drawable divider = layout.getDivider(); - assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); - } + private void assertPreferenceTemplateInflated(SetupWizardPreferenceLayout layout) { + View contentContainer = layout.findViewById(R.id.suw_layout_content); + assertTrue( + "@id/suw_layout_content should be a ViewGroup", contentContainer instanceof ViewGroup); - private void assertPreferenceTemplateInflated(SetupWizardPreferenceLayout layout) { - View contentContainer = layout.findViewById(R.id.suw_layout_content); - assertTrue("@id/suw_layout_content should be a ViewGroup", - contentContainer instanceof ViewGroup); - - assertNotNull("Header text view should not be null", - layout.findManagedViewById(R.id.suw_layout_title)); - assertNotNull("Decoration view should not be null", - layout.findManagedViewById(R.id.suw_layout_decor)); - } + assertNotNull( + "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title)); + assertNotNull( + "Decoration view should not be null", layout.findManagedViewById(R.id.suw_layout_decor)); + } } diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java index bbe773b..46a665d 100644 --- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java @@ -26,21 +26,18 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.Adapter; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.Adapter; -import androidx.recyclerview.widget.RecyclerView.ViewHolder; - +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import com.android.setupwizardlib.SetupWizardRecyclerLayout; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,133 +46,129 @@ import org.junit.runner.RunWith; @SmallTest public class SetupWizardRecyclerLayoutTest { - private Context mContext; - - @Before - public void setUp() throws Exception { - mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(), - R.style.SuwThemeMaterial_Light); - } - - @Test - public void testDefaultTemplate() { - SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); - } - - @Test - public void testInflateFromXml() { - LayoutInflater inflater = LayoutInflater.from(mContext); - SetupWizardRecyclerLayout layout = (SetupWizardRecyclerLayout) - inflater.inflate(R.layout.test_recycler_layout, null); - assertRecyclerTemplateInflated(layout); + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = + new ContextThemeWrapper( + InstrumentationRegistry.getContext(), R.style.SuwThemeMaterial_Light); + } + + @Test + public void testDefaultTemplate() { + SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + } + + @Test + public void testInflateFromXml() { + LayoutInflater inflater = LayoutInflater.from(mContext); + SetupWizardRecyclerLayout layout = + (SetupWizardRecyclerLayout) inflater.inflate(R.layout.test_recycler_layout, null); + assertRecyclerTemplateInflated(layout); + } + + @Test + public void testGetRecyclerView() { + SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); + } + + @Test + public void testAdapter() { + SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + + final Adapter adapter = createTestAdapter(1); + layout.setAdapter(adapter); + + final Adapter gotAdapter = layout.getAdapter(); + // Note: The wrapped adapter should be returned, not the HeaderAdapter. + assertSame("Adapter got from SetupWizardLayout should be same as set", adapter, gotAdapter); + } + + @Test + public void testLayout() { + SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); + assertRecyclerTemplateInflated(layout); + + layout.setAdapter(createTestAdapter(3)); + + layout.measure( + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); + layout.layout(0, 0, 500, 500); + // Test that the layout code doesn't crash. + } + + @Test + public void testDividerInsetLegacy() { + SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); } + assertRecyclerTemplateInflated(layout); - @Test - public void testGetRecyclerView() { - SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); - assertNotNull("getRecyclerView should not be null", layout.getRecyclerView()); - } - - @Test - public void testAdapter() { - SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); - - final Adapter adapter = createTestAdapter(1); - layout.setAdapter(adapter); - - final Adapter gotAdapter = layout.getAdapter(); - // Note: The wrapped adapter should be returned, not the HeaderAdapter. - assertSame("Adapter got from SetupWizardLayout should be same as set", - adapter, gotAdapter); - } - - @Test - public void testLayout() { - SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); - assertRecyclerTemplateInflated(layout); - - layout.setAdapter(createTestAdapter(3)); - - layout.measure( - MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY)); - layout.layout(0, 0, 500, 500); - // Test that the layout code doesn't crash. - } - - @Test - public void testDividerInsetLegacy() { - SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); - } - assertRecyclerTemplateInflated(layout); + layout.setDividerInset(10); + assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); - layout.setDividerInset(10); - assertEquals("Divider inset should be 10", 10, layout.getDividerInset()); + final Drawable divider = layout.getDivider(); + assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + } - final Drawable divider = layout.getDivider(); - assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + @Test + public void testDividerInsets() { + SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); } - - @Test - public void testDividerInsets() { - SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); - } - assertRecyclerTemplateInflated(layout); - - layout.setDividerInsets(10, 15); - assertEquals("Divider inset start should be 10", 10, layout.getDividerInsetStart()); - assertEquals("Divider inset end should be 15", 15, layout.getDividerInsetEnd()); - - final Drawable divider = layout.getDivider(); - assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); - } - - @Test - public void testTemplateWithNoRecyclerView() { - try { - new SetupWizardRecyclerLayout( - mContext, - R.layout.suw_glif_template, - R.id.suw_recycler_view); - fail("Creating SetupWizardRecyclerLayout with no recycler view should throw exception"); - } catch (Exception e) { - // pass - } - } - - private void assertRecyclerTemplateInflated(SetupWizardRecyclerLayout layout) { - View recyclerView = layout.findViewById(R.id.suw_recycler_view); - assertTrue("@id/suw_recycler_view should be a RecyclerView", - recyclerView instanceof RecyclerView); - - assertNotNull("Header text view should not be null", - layout.findManagedViewById(R.id.suw_layout_title)); - assertNotNull("Decoration view should not be null", - layout.findManagedViewById(R.id.suw_layout_decor)); - } - - private Adapter createTestAdapter(final int itemCount) { - return new Adapter() { - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int position) { - return new ViewHolder(new View(parent.getContext())) {}; - } - - @Override - public void onBindViewHolder(ViewHolder viewHolder, int position) { - } - - @Override - public int getItemCount() { - return itemCount; - } - }; + assertRecyclerTemplateInflated(layout); + + layout.setDividerInsets(10, 15); + assertEquals("Divider inset start should be 10", 10, layout.getDividerInsetStart()); + assertEquals("Divider inset end should be 15", 15, layout.getDividerInsetEnd()); + + final Drawable divider = layout.getDivider(); + assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + } + + @Test + public void testTemplateWithNoRecyclerView() { + try { + new SetupWizardRecyclerLayout(mContext, R.layout.suw_glif_template, R.id.suw_recycler_view); + fail("Creating SetupWizardRecyclerLayout with no recycler view should throw exception"); + } catch (Exception e) { + // pass } + } + + private void assertRecyclerTemplateInflated(SetupWizardRecyclerLayout layout) { + View recyclerView = layout.findViewById(R.id.suw_recycler_view); + assertTrue( + "@id/suw_recycler_view should be a RecyclerView", recyclerView instanceof RecyclerView); + + assertNotNull( + "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title)); + assertNotNull( + "Decoration view should not be null", layout.findManagedViewById(R.id.suw_layout_decor)); + } + + private Adapter createTestAdapter(final int itemCount) { + return new Adapter() { + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int position) { + return new ViewHolder(new View(parent.getContext())) {}; + } + + @Override + public void onBindViewHolder(ViewHolder viewHolder, int position) {} + + @Override + public int getItemCount() { + return itemCount; + } + }; + } } diff --git a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java index 6fa4c54..dd262da 100644 --- a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java +++ b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java @@ -26,9 +26,7 @@ import static org.robolectric.RuntimeEnvironment.application; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.OnScrollListener; - import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,52 +35,51 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; -@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK }) +@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK}) @RunWith(SuwLibRobolectricTestRunner.class) public class RecyclerViewScrollHandlingDelegateTest { - @Mock - private RequireScrollMixin mRequireScrollMixin; - - private RecyclerView mRecyclerView; - private RecyclerViewScrollHandlingDelegate mDelegate; - private ArgumentCaptor mListenerCaptor; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mRecyclerView = spy(new RecyclerView(application)); - doReturn(20).when(mRecyclerView).computeVerticalScrollRange(); - doReturn(0).when(mRecyclerView).computeVerticalScrollExtent(); - doReturn(0).when(mRecyclerView).computeVerticalScrollOffset(); - mListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class); - doNothing().when(mRecyclerView).addOnScrollListener(mListenerCaptor.capture()); - - mDelegate = new RecyclerViewScrollHandlingDelegate(mRequireScrollMixin, mRecyclerView); - mRecyclerView.layout(0, 0, 50, 50); - } - - @Test - public void testRequireScroll() { - mDelegate.startListening(); - verify(mRequireScrollMixin).notifyScrollabilityChange(true); - } - - @Test - public void testScrolledToBottom() { - mDelegate.startListening(); - verify(mRequireScrollMixin).notifyScrollabilityChange(true); - - doReturn(20).when(mRecyclerView).computeVerticalScrollOffset(); - mListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 20); - - verify(mRequireScrollMixin).notifyScrollabilityChange(false); - } - - @Test - public void testClickScrollButton() { - mDelegate.pageScrollDown(); - verify(mRecyclerView).smoothScrollBy(anyInt(), eq(50)); - } + @Mock private RequireScrollMixin mRequireScrollMixin; + + private RecyclerView mRecyclerView; + private RecyclerViewScrollHandlingDelegate mDelegate; + private ArgumentCaptor mListenerCaptor; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mRecyclerView = spy(new RecyclerView(application)); + doReturn(20).when(mRecyclerView).computeVerticalScrollRange(); + doReturn(0).when(mRecyclerView).computeVerticalScrollExtent(); + doReturn(0).when(mRecyclerView).computeVerticalScrollOffset(); + mListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class); + doNothing().when(mRecyclerView).addOnScrollListener(mListenerCaptor.capture()); + + mDelegate = new RecyclerViewScrollHandlingDelegate(mRequireScrollMixin, mRecyclerView); + mRecyclerView.layout(0, 0, 50, 50); + } + + @Test + public void testRequireScroll() { + mDelegate.startListening(); + verify(mRequireScrollMixin).notifyScrollabilityChange(true); + } + + @Test + public void testScrolledToBottom() { + mDelegate.startListening(); + verify(mRequireScrollMixin).notifyScrollabilityChange(true); + + doReturn(20).when(mRecyclerView).computeVerticalScrollOffset(); + mListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 20); + + verify(mRequireScrollMixin).notifyScrollabilityChange(false); + } + + @Test + public void testClickScrollButton() { + mDelegate.pageScrollDown(); + verify(mRecyclerView).smoothScrollBy(anyInt(), eq(50)); + } } -- cgit v1.2.3 From 93689222672ea2da9e3948785ab3c8d53d771c84 Mon Sep 17 00:00:00 2001 From: Setup Wizard Team Date: Wed, 10 Oct 2018 19:36:49 -0700 Subject: Import updated Android Setup Wizard Library 216634194 PiperOrigin-RevId: 216634194 Change-Id: I0a942bf9475129aebb77110e121b1b08af3cfe96 --- .../template/RecyclerViewScrollHandlingDelegateTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'library/recyclerview') diff --git a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java index dd262da..bcb51a8 100644 --- a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java +++ b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java @@ -26,17 +26,17 @@ import static org.robolectric.RuntimeEnvironment.application; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.OnScrollListener; -import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK}) -@RunWith(SuwLibRobolectricTestRunner.class) +@RunWith(RobolectricTestRunner.class) public class RecyclerViewScrollHandlingDelegateTest { @Mock private RequireScrollMixin mRequireScrollMixin; -- cgit v1.2.3