[SetupWizardLib] Add progress bar support

Added methods showProgressBar, hideProgressBar and isProgressBarShown
to SetupWizardLayout, that will show the bar if the corresponding
ViewStub exists.

This implementation has an improvement over existing Setup Wizard
implementations in that it supports ListViews as well as ScrollViews,
and the progress bar will be sticky together with the header instead
of being scrolled away.

Change-Id: Ifd7acae36a9b244d759b6528bb5dfdc3e1d15091
This commit is contained in:
Maurice Lam 2015-04-02 17:03:08 -07:00
parent e7b519c176
commit d61674efcf
19 changed files with 345 additions and 62 deletions

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/suw_progress_bar_margin_vertical"
android:layout_marginTop="@dimen/suw_progress_bar_margin_vertical"
android:indeterminate="true" />

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/suw_progress_bar_margin_vertical"
android:layout_marginTop="@dimen/suw_progress_bar_margin_vertical"
android:indeterminate="true" />

View file

@ -15,24 +15,32 @@
limitations under the License.
-->
<com.android.setupwizardlib.view.Illustration
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/suw_layout_decor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/suw_layout_background"
android:elevation="@dimen/suw_title_area_elevation"
android:tag="stickyContainer"
app:suwAspectRatio="2.22"
tools:ignore="UnusedAttribute">
android:orientation="vertical"
android:tag="stickyContainer">
<TextView
android:id="@+id/suw_layout_title"
style="@style/SuwHeaderTitle"
<com.android.setupwizardlib.view.Illustration
android:id="@+id/suw_layout_decor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="sticky" />
android:background="@drawable/suw_layout_background"
android:elevation="@dimen/suw_title_area_elevation"
app:suwAspectRatio="2.22"
tools:ignore="UnusedAttribute">
</com.android.setupwizardlib.view.Illustration>
<TextView
android:id="@+id/suw_layout_title"
style="@style/SuwHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="sticky" />
</com.android.setupwizardlib.view.Illustration>
<include layout="@layout/suw_progress_bar_stub" />
</LinearLayout>

View file

@ -42,14 +42,22 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ListView
android:id="@android:id/list"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="?attr/suwCardBackground"
android:elevation="@dimen/suw_card_elevation"
tools:ignore="UnusedAttribute" />
tools:ignore="UnusedAttribute">
<include layout="@layout/suw_progress_bar_stub" />
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>

View file

@ -42,15 +42,23 @@
android:layout_marginTop="@dimen/suw_tablet_illustration_height"
android:layout_weight="6" />
<ListView
android:id="@android:id/list"
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/suw_card_land_margin_top"
android:layout_weight="8"
android:background="?attr/suwCardBackground"
android:elevation="@dimen/suw_card_elevation"
tools:ignore="UnusedAttribute" />
tools:ignore="UnusedAttribute">
<include layout="@layout/suw_progress_bar_stub" />
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>

View file

@ -37,6 +37,8 @@
</FrameLayout>
<include layout="@layout/suw_progress_bar_stub" />
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"

View file

@ -46,13 +46,21 @@
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="?attr/suwCardBackground"
android:elevation="@dimen/suw_card_elevation"
tools:ignore="UnusedAttribute" />
tools:ignore="UnusedAttribute">
<include layout="@layout/suw_progress_bar_stub" />
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>

View file

@ -46,15 +46,22 @@
android:layout_weight="6" />
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/suw_card_land_margin_top"
android:layout_weight="8"
android:background="?attr/suwCardBackground"
android:elevation="@dimen/suw_card_elevation"
android:fillViewport="true"
tools:ignore="UnusedAttribute" />
tools:ignore="UnusedAttribute">
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/suw_progress_bar_stub" />
</FrameLayout>
</LinearLayout>

View file

@ -27,10 +27,11 @@
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
android:layout_weight="1"
android:orientation="vertical">
<com.android.setupwizardlib.view.Illustration
android:id="@+id/suw_layout_decor"
@ -51,13 +52,14 @@
</com.android.setupwizardlib.view.Illustration>
<include layout="@layout/suw_progress_bar_stub" />
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/suw_layout_decor" />
android:layout_height="wrap_content" />
</RelativeLayout>
</LinearLayout>
<com.android.setupwizardlib.view.NavigationBar
android:id="@+id/suw_layout_navigation_bar"

