diff options
140 files changed, 1994 insertions, 185 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 421dd64..b45eaff 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,5 +1,7 @@ [Hook Scripts] -checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} +checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py + --sha ${PREUPLOAD_COMMIT} + --config_xml tools/checkstyle/checkstyle.xml [Builtin Hooks] commit_msg_test_field = true diff --git a/library/Android.mk b/library/Android.mk index 094d795..26f5e1f 100644 --- a/library/Android.mk +++ b/library/Android.mk @@ -36,10 +36,10 @@ LOCAL_MANIFEST_FILE := main/AndroidManifest.xml LOCAL_MODULE := setup-wizard-lib-gingerbread-compat LOCAL_RESOURCE_DIR := \ $(LOCAL_PATH)/main/res \ - $(LOCAL_PATH)/eclair-mr1/res \ - $(LOCAL_PATH)/full-support/res + $(LOCAL_PATH)/gingerbread/res \ + $(LOCAL_PATH)/recyclerview/res LOCAL_SDK_VERSION := current -LOCAL_SRC_FILES := $(call all-java-files-under, main/src eclair-mr1/src full-support/src) +LOCAL_SRC_FILES := $(call all-java-files-under, main/src gingerbread/src recyclerview/src) ifdef LOCAL_USE_AAPT2 diff --git a/library/common-gingerbread.mk b/library/common-gingerbread.mk index 3cc58ec..6ced8b0 100644 --- a/library/common-gingerbread.mk +++ b/library/common-gingerbread.mk @@ -31,8 +31,8 @@ endif ifeq (,$(findstring setup-wizard-lib-gingerbread-compat,$(LOCAL_STATIC_JAVA_LIBRARIES))) LOCAL_RESOURCE_DIR += \ $(suwlib_dir)/main/res \ - $(suwlib_dir)/eclair-mr1/res \ - $(suwlib_dir)/full-support/res + $(suwlib_dir)/gingerbread/res \ + $(suwlib_dir)/recyclerview/res LOCAL_AAPT_FLAGS += --extra-packages com.android.setupwizardlib LOCAL_STATIC_JAVA_LIBRARIES += setup-wizard-lib-gingerbread-compat endif diff --git a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_left_arrow.png Binary files differdeleted file mode 100644 index 1364d5c..0000000 --- a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_left_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_right_arrow.png Binary files differdeleted file mode 100644 index ff7a66e..0000000 --- a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_right_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_left_arrow.png Binary files differdeleted file mode 100644 index 0bd2a76..0000000 --- a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_left_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_right_arrow.png Binary files differdeleted file mode 100644 index c367b7f..0000000 --- a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_right_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png Binary files differdeleted file mode 100644 index 2fac071..0000000 --- a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png Binary files differdeleted file mode 100644 index c1a0765..0000000 --- a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png Binary files differdeleted file mode 100644 index ea343d0..0000000 --- a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png Binary files differdeleted file mode 100644 index ae30d61..0000000 --- a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png b/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png Binary files differdeleted file mode 100644 index fcd2c64..0000000 --- a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png b/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png Binary files differdeleted file mode 100644 index eacab40..0000000 --- a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png +++ /dev/null diff --git a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_down_arrow.png Binary files differindex 3d7f83f..3d7f83f 100644 --- a/library/eclair-mr1/res/drawable-hdpi/suw_navbar_ic_down_arrow.png +++ b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_down_arrow.png diff --git a/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_left_arrow.png Binary files differnew file mode 100644 index 0000000..decccac --- /dev/null +++ b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_left_arrow.png diff --git a/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_right_arrow.png Binary files differnew file mode 100644 index 0000000..5ba56b7 --- /dev/null +++ b/library/gingerbread/res/drawable-hdpi/suw_navbar_ic_right_arrow.png diff --git a/library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_back.xml b/library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_back.xml index e164e8a..e164e8a 100644 --- a/library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_back.xml +++ b/library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_back.xml diff --git a/library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_next.xml b/library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_next.xml index 8a6e137..8a6e137 100644 --- a/library/eclair-mr1/res/drawable-ldrtl/suw_navbar_ic_next.xml +++ b/library/gingerbread/res/drawable-ldrtl/suw_navbar_ic_next.xml diff --git a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_down_arrow.png Binary files differindex 5b1fa06..5b1fa06 100644 --- a/library/eclair-mr1/res/drawable-mdpi/suw_navbar_ic_down_arrow.png +++ b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_down_arrow.png diff --git a/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_left_arrow.png Binary files differnew file mode 100644 index 0000000..1e2984a --- /dev/null +++ b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_left_arrow.png diff --git a/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_right_arrow.png Binary files differnew file mode 100644 index 0000000..b8365e6 --- /dev/null +++ b/library/gingerbread/res/drawable-mdpi/suw_navbar_ic_right_arrow.png diff --git a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png Binary files differindex 94016f4..94016f4 100644 --- a/library/eclair-mr1/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png +++ b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png diff --git a/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png Binary files differnew file mode 100644 index 0000000..2d2046c --- /dev/null +++ b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_left_arrow.png diff --git a/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png Binary files differnew file mode 100644 index 0000000..46abe31 --- /dev/null +++ b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_right_arrow.png diff --git a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png Binary files differindex 17811ae..17811ae 100644 --- a/library/eclair-mr1/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png +++ b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png diff --git a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png Binary files differnew file mode 100644 index 0000000..97fed92 --- /dev/null +++ b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png diff --git a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png Binary files differnew file mode 100644 index 0000000..f874955 --- /dev/null +++ b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png diff --git a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png Binary files differindex cb6a422..cb6a422 100644 --- a/library/eclair-mr1/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png +++ b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png diff --git a/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png Binary files differnew file mode 100644 index 0000000..a0ebbb9 --- /dev/null +++ b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_left_arrow.png diff --git a/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png Binary files differnew file mode 100644 index 0000000..c98b882 --- /dev/null +++ b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_right_arrow.png diff --git a/library/eclair-mr1/res/drawable/suw_card_bg_dark.xml b/library/gingerbread/res/drawable/suw_card_bg_dark.xml index cd115cf..cd115cf 100644 --- a/library/eclair-mr1/res/drawable/suw_card_bg_dark.xml +++ b/library/gingerbread/res/drawable/suw_card_bg_dark.xml diff --git a/library/eclair-mr1/res/drawable/suw_card_bg_light.xml b/library/gingerbread/res/drawable/suw_card_bg_light.xml index cd40dbe..cd40dbe 100644 --- a/library/eclair-mr1/res/drawable/suw_card_bg_light.xml +++ b/library/gingerbread/res/drawable/suw_card_bg_light.xml diff --git a/library/eclair-mr1/res/drawable/suw_ic_expand.xml b/library/gingerbread/res/drawable/suw_ic_expand.xml index 7e79f7d..7e79f7d 100644 --- a/library/eclair-mr1/res/drawable/suw_ic_expand.xml +++ b/library/gingerbread/res/drawable/suw_ic_expand.xml diff --git a/library/eclair-mr1/res/drawable/suw_ic_expand_less.xml b/library/gingerbread/res/drawable/suw_ic_expand_less.xml index e639a61..ef717c1 100644 --- a/library/eclair-mr1/res/drawable/suw_ic_expand_less.xml +++ b/library/gingerbread/res/drawable/suw_ic_expand_less.xml @@ -16,11 +16,13 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="18dp" - android:height="18dp" - android:viewportHeight="24.0" - android:viewportWidth="24.0"> + android:width="20dp" + android:height="24dp" + android:viewportWidth="20.0" + android:viewportHeight="24.0"> + <path android:fillColor="#ff000000" - android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z" /> + android:pathData="M10,9.17l-5,5 1.18,1.18L10,11.53l3.83,3.82L15,14.17z" /> + </vector> diff --git a/library/eclair-mr1/res/drawable/suw_ic_expand_more.xml b/library/gingerbread/res/drawable/suw_ic_expand_more.xml index ed63e21..ff3a0c5 100644 --- a/library/eclair-mr1/res/drawable/suw_ic_expand_more.xml +++ b/library/gingerbread/res/drawable/suw_ic_expand_more.xml @@ -16,11 +16,13 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="18dp" - android:height="18dp" - android:viewportWidth="24.0" + android:width="20dp" + android:height="24dp" + android:viewportWidth="20.0" android:viewportHeight="24.0"> + <path android:fillColor="#ff000000" - android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/> + android:pathData="M13.83,9.66L10,13.48 6.18,9.66 5,10.83l5,5 5,-5z"/> + </vector> diff --git a/library/eclair-mr1/res/drawable/suw_navbar_btn_bg_dark.xml b/library/gingerbread/res/drawable/suw_navbar_btn_bg_dark.xml index f4131e5..f4131e5 100644 --- a/library/eclair-mr1/res/drawable/suw_navbar_btn_bg_dark.xml +++ b/library/gingerbread/res/drawable/suw_navbar_btn_bg_dark.xml diff --git a/library/eclair-mr1/res/drawable/suw_navbar_btn_bg_light.xml b/library/gingerbread/res/drawable/suw_navbar_btn_bg_light.xml index 5d3bcfa..5d3bcfa 100644 --- a/library/eclair-mr1/res/drawable/suw_navbar_btn_bg_light.xml +++ b/library/gingerbread/res/drawable/suw_navbar_btn_bg_light.xml diff --git a/library/eclair-mr1/res/drawable/suw_navbar_ic_back.xml b/library/gingerbread/res/drawable/suw_navbar_ic_back.xml index 8a6e137..8a6e137 100644 --- a/library/eclair-mr1/res/drawable/suw_navbar_ic_back.xml +++ b/library/gingerbread/res/drawable/suw_navbar_ic_back.xml diff --git a/library/eclair-mr1/res/drawable/suw_navbar_ic_more.xml b/library/gingerbread/res/drawable/suw_navbar_ic_more.xml index 603e08d..603e08d 100644 --- a/library/eclair-mr1/res/drawable/suw_navbar_ic_more.xml +++ b/library/gingerbread/res/drawable/suw_navbar_ic_more.xml diff --git a/library/eclair-mr1/res/drawable/suw_navbar_ic_next.xml b/library/gingerbread/res/drawable/suw_navbar_ic_next.xml index e164e8a..e164e8a 100644 --- a/library/eclair-mr1/res/drawable/suw_navbar_ic_next.xml +++ b/library/gingerbread/res/drawable/suw_navbar_ic_next.xml diff --git a/library/eclair-mr1/res/layout/suw_items_expandable_switch.xml b/library/gingerbread/res/layout/suw_items_expandable_switch.xml index 91e482d..2b98a9f 100644 --- a/library/eclair-mr1/res/layout/suw_items_expandable_switch.xml +++ b/library/gingerbread/res/layout/suw_items_expandable_switch.xml @@ -94,7 +94,8 @@ android:id="@+id/suw_items_switch_divider" android:layout_width="1dp" android:layout_height="@dimen/suw_switch_divider_height" - android:layout_gravity="center_vertical" + android:layout_gravity="top" + android:layout_marginTop="@dimen/suw_switch_divider_padding_top" android:background="?android:attr/listDivider" /> <android.support.v7.widget.SwitchCompat @@ -102,6 +103,8 @@ style="@style/SuwSwitchStyle.Divided" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_gravity="center_vertical" /> + android:layout_gravity="top" + android:gravity="top" + android:paddingTop="@dimen/suw_switch_padding_top" /> </LinearLayout> diff --git a/library/eclair-mr1/res/layout/suw_items_switch.xml b/library/gingerbread/res/layout/suw_items_switch.xml index af326b2..af326b2 100644 --- a/library/eclair-mr1/res/layout/suw_items_switch.xml +++ b/library/gingerbread/res/layout/suw_items_switch.xml diff --git a/library/eclair-mr1/res/layout/suw_progress_bar.xml b/library/gingerbread/res/layout/suw_progress_bar.xml index 35e0faf..35e0faf 100644 --- a/library/eclair-mr1/res/layout/suw_progress_bar.xml +++ b/library/gingerbread/res/layout/suw_progress_bar.xml diff --git a/library/eclair-mr1/res/values/attrs.xml b/library/gingerbread/res/values/attrs.xml index 2a7b5cd..2a7b5cd 100644 --- a/library/eclair-mr1/res/values/attrs.xml +++ b/library/gingerbread/res/values/attrs.xml diff --git a/library/eclair-mr1/res/values/dimens.xml b/library/gingerbread/res/values/dimens.xml index d40d7de..d06673f 100644 --- a/library/eclair-mr1/res/values/dimens.xml +++ b/library/gingerbread/res/values/dimens.xml @@ -20,10 +20,12 @@ <!-- SwitchItem --> <dimen name="suw_switch_padding_start">16dp</dimen> <dimen name="suw_switch_padding_end">0dp</dimen> + <dimen name="suw_switch_padding_top">39dp</dimen> <dimen name="suw_switch_divider_height">32dp</dimen> + <dimen name="suw_switch_divider_padding_top">36dp</dimen> <dimen name="suw_switch_content_padding_end">16dp</dimen> <!-- ExpandableSwithItem --> - <dimen name="suw_expand_arrow_drawable_padding">6dp</dimen> + <dimen name="suw_expand_arrow_drawable_padding">4dp</dimen> </resources> diff --git a/library/eclair-mr1/res/values/styles.xml b/library/gingerbread/res/values/styles.xml index 1ea468d..6e525ef 100644 --- a/library/eclair-mr1/res/values/styles.xml +++ b/library/gingerbread/res/values/styles.xml @@ -24,6 +24,8 @@ <!-- Specify the indeterminateTintMode to work around a bug in Lollipop --> <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item> <item name="android:listPreferredItemHeight">@dimen/suw_items_preferred_height</item> + <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/suwMarginSides</item> + <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/suwMarginSides</item> <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item> <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item> <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item> @@ -33,10 +35,15 @@ <item name="android:windowSoftInputMode">adjustResize</item> <item name="colorAccent">@color/suw_color_accent_dark</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item> <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item> <item name="suwCardBackground">@drawable/suw_card_bg_dark</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item> <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item> <item name="suwNavBarTheme">@style/SuwNavBarThemeDark</item> @@ -48,6 +55,8 @@ <!-- Specify the indeterminateTintMode to work around a bug in Lollipop --> <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item> <item name="android:listPreferredItemHeight">@dimen/suw_items_preferred_height</item> + <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/suwMarginSides</item> + <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/suwMarginSides</item> <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item> <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item> <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item> @@ -57,10 +66,15 @@ <item name="android:windowSoftInputMode">adjustResize</item> <item name="colorAccent">@color/suw_color_accent_light</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item> <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item> <item name="suwCardBackground">@drawable/suw_card_bg_light</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item> <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item> <item name="suwNavBarTheme">@style/SuwNavBarThemeLight</item> @@ -68,6 +82,7 @@ </style> <style name="SuwThemeGlif" parent="Theme.AppCompat.NoActionBar"> + <item name="android:colorBackground">@color/suw_glif_background_color_dark</item> <item name="android:indeterminateTint" tools:ignore="NewApi">?attr/colorControlActivated</item> <!-- Specify the indeterminateTintMode to work around a bug in Lollipop --> <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item> @@ -77,7 +92,7 @@ <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item> <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item> <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item> - <item name="android:textColorLink">@color/suw_link_color_dark</item> + <item name="android:textColorLink">@color/suw_color_accent_glif_dark</item> <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item> <item name="android:windowDisablePreview">true</item> <item name="android:windowSoftInputMode">adjustResize</item> @@ -87,9 +102,13 @@ <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item> <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item> <item name="suwColorPrimary">?attr/colorPrimary</item> - <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item> <item name="suwGlifHeaderGravity">start</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item> <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item> <item name="suwScrollIndicators">bottom</item> @@ -98,6 +117,7 @@ </style> <style name="SuwThemeGlif.Light" parent="Theme.AppCompat.Light.NoActionBar"> + <item name="android:colorBackground">@color/suw_glif_background_color_light</item> <item name="android:indeterminateTint" tools:ignore="NewApi">?attr/colorControlActivated</item> <!-- Specify the indeterminateTintMode to work around a bug in Lollipop --> <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item> @@ -107,7 +127,7 @@ <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item> <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item> <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item> - <item name="android:textColorLink">@color/suw_link_color_light</item> + <item name="android:textColorLink">@color/suw_color_accent_glif_light</item> <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item> <item name="android:windowDisablePreview">true</item> <item name="android:windowSoftInputMode">adjustResize</item> @@ -117,9 +137,13 @@ <item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item> <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item> <item name="suwColorPrimary">?attr/colorPrimary</item> - <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item> <item name="suwGlifHeaderGravity">start</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item> <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item> <item name="suwScrollIndicators">bottom</item> @@ -176,7 +200,6 @@ <item name="buttonStyle">@style/SuwGlifButton.Primary</item> <!-- Values used in styles --> - <item name="android:minWidth">0dp</item> <item name="android:paddingLeft">@dimen/suw_glif_button_padding</item> <item name="android:paddingRight">@dimen/suw_glif_button_padding</item> </style> diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java b/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java index be9916e..be9916e 100644 --- a/library/eclair-mr1/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java +++ b/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/items/SwitchItem.java b/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java index 604e47f..7459d77 100644 --- a/library/eclair-mr1/src/com/android/setupwizardlib/items/SwitchItem.java +++ b/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java @@ -33,17 +33,35 @@ import com.android.setupwizardlib.R; */ public class SwitchItem extends Item implements CompoundButton.OnCheckedChangeListener { + /** + * Listener for check state changes of this switch item. + */ public interface OnCheckedChangeListener { + + /** + * Callback when checked state of a {@link SwitchItem} is changed. + * + * @see #setOnCheckedChangeListener(OnCheckedChangeListener) + */ void onCheckedChange(SwitchItem item, boolean isChecked); } private boolean mChecked = false; private OnCheckedChangeListener mListener; + /** + * Creates a default switch item. + */ public SwitchItem() { super(); } + /** + * Creates a switch item. This constructor is used for inflation from XML. + * + * @param context The context which this item is inflated in. + * @param attrs The XML attributes defined on the item. + */ public SwitchItem(Context context, AttributeSet attrs) { super(context, attrs); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwSwitchItem); @@ -51,6 +69,9 @@ public class SwitchItem extends Item implements CompoundButton.OnCheckedChangeLi a.recycle(); } + /** + * Sets whether this item should be checked. + */ public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; @@ -61,6 +82,9 @@ public class SwitchItem extends Item implements CompoundButton.OnCheckedChangeLi } } + /** + * @return True if this switch item is currently checked. + */ public boolean isChecked() { return mChecked; } @@ -91,6 +115,10 @@ public class SwitchItem extends Item implements CompoundButton.OnCheckedChangeLi switchView.setEnabled(isEnabled()); } + /** + * Sets a listener to listen for changes in checked state. This listener is invoked in both + * user toggling the switch and calls to {@link #setChecked(boolean)}. + */ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { mListener = listener; } diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java index d714eb8..1e663d6 100644 --- a/library/eclair-mr1/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java +++ b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java @@ -16,9 +16,8 @@ package com.android.setupwizardlib.util; -import static android.support.v4.os.BuildCompat.isAtLeastO; - import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; @@ -74,7 +73,7 @@ public class LinkAccessibilityHelper extends AccessibilityDelegateCompat { private final ExploreByTouchHelper mExploreByTouchHelper; public LinkAccessibilityHelper(TextView view) { - if (!isAtLeastO()) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) { // Pre-O, we essentially extend ExploreByTouchHelper to expose a virtual view hierarchy mExploreByTouchHelper = new ExploreByTouchHelper(view) { @Override @@ -90,15 +89,15 @@ public class LinkAccessibilityHelper extends AccessibilityDelegateCompat { @Override protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { - LinkAccessibilityHelper.this - .onPopulateEventForVirtualView(virtualViewId, event); + LinkAccessibilityHelper + .this.onPopulateEventForVirtualView(virtualViewId, event); } @Override protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfoCompat infoCompat) { - LinkAccessibilityHelper.this - .onPopulateNodeForVirtualView(virtualViewId, infoCompat); + LinkAccessibilityHelper + .this.onPopulateNodeForVirtualView(virtualViewId, infoCompat); } diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/view/NavigationBarButton.java b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java index 35e7050..5172c47 100644 --- a/library/eclair-mr1/src/com/android/setupwizardlib/view/NavigationBarButton.java +++ b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java @@ -26,6 +26,10 @@ import android.support.annotation.NonNull; import android.util.AttributeSet; import android.widget.Button; +/** + * Button for navigation bar, which includes tinting of its compound drawables to be used for dark + * and light themes. + */ public class NavigationBarButton extends Button { public NavigationBarButton(Context context) { @@ -120,7 +124,7 @@ public class NavigationBarButton extends Button { private ColorStateList mTintList = null; - public TintedDrawable(Drawable wrapped) { + TintedDrawable(Drawable wrapped) { super(new Drawable[] { wrapped }); } diff --git a/library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java index 6ccedf0..e6bc9da 100644 --- a/library/eclair-mr1/src/com/android/setupwizardlib/view/RichTextView.java +++ b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java @@ -21,6 +21,7 @@ import android.graphics.drawable.Drawable; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.support.v4.view.ViewCompat; +import android.support.v7.widget.AppCompatTextView; import android.text.Annotation; import android.text.SpannableString; import android.text.Spanned; @@ -30,9 +31,9 @@ import android.text.style.TextAppearanceSpan; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; -import android.widget.TextView; import com.android.setupwizardlib.span.LinkSpan; +import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener; import com.android.setupwizardlib.span.SpanHelper; import com.android.setupwizardlib.util.LinkAccessibilityHelper; @@ -40,7 +41,7 @@ import com.android.setupwizardlib.util.LinkAccessibilityHelper; * An extension of TextView that automatically replaces the annotation tags as specified in * {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)} */ -public class RichTextView extends TextView { +public class RichTextView extends AppCompatTextView implements OnLinkClickListener { /* static section */ @@ -89,6 +90,7 @@ public class RichTextView extends TextView { /* non-static section */ private LinkAccessibilityHelper mAccessibilityHelper; + private OnLinkClickListener mOnLinkClickListener; public RichTextView(Context context) { super(context); @@ -164,4 +166,20 @@ public class RichTextView extends TextView { } } } + + public void setOnLinkClickListener(OnLinkClickListener listener) { + mOnLinkClickListener = listener; + } + + public OnLinkClickListener getOnLinkClickListener() { + return mOnLinkClickListener; + } + + @Override + public boolean onLinkClick(LinkSpan span) { + if (mOnLinkClickListener != null) { + return mOnLinkClickListener.onLinkClick(span); + } + return false; + } } diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java index 74d3be6..74d3be6 100644 --- a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java +++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java index e2c492a..844e73e 100644 --- a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java +++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java @@ -16,12 +16,11 @@ package com.android.setupwizardlib.test; -import static android.support.v4.os.BuildCompat.isAtLeastO; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -59,7 +58,7 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVirtualViewAt() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10)); assertEquals("Virtual view ID should be 1", 1, virtualViewId); @@ -67,7 +66,7 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVirtualViewAtHost() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100)); assertEquals("Virtual view ID should be INVALID_ID", @@ -76,7 +75,7 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVisibleVirtualViews() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); List<Integer> virtualViewIds = new ArrayList<>(); mHelper.getVisibleVirtualViews(virtualViewIds); @@ -87,7 +86,7 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateEventForVirtualView() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityEvent event = AccessibilityEvent.obtain(); mHelper.onPopulateEventForVirtualView(1, event); @@ -101,7 +100,7 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateEventForVirtualViewHost() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityEvent event = AccessibilityEvent.obtain(); mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event); @@ -114,7 +113,7 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateNodeForVirtualView() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); mHelper.onPopulateNodeForVirtualView(1, info); @@ -133,7 +132,7 @@ public class LinkAccessibilityHelperTest { @Test public void testNullLayout() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); // Setting the padding will cause the layout to be null-ed out. mTextView.setPadding(1, 1, 1, 1); @@ -151,7 +150,7 @@ public class LinkAccessibilityHelperTest { @Test public void testRtlLayout() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום"); ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */); initTextView(ssb); @@ -171,7 +170,7 @@ public class LinkAccessibilityHelperTest { @Test public void testMultilineLink() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; SpannableStringBuilder ssb = new SpannableStringBuilder( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Praesent accumsan efficitur eros eu porttitor."); @@ -193,7 +192,7 @@ public class LinkAccessibilityHelperTest { @Test public void testRtlMultilineLink() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים " + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד " + "דפים המחשב מיזמים ב."; @@ -217,7 +216,7 @@ public class LinkAccessibilityHelperTest { @Test public void testBidiMultilineLink() { - if (isAtLeastO()) return; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים " + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד " + "דפים המחשב מיזמים ב."; diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java index 154339a..154339a 100644 --- a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java +++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java diff --git a/library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java index 0ae0737..0ae0737 100644 --- a/library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java +++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java diff --git a/library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java index d391d80..d391d80 100644 --- a/library/eclair-mr1/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java +++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java diff --git a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java new file mode 100644 index 0000000..43e7f03 --- /dev/null +++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java @@ -0,0 +1,69 @@ +/* + * 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.util; + +import static org.junit.Assert.assertEquals; +import static org.robolectric.RuntimeEnvironment.application; + +import android.content.Context; +import android.content.res.Resources; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; + +import com.android.setupwizardlib.BuildConfig; +import com.android.setupwizardlib.R; +import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(SuwLibRobolectricTestRunner.class) +@Config(constants = BuildConfig.class, sdk = Config.ALL_SDKS) +public class DimensionConsistencyTest { + + // Visual height of the framework switch widget + private static final int SWTICH_HEIGHT_DP = 26; + + private Context mContext; + + @Before + public void setUp() { + mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light); + } + + @Test + public void testSwitchPaddingTop() { + final Resources res = mContext.getResources(); + + assertEquals( + "Switch and divider should be aligned at center vertically: " + + "suw_switch_padding_top + SWITCH_HEIGHT / 2 = " + + "suw_switch_divider_padding_top + suw_switch_divider_height / 2", + res.getDimensionPixelSize(R.dimen.suw_switch_divider_padding_top) + + (res.getDimensionPixelSize(R.dimen.suw_switch_divider_height) / 2), + res.getDimensionPixelSize(R.dimen.suw_switch_padding_top) + + (dp2Px(SWTICH_HEIGHT_DP) / 2)); + } + + private int dp2Px(float dp) { + DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics); + } +} diff --git a/library/main/res/layout/suw_items_description.xml b/library/main/res/layout/suw_items_description.xml index 26a604b..e27d7b8 100644 --- a/library/main/res/layout/suw_items_description.xml +++ b/library/main/res/layout/suw_items_description.xml @@ -48,7 +48,7 @@ <com.android.setupwizardlib.view.RichTextView android:id="@+id/suw_items_title" - style="@style/SuwItemTitle.Description" + style="?attr/suwItemDescriptionTitleStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:lineSpacingExtra="@dimen/suw_description_line_spacing_extra" diff --git a/library/main/res/values-pt-rBR/strings.xml b/library/main/res/values-pt-rBR/strings.xml index 41032f8..79d86e2 100644 --- a/library/main/res/values-pt-rBR/strings.xml +++ b/library/main/res/values-pt-rBR/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="suw_next_button_label" msgid="7269625133873553978">"Próximo"</string> + <string name="suw_next_button_label" msgid="7269625133873553978">"Próxima"</string> <string name="suw_back_button_label" msgid="1460929053642711025">"Voltar"</string> <string name="suw_more_button_label" msgid="7769076059705546563">"Mais"</string> </resources> diff --git a/library/main/res/values-pt/strings.xml b/library/main/res/values-pt/strings.xml index 41032f8..79d86e2 100644 --- a/library/main/res/values-pt/strings.xml +++ b/library/main/res/values-pt/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="suw_next_button_label" msgid="7269625133873553978">"Próximo"</string> + <string name="suw_next_button_label" msgid="7269625133873553978">"Próxima"</string> <string name="suw_back_button_label" msgid="1460929053642711025">"Voltar"</string> <string name="suw_more_button_label" msgid="7769076059705546563">"Mais"</string> </resources> diff --git a/library/main/res/values-sw600dp/dimens.xml b/library/main/res/values-sw600dp/dimens.xml index 2b5f3be..d1734ab 100644 --- a/library/main/res/values-sw600dp/dimens.xml +++ b/library/main/res/values-sw600dp/dimens.xml @@ -32,6 +32,8 @@ <dimen name="suw_header_title_size">34sp</dimen> <!-- Items --> + <dimen name="suw_items_icon_divider_inset">88dp</dimen> + <dimen name="suw_items_text_divider_inset">40dp</dimen> <dimen name="suw_items_glif_icon_divider_inset">88dp</dimen> <dimen name="suw_items_glif_text_divider_inset">40dp</dimen> diff --git a/library/main/res/values-v21/styles.xml b/library/main/res/values-v21/styles.xml index fddfdc7..ab6f887 100644 --- a/library/main/res/values-v21/styles.xml +++ b/library/main/res/values-v21/styles.xml @@ -36,6 +36,12 @@ <item name="android:background">?android:attr/colorPrimary</item> </style> + <!-- Button styles --> + + <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary"> + <item name="android:fontFamily">sans-serif-medium</item> + </style> + <!-- Items styles --> <style name="SuwItemContainer"> diff --git a/library/main/res/values/attrs.xml b/library/main/res/values/attrs.xml index 14799df..36d5fb7 100644 --- a/library/main/res/values/attrs.xml +++ b/library/main/res/values/attrs.xml @@ -39,6 +39,7 @@ </attr> <attr name="suwCardBackground" format="color|reference" /> + <attr name="suwFillContentLayoutStyle" format="reference" /> <attr name="suwDividerCondition"> <enum name="either" value="0" /> <enum name="both" value="1" /> @@ -72,8 +73,13 @@ <!-- Custom view attributes --> <attr name="suwColorPrimary" format="color" /> <attr name="suwHeader" format="reference" /> + <!-- Deprecated. Use suwDividerInsetStart and suwDividerInsetEnd instead --> <attr name="suwDividerInset" format="dimension|reference" /> + <attr name="suwDividerInsetEnd" format="dimension|reference" /> + <attr name="suwDividerInsetStart" format="dimension|reference" /> + <attr name="suwDividerInsetStartNoIcon" format="dimension|reference" /> <attr name="suwItemDescriptionStyle" format="reference" /> + <attr name="suwItemDescriptionTitleStyle" format="reference" /> <declare-styleable name="SuwIllustration"> <attr name="suwAspectRatio" format="float" /> @@ -87,6 +93,10 @@ <attr name="suwHeader" /> </declare-styleable> + <declare-styleable name="SuwIllustrationVideoView"> + <attr name="suwVideo" format="reference" /> + </declare-styleable> + <declare-styleable name="SuwGlifLayout"> <attr name="suwBackgroundPatterned" format="boolean" /> <attr name="suwBackgroundBaseColor" format="color" /> @@ -98,11 +108,16 @@ <attr name="suwStatusBarBackground" format="color|reference" /> </declare-styleable> - <declare-styleable name="SuwMaxSizeFrameLayout"> + <declare-styleable name="SuwIntrinsicSizeFrameLayout"> <attr name="android:height" /> <attr name="android:width" /> </declare-styleable> + <declare-styleable name="SuwFillContentLayout"> + <attr name="android:maxHeight" /> + <attr name="android:maxWidth" /> + </declare-styleable> + <declare-styleable name="SuwSetupWizardLayout"> <attr name="suwBackground" format="color|reference" /> <attr name="suwBackgroundTile" format="color|reference" /> @@ -159,6 +174,8 @@ <declare-styleable name="SuwListMixin"> <attr name="android:entries" /> <attr name="suwDividerInset" /> + <attr name="suwDividerInsetEnd" /> + <attr name="suwDividerInsetStart" /> </declare-styleable> </resources> diff --git a/library/main/res/values/colors.xml b/library/main/res/values/colors.xml index f9c5da3..cd57a8a 100644 --- a/library/main/res/values/colors.xml +++ b/library/main/res/values/colors.xml @@ -40,5 +40,7 @@ <!-- GLIF colors --> <color name="suw_color_accent_glif_dark">#ff4285f4</color> <color name="suw_color_accent_glif_light">#ff4285f4</color> + <color name="suw_glif_background_color_dark">#ff000000</color> + <color name="suw_glif_background_color_light">#ffffffff</color> </resources> diff --git a/library/main/res/values/dimens.xml b/library/main/res/values/dimens.xml index 96712f7..14d7429 100644 --- a/library/main/res/values/dimens.xml +++ b/library/main/res/values/dimens.xml @@ -25,7 +25,7 @@ <!-- Calculated by (suw_glif_margin_sides - suw_glif_button_padding) --> <dimen name="suw_glif_button_margin_start">8dp</dimen> <dimen name="suw_glif_button_padding">16dp</dimen> - <dimen name="suw_glif_footer_bottom_padding">4dp</dimen> + <dimen name="suw_glif_footer_bottom_padding">0dp</dimen> <dimen name="suw_glif_footer_min_height">80dp</dimen> <dimen name="suw_glif_margin_sides">24dp</dimen> <dimen name="suw_glif_margin_top">48dp</dimen> @@ -49,6 +49,12 @@ <dimen name="suw_description_glif_margin_top">3dp</dimen> <dimen name="suw_description_glif_margin_bottom_lists">24dp</dimen> + <dimen name="suw_content_illustration_max_height">312dp</dimen> + <dimen name="suw_content_illustration_max_width">312dp</dimen> + <dimen name="suw_content_illustration_min_height">172dp</dimen> + <dimen name="suw_content_illustration_min_width">172dp</dimen> + <dimen name="suw_content_illustration_padding_vertical">24dp</dimen> + <!-- Margin on the start to offset for margin in the drawable --> <dimen name="suw_radio_button_margin_start">-6dp</dimen> <dimen name="suw_radio_button_margin_top">0dp</dimen> @@ -116,7 +122,7 @@ <dimen name="suw_items_preferred_height">56dp</dimen> <!-- Navigation bar --> - <!-- TODO: Move this dimen to eclair-mr1, or see if it should be in platform style as well --> + <!-- TODO: Move this dimen to gingerbread, or see if it should be in platform style as well --> <dimen name="suw_navbar_button_drawable_padding" tools:ignore="UnusedResources">6dp</dimen> <dimen name="suw_navbar_button_padding_sides">10dp</dimen> <dimen name="suw_navbar_height">56dp</dimen> diff --git a/library/main/res/values/styles.xml b/library/main/res/values/styles.xml index 4bd99de..736bcc4 100644 --- a/library/main/res/values/styles.xml +++ b/library/main/res/values/styles.xml @@ -19,24 +19,38 @@ <!-- General styles --> - <style name="SuwThemeGlifPixel" parent="SuwThemeGlif"> + <style name="SuwThemeGlifV2" parent="SuwThemeGlif"> + <item name="android:colorBackground">@color/suw_glif_background_color_dark</item> <item name="android:windowLightStatusBar" tools:targetApi="m">false</item> <item name="suwBackgroundBaseColor">?android:attr/colorBackground</item> <item name="suwBackgroundPatterned">false</item> + <item name="suwDividerInsetEnd">?attr/suwMarginSides</item> + <item name="suwDividerInsetStart">?attr/suwMarginSides</item> + <item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item> <item name="suwGlifHeaderGravity">center_horizontal</item> <item name="suwScrollIndicators">top|bottom</item> </style> - <style name="SuwThemeGlifPixel.Light" parent="SuwThemeGlif.Light"> + <!-- Deprecated. Use SuwThemeGlifV2 instead --> + <style name="SuwThemeGlifPixel" parent="SuwThemeGlifV2" /> + + <style name="SuwThemeGlifV2.Light" parent="SuwThemeGlif.Light"> + <item name="android:colorBackground">@color/suw_glif_background_color_light</item> <item name="android:windowLightStatusBar" tools:targetApi="m">true</item> <item name="suwBackgroundBaseColor">?android:attr/colorBackground</item> <item name="suwBackgroundPatterned">false</item> + <item name="suwDividerInsetEnd">?attr/suwMarginSides</item> + <item name="suwDividerInsetStart">?attr/suwMarginSides</item> + <item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item> <item name="suwGlifHeaderGravity">center_horizontal</item> <item name="suwScrollIndicators">top|bottom</item> </style> + <!-- Deprecated. Use SuwThemeGlifV2.Light instead --> + <style name="SuwThemeGlifPixel.Light" parent="SuwThemeGlifV2.Light" /> + <style name="Animation.SuwWindowAnimation" parent="@android:style/Animation.Activity"> <item name="android:activityOpenEnterAnimation">@anim/suw_slide_next_in</item> <item name="android:activityOpenExitAnimation">@anim/suw_slide_next_out</item> @@ -109,6 +123,21 @@ <item name="android:gravity">top</item> </style> + <style name="SuwFillContentLayout"> + <item name="android:minWidth">@dimen/suw_content_illustration_min_width</item> + <item name="android:minHeight">@dimen/suw_content_illustration_min_height</item> + <item name="android:maxWidth">@dimen/suw_content_illustration_max_width</item> + <item name="android:maxHeight">@dimen/suw_content_illustration_max_height</item> + <item name="android:paddingTop">@dimen/suw_content_illustration_padding_vertical</item> + <item name="android:paddingBottom">@dimen/suw_content_illustration_padding_vertical</item> + </style> + + <!-- Ignore UnusedResources: used by clients --> + <style name="SuwContentIllustration" tools:ignore="UnusedResources"> + <item name="android:layout_gravity">center</item> + <item name="android:scaleType">fitCenter</item> + </style> + <!-- Card layout (for tablets) --> <style name="SuwBaseCardTitle"> @@ -160,7 +189,7 @@ <!-- Use this style with a Button not a TextView, so that TalkBack will announce it as a button. --> - <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.Secondary"> + <style name="SuwGlifButton.BaseTertiary" parent="SuwGlifButton.Secondary"> <!-- This style can be applied to a button either as a "style" in XML, or as a theme in ContextThemeWrapper. These self-referencing attributes make sure this is applied as both to the button. --> @@ -169,13 +198,17 @@ <item name="android:background">@null</item> <item name="android:fontFamily" tools:targetApi="jelly_bean">sans-serif</item> + <item name="android:layout_gravity">?attr/suwGlifHeaderGravity</item> <item name="android:padding">0dp</item> <item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">false</item> </style> + <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary" /> + <!-- The start and end paddings are asymmetric because start buttons are borderless buttons which aligns the text label. --> <style name="SuwGlifButtonBar"> + <item name="android:baselineAligned">false</item> <item name="android:clipChildren">false</item> <item name="android:clipToPadding">false</item> <item name="android:gravity">center_vertical</item> @@ -206,7 +239,7 @@ <item name="android:paddingTop">@dimen/suw_items_verbose_padding_vertical</item> </style> - <style name="SuwItemTitle.Description" parent="SuwItemTitle"> + <style name="SuwItemTitle.GlifDescription" parent="SuwItemTitle"> <item name="android:gravity">?attr/suwGlifHeaderGravity</item> <item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item> </style> @@ -228,6 +261,7 @@ <item name="android:layout_marginLeft">?attr/suwMarginSides</item> <item name="android:layout_marginRight">?attr/suwMarginSides</item> <item name="android:layout_marginTop">@dimen/suw_glif_header_title_margin_top</item> + <item name="android:fontFamily" tools:targetApi="jelly_bean">google-sans</item> <item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> diff --git a/library/main/src/com/android/setupwizardlib/GlifListLayout.java b/library/main/src/com/android/setupwizardlib/GlifListLayout.java index c6443f9..8266e5f 100644 --- a/library/main/src/com/android/setupwizardlib/GlifListLayout.java +++ b/library/main/src/com/android/setupwizardlib/GlifListLayout.java @@ -113,27 +113,51 @@ public class GlifListLayout extends GlifLayout { } /** + * @deprecated Use {@link #setDividerInsets(int, int)} instead. + */ + @Deprecated + public void setDividerInset(int inset) { + mListMixin.setDividerInset(inset); + } + + /** * 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). + * theme and apply insets to it. * - * @param inset The number of pixels to inset on the "start" side of the list divider. Typically + * @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. * - * @see ListMixin#setDividerInset(int) + * @see ListMixin#setDividerInsets(int, int) */ - public void setDividerInset(int inset) { - mListMixin.setDividerInset(inset); + public void setDividerInsets(int start, int end) { + mListMixin.setDividerInsets(start, end); } /** - * @see ListMixin#getDividerInset() + * @deprecated Use {@link #getDividerInsetStart()} instead. */ + @Deprecated public int getDividerInset() { return mListMixin.getDividerInset(); } /** + * @see ListMixin#getDividerInsetStart() + */ + public int getDividerInsetStart() { + return mListMixin.getDividerInsetStart(); + } + + /** + * @see ListMixin#getDividerInsetEnd() + */ + public int getDividerInsetEnd() { + return mListMixin.getDividerInsetEnd(); + } + + /** * @see ListMixin#getDivider() */ public Drawable getDivider() { diff --git a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java index e31edb3..51c1a49 100644 --- a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java +++ b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java @@ -228,7 +228,7 @@ public class GlifPatternDrawable extends Drawable { p.close(); p = sPatternPaths[5] = new Path(); - p.moveTo(178.44286f, 766.85714f); + p.moveTo(178.44286f, 766.8571f); p.lineTo(308.7f, 768f); p.cubicTo(381.7f, 604.6f, 481.6f, 344.3f, 562.2f, 0f); p.lineTo(0f, 0f); diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java index 0457451..050d566 100644 --- a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java +++ b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java @@ -113,19 +113,51 @@ public class SetupWizardListLayout extends SetupWizardLayout { * {@code @dimen/suw_items_text_divider_inset}. * * @see ListMixin#setDividerInset(int) + * @deprecated Use {@link #setDividerInsets(int, int)} instead. */ + @Deprecated public void setDividerInset(int inset) { mListMixin.setDividerInset(inset); } /** - * @see ListMixin#getDividerInset() + * 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 ListMixin#setDividerInsets(int, int) + */ + public void setDividerInsets(int start, int end) { + mListMixin.setDividerInsets(start, end); + } + + /** + * @deprecated Use {@link #getDividerInsetStart()} instead. */ + @Deprecated public int getDividerInset() { return mListMixin.getDividerInset(); } /** + * @see ListMixin#getDividerInsetStart() + */ + public int getDividerInsetStart() { + return mListMixin.getDividerInsetStart(); + } + + /** + * @see ListMixin#getDividerInsetEnd() + */ + public int getDividerInsetEnd() { + return mListMixin.getDividerInsetEnd(); + } + + /** * @see ListMixin#getDivider() */ public Drawable getDivider() { diff --git a/library/main/src/com/android/setupwizardlib/items/Item.java b/library/main/src/com/android/setupwizardlib/items/Item.java index 59ab1a1..fc8823e 100644 --- a/library/main/src/com/android/setupwizardlib/items/Item.java +++ b/library/main/src/com/android/setupwizardlib/items/Item.java @@ -155,8 +155,7 @@ public class Item extends AbstractItem { // Set the image drawable to null before setting the state and level to avoid affecting // any recycled drawable in the ImageView iconView.setImageDrawable(null); - iconView.setImageState(icon.getState(), false /* merge */); - iconView.setImageLevel(icon.getLevel()); + onMergeIconStateAndLevels(iconView, icon); iconView.setImageDrawable(icon); iconContainer.setVisibility(View.VISIBLE); } else { @@ -165,4 +164,14 @@ public class Item extends AbstractItem { view.setId(getViewId()); } + + /** + * Copies state and level information from {@link #getIcon()} to the currently bound view's + * ImageView. Subclasses can override this method to change whats being copied from the icon + * to the ImageView. + */ + protected void onMergeIconStateAndLevels(ImageView iconView, Drawable icon) { + iconView.setImageState(icon.getState(), false /* merge */); + iconView.setImageLevel(icon.getLevel()); + } } diff --git a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java index e4f9854..a5f0424 100644 --- a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java +++ b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java @@ -17,8 +17,10 @@ package com.android.setupwizardlib.span; import android.content.Context; +import android.content.ContextWrapper; import android.graphics.Typeface; import android.os.Build; +import android.support.annotation.Nullable; import android.text.TextPaint; import android.text.style.ClickableSpan; import android.util.Log; @@ -26,8 +28,8 @@ import android.view.View; /** * A clickable span that will listen for click events and send it back to the context. To use this - * class, implement {@link com.android.setupwizardlib.span.LinkSpan.OnClickListener} in your - * context (typically your Activity). + * class, implement {@link OnLinkClickListener} in your TextView, or use + * {@link com.android.setupwizardlib.view.RichTextView#setOnClickListener(View.OnClickListener)}. * * <p />Note on accessibility: For TalkBack to be able to traverse and interact with the links, you * should use {@code LinkAccessibilityHelper} in your {@code TextView} subclass. Optionally you can @@ -49,10 +51,29 @@ public class LinkSpan extends ClickableSpan { private static final Typeface TYPEFACE_MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL); + /** + * @deprecated Use {@link OnLinkClickListener} + */ + @Deprecated public interface OnClickListener { void onClick(LinkSpan span); } + /** + * Listener that is invoked when a link span is clicked. If the containing view of this span + * implements this interface, this will be invoked when the link is clicked. + */ + public interface OnLinkClickListener { + + /** + * Called when a link has been clicked. + * + * @param span The span that was clicked. + * @return True if the click was handled, stopping further propagation of the click event. + */ + boolean onLinkClick(LinkSpan span); + } + /* non-static section */ private final String mId; @@ -63,9 +84,7 @@ public class LinkSpan extends ClickableSpan { @Override public void onClick(View view) { - final Context context = view.getContext(); - if (context instanceof OnClickListener) { - ((OnClickListener) context).onClick(this); + if (dispatchClick(view)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { view.cancelPendingInputEvents(); } @@ -74,6 +93,41 @@ public class LinkSpan extends ClickableSpan { } } + private boolean dispatchClick(View view) { + boolean handled = false; + if (view instanceof OnLinkClickListener) { + handled = ((OnLinkClickListener) view).onLinkClick(this); + } + if (!handled) { + final OnClickListener listener = getLegacyListenerFromContext(view.getContext()); + if (listener != null) { + listener.onClick(this); + handled = true; + } + } + return handled; + } + + /** + * @deprecated Deprecated together with {@link OnClickListener} + */ + @Nullable + @Deprecated + private OnClickListener getLegacyListenerFromContext(@Nullable Context context) { + while (true) { + if (context instanceof OnClickListener) { + return (OnClickListener) context; + } else if (context instanceof ContextWrapper) { + // Unwrap any context wrapper, in base the base context implements onClickListener. + // ContextWrappers cannot have circular base contexts, so at some point this will + // reach the one of the other cases and return. + context = ((ContextWrapper) context).getBaseContext(); + } else { + return null; + } + } + } + @Override public void updateDrawState(TextPaint drawState) { super.updateDrawState(drawState); diff --git a/library/main/src/com/android/setupwizardlib/template/ListMixin.java b/library/main/src/com/android/setupwizardlib/template/ListMixin.java index 497d46e..10e85a9 100644 --- a/library/main/src/com/android/setupwizardlib/template/ListMixin.java +++ b/library/main/src/com/android/setupwizardlib/template/ListMixin.java @@ -49,7 +49,9 @@ public class ListMixin implements Mixin { private Drawable mDivider; private Drawable mDefaultDivider; - private int mDividerInset; + + private int mDividerInsetStart; + private int mDividerInsetEnd; /** * @param layout The layout this mixin belongs to. @@ -69,8 +71,16 @@ public class ListMixin implements Mixin { setAdapter(new ItemAdapter(inflated)); } int dividerInset = - a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInset, 0); - setDividerInset(dividerInset); + a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInset, -1); + if (dividerInset != -1) { + setDividerInset(dividerInset); + } else { + int dividerInsetStart = + a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetStart, 0); + int dividerInsetEnd = + a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetEnd, 0); + setDividerInsets(dividerInsetStart, dividerInsetEnd); + } a.recycle(); } @@ -136,23 +146,49 @@ public class ListMixin implements Mixin { } /** + * @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 inset it {@code inset} pixels to the right (or left in RTL layouts). + * theme and apply insets to it. * - * @param inset The number of pixels to inset on the "start" side of the list divider. Typically + * @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 setDividerInset(int inset) { - mDividerInset = inset; + 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 mDividerInset; + 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() { @@ -170,9 +206,9 @@ public class ListMixin implements Mixin { } mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable( mDefaultDivider, - mDividerInset /* start */, + mDividerInsetStart /* start */, 0 /* top */, - 0 /* end */, + mDividerInsetEnd /* end */, 0 /* bottom */, mTemplateLayout); listView.setDivider(mDivider); diff --git a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java index 5ccd081..a93694c 100644 --- a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java +++ b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java @@ -43,6 +43,8 @@ public class WizardManagerHelper { private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode"; @VisibleForTesting static final String EXTRA_IS_FIRST_RUN = "firstRun"; + @VisibleForTesting + static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup"; public static final String EXTRA_THEME = "theme"; public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode"; @@ -71,13 +73,25 @@ public class WizardManagerHelper { * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the * theme used in setup wizard for O DR. */ - public static final String THEME_GLIF_PIXEL = "glif_pixel"; + public static final String THEME_GLIF_V2 = "glif_v2"; + + /** + * @deprecated Use {@link #THEME_GLIF_V2} instead. + */ + @Deprecated + public static final String THEME_GLIF_PIXEL = THEME_GLIF_V2; /** * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in * setup wizard for O DR. */ - public static final String THEME_GLIF_PIXEL_LIGHT = "glif_pixel_light"; + public static final String THEME_GLIF_V2_LIGHT = "glif_v2_light"; + + /** + * @deprecated Use {@link #THEME_GLIF_V2_LIGHT} instead. + */ + @Deprecated + public static final String THEME_GLIF_PIXEL_LIGHT = THEME_GLIF_V2_LIGHT; /** * Get an intent that will invoke the next step of setup wizard. @@ -129,6 +143,8 @@ public class WizardManagerHelper { dstIntent.putExtra(EXTRA_THEME, srcIntent.getStringExtra(EXTRA_THEME)); dstIntent.putExtra(EXTRA_IS_FIRST_RUN, srcIntent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false)); + dstIntent.putExtra(EXTRA_IS_DEFERRED_SETUP, + srcIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false)); dstIntent.putExtra(EXTRA_SCRIPT_URI, srcIntent.getStringExtra(EXTRA_SCRIPT_URI)); dstIntent.putExtra(EXTRA_ACTION_ID, srcIntent.getStringExtra(EXTRA_ACTION_ID)); } @@ -185,6 +201,18 @@ public class WizardManagerHelper { } /** + * Checks whether an intent is running in the deferred setup wizard flow. + * + * @param originalIntent The original intent that was used to start the step, usually via + * {@link android.app.Activity#getIntent()}. + * @return true if the intent passed in was running in deferred setup wizard. + */ + public static boolean isDeferredSetupWizard(Intent originalIntent) { + return originalIntent != null + && originalIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false); + } + + /** * Checks the intent whether the extra indicates that the light theme should be used or not. If * the theme is not specified in the intent, or the theme specified is unknown, the value def * will be returned. @@ -208,10 +236,10 @@ public class WizardManagerHelper { */ public static boolean isLightTheme(String theme, boolean def) { if (THEME_HOLO_LIGHT.equals(theme) || THEME_MATERIAL_LIGHT.equals(theme) - || THEME_GLIF_LIGHT.equals(theme) || THEME_GLIF_PIXEL_LIGHT.equals(theme)) { + || THEME_GLIF_LIGHT.equals(theme) || THEME_GLIF_V2_LIGHT.equals(theme)) { return true; } else if (THEME_HOLO.equals(theme) || THEME_MATERIAL.equals(theme) - || THEME_GLIF.equals(theme) || THEME_GLIF_PIXEL.equals(theme)) { + || THEME_GLIF.equals(theme) || THEME_GLIF_V2.equals(theme)) { return false; } else { return def; @@ -256,10 +284,10 @@ public class WizardManagerHelper { public static @StyleRes int getThemeRes(String theme, @StyleRes int defaultTheme) { if (theme != null) { switch (theme) { - case THEME_GLIF_PIXEL_LIGHT: - return R.style.SuwThemeGlifPixel_Light; - case THEME_GLIF_PIXEL: - return R.style.SuwThemeGlifPixel; + case THEME_GLIF_V2_LIGHT: + return R.style.SuwThemeGlifV2_Light; + case THEME_GLIF_V2: + return R.style.SuwThemeGlifV2; case THEME_GLIF_LIGHT: return R.style.SuwThemeGlif_Light; case THEME_GLIF: @@ -268,6 +296,8 @@ public class WizardManagerHelper { return R.style.SuwThemeMaterial_Light; case THEME_MATERIAL: return R.style.SuwThemeMaterial; + default: + // fall through } } return defaultTheme; diff --git a/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java b/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java new file mode 100644 index 0000000..2c28090 --- /dev/null +++ b/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java @@ -0,0 +1,125 @@ +/* + * 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.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.setupwizardlib.R; + +/** + * A layout that will measure its children size based on the space it is given, by using its + * {@code android:minWidth}, {@code android:minHeight}, {@code android:maxWidth}, and + * {@code android:maxHeight} values. + * + * <p>Typically this is used to show an illustration image or video on the screen. For optimal UX, + * those assets typically want to occupy the remaining space available on screen within a certain + * range, and then stop scaling beyond the min/max size attributes. Therefore this view is typically + * used inside a ScrollView with {@code fillViewport} set to true, together with a linear layout + * weight or relative layout to fill the remaining space visible on screen. + * + * <p>When measuring, this view ignores its children and simply layout according to the minWidth / + * minHeight given. Therefore it is common for children of this layout to have width / height set to + * {@code match_parent}. The maxWidth / maxHeight values will then be applied to the children to + * make sure they are not too big. + */ +public class FillContentLayout extends FrameLayout { + + private int mMaxWidth; + private int mMaxHeight; + + public FillContentLayout(Context context) { + this(context, null); + } + + public FillContentLayout(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.suwFillContentLayoutStyle); + } + + public FillContentLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs, defStyleAttr); + } + + private void init(Context context, AttributeSet attrs, int defStyleAttr) { + TypedArray a = context.obtainStyledAttributes( + attrs, + R.styleable.SuwFillContentLayout, + defStyleAttr, + 0); + + mMaxHeight = a.getDimensionPixelSize( + R.styleable.SuwFillContentLayout_android_maxHeight, -1); + mMaxWidth = a.getDimensionPixelSize(R.styleable.SuwFillContentLayout_android_maxWidth, -1); + + a.recycle(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Measure this view with the minWidth and minHeight, without asking the children. + // (Children size is the drawable's intrinsic size, and we don't want that to influence + // the size of the illustration). + setMeasuredDimension( + getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), + getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); + + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + measureIllustrationChild(getChildAt(i), getMeasuredWidth(), getMeasuredHeight()); + } + } + + private void measureIllustrationChild(View child, int parentWidth, int parentHeight) { + // Modified from ViewGroup#measureChildWithMargins + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + // Create measure specs that are no bigger than min(parentSize, maxSize) + + int childWidthMeasureSpec = getMaxSizeMeasureSpec( + Math.min(mMaxWidth, parentWidth), + getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, + lp.width); + int childHeightMeasureSpec = getMaxSizeMeasureSpec( + Math.min(mMaxHeight, parentHeight), + getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, + lp.height); + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + private static int getMaxSizeMeasureSpec(int maxSize, int padding, int childDimension) { + // Modified from ViewGroup#getChildMeasureSpec + int size = Math.max(0, maxSize - padding); + + if (childDimension >= 0) { + // Child wants a specific size... so be it + return MeasureSpec.makeMeasureSpec(childDimension, MeasureSpec.EXACTLY); + } else if (childDimension == LayoutParams.MATCH_PARENT) { + // Child wants to be our size. So be it. + return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); + } else if (childDimension == LayoutParams.WRAP_CONTENT) { + // Child wants to determine its own size. It can't be + // bigger than us. + return MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST); + } + return 0; + } +} diff --git a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java new file mode 100644 index 0000000..989f3e6 --- /dev/null +++ b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java @@ -0,0 +1,230 @@ +/* + * 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.view; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.SurfaceTexture; +import android.graphics.drawable.Animatable; +import android.media.MediaPlayer; +import android.os.Build.VERSION_CODES; +import android.support.annotation.RawRes; +import android.support.annotation.VisibleForTesting; +import android.util.AttributeSet; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; + +import com.android.setupwizardlib.R; + +/** + * A view for displaying videos in a continuous loop (without audio). This is typically used for + * animated illustrations. + * + * <p>The video can be specified using {@code app:suwVideo}, specifying the raw resource to the mp4 + * video. Optionally, {@code app:suwLoopStartMs} can be used to specify which part of the video it + * should loop back to + * + * <p>For optimal file size, use avconv or other video compression tool to remove the unused audio + * track and reduce the size of your video asset: + * avconv -i [input file] -vcodec h264 -crf 20 -an [output_file] + */ +@TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH) +public class IllustrationVideoView extends TextureView implements Animatable, + TextureView.SurfaceTextureListener, + MediaPlayer.OnPreparedListener, + MediaPlayer.OnSeekCompleteListener, + MediaPlayer.OnInfoListener { + + protected float mAspectRatio = 1.0f; // initial guess until we know + + @VisibleForTesting MediaPlayer mMediaPlayer; + + private @RawRes int mVideoResId = 0; + + @VisibleForTesting Surface mSurface; + + public IllustrationVideoView(Context context, AttributeSet attrs) { + super(context, attrs); + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.SuwIllustrationVideoView); + mVideoResId = a.getResourceId(R.styleable.SuwIllustrationVideoView_suwVideo, 0); + a.recycle(); + + setSurfaceTextureListener(this); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + + if (height < width * mAspectRatio) { + // Height constraint is tighter. Need to scale down the width to fit aspect ratio. + width = (int) (height / mAspectRatio); + } else { + // Width constraint is tighter. Need to scale down the height to fit aspect ratio. + height = (int) (width * mAspectRatio); + } + + super.onMeasure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + } + + /** + * Set the video to be played by this view. + * + * @param resId Resource ID of the video, typically an MP4 under res/raw. + */ + public void setVideoResource(@RawRes int resId) { + if (resId != mVideoResId) { + mVideoResId = resId; + createMediaPlayer(); + } + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (hasWindowFocus) { + start(); + } else { + stop(); + } + } + + /** + * Creates a media player for the current URI. The media player will be started immediately if + * the view's window is visible. If there is an existing media player, it will be released. + */ + private void createMediaPlayer() { + if (mMediaPlayer != null) { + mMediaPlayer.release(); + } + if (mSurface == null || mVideoResId == 0) { + return; + } + + mMediaPlayer = MediaPlayer.create(getContext(), mVideoResId); + + mMediaPlayer.setSurface(mSurface); + mMediaPlayer.setOnPreparedListener(this); + mMediaPlayer.setOnSeekCompleteListener(this); + mMediaPlayer.setOnInfoListener(this); + + float aspectRatio = (float) mMediaPlayer.getVideoHeight() / mMediaPlayer.getVideoWidth(); + if (mAspectRatio != aspectRatio) { + mAspectRatio = aspectRatio; + requestLayout(); + } + if (getWindowVisibility() == View.VISIBLE) { + start(); + } + } + + /** + * Whether the media player should play the video in a continuous loop. The default value is + * true. + */ + protected boolean shouldLoop() { + return true; + } + + /** + * Release any resources used by this view. This is automatically called in + * onSurfaceTextureDestroyed so in most cases you don't have to call this. + */ + public void release() { + if (mMediaPlayer != null) { + mMediaPlayer.stop(); + mMediaPlayer.release(); + mMediaPlayer = null; + } + if (mSurface != null) { + mSurface.release(); + mSurface = null; + } + } + + /* SurfaceTextureListener methods */ + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { + // Keep the view hidden until video starts + setVisibility(View.INVISIBLE); + mSurface = new Surface(surfaceTexture); + createMediaPlayer(); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { + release(); + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + } + + /* Animatable methods */ + + @Override + public void start() { + if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) { + mMediaPlayer.start(); + } + } + + @Override + public void stop() { + if (mMediaPlayer != null) { + mMediaPlayer.pause(); + } + } + + @Override + public boolean isRunning() { + return mMediaPlayer != null && mMediaPlayer.isPlaying(); + } + + /* MediaPlayer callbacks */ + + @Override + public boolean onInfo(MediaPlayer mp, int what, int extra) { + if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { + // Video available, show view now + setVisibility(View.VISIBLE); + } + return false; + } + + @Override + public void onPrepared(MediaPlayer mp) { + mp.setLooping(shouldLoop()); + } + + @Override + public void onSeekComplete(MediaPlayer mp) { + mp.start(); + } +} diff --git a/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java b/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java index e9ab1a7..02fdcc7 100644 --- a/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java +++ b/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java @@ -56,11 +56,11 @@ public class IntrinsicSizeFrameLayout extends FrameLayout { private void init(Context context, AttributeSet attrs, int defStyleAttr) { final TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.SuwMaxSizeFrameLayout, defStyleAttr, 0); + R.styleable.SuwIntrinsicSizeFrameLayout, defStyleAttr, 0); mIntrinsicHeight = - a.getDimensionPixelSize(R.styleable.SuwMaxSizeFrameLayout_android_height, 0); + a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_height, 0); mIntrinsicWidth = - a.getDimensionPixelSize(R.styleable.SuwMaxSizeFrameLayout_android_width, 0); + a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_width, 0); a.recycle(); } diff --git a/library/platform/res/values-v23/styles.xml b/library/platform/res/values-v23/styles.xml index 59265d9..2eb5caf 100644 --- a/library/platform/res/values-v23/styles.xml +++ b/library/platform/res/values-v23/styles.xml @@ -41,7 +41,12 @@ <item name="android:windowSoftInputMode">adjustResize</item> <item name="suwCardBackground">@drawable/suw_card_bg</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item> <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item> <item name="suwNavBarTheme">@style/SuwNavBarThemeDark</item> @@ -64,7 +69,12 @@ <item name="android:windowSoftInputMode">adjustResize</item> <item name="suwCardBackground">@drawable/suw_card_bg</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_text_divider_inset</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item> <item name="suwMarginSides">@dimen/suw_layout_margin_sides</item> <item name="suwNavBarTheme">@style/SuwNavBarThemeLight</item> @@ -73,7 +83,8 @@ <!-- Placeholder for GLIF dark theme, colors are not updated yet --> <style name="SuwThemeGlif" parent="android:Theme.Material.NoActionBar"> <item name="android:colorAccent">@color/suw_color_accent_glif_dark</item> - <item name="android:colorPrimary">@color/suw_color_accent_glif_light</item> + <item name="android:colorBackground">@color/suw_glif_background_color_dark</item> + <item name="android:colorPrimary">@color/suw_color_accent_glif_dark</item> <item name="android:indeterminateTint">?android:attr/colorPrimary</item> <!-- Specify the indeterminateTintMode to work around a bug in Lollipop --> <item name="android:indeterminateTintMode">src_in</item> @@ -84,22 +95,27 @@ <item name="android:statusBarColor">?android:attr/colorPrimary</item> <item name="android:textAppearanceListItem">@style/TextAppearance.SuwGlifItemTitle</item> <item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwGlifItemSummary</item> - <item name="android:textColorLink">@color/suw_link_color_light</item> + <item name="android:textColorLink">@color/suw_color_accent_glif_dark</item> <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item> <item name="android:windowDisablePreview">true</item> <item name="android:windowSoftInputMode">adjustResize</item> <item name="suwColorPrimary">?android:attr/colorPrimary</item> - <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item> <item name="suwGlifHeaderGravity">start</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_dark</item> <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item> <item name="suwScrollIndicators">bottom</item> </style> <style name="SuwThemeGlif.Light" parent="android:Theme.Material.Light.NoActionBar"> - <item name="android:colorAccent">@color/suw_color_accent_glif_dark</item> + <item name="android:colorAccent">@color/suw_color_accent_glif_light</item> + <item name="android:colorBackground">@color/suw_glif_background_color_light</item> <item name="android:colorPrimary">@color/suw_color_accent_glif_light</item> <item name="android:indeterminateTint">?android:attr/colorPrimary</item> <!-- Specify the indeterminateTintMode to work around a bug in Lollipop --> @@ -111,15 +127,19 @@ <item name="android:statusBarColor">?android:attr/colorPrimary</item> <item name="android:textAppearanceListItem">@style/TextAppearance.SuwGlifItemTitle</item> <item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwGlifItemSummary</item> - <item name="android:textColorLink">@color/suw_link_color_light</item> + <item name="android:textColorLink">@color/suw_color_accent_glif_light</item> <item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item> <item name="android:windowDisablePreview">true</item> <item name="android:windowSoftInputMode">adjustResize</item> <item name="suwColorPrimary">?android:attr/colorPrimary</item> - <item name="suwDividerInset">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item> + <item name="suwDividerInsetEnd">0dp</item> + <item name="suwDividerInsetStart">@dimen/suw_items_glif_icon_divider_inset</item> + <item name="suwDividerInsetStartNoIcon">@dimen/suw_items_glif_text_divider_inset</item> <item name="suwGlifHeaderGravity">start</item> <item name="suwItemDescriptionStyle">@style/SuwItemContainer.Description.Glif</item> + <item name="suwItemDescriptionTitleStyle">@style/SuwItemTitle.GlifDescription</item> <item name="suwListItemIconColor">@color/suw_list_item_icon_color_light</item> <item name="suwMarginSides">@dimen/suw_glif_margin_sides</item> <item name="suwScrollIndicators">bottom</item> @@ -134,7 +154,6 @@ <item name="android:buttonStyle">@style/SuwGlifButton.Primary</item> <!-- Values used in styles --> - <item name="android:minWidth">0dp</item> <item name="android:paddingLeft">@dimen/suw_glif_button_padding</item> <item name="android:paddingRight">@dimen/suw_glif_button_padding</item> </style> diff --git a/library/platform/src/com/android/setupwizardlib/view/RichTextView.java b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java new file mode 100644 index 0000000..5a78561 --- /dev/null +++ b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java @@ -0,0 +1,150 @@ +/* + * 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.view; + +import android.content.Context; +import android.text.Annotation; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.text.style.TextAppearanceSpan; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.TextView; + +import com.android.setupwizardlib.span.LinkSpan; +import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener; +import com.android.setupwizardlib.span.SpanHelper; + +/** + * An extension of TextView that automatically replaces the annotation tags as specified in + * {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)} + * + * <p>Note: The accessibility interaction for ClickableSpans (and therefore LinkSpans) are built + * into platform in O, although the interaction paradigm is different. (See b/17726921). In this + * platform version, the links are exposed in the Local Context Menu of TalkBack instead of + * accessible directly through swiping. + */ +public class RichTextView extends TextView implements OnLinkClickListener { + + /* static section */ + + private static final String TAG = "RichTextView"; + + private static final String ANNOTATION_LINK = "link"; + private static final String ANNOTATION_TEXT_APPEARANCE = "textAppearance"; + + /** + * Replace <annotation> tags in strings to become their respective types. Currently 2 + * types are supported: + * <ol> + * <li><annotation link="foobar"> will create a + * {@link com.android.setupwizardlib.span.LinkSpan} that broadcasts with the key + * "foobar"</li> + * <li><annotation textAppearance="TextAppearance.FooBar"> will create a + * {@link android.text.style.TextAppearanceSpan} with @style/TextAppearance.FooBar</li> + * </ol> + */ + public static CharSequence getRichText(Context context, CharSequence text) { + if (text instanceof Spanned) { + final SpannableString spannable = new SpannableString(text); + final Annotation[] spans = spannable.getSpans(0, spannable.length(), Annotation.class); + for (Annotation span : spans) { + final String key = span.getKey(); + if (ANNOTATION_TEXT_APPEARANCE.equals(key)) { + String textAppearance = span.getValue(); + final int style = context.getResources() + .getIdentifier(textAppearance, "style", context.getPackageName()); + if (style == 0) { + Log.w(TAG, "Cannot find resource: " + style); + } + final TextAppearanceSpan textAppearanceSpan = + new TextAppearanceSpan(context, style); + SpanHelper.replaceSpan(spannable, span, textAppearanceSpan); + } else if (ANNOTATION_LINK.equals(key)) { + LinkSpan link = new LinkSpan(span.getValue()); + SpanHelper.replaceSpan(spannable, span, link); + } + } + return spannable; + } + return text; + } + + /* non-static section */ + + private OnLinkClickListener mOnLinkClickListener; + + public RichTextView(Context context) { + super(context); + } + + public RichTextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void setText(CharSequence text, BufferType type) { + text = getRichText(getContext(), text); + // Set text first before doing anything else because setMovementMethod internally calls + // setText. This in turn ends up calling this method with mText as the first parameter + super.setText(text, type); + boolean hasLinks = hasLinks(text); + + if (hasLinks) { + // When a TextView has a movement method, it will set the view to clickable. This makes + // View.onTouchEvent always return true and consumes the touch event, essentially + // nullifying any return values of MovementMethod.onTouchEvent. + // To still allow propagating touch events to the parent when this view doesn't have + // links, we only set the movement method here if the text contains links. + setMovementMethod(LinkMovementMethod.getInstance()); + } else { + setMovementMethod(null); + } + // ExploreByTouchHelper automatically enables focus for RichTextView + // even though it may not have any links. Causes problems during talkback + // as individual TextViews consume touch events and thereby reducing the focus window + // shown by Talkback. Disable focus if there are no links + setFocusable(hasLinks); + } + + private boolean hasLinks(CharSequence text) { + if (text instanceof Spanned) { + final ClickableSpan[] spans = + ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class); + return spans.length > 0; + } + return false; + } + + public void setOnLinkClickListener(OnLinkClickListener listener) { + mOnLinkClickListener = listener; + } + + public OnLinkClickListener getOnLinkClickListener() { + return mOnLinkClickListener; + } + + @Override + public boolean onLinkClick(LinkSpan span) { + if (mOnLinkClickListener != null) { + return mOnLinkClickListener.onLinkClick(span); + } + return false; + } +} diff --git a/library/full-support/res/layout/suw_glif_preference_recycler_view.xml b/library/recyclerview/res/layout/suw_glif_preference_recycler_view.xml index af00160..af00160 100644 --- a/library/full-support/res/layout/suw_glif_preference_recycler_view.xml +++ b/library/recyclerview/res/layout/suw_glif_preference_recycler_view.xml diff --git a/library/full-support/res/layout/suw_glif_preference_template_header.xml b/library/recyclerview/res/layout/suw_glif_preference_template_header.xml index b870251..b870251 100644 --- a/library/full-support/res/layout/suw_glif_preference_template_header.xml +++ b/library/recyclerview/res/layout/suw_glif_preference_template_header.xml diff --git a/library/full-support/res/layout/suw_glif_recycler_template_card.xml b/library/recyclerview/res/layout/suw_glif_recycler_template_card.xml index 7b5c6b0..7b5c6b0 100644 --- a/library/full-support/res/layout/suw_glif_recycler_template_card.xml +++ b/library/recyclerview/res/layout/suw_glif_recycler_template_card.xml diff --git a/library/full-support/res/layout/suw_glif_recycler_template_compact.xml b/library/recyclerview/res/layout/suw_glif_recycler_template_compact.xml index 9081efb..9081efb 100644 --- a/library/full-support/res/layout/suw_glif_recycler_template_compact.xml +++ b/library/recyclerview/res/layout/suw_glif_recycler_template_compact.xml diff --git a/library/full-support/res/layout/suw_glif_recycler_template_content.xml b/library/recyclerview/res/layout/suw_glif_recycler_template_content.xml index e8d209b..e8d209b 100644 --- a/library/full-support/res/layout/suw_glif_recycler_template_content.xml +++ b/library/recyclerview/res/layout/suw_glif_recycler_template_content.xml diff --git a/library/full-support/res/layout/suw_preference_recycler_view_header.xml b/library/recyclerview/res/layout/suw_preference_recycler_view_header.xml index 20e1d19..20e1d19 100644 --- a/library/full-support/res/layout/suw_preference_recycler_view_header.xml +++ b/library/recyclerview/res/layout/suw_preference_recycler_view_header.xml diff --git a/library/full-support/res/layout/suw_preference_recycler_view_normal.xml b/library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml index 0979d91..0979d91 100644 --- a/library/full-support/res/layout/suw_preference_recycler_view_normal.xml +++ b/library/recyclerview/res/layout/suw_preference_recycler_view_normal.xml diff --git a/library/full-support/res/layout/suw_preference_template_header.xml b/library/recyclerview/res/layout/suw_preference_template_header.xml index 6377616..6377616 100644 --- a/library/full-support/res/layout/suw_preference_template_header.xml +++ b/library/recyclerview/res/layout/suw_preference_template_header.xml diff --git a/library/full-support/res/layout/suw_recycler_template_card.xml b/library/recyclerview/res/layout/suw_recycler_template_card.xml index 1d7b143..1d7b143 100644 --- a/library/full-support/res/layout/suw_recycler_template_card.xml +++ b/library/recyclerview/res/layout/suw_recycler_template_card.xml diff --git a/library/full-support/res/layout/suw_recycler_template_card_wide.xml b/library/recyclerview/res/layout/suw_recycler_template_card_wide.xml index e5e876f..e5e876f 100644 --- a/library/full-support/res/layout/suw_recycler_template_card_wide.xml +++ b/library/recyclerview/res/layout/suw_recycler_template_card_wide.xml diff --git a/library/full-support/res/layout/suw_recycler_template_header.xml b/library/recyclerview/res/layout/suw_recycler_template_header.xml index d2c9622..d2c9622 100644 --- a/library/full-support/res/layout/suw_recycler_template_header.xml +++ b/library/recyclerview/res/layout/suw_recycler_template_header.xml diff --git a/library/full-support/res/layout/suw_recycler_template_header_collapsed.xml b/library/recyclerview/res/layout/suw_recycler_template_header_collapsed.xml index 1960f0d..1960f0d 100644 --- a/library/full-support/res/layout/suw_recycler_template_header_collapsed.xml +++ b/library/recyclerview/res/layout/suw_recycler_template_header_collapsed.xml diff --git a/library/full-support/res/values-land/layouts.xml b/library/recyclerview/res/values-land/layouts.xml index 3aacec9..3aacec9 100644 --- a/library/full-support/res/values-land/layouts.xml +++ b/library/recyclerview/res/values-land/layouts.xml diff --git a/library/full-support/res/values-sw600dp-land/layouts.xml b/library/recyclerview/res/values-sw600dp-land/layouts.xml index 0feed90..0feed90 100644 --- a/library/full-support/res/values-sw600dp-land/layouts.xml +++ b/library/recyclerview/res/values-sw600dp-land/layouts.xml diff --git a/library/full-support/res/values-sw600dp/layouts.xml b/library/recyclerview/res/values-sw600dp/layouts.xml index bfd4863..bfd4863 100644 --- a/library/full-support/res/values-sw600dp/layouts.xml +++ b/library/recyclerview/res/values-sw600dp/layouts.xml diff --git a/library/full-support/res/values/attrs.xml b/library/recyclerview/res/values/attrs.xml index e4fb41f..a098d5a 100644 --- a/library/full-support/res/values/attrs.xml +++ b/library/recyclerview/res/values/attrs.xml @@ -28,6 +28,8 @@ <declare-styleable name="SuwRecyclerMixin"> <attr name="android:entries" /> <attr name="suwDividerInset" /> + <attr name="suwDividerInsetEnd" /> + <attr name="suwDividerInsetStart" /> <attr name="suwHasStableIds" /> </declare-styleable> diff --git a/library/full-support/res/values/layouts.xml b/library/recyclerview/res/values/layouts.xml index f0b1e9d..f0b1e9d 100644 --- a/library/full-support/res/values/layouts.xml +++ b/library/recyclerview/res/values/layouts.xml diff --git a/library/full-support/src/com/android/setupwizardlib/DividerItemDecoration.java b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java index bbd7b50..6c1a928 100644 --- a/library/full-support/src/com/android/setupwizardlib/DividerItemDecoration.java +++ b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java @@ -36,12 +36,16 @@ import java.lang.annotation.RetentionPolicy; * default, and the behavior of whether the divider is shown can be customized by subclassing * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}. * - * <p>Modified from v14 PreferenceFragment.DividerDecoration, added with inset capabilities. + * <p>Modified from v14 PreferenceFragment.DividerDecoration. */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { /* 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 { /** diff --git a/library/full-support/src/com/android/setupwizardlib/GlifPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java index d337e84..d337e84 100644 --- a/library/full-support/src/com/android/setupwizardlib/GlifPreferenceLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java diff --git a/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java index d1a7947..75b1c7a 100644 --- a/library/full-support/src/com/android/setupwizardlib/GlifRecyclerLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java @@ -147,20 +147,43 @@ public class GlifRecyclerLayout extends GlifLayout { } /** - * @see RecyclerMixin#setDividerInset(int) + * @deprecated Use {@link #setDividerInsets(int, int)} instead. */ + @Deprecated public void setDividerInset(int inset) { mRecyclerMixin.setDividerInset(inset); } /** - * @see RecyclerMixin#getDividerInset() + * @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() { diff --git a/library/full-support/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java index f0df24c..6570694 100644 --- a/library/full-support/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java @@ -59,8 +59,7 @@ import com.android.setupwizardlib.template.RecyclerMixin; * }</pre> * * <p />Fragments using this layout <em>must</em> delegate {@code onCreateRecyclerView} to the - * implementation in this class: - * {@link #onCreateRecyclerView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)} + * implementation in this class: {@link #onCreateRecyclerView} */ public class SetupWizardPreferenceLayout extends SetupWizardRecyclerLayout { diff --git a/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java index 870a805..5ff825d 100644 --- a/library/full-support/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java +++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java @@ -139,27 +139,51 @@ public class SetupWizardRecyclerLayout extends SetupWizardLayout { } /** + * @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 inset it {@code inset} pixels to the right (or left in RTL layouts). + * theme and apply insets to it. * - * @param inset The number of pixels to inset on the "start" side of the list divider. Typically + * @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#setDividerInset(int) + * @see RecyclerMixin#setDividerInsets(int, int) */ - public void setDividerInset(int inset) { - mRecyclerMixin.setDividerInset(inset); + public void setDividerInsets(int start, int end) { + mRecyclerMixin.setDividerInsets(start, end); } /** - * @see RecyclerMixin#getDividerInset() + * @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() { diff --git a/library/full-support/src/com/android/setupwizardlib/items/ItemViewHolder.java b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java index 8f89603..231f81d 100644 --- a/library/full-support/src/com/android/setupwizardlib/items/ItemViewHolder.java +++ b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java @@ -27,7 +27,7 @@ class ItemViewHolder extends RecyclerView.ViewHolder private boolean mIsEnabled; private IItem mItem; - public ItemViewHolder(View itemView) { + ItemViewHolder(View itemView) { super(itemView); } diff --git a/library/full-support/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java index 9b7f6c3..78280a6 100644 --- a/library/full-support/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java +++ b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java @@ -46,7 +46,16 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder> */ 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); } @@ -58,6 +67,11 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder> mItemHierarchy.registerObserver(this); } + /** + * Gets the item at the given position. + * + * @see ItemHierarchy#getItemAt(int) + */ public IItem getItem(int position) { return mItemHierarchy.getItemAt(position); } @@ -95,8 +109,11 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder> R.styleable.SuwRecyclerItemAdapter_selectableItemBackground); } - final Drawable background = typedArray.getDrawable( - R.styleable.SuwRecyclerItemAdapter_android_colorBackground); + 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." @@ -177,14 +194,27 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder> 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; } diff --git a/library/full-support/src/com/android/setupwizardlib/template/RecyclerMixin.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java index 56751d4..fa5ac4a 100644 --- a/library/full-support/src/com/android/setupwizardlib/template/RecyclerMixin.java +++ b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java @@ -63,7 +63,9 @@ public class RecyclerMixin implements Mixin { private Drawable mDefaultDivider; private Drawable mDivider; - private int mDividerInset; + + private int mDividerInsetStart; + private int mDividerInsetEnd; /** * Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this @@ -112,8 +114,17 @@ public class RecyclerMixin implements Mixin { setAdapter(adapter); } int dividerInset = - a.getDimensionPixelSize(R.styleable.SuwRecyclerMixin_suwDividerInset, 0); - setDividerInset(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(); } @@ -174,23 +185,49 @@ public class RecyclerMixin implements Mixin { } /** + * @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 inset it {@code inset} pixels to the right (or left in RTL layouts). + * theme and apply insets to it. * - * @param inset The number of pixels to inset on the "start" side of the list divider. Typically + * @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 setDividerInset(int inset) { - mDividerInset = inset; + 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 mDividerInset; + 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() { @@ -204,9 +241,9 @@ public class RecyclerMixin implements Mixin { } mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable( mDefaultDivider, - mDividerInset /* start */, + mDividerInsetStart /* start */, 0 /* top */, - 0 /* end */, + mDividerInsetEnd /* end */, 0 /* bottom */, mTemplateLayout); mDividerDecoration.setDivider(mDivider); diff --git a/library/full-support/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java index 41fb03e..41fb03e 100644 --- a/library/full-support/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java +++ b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java diff --git a/library/full-support/src/com/android/setupwizardlib/view/HeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java index 09ddd33..cf13d01 100644 --- a/library/full-support/src/com/android/setupwizardlib/view/HeaderRecyclerView.java +++ b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java @@ -16,7 +16,6 @@ package com.android.setupwizardlib.view; -import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; @@ -41,7 +40,7 @@ public class HeaderRecyclerView extends RecyclerView { private static class HeaderViewHolder extends ViewHolder implements DividerItemDecoration.DividedViewHolder { - public HeaderViewHolder(View itemView) { + HeaderViewHolder(View itemView) { super(itemView); } @@ -119,23 +118,18 @@ public class HeaderRecyclerView extends RecyclerView { setHasStableIds(mAdapter.hasStableIds()); } - @SuppressLint("InlinedApi") // MATCH_PARENT is the same constant as FILL_PARENT available - // on earlier versions. @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 headerview - * to use after the animation. We work around this restriction by returning an empty - * framelayout to which the header is attached using #onBindViewHolder method. - */ + // 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.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT); frameLayout.setLayoutParams(params); return new HeaderViewHolder(frameLayout); } else { diff --git a/library/full-support/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java index a733b6c..d51ea56 100644 --- a/library/full-support/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java +++ b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java @@ -79,6 +79,10 @@ public class StickyHeaderRecyclerView extends HeaderRecyclerView { } } + /** + * 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) { diff --git a/library/recyclerview/test/instrumentation/res/drawable/item_bg.xml b/library/recyclerview/test/instrumentation/res/drawable/item_bg.xml new file mode 100644 index 0000000..285ae28 --- /dev/null +++ b/library/recyclerview/test/instrumentation/res/drawable/item_bg.xml @@ -0,0 +1,19 @@ +<!-- + 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. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="1dp"/> +</shape>
\ No newline at end of file diff --git a/library/recyclerview/test/instrumentation/res/layout/test_existing_background.xml b/library/recyclerview/test/instrumentation/res/layout/test_existing_background.xml new file mode 100644 index 0000000..fa2b22a --- /dev/null +++ b/library/recyclerview/test/instrumentation/res/layout/test_existing_background.xml @@ -0,0 +1,20 @@ +<!-- + Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/item_bg" /> diff --git a/library/full-support/test/instrumentation/res/layout/test_glif_recycler_layout.xml b/library/recyclerview/test/instrumentation/res/layout/test_glif_recycler_layout.xml index 45a3928..45a3928 100644 --- a/library/full-support/test/instrumentation/res/layout/test_glif_recycler_layout.xml +++ b/library/recyclerview/test/instrumentation/res/layout/test_glif_recycler_layout.xml diff --git a/library/full-support/test/instrumentation/res/layout/test_list_item.xml b/library/recyclerview/test/instrumentation/res/layout/test_list_item.xml index 220067d..220067d 100644 --- a/library/full-support/test/instrumentation/res/layout/test_list_item.xml +++ b/library/recyclerview/test/instrumentation/res/layout/test_list_item.xml diff --git a/library/full-support/test/instrumentation/res/layout/test_list_item_no_background.xml b/library/recyclerview/test/instrumentation/res/layout/test_list_item_no_background.xml index 0968e92..0968e92 100644 --- a/library/full-support/test/instrumentation/res/layout/test_list_item_no_background.xml +++ b/library/recyclerview/test/instrumentation/res/layout/test_list_item_no_background.xml diff --git a/library/full-support/test/instrumentation/res/layout/test_recycler_layout.xml b/library/recyclerview/test/instrumentation/res/layout/test_recycler_layout.xml index 8b7602e..8b7602e 100644 --- a/library/full-support/test/instrumentation/res/layout/test_recycler_layout.xml +++ b/library/recyclerview/test/instrumentation/res/layout/test_recycler_layout.xml diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java index 3867bfe..1bfbb95 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RectShape; import android.support.test.InstrumentationRegistry; @@ -133,7 +134,7 @@ public class RecyclerItemAdapterTest { } @Test - public void testCreateViewHolderNoBcakground() { + public void testCreateViewHolderNoBackground() { RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup); FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext()); @@ -141,4 +142,18 @@ public class RecyclerItemAdapterTest { 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/full-support/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java index 79105d6..ce73791 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java @@ -96,7 +96,7 @@ public class RecyclerMixinTest { } @Test - public void testDividerInset() { + public void testDividerLegacyInset() { RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); mixin.setDividerInset(123); @@ -111,7 +111,23 @@ public class RecyclerMixinTest { } @Test - public void testDividerInsetRtl() { + public void testDividerInsets() { + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + mixin.setDividerInsets(123, 456); + + assertEquals(123, mixin.getDividerInsetStart()); + assertEquals(456, mixin.getDividerInsetEnd()); + + final Drawable divider = mixin.getDivider(); + InsetDrawable insetDrawable = (InsetDrawable) divider; + Rect rect = new Rect(); + insetDrawable.getPadding(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(); @@ -129,4 +145,25 @@ public class RecyclerMixinTest { } // 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(); + + RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView); + mixin.setDividerInsets(123, 456); + + assertEquals(123, mixin.getDividerInsetStart()); + assertEquals(456, mixin.getDividerInsetEnd()); + + 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 + } } diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java index 747d1ba..747d1ba 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java index 791e11f..791e11f 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java index b27564d..4bd98ce 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java @@ -105,7 +105,7 @@ public class GlifRecyclerLayoutTest { } @Test - public void testDividerInset() { + public void testDividerInsetLegacy() { GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); @@ -120,6 +120,22 @@ public class GlifRecyclerLayoutTest { } @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); diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java index d9f52cd..d9f52cd 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java index 486d2cf..486d2cf 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java diff --git a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java index 4a72992..4b38945 100644 --- a/library/full-support/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java +++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java @@ -106,7 +106,7 @@ public class SetupWizardRecyclerLayoutTest { } @Test - public void testDividerInset() { + public void testDividerInsetLegacy() { SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); @@ -121,6 +121,22 @@ public class SetupWizardRecyclerLayoutTest { } @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( diff --git a/library/full-support/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java index b509389..b509389 100644 --- a/library/full-support/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java +++ b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java diff --git a/library/rules.gradle b/library/rules.gradle index a3868cc..6b0acce 100644 --- a/library/rules.gradle +++ b/library/rules.gradle @@ -63,8 +63,8 @@ android { } gingerbreadCompat { - java.srcDirs = ['eclair-mr1/src', 'full-support/src'] - res.srcDirs = ['eclair-mr1/res', 'full-support/res'] + java.srcDirs = ['gingerbread/src', 'recyclerview/src'] + res.srcDirs = ['gingerbread/res', 'recyclerview/res'] } } } diff --git a/library/self.gradle b/library/self.gradle index f6d14af..6a405e2 100644 --- a/library/self.gradle +++ b/library/self.gradle @@ -1,8 +1,10 @@ /** * This self.gradle build file is only run when built in ub-setupwizard-* branches. */ -apply plugin: 'dist' apply from: 'standalone-rules.gradle' +apply from: '../tools/gradle/dist-library-instrumentation-tests.gradle' +apply from: '../tools/gradle/dist-unit-tests.gradle' + // Add targets for tests android.sourceSets { androidTest { @@ -25,8 +27,11 @@ android.sourceSets { } androidTestGingerbreadCompat { - java.srcDirs = ['full-support/test/instrumentation/src', 'eclair-mr1/test/instrumentation/src'] - res.srcDirs = ['full-support/test/instrumentation/res'] + java.srcDirs = [ + 'gingerbread/test/instrumentation/src', + 'recyclerview/test/instrumentation/src' + ] + res.srcDirs = ['recyclerview/test/instrumentation/res'] } test { @@ -43,7 +48,7 @@ android.sourceSets { } testGingerbreadCompat { - java.srcDirs = ['eclair-mr1/test/robotest/src', 'full-support/test/robotest/src'] + java.srcDirs = ['gingerbread/test/robotest/src', 'recyclerview/test/robotest/src'] } } android.defaultConfig.testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -59,12 +64,14 @@ android.lintOptions { android.libraryVariants.all { variant -> variant.assemble.dependsOn(tasks.findByName('lint')) } -// Output all test APKs to the distribution folder -def distTask = tasks.findByName('dist') -if (distTask) { - android.testVariants.all { variant -> - // Make the dist task depend on the test variant, so the test APK will be built - distTask.dependsOn variant.assemble - // TODO: remap the different test variants to different file names - } + +// For compatibility with existing continuous test configurations, copy the file to +// setup-wizard-libTest.apk +// TODO: Remove this once continuous test configurations are updated to handle the new file name +task createLegacyTestApk(type: Copy) { + from "${project.ext.distDir}/setup-wizard-lib-gingerbreadCompat-debug-androidTest.apk" + into "${project.ext.distDir}" + rename ('setup-wizard-lib-gingerbreadCompat-debug-androidTest.apk', 'setup-wizard-libTest.apk') } + +tasks.dist.finalizedBy createLegacyTestApk diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java index 93201ca..30d68f1 100644 --- a/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java +++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java @@ -101,7 +101,7 @@ public class ListMixinTest { } @Test - public void testDividerInset() { + public void testDividerInsetLegacy() { ListMixin mixin = new ListMixin(mTemplateLayout, null, 0); mixin.setDividerInset(123); @@ -116,7 +116,23 @@ public class ListMixinTest { } @Test - public void testDividerInsetRtl() { + public void testDividerInsets() { + ListMixin mixin = new ListMixin(mTemplateLayout, null, 0); + mixin.setDividerInsets(123, 456); + + assertEquals(123, mixin.getDividerInsetStart()); + assertEquals(456, mixin.getDividerInsetEnd()); + + final Drawable divider = mListView.getDivider(); + InsetDrawable insetDrawable = (InsetDrawable) divider; + Rect rect = new Rect(); + insetDrawable.getPadding(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(); @@ -136,6 +152,27 @@ public class ListMixinTest { } @Test + public void testDividerInsetsRtl() { + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { + doReturn(View.LAYOUT_DIRECTION_RTL).when(mTemplateLayout).getLayoutDirection(); + + ListMixin mixin = new ListMixin(mTemplateLayout, null, 0); + mixin.setDividerInsets(123, 456); + + assertEquals(123, mixin.getDividerInsetStart()); + assertEquals(456, mixin.getDividerInsetEnd()); + + final Drawable divider = mListView.getDivider(); + InsetDrawable insetDrawable = (InsetDrawable) divider; + Rect rect = new Rect(); + insetDrawable.getPadding(rect); + + assertEquals(new Rect(456, 0, 123, 0), rect); + } + // else the test passes + } + + @Test public void testNoList() { doReturn(null).when(mTemplateLayout).findManagedViewById(eq(android.R.id.list)); diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java index acb7fbb..e12b31d 100644 --- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java +++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java @@ -95,29 +95,29 @@ public class GlifLayoutTest { } @Test - public void testGlifPixelTheme() { + public void testGlifV2Theme() { mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(), - R.style.SuwThemeGlifPixel_Light); + R.style.SuwThemeGlifV2_Light); final GlifLayout glifLayout = new GlifLayout(mContext); final TextView titleView = (TextView) glifLayout.findManagedViewById(R.id.suw_layout_title); if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { assertEquals(View.TEXT_ALIGNMENT_GRAVITY, titleView.getTextAlignment()); } - assertEquals("Title text should be center aligned on GLIF Pixel theme", + assertEquals("Title text should be center aligned on GLIF v2 theme", Gravity.CENTER_HORIZONTAL, titleView.getGravity() & Gravity.CENTER_HORIZONTAL); if (VERSION.SDK_INT >= VERSION_CODES.N) { // LinearLayout.getGravity is only available on versions >= N final View iconView = glifLayout.findManagedViewById(R.id.suw_layout_icon); final LinearLayout parent = (LinearLayout) iconView.getParent(); - assertEquals("Icon should be center aligned on GLIF Pixel theme", + assertEquals("Icon should be center aligned on GLIF v2 theme", Gravity.CENTER_HORIZONTAL, parent.getGravity() & Gravity.CENTER_HORIZONTAL); } - assertEquals("Status bar color should be white in GLIF Pixel theme", - "fffafafa", + assertEquals("Status bar color should be white in GLIF v2 theme", + "ffffffff", Integer.toHexString(glifLayout.getBackgroundBaseColor().getDefaultColor())); - assertFalse("GLIF Pixel theme shuold not have patterned background", + assertFalse("GLIF v2 theme shuold not have patterned background", glifLayout.isBackgroundPatterned()); if (VERSION.SDK_INT >= VERSION_CODES.M) { diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java index 7b70cf7..c2e932c 100644 --- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java +++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java @@ -105,7 +105,7 @@ public class GlifListLayoutTest { } @Test - public void testDividerInset() { + public void testDividerInsetLegacy() { GlifListLayout layout = new GlifListLayout(mContext); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); @@ -119,6 +119,22 @@ public class GlifListLayoutTest { assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); } + @Test + public void testDividerInsets() { + GlifListLayout layout = new GlifListLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); + } + assertListTemplateInflated(layout); + + layout.setDividerInsets(10, 15); + assertEquals("Divider inset should be 10", 10, layout.getDividerInsetStart()); + assertEquals("Divider inset should be 15", 15, layout.getDividerInsetEnd()); + + final Drawable divider = layout.getDivider(); + assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable); + } + private void assertListTemplateInflated(GlifListLayout layout) { View title = layout.findViewById(R.id.suw_layout_title); assertNotNull("@id/suw_layout_title should not be null", title); diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java new file mode 100644 index 0000000..85876b4 --- /dev/null +++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java @@ -0,0 +1,94 @@ +/* + * 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.test; + +import static android.support.test.InstrumentationRegistry.getTargetContext; + +import static org.junit.Assert.assertNotNull; + +import android.content.Context; +import android.support.test.filters.SmallTest; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import com.android.setupwizardlib.R; +import com.android.setupwizardlib.items.Item; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +/** + * Sanity test for all the item layouts to make sure they won't crash when being inflated in + * different themes. + */ +@RunWith(Parameterized.class) +@SmallTest +public class ItemLayoutTest { + + @Parameters + public static Iterable<Object[]> data() { + int[] themes = new int[] { + R.style.SuwThemeMaterial_Light, + R.style.SuwThemeMaterial, + R.style.SuwThemeGlif_Light, + R.style.SuwThemeGlif, + R.style.SuwThemeGlifV2_Light, + R.style.SuwThemeGlifV2 + }; + int[] layouts = new int[] { + R.layout.suw_items_default, + R.layout.suw_items_verbose, + R.layout.suw_items_description + }; + + // Test all the possible combinations of themes and layouts. + List<Object[]> params = new ArrayList<>(); + for (int theme : themes) { + for (int layout : layouts) { + params.add(new Object[] { theme, layout }); + } + } + return params; + } + + private final Context mContext; + private final FrameLayout mParent; + private final Item mItem; + + public ItemLayoutTest(int theme, int layout) { + mContext = new ContextThemeWrapper(getTargetContext(), theme); + mParent = new FrameLayout(mContext); + mItem = new Item(); + mItem.setLayoutResource(layout); + } + + @Test + public void testInflateLayoutHasBasicViews() { + LayoutInflater.from(mContext).inflate(mItem.getLayoutResource(), mParent, true); + mItem.onBindView(mParent); + + assertNotNull("Title should exist", mParent.findViewById(R.id.suw_items_title)); + assertNotNull("Summary should exist", mParent.findViewById(R.id.suw_items_summary)); + assertNotNull("Icon should exist", mParent.findViewById(R.id.suw_items_icon)); + } +} diff --git a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java index 2e2b01e..5f3eb9f 100644 --- a/library/eclair-mr1/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java +++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java @@ -18,9 +18,16 @@ package com.android.setupwizardlib.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import android.annotation.SuppressLint; +import android.content.Context; +import android.content.ContextWrapper; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -30,6 +37,7 @@ import android.text.Spanned; import android.text.style.TextAppearanceSpan; import com.android.setupwizardlib.span.LinkSpan; +import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener; import com.android.setupwizardlib.view.RichTextView; import org.junit.Test; @@ -64,6 +72,45 @@ public class RichTextViewTest { } @Test + public void testOnLinkClickListener() { + Annotation link = new Annotation("link", "foobar"); + SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world"); + ssb.setSpan(link, 1, 2, 0 /* flags */); + + RichTextView textView = new RichTextView(InstrumentationRegistry.getContext()); + textView.setText(ssb); + + OnLinkClickListener listener = mock(OnLinkClickListener.class); + textView.setOnLinkClickListener(listener); + + assertSame(listener, textView.getOnLinkClickListener()); + + CharSequence text = textView.getText(); + LinkSpan[] spans = ((Spanned) text).getSpans(0, text.length(), LinkSpan.class); + spans[0].onClick(textView); + + verify(listener).onLinkClick(eq(spans[0])); + } + + @Test + public void testLegacyContextOnClickListener() { + // Click listener implemented by context should still be invoked for compatibility. + Annotation link = new Annotation("link", "foobar"); + SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world"); + ssb.setSpan(link, 1, 2, 0 /* flags */); + + TestContext context = spy(new TestContext(InstrumentationRegistry.getTargetContext())); + RichTextView textView = new RichTextView(context); + textView.setText(ssb); + + CharSequence text = textView.getText(); + LinkSpan[] spans = ((Spanned) text).getSpans(0, text.length(), LinkSpan.class); + spans[0].onClick(textView); + + verify(context).onClick(eq(spans[0])); + } + + @Test public void testTextStyle() { Annotation link = new Annotation("textAppearance", "foobar"); SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world"); @@ -85,7 +132,7 @@ public class RichTextViewTest { } @Test - public void testTextContaininingLinksAreFocusable() { + public void testTextContainingLinksAreFocusable() { Annotation testLink = new Annotation("link", "value"); SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Linked"); spannableStringBuilder.setSpan(testLink, 0, 3, 0); @@ -112,7 +159,7 @@ public class RichTextViewTest { // should also be automatically changed. @SuppressLint("SetTextI18n") // It's OK. This is just a test. @Test - public void testRichTxtViewFocusChangesWithTextChange() { + public void testRichTextViewFocusChangesWithTextChange() { RichTextView textView = new RichTextView(InstrumentationRegistry.getContext()); textView.setText("Thou shall not be focusable!"); @@ -124,4 +171,16 @@ public class RichTextViewTest { textView.setText(spannableStringBuilder); assertTrue(textView.isFocusable()); } + + public static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener { + + public TestContext(Context base) { + super(base); + } + + @Override + public void onClick(LinkSpan span) { + // Ignore. Can be verified using Mockito + } + } } diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java index b33ef31..5c34fe0 100644 --- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java +++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java @@ -16,11 +16,15 @@ package com.android.setupwizardlib.test; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; 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; @@ -87,6 +91,37 @@ public class SetupWizardListLayoutTest { progressBar instanceof ProgressBar && progressBar.getVisibility() == View.VISIBLE); } + @Test + public void testDividerInsetLegacy() { + SetupWizardListLayout layout = new SetupWizardListLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); + } + assertListTemplateInflated(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() { + SetupWizardListLayout layout = new SetupWizardListLayout(mContext); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); + } + assertListTemplateInflated(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); + } + private void assertListTemplateInflated(SetupWizardLayout layout) { View decorView = layout.findViewById(R.id.suw_layout_decor); View navbar = layout.findViewById(R.id.suw_layout_navigation_bar); diff --git a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java index 7dd0a6f..f86e057 100644 --- a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java +++ b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java @@ -56,6 +56,18 @@ public class LinkSpanTest { // Just check that no uncaught exception here. } + @Test + public void testWrappedListener() { + final TestContext context = new TestContext(application); + final Context wrapperContext = new ContextWrapper(context); + final TextView textView = new TextView(wrapperContext); + final LinkSpan linkSpan = new LinkSpan("test_id"); + + + linkSpan.onClick(textView); + assertSame("Clicked LinkSpan should be passed to setup", linkSpan, context.clickedSpan); + } + private static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener { public LinkSpan clickedSpan = null; diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java index d518e08..4c460c8 100644 --- a/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java +++ b/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java @@ -80,6 +80,14 @@ public class WizardManagerHelperTest { } @Test + public void testIsDeferredSetupTrue() { + final Intent intent = new Intent(); + intent.putExtra("deferredSetup", true); + assertTrue("Is deferred setup wizard should be true", + WizardManagerHelper.isDeferredSetupWizard(intent)); + } + + @Test public void testIsSetupWizardFalse() { final Intent intent = new Intent(); intent.putExtra("firstRun", false); @@ -140,22 +148,22 @@ public class WizardManagerHelperTest { } @Test - public void testGlifPixelIsDarkTheme() { + public void testGlifV2IsDarkTheme() { final Intent intent = new Intent(); - intent.putExtra("theme", "glif_pixel"); - assertFalse("Theme glif_pixel should be dark theme", + intent.putExtra("theme", "glif_v2"); + assertFalse("Theme glif_v2 should be dark theme", WizardManagerHelper.isLightTheme(intent, false)); - assertFalse("Theme glif_pixel should be dark theme", + assertFalse("Theme glif_v2 should be dark theme", WizardManagerHelper.isLightTheme(intent, true)); } @Test - public void testGlifPixelLightIsLightTheme() { + public void testGlifV2LightIsLightTheme() { final Intent intent = new Intent(); - intent.putExtra("theme", "glif_pixel_light"); - assertTrue("Theme glif_pixel_light should be light theme", + intent.putExtra("theme", "glif_v2_light"); + assertTrue("Theme glif_v2_light should be light theme", WizardManagerHelper.isLightTheme(intent, false)); - assertTrue("Theme glif_pixel_light should be light theme", + assertTrue("Theme glif_v2_light should be light theme", WizardManagerHelper.isLightTheme(intent, true)); } @@ -195,15 +203,15 @@ public class WizardManagerHelperTest { } @Test - public void testGetThemeResGlifPixelLight() { - assertEquals(R.style.SuwThemeGlifPixel_Light, - WizardManagerHelper.getThemeRes("glif_pixel_light", 0)); + public void testGetThemeResGlifV2Light() { + assertEquals(R.style.SuwThemeGlifV2_Light, + WizardManagerHelper.getThemeRes("glif_v2_light", 0)); } @Test - public void testGetThemeResGlifPixel() { - assertEquals(R.style.SuwThemeGlifPixel, - WizardManagerHelper.getThemeRes("glif_pixel", 0)); + public void testGetThemeResGlifV2() { + assertEquals(R.style.SuwThemeGlifV2, + WizardManagerHelper.getThemeRes("glif_v2", 0)); } @Test @@ -257,6 +265,7 @@ public class WizardManagerHelperTest { .putExtra(WizardManagerHelper.EXTRA_THEME, "test_theme") .putExtra(WizardManagerHelper.EXTRA_WIZARD_BUNDLE, wizardBundle) .putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true) + .putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true) // Script URI and Action ID are kept for backwards compatibility .putExtra(WizardManagerHelper.EXTRA_SCRIPT_URI, "test_script_uri") .putExtra(WizardManagerHelper.EXTRA_ACTION_ID, "test_action_id"); @@ -273,6 +282,8 @@ public class WizardManagerHelperTest { assertTrue("EXTRA_IS_FIRST_RUN should be copied", intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, false)); + assertTrue("EXTRA_IS_DEFERRED_SETUP should be copied", + intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false)); // Script URI and Action ID are replaced by Wizard Bundle in M, but are kept for backwards // compatibility diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java new file mode 100644 index 0000000..f1332c0 --- /dev/null +++ b/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java @@ -0,0 +1,88 @@ +/* + * 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.view; + +import static org.junit.Assert.assertEquals; +import static org.robolectric.RuntimeEnvironment.application; + +import android.view.View; +import android.view.View.MeasureSpec; + +import com.android.setupwizardlib.BuildConfig; +import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; + +@RunWith(SuwLibRobolectricTestRunner.class) +@Config(constants = BuildConfig.class, sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK}) +public class FillContentLayoutTest { + + @Test + public void testMeasureMinSize() { + FillContentLayout layout = new FillContentLayout( + application, + Robolectric.buildAttributeSet() + .addAttribute(android.R.attr.minWidth, "123dp") + .addAttribute(android.R.attr.minHeight, "123dp") + .build()); + layout.measure( + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + + assertEquals(123, layout.getMeasuredWidth()); + assertEquals(123, layout.getMeasuredHeight()); + } + + @Test + public void testMeasureChildIsSmallerThanMaxSize() { + View child = new View(application); + FillContentLayout layout = new FillContentLayout( + application, + Robolectric.buildAttributeSet() + .addAttribute(android.R.attr.maxWidth, "123dp") + .addAttribute(android.R.attr.maxHeight, "123dp") + .build()); + layout.addView(child); + layout.measure( + MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY)); + + assertEquals(123, child.getMeasuredWidth()); + assertEquals(123, child.getMeasuredHeight()); + } + + @Test + public void testMeasureChildIsSmallerThanParent() { + View child = new View(application); + FillContentLayout layout = new FillContentLayout( + application, + Robolectric.buildAttributeSet() + .addAttribute(android.R.attr.maxWidth, "123dp") + .addAttribute(android.R.attr.maxHeight, "123dp") + .build()); + layout.addView(child); + layout.measure( + MeasureSpec.makeMeasureSpec(88, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(88, MeasureSpec.EXACTLY)); + + assertEquals(88, child.getMeasuredWidth()); + assertEquals(88, child.getMeasuredHeight()); + } +} diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java new file mode 100644 index 0000000..ffa228d --- /dev/null +++ b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java @@ -0,0 +1,178 @@ +/* + * 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.view; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.robolectric.RuntimeEnvironment.application; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.media.MediaPlayer; +import android.os.Build.VERSION_CODES; +import android.support.annotation.RawRes; +import android.view.Surface; + +import com.android.setupwizardlib.BuildConfig; +import com.android.setupwizardlib.R; +import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner; +import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowMockMediaPlayer; +import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowSurface; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.RealObject; +import org.robolectric.internal.Shadow; +import org.robolectric.shadows.ShadowMediaPlayer; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(SuwLibRobolectricTestRunner.class) +@Config( + constants = BuildConfig.class, + sdk = Config.NEWEST_SDK, + shadows = { + ShadowMockMediaPlayer.class, + ShadowSurface.class + }) +public class IllustrationVideoViewTest { + + @Mock + private SurfaceTexture mSurfaceTexture; + + private IllustrationVideoView mView; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @After + public void tearDown() { + ShadowMockMediaPlayer.reset(); + } + + @Test + public void testPausedWhenWindowFocusLost() { + createDefaultView(); + mView.start(); + + assertNotNull(mView.mMediaPlayer); + assertNotNull(mView.mSurface); + + mView.onWindowFocusChanged(false); + verify(ShadowMockMediaPlayer.getMock()).pause(); + } + + @Test + public void testStartedWhenWindowFocusRegained() { + testPausedWhenWindowFocusLost(); + + // Clear verifications for calls in the other test + reset(ShadowMockMediaPlayer.getMock()); + + mView.onWindowFocusChanged(true); + verify(ShadowMockMediaPlayer.getMock()).start(); + } + + @Test + public void testSurfaceReleasedWhenTextureDestroyed() { + createDefaultView(); + mView.start(); + + assertNotNull(mView.mMediaPlayer); + assertNotNull(mView.mSurface); + + mView.onSurfaceTextureDestroyed(mSurfaceTexture); + verify(ShadowMockMediaPlayer.getMock()).release(); + } + + @Test + public void testXmlSetVideoResId() { + createDefaultView(); + assertEquals(android.R.color.white, ShadowMockMediaPlayer.sResId); + } + + @Test + public void testSetVideoResId() { + createDefaultView(); + + @RawRes int black = android.R.color.black; + mView.setVideoResource(black); + + assertEquals(android.R.color.black, ShadowMockMediaPlayer.sResId); + } + + private void createDefaultView() { + mView = new IllustrationVideoView( + application, + Robolectric.buildAttributeSet() + // Any resource attribute should work, since the media player is mocked + .addAttribute(R.attr.suwVideo, "@android:color/white") + .build()); + mView.onSurfaceTextureAvailable(mSurfaceTexture, 500, 500); + } + + @Implements(MediaPlayer.class) + public static class ShadowMockMediaPlayer extends ShadowMediaPlayer { + + private static MediaPlayer sMediaPlayer = mock(MediaPlayer.class); + private static int sResId; + + public static void reset() { + sMediaPlayer = mock(MediaPlayer.class); + sResId = 0; + } + + @Implementation + public static MediaPlayer create(Context context, int resId) { + sResId = resId; + return sMediaPlayer; + } + + public static MediaPlayer getMock() { + return sMediaPlayer; + } + } + + @Implements(Surface.class) + @TargetApi(VERSION_CODES.HONEYCOMB) + public static class ShadowSurface extends org.robolectric.shadows.ShadowSurface { + + @RealObject + private Surface mRealSurface; + + public void __constructor__(SurfaceTexture surfaceTexture) { + // Call the constructor on the real object, so that critical fields such as mLock is + // initialized properly. + Shadow.invokeConstructor(Surface.class, mRealSurface, + ReflectionHelpers.ClassParameter.from(SurfaceTexture.class, surfaceTexture)); + super.__constructor__(surfaceTexture); + } + } +} diff --git a/navigationbar/res/values-pt-rBR/strings.xml b/navigationbar/res/values-pt-rBR/strings.xml index c8104d7..181fafe 100644 --- a/navigationbar/res/values-pt-rBR/strings.xml +++ b/navigationbar/res/values-pt-rBR/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próximo"</string> + <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próxima"</string> <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Voltar"</string> </resources> diff --git a/navigationbar/res/values-pt/strings.xml b/navigationbar/res/values-pt/strings.xml index c8104d7..181fafe 100644 --- a/navigationbar/res/values-pt/strings.xml +++ b/navigationbar/res/values-pt/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próximo"</string> + <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"Próxima"</string> <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Voltar"</string> </resources> diff --git a/tools/build_for_build_server.sh b/tools/build_for_build_server.sh index 14b5a3b..a90ae67 100755 --- a/tools/build_for_build_server.sh +++ b/tools/build_for_build_server.sh @@ -6,4 +6,4 @@ export TARGET_BUILD_DENSITY="alldpi" export TARGET_BUILD_TYPE="release" export TARGET_BUILD_APPS="setup-wizard-lib" -./gradlew +./gradlew buildProjectFull test diff --git a/tools/checkstyle/checkstyle.xml b/tools/checkstyle/checkstyle.xml new file mode 100644 index 0000000..0dbccae --- /dev/null +++ b/tools/checkstyle/checkstyle.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd" [ + <!ENTITY defaultCopyrightCheck SYSTEM "../../../../../prebuilts/checkstyle/default-copyright-check.xml"> + <!ENTITY defaultJavadocChecks SYSTEM "../../../../../prebuilts/checkstyle/default-javadoc-checks.xml"> + <!ENTITY defaultTreewalkerChecks SYSTEM "../../../../../prebuilts/checkstyle/default-treewalker-checks.xml"> + <!ENTITY defaultModuleChecks SYSTEM "../../../../../prebuilts/checkstyle/default-module-checks.xml"> +]> + +<module name="Checker"> + &defaultModuleChecks; + &defaultCopyrightCheck; + <module name="TreeWalker"> + &defaultJavadocChecks; + &defaultTreewalkerChecks; + </module> + + <module name="SuppressionFilter"> + <property name="file" value="tools/checkstyle/checkstyle_suppression.xml" /> + </module> +</module> diff --git a/tools/checkstyle/checkstyle_suppression.xml b/tools/checkstyle/checkstyle_suppression.xml new file mode 100644 index 0000000..6bf7b21 --- /dev/null +++ b/tools/checkstyle/checkstyle_suppression.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN" "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd"> +<suppressions> + + <!-- Note: Checkstyle puts the absolute path of files through the suppress filter, so the + patterns below will match sub-directories. Notably, for overlays where the path is + something like overlay/frameworks/opt/setupwizard will match the regex filter + "frameworks/opt/setupwizard". This is probably OK for most cases since they are overlay + of the original app and should have the same coding style. --> + + <!-- Robolectric uses magic method names like `__constructor__` --> + <suppress files="/robotest/" checks="MethodName" /> + +</suppressions> diff --git a/tools/gradle/dist-library-instrumentation-tests.gradle b/tools/gradle/dist-library-instrumentation-tests.gradle new file mode 100644 index 0000000..2be6349 --- /dev/null +++ b/tools/gradle/dist-library-instrumentation-tests.gradle @@ -0,0 +1,33 @@ +/** + * This script plugin is used to build and dist the test APK outputs of a library with multiple + * build flavors. + * + * Compared to the defaults of the 'dist' plugin, it does two additional things: + * 1. It builds the "debug" test APKs when the 'dist' task is run. + * 2. It dist the test APKs using the original output file name instead of hard coding + * "${project.archivesBaseName}Tests.apk". This allows multiple flavors of test APKs to be built + * without conflicting file names. + */ + +apply plugin: 'dist' + +// Set the dist files to empty map, and to tell DistExtension to not include the default files, +// because the default output only supports one test APK output for libraries. +dist.files = [:] +android.testVariants.all { variant -> + // "Debug" tests are not built by BuildSrc by default. Depend on the task so it will be built. + tasks.dist.dependsOn variant.assemble + + // Output all test APKs to the distribution folder. + // For a project named "setup-wizard-lib" with build flavor "platform" and build type "debug", + // the output file will be named "setup-wizard-lib-platform-debug-androidTest.apk" + variant.outputs.each { output -> + dist.file output.outputFile.canonicalPath, output.outputFile.name + } +} +android.libraryVariants.all { variant -> + // Output all library AARs to the distribution folder + variant.outputs.each { output -> + dist.file output.outputFile.canonicalPath, output.outputFile.name + } +} diff --git a/tools/gradle/dist-unit-tests.gradle b/tools/gradle/dist-unit-tests.gradle new file mode 100644 index 0000000..faae260 --- /dev/null +++ b/tools/gradle/dist-unit-tests.gradle @@ -0,0 +1,38 @@ +/** + * This script plugin is used to bundle the host test (e.g. Robolectric) results and dist it in + * a location where TradeFed knows how to parse. + * + * - If a non-dist build is run with test, it will run the normal unit tests, failing the build if + * there are test failures. + * - If a dist build is run with test (e.g. ./gradlew dist test), the build will ignore any test + * failures, and will create a zip of the XML test reports for each test run, and copy them to + * dist/host-test-reports for consumption by TradeFed. + */ + +apply plugin: 'dist' + +// If unit tests are run as part of the build, dist the test XML reports to host-test-reports/*.zip +android.unitTestVariants.all { variant -> + def task = tasks.findByName('test' + variant.name.capitalize()) + gradle.taskGraph.whenReady { taskGraph -> + // Ignore the failures, so the build continues even on test errors when the build is + // running with 'dist'. (Usually as part of a build server build) + task.ignoreFailures = taskGraph.hasTask(tasks.dist) + } + + def junitReport = task.reports.junitXml + if (junitReport.enabled) { + // Create a zip file of the XML test reports + def zipTask = tasks.create("zipResultsOf${task.name.capitalize()}", Zip) { + from junitReport.destination + archiveName = task.name + 'Result.zip' + destinationDir = junitReport.destination.parentFile + } + zipTask.mustRunAfter task + + // Copy the test reports to dist/host-test-reports + // The file path and format should match GradleHostBasedTest class in TradeFed. + tasks.dist.dependsOn zipTask + dist.file zipTask.archivePath.path, "host-test-reports/${zipTask.archiveName}" + } +} |