summaryrefslogtreecommitdiff
path: root/library/main/src
diff options
context:
space:
mode:
authorMaurice Lam <yukl@google.com>2017-03-17 17:43:12 -0700
committerMaurice Lam <yukl@google.com>2017-03-24 14:57:06 -0700
commit0ceb8d53e39ebb5bc103863787afb39ec5c41ad8 (patch)
tree372a98e23f814ddd8989fee29e1785a51d54ded9 /library/main/src
parente4b2bc02f76b9d43fe0629f66dfb7efe7b0b7132 (diff)
downloadsetupwizard-0ceb8d53e39ebb5bc103863787afb39ec5c41ad8.tar.gz
Add ability to require scroll to GlifLayout
Generalize RequireScrollHelper and turn it into a mixin to allow GlifLayout to use require scrolling. This is only applicable to GLIF with sticky footer, since inline content button requires scrolling by definition. Also added Robolectric test support for full-support, by moving full-support/test's contents into full-support/test/instrumentation and put the new Robolectric tests in full-support/test/robotest. Bug: 36387078 Test: ./gradlew connectedAndroidTest test Change-Id: Ib07ec0ddf07affa30c46e786f4e9be7853a243c4
Diffstat (limited to 'library/main/src')
-rw-r--r--library/main/src/com/android/setupwizardlib/GlifLayout.java10
-rw-r--r--library/main/src/com/android/setupwizardlib/GlifListLayout.java6
-rw-r--r--library/main/src/com/android/setupwizardlib/SetupWizardLayout.java27
-rw-r--r--library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java21
-rw-r--r--library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java87
-rw-r--r--library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java260
-rw-r--r--library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java80
-rw-r--r--library/main/src/com/android/setupwizardlib/util/AbstractRequireScrollHelper.java78
-rw-r--r--library/main/src/com/android/setupwizardlib/util/ListViewRequireScrollHelper.java81
-rw-r--r--library/main/src/com/android/setupwizardlib/util/RequireScrollHelper.java64
-rw-r--r--library/main/src/com/android/setupwizardlib/view/NavigationBar.java2
11 files changed, 465 insertions, 251 deletions
diff --git a/library/main/src/com/android/setupwizardlib/GlifLayout.java b/library/main/src/com/android/setupwizardlib/GlifLayout.java
index 667d699..f4d52a5 100644
--- a/library/main/src/com/android/setupwizardlib/GlifLayout.java
+++ b/library/main/src/com/android/setupwizardlib/GlifLayout.java
@@ -41,6 +41,8 @@ import com.android.setupwizardlib.template.ColoredHeaderMixin;
import com.android.setupwizardlib.template.HeaderMixin;
import com.android.setupwizardlib.template.IconMixin;
import com.android.setupwizardlib.template.ProgressBarMixin;
+import com.android.setupwizardlib.template.RequireScrollMixin;
+import com.android.setupwizardlib.template.ScrollViewScrollHandlingDelegate;
import com.android.setupwizardlib.view.StatusBarBackgroundLayout;
/**
@@ -106,6 +108,14 @@ public class GlifLayout extends TemplateLayout {
registerMixin(IconMixin.class, new IconMixin(this, attrs, defStyleAttr));
registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
registerMixin(ButtonFooterMixin.class, new ButtonFooterMixin(this));
+ final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
+ registerMixin(RequireScrollMixin.class, requireScrollMixin);
+
+ final ScrollView scrollView = getScrollView();
+ if (scrollView != null) {
+ requireScrollMixin.setScrollHandlingDelegate(
+ new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView));
+ }
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.SuwGlifLayout, defStyleAttr, 0);
diff --git a/library/main/src/com/android/setupwizardlib/GlifListLayout.java b/library/main/src/com/android/setupwizardlib/GlifListLayout.java
index 14c7bd7..c6443f9 100644
--- a/library/main/src/com/android/setupwizardlib/GlifListLayout.java
+++ b/library/main/src/com/android/setupwizardlib/GlifListLayout.java
@@ -28,6 +28,8 @@ import android.widget.ListAdapter;
import android.widget.ListView;
import com.android.setupwizardlib.template.ListMixin;
+import com.android.setupwizardlib.template.ListViewScrollHandlingDelegate;
+import com.android.setupwizardlib.template.RequireScrollMixin;
/**
* A GLIF themed layout with a ListView. {@code android:entries} can also be used to specify an
@@ -70,6 +72,10 @@ public class GlifListLayout extends GlifLayout {
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
mListMixin = new ListMixin(this, attrs, defStyleAttr);
registerMixin(ListMixin.class, mListMixin);
+
+ final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
+ requireScrollMixin.setScrollHandlingDelegate(
+ new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
}
@Override
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
index 2364e9b..065d2ef 100644
--- a/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
+++ b/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
@@ -42,8 +42,8 @@ import android.widget.TextView;
import com.android.setupwizardlib.template.HeaderMixin;
import com.android.setupwizardlib.template.NavigationBarMixin;
import com.android.setupwizardlib.template.ProgressBarMixin;
-import com.android.setupwizardlib.util.RequireScrollHelper;
-import com.android.setupwizardlib.view.BottomScrollView;
+import com.android.setupwizardlib.template.RequireScrollMixin;
+import com.android.setupwizardlib.template.ScrollViewScrollHandlingDelegate;
import com.android.setupwizardlib.view.Illustration;
import com.android.setupwizardlib.view.NavigationBar;
@@ -82,6 +82,14 @@ public class SetupWizardLayout extends TemplateLayout {
registerMixin(HeaderMixin.class, new HeaderMixin(this, attrs, defStyleAttr));
registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
registerMixin(NavigationBarMixin.class, new NavigationBarMixin(this));
+ final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
+ registerMixin(RequireScrollMixin.class, requireScrollMixin);
+
+ final ScrollView scrollView = getScrollView();
+ if (scrollView != null) {
+ requireScrollMixin.setScrollHandlingDelegate(
+ new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView));
+ }
final TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.SuwSetupWizardLayout, defStyleAttr, 0);
@@ -156,11 +164,7 @@ public class SetupWizardLayout extends TemplateLayout {
final SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
final boolean isProgressBarShown = ss.mIsProgressBarShown;
- if (isProgressBarShown) {
- showProgressBar();
- } else {
- hideProgressBar();
- }
+ setProgressBarShown(isProgressBarShown);
}
@Override
@@ -189,13 +193,12 @@ public class SetupWizardLayout extends TemplateLayout {
}
public void requireScrollToBottom() {
+ final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
final NavigationBar navigationBar = getNavigationBar();
- final ScrollView scrollView = getScrollView();
- if (navigationBar != null && (scrollView instanceof BottomScrollView)) {
- RequireScrollHelper.requireScroll(navigationBar, (BottomScrollView) scrollView);
+ if (navigationBar != null) {
+ requireScrollMixin.requireScrollWithNavigationBar(navigationBar);
} else {
- Log.e(TAG, "Both suw_layout_navigation_bar and suw_bottom_scroll_view must exist in"
- + " the template to require scrolling.");
+ Log.e(TAG, "Cannot require scroll. Navigation bar is null.");
}
}
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
index bb96d58..0457451 100644
--- a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
+++ b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -29,8 +28,8 @@ import android.widget.ListAdapter;
import android.widget.ListView;
import com.android.setupwizardlib.template.ListMixin;
-import com.android.setupwizardlib.util.ListViewRequireScrollHelper;
-import com.android.setupwizardlib.view.NavigationBar;
+import com.android.setupwizardlib.template.ListViewScrollHandlingDelegate;
+import com.android.setupwizardlib.template.RequireScrollMixin;
public class SetupWizardListLayout extends SetupWizardLayout {
@@ -65,6 +64,10 @@ public class SetupWizardListLayout extends SetupWizardLayout {
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
mListMixin = new ListMixin(this, attrs, defStyleAttr);
registerMixin(ListMixin.class, mListMixin);
+
+ final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
+ requireScrollMixin.setScrollHandlingDelegate(
+ new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
}
@Override
@@ -101,18 +104,6 @@ public class SetupWizardListLayout extends SetupWizardLayout {
return mListMixin.getAdapter();
}
- @Override
- public void requireScrollToBottom() {
- final NavigationBar navigationBar = getNavigationBar();
- final ListView listView = getListView();
- if (navigationBar != null && listView != null) {
- ListViewRequireScrollHelper.requireScroll(navigationBar, listView);
- } else {
- Log.e(TAG, "Both suw_layout_navigation_bar and list must exist in"
- + " the template to require scrolling.");
- }
- }
-
/**
* Sets the start inset of the divider. This will use the default divider drawable set in the
* theme and inset it {@code inset} pixels to the right (or left in RTL layouts).
diff --git a/library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java b/library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java
new file mode 100644
index 0000000..f55d06d
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDelegate;
+
+/**
+ * {@link ScrollHandlingDelegate} which analyzes scroll events from {@link ListView} and
+ * notifies {@link RequireScrollMixin} about scrollability changes.
+ */
+public class ListViewScrollHandlingDelegate implements ScrollHandlingDelegate,
+ AbsListView.OnScrollListener {
+
+ private static final String TAG = "ListViewDelegate";
+
+ private static final int SCROLL_DURATION = 500;
+
+ @NonNull
+ private final RequireScrollMixin mRequireScrollMixin;
+
+ @Nullable
+ private final ListView mListView;
+
+ public ListViewScrollHandlingDelegate(
+ @NonNull RequireScrollMixin requireScrollMixin,
+ @Nullable ListView listView) {
+ mRequireScrollMixin = requireScrollMixin;
+ mListView = listView;
+ }
+
+ @Override
+ public void startListening() {
+ if (mListView != null) {
+ mListView.setOnScrollListener(this);
+
+ final ListAdapter adapter = mListView.getAdapter();
+ if (mListView.getLastVisiblePosition() < adapter.getCount()) {
+ mRequireScrollMixin.notifyScrollabilityChange(true);
+ }
+ } else {
+ Log.w(TAG, "Cannot require scroll. List view is null");
+ }
+ }
+
+ @Override
+ public void pageScrollDown() {
+ if (mListView != null) {
+ final int height = mListView.getHeight();
+ mListView.smoothScrollBy(height, SCROLL_DURATION);
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if (firstVisibleItem + visibleItemCount >= totalItemCount) {
+ mRequireScrollMixin.notifyScrollabilityChange(false);
+ } else {
+ mRequireScrollMixin.notifyScrollabilityChange(true);
+ }
+ }
+}
diff --git a/library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java b/library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java
new file mode 100644
index 0000000..231c064
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.setupwizardlib.TemplateLayout;
+import com.android.setupwizardlib.view.NavigationBar;
+
+/**
+ * A mixin to require the a scrollable container (BottomScrollView, RecyclerView or ListView) to
+ * be scrolled to bottom, making sure that the user sees all content above and below the fold.
+ */
+public class RequireScrollMixin implements Mixin {
+
+ /* static section */
+
+ /**
+ * Listener for when the require-scroll state changes. Note that this only requires the user to
+ * scroll to the bottom once - if the user scrolled to the bottom and back-up, scrolling to
+ * bottom is not required again.
+ */
+ public interface OnRequireScrollStateChangedListener {
+
+ /**
+ * Called when require-scroll state changed.
+ *
+ * @param scrollNeeded True if the user should be required to scroll to bottom.
+ */
+ void onRequireScrollStateChanged(boolean scrollNeeded);
+ }
+
+ /**
+ * A delegate to detect scrollability changes and to scroll the page. This provides a layer
+ * of abstraction for BottomScrollView, RecyclerView and ListView. The delegate should call
+ * {@link #notifyScrollabilityChange(boolean)} when the view scrollability is changed.
+ */
+ interface ScrollHandlingDelegate {
+
+ /**
+ * Starts listening to scrollability changes at the target scrollable container.
+ */
+ void startListening();
+
+ /**
+ * Scroll the page content down by one page.
+ */
+ void pageScrollDown();
+ }
+
+ /* non-static section */
+
+ @NonNull
+ private final TemplateLayout mTemplateLayout;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ private boolean mRequiringScrollToBottom = false;
+
+ // Whether the user have seen the more button yet.
+ private boolean mEverScrolledToBottom = false;
+
+ private ScrollHandlingDelegate mDelegate;
+
+ @Nullable
+ private OnRequireScrollStateChangedListener mListener;
+
+ /**
+ * @param templateLayout The template containing this mixin
+ */
+ public RequireScrollMixin(@NonNull TemplateLayout templateLayout) {
+ mTemplateLayout = templateLayout;
+ }
+
+ /**
+ * Sets the delegate to handle scrolling. The type of delegate should depend on whether the
+ * scrolling view is a BottomScrollView, RecyclerView or ListView.
+ */
+ public void setScrollHandlingDelegate(@NonNull ScrollHandlingDelegate delegate) {
+ mDelegate = delegate;
+ }
+
+ /**
+ * Listen to require scroll state changes. When scroll is required,
+ * {@link OnRequireScrollStateChangedListener#onRequireScrollStateChanged(boolean)} is called
+ * with {@code true}, and vice versa.
+ */
+ public void setOnRequireScrollStateChangedListener(
+ @Nullable OnRequireScrollStateChangedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * @return The scroll state listener previously set, or {@code null} if none is registered.
+ */
+ public OnRequireScrollStateChangedListener getOnRequireScrollStateChangedListener() {
+ return mListener;
+ }
+
+ /**
+ * Creates an {@link OnClickListener} which if scrolling is required, will scroll the page down,
+ * and if scrolling is not required, delegates to the wrapped {@code listener}. Note that you
+ * should call {@link #requireScroll()} as well in order to start requiring scrolling.
+ *
+ * @param listener The listener to be invoked when scrolling is not needed and the user taps on
+ * the button. If {@code null}, the click listener will be a no-op when scroll
+ * is not required.
+ * @return A new {@link OnClickListener} which will scroll the page down or delegate to the
+ * given listener depending on the current require-scroll state.
+ */
+ public OnClickListener createOnClickListener(@Nullable final OnClickListener listener) {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mRequiringScrollToBottom) {
+ mDelegate.pageScrollDown();
+ } else if (listener != null) {
+ listener.onClick(view);
+ }
+ }
+ };
+ }
+
+ /**
+ * Coordinate with the given navigation bar to require scrolling on the page. The more button
+ * will be shown instead of the next button while scrolling is required.
+ */
+ public void requireScrollWithNavigationBar(@NonNull final NavigationBar navigationBar) {
+ setOnRequireScrollStateChangedListener(
+ new OnRequireScrollStateChangedListener() {
+ @Override
+ public void onRequireScrollStateChanged(boolean scrollNeeded) {
+ navigationBar.getMoreButton()
+ .setVisibility(scrollNeeded ? View.VISIBLE : View.GONE);
+ navigationBar.getNextButton()
+ .setVisibility(scrollNeeded ? View.GONE : View.VISIBLE);
+ }
+ });
+ navigationBar.getMoreButton().setOnClickListener(createOnClickListener(null));
+ requireScroll();
+ }
+
+ /**
+ * @see #requireScrollWithButton(Button, CharSequence, OnClickListener)
+ */
+ public void requireScrollWithButton(
+ @NonNull Button button,
+ @StringRes int moreText,
+ @Nullable OnClickListener onClickListener) {
+ requireScrollWithButton(button, button.getContext().getText(moreText), onClickListener);
+ }
+
+ /**
+ * Use the given {@code button} to require scrolling. When scrolling is required, the button
+ * label will change to {@code moreText}, and tapping the button will cause the page to scroll
+ * down.
+ *
+ * <p>Note: Calling {@link View#setOnClickListener} on the button after this method will remove
+ * its link to the require-scroll mechanism. If you need to do that, obtain the click listener
+ * from {@link #createOnClickListener(OnClickListener)}.
+ *
+ * <p>Note: The normal button label is taken from the button's text at the time of calling this
+ * method. Calling {@link android.widget.TextView#setText} after calling this method causes
+ * undefined behavior.
+ *
+ * @param button The button to use for require scroll. The button's "normal" label is taken from
+ * the text at the time of calling this method, and the click listener of it will
+ * be replaced.
+ * @param moreText The button label when scroll is required.
+ * @param onClickListener The listener for clicks when scrolling is not required.
+ */
+ public void requireScrollWithButton(
+ @NonNull final Button button,
+ final CharSequence moreText,
+ @Nullable OnClickListener onClickListener) {
+ final CharSequence nextText = button.getText();
+ button.setOnClickListener(createOnClickListener(onClickListener));
+ setOnRequireScrollStateChangedListener(new OnRequireScrollStateChangedListener() {
+ @Override
+ public void onRequireScrollStateChanged(boolean scrollNeeded) {
+ button.setText(scrollNeeded ? moreText : nextText);
+ }
+ });
+ requireScroll();
+ }
+
+ /**
+ * @return True if scrolling is required. Note that this mixin only requires the user to
+ * scroll to the bottom once - if the user scrolled to the bottom and back-up, scrolling to
+ * bottom is not required again.
+ */
+ public boolean isScrollingRequired() {
+ return mRequiringScrollToBottom;
+ }
+
+ /**
+ * Start requiring scrolling on the layout. After calling this method, this mixin will start
+ * listening to scroll events from the scrolling container, and call
+ * {@link OnRequireScrollStateChangedListener} when the scroll state changes.
+ */
+ public void requireScroll() {
+ mDelegate.startListening();
+ }
+
+ /**
+ * {@link ScrollHandlingDelegate} should call this method when the scrollability of the
+ * scrolling container changed, so this mixin can recompute whether scrolling should be
+ * required.
+ *
+ * @param canScrollDown True if the view can scroll down further.
+ */
+ void notifyScrollabilityChange(boolean canScrollDown) {
+ if (canScrollDown == mRequiringScrollToBottom) {
+ // Already at the desired require-scroll state
+ return;
+ }
+ if (canScrollDown) {
+ if (!mEverScrolledToBottom) {
+ postScrollStateChange(true);
+ mRequiringScrollToBottom = true;
+ }
+ } else {
+ postScrollStateChange(false);
+ mRequiringScrollToBottom = false;
+ mEverScrolledToBottom = true;
+ }
+ }
+
+ private void postScrollStateChange(final boolean scrollNeeded) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mListener != null) {
+ mListener.onRequireScrollStateChanged(scrollNeeded);
+ }
+ }
+ });
+ }
+}
diff --git a/library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java b/library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java
new file mode 100644
index 0000000..d159465
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.template;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.widget.ScrollView;
+
+import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDelegate;
+import com.android.setupwizardlib.view.BottomScrollView;
+import com.android.setupwizardlib.view.BottomScrollView.BottomScrollListener;
+
+/**
+ * {@link ScrollHandlingDelegate} which analyzes scroll events from {@link BottomScrollView} and
+ * notifies {@link RequireScrollMixin} about scrollability changes.
+ */
+public class ScrollViewScrollHandlingDelegate
+ implements ScrollHandlingDelegate, BottomScrollListener {
+
+ private static final String TAG = "ScrollViewDelegate";
+
+ @NonNull
+ private final RequireScrollMixin mRequireScrollMixin;
+
+ @Nullable
+ private final BottomScrollView mScrollView;
+
+ public ScrollViewScrollHandlingDelegate(
+ @NonNull RequireScrollMixin requireScrollMixin,
+ @Nullable ScrollView scrollView) {
+ mRequireScrollMixin = requireScrollMixin;
+ if (scrollView instanceof BottomScrollView) {
+ mScrollView = (BottomScrollView) scrollView;
+ } else {
+ Log.w(TAG, "Cannot set non-BottomScrollView. Found=" + scrollView);
+ mScrollView = null;
+ }
+ }
+
+ @Override
+ public void onScrolledToBottom() {
+ mRequireScrollMixin.notifyScrollabilityChange(false);
+ }
+
+ @Override
+ public void onRequiresScroll() {
+ mRequireScrollMixin.notifyScrollabilityChange(true);
+ }
+
+ @Override
+ public void startListening() {
+ if (mScrollView != null) {
+ mScrollView.setBottomScrollListener(this);
+ } else {
+ Log.w(TAG, "Cannot require scroll. Scroll view is null.");
+ }
+ }
+
+ @Override
+ public void pageScrollDown() {
+ if (mScrollView != null) {
+ mScrollView.pageScroll(ScrollView.FOCUS_DOWN);
+ }
+ }
+}
diff --git a/library/main/src/com/android/setupwizardlib/util/AbstractRequireScrollHelper.java b/library/main/src/com/android/setupwizardlib/util/AbstractRequireScrollHelper.java
deleted file mode 100644
index 2697371..0000000
--- a/library/main/src/com/android/setupwizardlib/util/AbstractRequireScrollHelper.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.setupwizardlib.util;
-
-import android.view.View;
-
-import com.android.setupwizardlib.view.NavigationBar;
-
-/**
- * Add this helper to require the scroll view to be scrolled to the bottom, making sure that the
- * user sees all content on the screen. This will change the navigation bar to show the more button
- * instead of the next button when there is more content to be seen. When the more button is
- * clicked, the scroll view will be scrolled one page down.
- */
-public abstract class AbstractRequireScrollHelper implements View.OnClickListener {
-
- private final NavigationBar mNavigationBar;
-
- private boolean mScrollNeeded;
- // Whether the user have seen the more button yet.
- private boolean mScrollNotified = false;
-
- protected AbstractRequireScrollHelper(NavigationBar navigationBar) {
- mNavigationBar = navigationBar;
- }
-
- protected void requireScroll() {
- mNavigationBar.getMoreButton().setOnClickListener(this);
- }
-
- protected void notifyScrolledToBottom() {
- if (mScrollNeeded) {
- mNavigationBar.post(new Runnable() {
- @Override
- public void run() {
- mNavigationBar.getNextButton().setVisibility(View.VISIBLE);
- mNavigationBar.getMoreButton().setVisibility(View.GONE);
- }
- });
- mScrollNeeded = false;
- mScrollNotified = true;
- }
- }
-
- protected void notifyRequiresScroll() {
- if (!mScrollNeeded && !mScrollNotified) {
- mNavigationBar.post(new Runnable() {
- @Override
- public void run() {
- mNavigationBar.getNextButton().setVisibility(View.GONE);
- mNavigationBar.getMoreButton().setVisibility(View.VISIBLE);
- }
- });
- mScrollNeeded = true;
- }
- }
-
- @Override
- public void onClick(View view) {
- pageScrollDown();
- }
-
- protected abstract void pageScrollDown();
-}
diff --git a/library/main/src/com/android/setupwizardlib/util/ListViewRequireScrollHelper.java b/library/main/src/com/android/setupwizardlib/util/ListViewRequireScrollHelper.java
deleted file mode 100644
index 7877569..0000000
--- a/library/main/src/com/android/setupwizardlib/util/ListViewRequireScrollHelper.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.setupwizardlib.util;
-
-import android.os.Build;
-import android.widget.AbsListView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-
-import com.android.setupwizardlib.view.NavigationBar;
-
-/**
- * Add this helper to require the list view to be scrolled to the bottom, making sure that the
- * user sees all content on the screen. This will change the navigation bar to show the more button
- * instead of the next button when there is more content to be seen. When the more button is
- * clicked, the list view will be scrolled one page down.
- */
-public class ListViewRequireScrollHelper extends AbstractRequireScrollHelper
- implements AbsListView.OnScrollListener {
-
- public static void requireScroll(NavigationBar navigationBar, ListView listView) {
- new ListViewRequireScrollHelper(navigationBar, listView).requireScroll();
- }
-
- private final ListView mListView;
-
- private ListViewRequireScrollHelper(NavigationBar navigationBar, ListView listView) {
- super(navigationBar);
- mListView = listView;
- }
-
- @Override
- protected void requireScroll() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
- // APIs to scroll a list only exists on Froyo or above.
- super.requireScroll();
- mListView.setOnScrollListener(this);
-
- final ListAdapter adapter = mListView.getAdapter();
- if (mListView.getLastVisiblePosition() < adapter.getCount()) {
- notifyRequiresScroll();
- }
- }
- }
-
- @Override
- protected void pageScrollDown() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
- final int height = mListView.getHeight();
- mListView.smoothScrollBy(height, 500);
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- if (firstVisibleItem + visibleItemCount >= totalItemCount) {
- notifyScrolledToBottom();
- } else {
- notifyRequiresScroll();
- }
- }
-}
diff --git a/library/main/src/com/android/setupwizardlib/util/RequireScrollHelper.java b/library/main/src/com/android/setupwizardlib/util/RequireScrollHelper.java
deleted file mode 100644
index cce336f..0000000
--- a/library/main/src/com/android/setupwizardlib/util/RequireScrollHelper.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.setupwizardlib.util;
-
-import android.widget.ScrollView;
-
-import com.android.setupwizardlib.view.BottomScrollView;
-import com.android.setupwizardlib.view.NavigationBar;
-
-/**
- * Add this helper to require the scroll view to be scrolled to the bottom, making sure that the
- * user sees all content on the screen. This will change the navigation bar to show the more button
- * instead of the next button when there is more content to be seen. When the more button is
- * clicked, the scroll view will be scrolled one page down.
- */
-public class RequireScrollHelper extends AbstractRequireScrollHelper
- implements BottomScrollView.BottomScrollListener {
-
- public static void requireScroll(NavigationBar navigationBar, BottomScrollView scrollView) {
- new RequireScrollHelper(navigationBar, scrollView).requireScroll();
- }
-
- private final BottomScrollView mScrollView;
-
- private RequireScrollHelper(NavigationBar navigationBar, BottomScrollView scrollView) {
- super(navigationBar);
- mScrollView = scrollView;
- }
-
- @Override
- protected void requireScroll() {
- super.requireScroll();
- mScrollView.setBottomScrollListener(this);
- }
-
- @Override
- protected void pageScrollDown() {
- mScrollView.pageScroll(ScrollView.FOCUS_DOWN);
- }
-
- @Override
- public void onScrolledToBottom() {
- notifyScrolledToBottom();
- }
-
- @Override
- public void onRequiresScroll() {
- notifyRequiresScroll();
- }
-}
diff --git a/library/main/src/com/android/setupwizardlib/view/NavigationBar.java b/library/main/src/com/android/setupwizardlib/view/NavigationBar.java
index 9bb123f..2a1dd28 100644
--- a/library/main/src/com/android/setupwizardlib/view/NavigationBar.java
+++ b/library/main/src/com/android/setupwizardlib/view/NavigationBar.java
@@ -35,7 +35,7 @@ import com.android.setupwizardlib.R;
* next button. By default, the more button is hidden, and typically the next button will be hidden
* if the more button is shown.
*
- * @see com.android.setupwizardlib.util.RequireScrollHelper
+ * @see com.android.setupwizardlib.template.RequireScrollMixin
*/
public class NavigationBar extends LinearLayout implements View.OnClickListener {