View file

@ -40,6 +40,8 @@
</FrameLayout>
<include layout="@layout/suw_progress_bar_stub" />
<FrameLayout android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="0dp"

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<ViewStub xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/suw_layout_progress_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/suw_progress_bar_margin_vertical"
android:layout_marginTop="@dimen/suw_progress_bar_margin_vertical"
android:inflatedId="@+id/suw_layout_progress"
android:layout="@layout/suw_progress_bar" />

View file

@ -42,23 +42,31 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.android.setupwizardlib.view.BottomScrollView
android:id="@+id/suw_bottom_scroll_view"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="?attr/suwCardBackground"
android:elevation="@dimen/suw_card_elevation"
android:fillViewport="true"
tools:ignore="UnusedAttribute">
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false" />
<include layout="@layout/suw_progress_bar_stub" />
</com.android.setupwizardlib.view.BottomScrollView>
<com.android.setupwizardlib.view.BottomScrollView
android:id="@+id/suw_bottom_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false" />
</com.android.setupwizardlib.view.BottomScrollView>
</FrameLayout>
</LinearLayout>

View file

@ -42,23 +42,31 @@
android:layout_marginTop="@dimen/suw_tablet_illustration_height"
android:layout_weight="6" />
<com.android.setupwizardlib.view.BottomScrollView
android:id="@+id/suw_bottom_scroll_view"
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/suw_card_land_margin_top"
android:layout_weight="8"
android:background="?attr/suwCardBackground"
android:elevation="@dimen/suw_card_elevation"
android:fillViewport="true"
tools:ignore="UnusedAttribute">
<FrameLayout android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false" />
<include layout="@layout/suw_progress_bar_stub" />
</com.android.setupwizardlib.view.BottomScrollView>
<com.android.setupwizardlib.view.BottomScrollView
android:id="@+id/suw_bottom_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<FrameLayout android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false" />
</com.android.setupwizardlib.view.BottomScrollView>
</FrameLayout>
</LinearLayout>

View file

@ -29,38 +29,49 @@
android:layout_height="0dp"
android:fillViewport="true">
<RelativeLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false">
android:clipChildren="false"
android:orientation="vertical">
<com.android.setupwizardlib.view.Illustration
android:id="@+id/suw_layout_decor"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/suw_layout_background"
android:elevation="@dimen/suw_title_area_elevation"
android:tag="stickyContainer"
app:suwAspectRatio="2.22"
tools:ignore="UnusedAttribute">
android:clipChildren="false"
android:orientation="vertical"
android:tag="stickyContainer">
<TextView
android:id="@+id/suw_layout_title"
style="@style/SuwHeaderTitle"
<com.android.setupwizardlib.view.Illustration
android:id="@+id/suw_layout_decor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="sticky" />
android:background="@drawable/suw_layout_background"
android:elevation="@dimen/suw_title_area_elevation"
android:orientation="vertical"
app:suwAspectRatio="2.22"
tools:ignore="UnusedAttribute">
</com.android.setupwizardlib.view.Illustration>
<TextView
android:id="@+id/suw_layout_title"
style="@style/SuwHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="sticky" />
</com.android.setupwizardlib.view.Illustration>
<include layout="@layout/suw_progress_bar_stub" />
</LinearLayout>
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/suw_layout_decor"
android:clipChildren="false" />
</RelativeLayout>
</LinearLayout>
</com.android.setupwizardlib.view.StickyHeaderScrollView>

View file

@ -37,6 +37,15 @@
</FrameLayout>
<ViewStub
android:id="@+id/suw_layout_progress_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/suw_progress_bar_margin_vertical"
android:layout_marginTop="@dimen/suw_progress_bar_margin_vertical"
android:inflatedId="@+id/suw_layout_progress"
android:layout="@layout/suw_progress_bar" />
<com.android.setupwizardlib.view.BottomScrollView
android:id="@+id/suw_bottom_scroll_view"
android:layout_width="match_parent"

View file

@ -60,4 +60,8 @@
<dimen name="suw_navbar_padding_sides">24dp</dimen>
<dimen name="suw_navbar_text_size">16sp</dimen>
<!-- Progress bar -->
<!-- The margin to compensate for the padding built-in to the widget itself -->
<dimen name="suw_progress_bar_margin_vertical">-7dp</dimen>
</resources>

View file

@ -26,11 +26,14 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.TextView;
@ -124,6 +127,26 @@ public class SetupWizardLayout extends FrameLayout {
a.recycle();
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable parcelable = super.onSaveInstanceState();
final SavedState ss = new SavedState(parcelable);
ss.isProgressBarShown = isProgressBarShown();
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
final SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
final boolean isProgressBarShown = ss.isProgressBarShown;
if (isProgressBarShown) {
showProgressBar();
} else {
hideProgressBar();
}
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
mContainer.addView(child, index, params);
@ -298,4 +321,62 @@ public class SetupWizardLayout extends FrameLayout {
return asset;
}
}
public boolean isProgressBarShown() {
final View progressBar = findViewById(R.id.suw_layout_progress);
return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
}
public void showProgressBar() {
final View progressBar = findViewById(R.id.suw_layout_progress);
if (progressBar != null) {
progressBar.setVisibility(View.VISIBLE);
} else {
final ViewStub progressBarStub = (ViewStub) findViewById(R.id.suw_layout_progress_stub);
if (progressBarStub != null) {
progressBarStub.inflate();
}
}
}
public void hideProgressBar() {
final View progressBar = findViewById(R.id.suw_layout_progress);
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}
protected static class SavedState extends BaseSavedState {
boolean isProgressBarShown = false;
public SavedState(Parcelable parcelable) {
super(parcelable);
}
public SavedState(Parcel source) {
super(source);
isProgressBarShown = source.readInt() != 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(isProgressBarShown ? 1 : 0);
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel parcel) {
return new SavedState(parcel);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

View file

@ -24,6 +24,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.setupwizardlib.SetupWizardLayout;
@ -112,6 +113,36 @@ public class SetupWizardLayoutTests extends InstrumentationTestCase {
assertNull("getNavigationBar() in test_template should return null", navigationBar);
}
@SmallTest
public void testShowProgressBar() {
final SetupWizardLayout layout = new SetupWizardLayout(mContext);
layout.showProgressBar();
assertTrue("Progress bar should be shown", layout.isProgressBarShown());
final View progressBar = layout.findViewById(R.id.suw_layout_progress);
assertTrue("Progress bar view should be shown",
progressBar instanceof ProgressBar && progressBar.getVisibility() == View.VISIBLE);
}
@SmallTest
public void testHideProgressBar() {
final SetupWizardLayout layout = new SetupWizardLayout(mContext);
layout.showProgressBar();
assertTrue("Progress bar should be shown", layout.isProgressBarShown());
layout.hideProgressBar();
assertFalse("Progress bar should be hidden", layout.isProgressBarShown());
final View progressBar = layout.findViewById(R.id.suw_layout_progress);
assertTrue("Progress bar view should exist",
progressBar == null || progressBar.getVisibility() != View.VISIBLE);
}
@SmallTest
public void testShowProgressBarNotExist() {
// test_template does not have progress bar, so showNavigationBar() should do nothing.
final SetupWizardLayout layout = new SetupWizardLayout(mContext, R.layout.test_template);
layout.showProgressBar();
assertFalse("Progress bar should not be shown", layout.isProgressBarShown());
}
private void assertDefaultTemplateInflated(SetupWizardLayout layout) {
View decorView = layout.findViewById(R.id.suw_layout_decor);
View navbar = layout.findViewById(R.id.suw_layout_navigation_bar);

View file

@ -23,6 +23,7 @@ import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.setupwizardlib.SetupWizardLayout;
@ -66,6 +67,16 @@ public class SetupWizardListLayoutTests extends InstrumentationTestCase {
assertListTemplateInflated(layout);
}
@SmallTest
public void testShowProgressBar() {
final SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
layout.showProgressBar();
assertTrue("Progress bar should be shown", layout.isProgressBarShown());
final View progressBar = layout.findViewById(R.id.suw_layout_progress);
assertTrue("Progress bar view should be shown",
progressBar instanceof ProgressBar && progressBar.getVisibility() == View.VISIBLE);
}
private void assertListTemplateInflated(SetupWizardLayout layout) {
View decorView = layout.findViewById(R.id.suw_layout_decor);
View navbar = layout.findViewById(R.id.suw_layout_navigation_bar);