summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--OWNERS4
-rw-r--r--library/Android.mk29
-rw-r--r--library/common-gingerbread.mk42
-rw-r--r--library/common-platform-deprecated.mk (renamed from library/common.mk)3
-rw-r--r--library/gingerbread/res/layout/suw_items_expandable_switch.xml1
-rw-r--r--library/gingerbread/res/layout/suw_items_switch.xml1
-rw-r--r--library/gingerbread/res/values-v27/styles.xml30
-rw-r--r--library/gingerbread/res/values/styles.xml55
-rw-r--r--library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java4
-rw-r--r--library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java399
-rw-r--r--library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java2
-rw-r--r--library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java35
-rw-r--r--library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java57
-rw-r--r--library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java (renamed from library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java)95
-rw-r--r--library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java74
-rw-r--r--library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java3
-rw-r--r--library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java3
-rw-r--r--library/lint.xml4
-rw-r--r--library/main/res/drawable-v21/suw_edit_text_bg_shape.xml21
-rw-r--r--library/main/res/drawable-v21/suw_edittext_bg.xml52
-rw-r--r--library/main/res/drawable-v21/suw_fourcolor_progress_bar.xml194
-rw-r--r--library/main/res/layout/suw_glif_blank_template_content.xml5
-rw-r--r--library/main/res/layout/suw_glif_header.xml5
-rw-r--r--library/main/res/layout/suw_glif_list_template_content.xml5
-rw-r--r--library/main/res/layout/suw_glif_loading_screen.xml40
-rw-r--r--library/main/res/layout/suw_glif_template_content.xml5
-rw-r--r--library/main/res/values-en-rCA/strings.xml23
-rw-r--r--library/main/res/values-en-rXC/strings.xml23
-rw-r--r--library/main/res/values-v11/styles.xml22
-rw-r--r--library/main/res/values-v21/styles.xml17
-rw-r--r--library/main/res/values-v22/styles.xml23
-rw-r--r--library/main/res/values/attrs.xml9
-rw-r--r--library/main/res/values/colors.xml6
-rw-r--r--library/main/res/values/config.xml4
-rw-r--r--library/main/res/values/dimens.xml11
-rw-r--r--library/main/res/values/styles.xml39
-rw-r--r--library/main/src/com/android/setupwizardlib/GlifLayout.java34
-rw-r--r--library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java23
-rw-r--r--library/main/src/com/android/setupwizardlib/TemplateLayout.java4
-rw-r--r--library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java6
-rw-r--r--library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java1
-rw-r--r--library/main/src/com/android/setupwizardlib/span/LinkSpan.java11
-rw-r--r--library/main/src/com/android/setupwizardlib/template/IconMixin.java40
-rw-r--r--library/main/src/com/android/setupwizardlib/util/Partner.java10
-rw-r--r--library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java18
-rw-r--r--library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java59
-rw-r--r--library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java4
-rw-r--r--library/main/src/com/android/setupwizardlib/view/Illustration.java3
-rw-r--r--library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java28
-rw-r--r--library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java83
-rw-r--r--library/platform/res/values-v27/styles.xml (renamed from library/platform/res/values-v23/styles.xml)54
-rw-r--r--library/platform/src/com/android/setupwizardlib/view/RichTextView.java30
-rw-r--r--library/recyclerview/res/layout/suw_glif_recycler_template_content.xml5
-rw-r--r--library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java6
-rw-r--r--library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java6
-rw-r--r--library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java2
-rw-r--r--library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java3
-rw-r--r--library/rules.gradle78
-rw-r--r--library/self.gradle52
-rw-r--r--library/standalone.gradle4
-rw-r--r--library/test/instrumentation/AndroidManifest.xml2
-rw-r--r--library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java39
-rw-r--r--library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java2
-rw-r--r--library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java25
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java72
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java118
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java5
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java5
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/robolectric/PatchedGradleManifestFactory.java126
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java29
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowLog.java51
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java36
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java3
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java3
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java3
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java3
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java22
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java84
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java42
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java154
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java3
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java19
-rw-r--r--library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java (renamed from library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java)81
-rw-r--r--navigationbar/res/values-en-rCA/strings.xml6
-rw-r--r--navigationbar/res/values-en-rXC/strings.xml6
-rwxr-xr-xtools/build_for_build_server.sh2
-rw-r--r--tools/gradle/android.properties4
-rw-r--r--tools/gradle/dist-unit-tests.gradle74
-rw-r--r--tools/root.mk2
89 files changed, 2087 insertions, 843 deletions
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..5245206
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,4 @@
+russellbrenner@google.com
+ajayns@google.com
+iofir@google.com
+yukl@google.com
diff --git a/library/Android.mk b/library/Android.mk
index b0fa9f0..ff92194 100644
--- a/library/Android.mk
+++ b/library/Android.mk
@@ -26,11 +26,9 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
include $(CLEAR_VARS)
-ifeq ($(TARGET_BUILD_APPS),)
-# Use AAPT2 only when TARGET_BUILD_APPS is empty because AAPT2 is not compatible with the current
-# setup of prebuilt support libs used in unbundled builds. b/29836407
LOCAL_USE_AAPT2 := true
-endif
+
+LOCAL_AAPT2_ONLY := true
LOCAL_MANIFEST_FILE := main/AndroidManifest.xml
LOCAL_MODULE := setup-wizard-lib-gingerbread-compat
@@ -41,8 +39,6 @@ LOCAL_RESOURCE_DIR := \
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, main/src gingerbread/src recyclerview/src)
-ifdef LOCAL_USE_AAPT2
-
LOCAL_JAVA_LIBRARIES := \
android-support-annotations
@@ -52,25 +48,4 @@ LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-appcompat \
android-support-v7-recyclerview
-else
-
-LOCAL_AAPT_FLAGS := --auto-add-overlay \
- --extra-packages android.support.compat \
- --extra-packages android.support.v7.appcompat \
- --extra-packages android.support.v7.recyclerview
-
-LOCAL_RESOURCE_DIR += \
- prebuilts/sdk/current/supportcompat/res \
- prebuilts/sdk/current/supportv7/appcompat/res \
- prebuilts/sdk/current/supportv7/recyclerview/res
-
-LOCAL_JAVA_LIBRARIES := \
- android-support-annotations \
- android-support-compat \
- android-support-core-ui \
- android-support-v7-appcompat \
- android-support-v7-recyclerview
-
-endif
-
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/library/common-gingerbread.mk b/library/common-gingerbread.mk
index 02642f2..70231df 100644
--- a/library/common-gingerbread.mk
+++ b/library/common-gingerbread.mk
@@ -15,46 +15,6 @@
# Path to directory of setup wizard lib (e.g. frameworks/opt/setupwizard/library)
suwlib_dir := $(dir $(lastword $(MAKEFILE_LIST)))
-ifneq ($(LOCAL_USE_AAPT2),true)
-
-# Check that LOCAL_RESOURCE_DIR is defined
-ifeq (,$(LOCAL_RESOURCE_DIR))
-$(error LOCAL_RESOURCE_DIR must be defined)
-endif
-
-# Add --auto-add-overlay flag if not present
-ifeq (,$(findstring --auto-add-overlay, $(LOCAL_AAPT_FLAGS)))
-LOCAL_AAPT_FLAGS += --auto-add-overlay
-endif
-
-# Include setup wizard library, if not already included
-ifeq (,$(findstring setup-wizard-lib-gingerbread-compat,$(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_RESOURCE_DIR += \
- $(suwlib_dir)/main/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
-
-## Include transitive dependencies below
-
-# Include support-v7-appcompat, if not already included
-ifeq (,$(findstring android-support-v7-appcompat,$(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/appcompat/res
-LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
-endif
-
-# Include support-v7-recyclerview, if not already included
-ifeq (,$(findstring android-support-v7-recyclerview,$(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/recyclerview/res
-LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
-endif
-
-else # LOCAL_USE_AAPT2 := true
-
ifeq (,$(findstring setup-wizard-lib-gingerbread-compat,$(LOCAL_STATIC_ANDROID_LIBRARIES)))
LOCAL_STATIC_ANDROID_LIBRARIES += setup-wizard-lib-gingerbread-compat
endif
@@ -66,5 +26,3 @@ endif
ifeq (,$(findstring android-support-v7-recyclerview,$(LOCAL_STATIC_ANDROID_LIBRARIES)))
LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v7-recyclerview
endif
-
-endif # LOCAL_USE_AAPT2
diff --git a/library/common.mk b/library/common-platform-deprecated.mk
index 3372cf2..bc190d0 100644
--- a/library/common.mk
+++ b/library/common-platform-deprecated.mk
@@ -1,3 +1,4 @@
+# DEPRECATED: This variant is no longer maintained. Use common-gingerbread instead
#
# Include this make file to build your application against this module.
#
@@ -9,7 +10,7 @@
# LOCAL_RESOURCE_DIR := \
# $(LOCAL_PATH)/res
#
-# include frameworks/opt/setupwizard/library/common.mk
+# include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
#
# Path to directory of setup wizard lib (e.g. frameworks/opt/setupwizard/library)
diff --git a/library/gingerbread/res/layout/suw_items_expandable_switch.xml b/library/gingerbread/res/layout/suw_items_expandable_switch.xml
index 2b98a9f..21c2c22 100644
--- a/library/gingerbread/res/layout/suw_items_expandable_switch.xml
+++ b/library/gingerbread/res/layout/suw_items_expandable_switch.xml
@@ -20,7 +20,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
- android:descendantFocusability="blocksDescendants"
android:orientation="horizontal"
android:tag="noBackground">
diff --git a/library/gingerbread/res/layout/suw_items_switch.xml b/library/gingerbread/res/layout/suw_items_switch.xml
index af326b2..3f101d7 100644
--- a/library/gingerbread/res/layout/suw_items_switch.xml
+++ b/library/gingerbread/res/layout/suw_items_switch.xml
@@ -20,7 +20,6 @@
style="@style/SuwItemContainer.Verbose"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:descendantFocusability="blocksDescendants"
android:orientation="horizontal">
<FrameLayout
diff --git a/library/gingerbread/res/values-v27/styles.xml b/library/gingerbread/res/values-v27/styles.xml
new file mode 100644
index 0000000..305a55e
--- /dev/null
+++ b/library/gingerbread/res/values-v27/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 Google Inc.
+
+ 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.
+-->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+
+ <!-- Not needed for dark theme, as default nav bar bg color is black. We need a separate style
+ override here since windowLightNavigationBar is new in v27, and these two styles need to be
+ applied together as a unit. -->
+ <style name="SuwThemeGlifV3.Light" parent="SuwBaseThemeGlifV3.Light">
+ <item name="android:navigationBarColor">@color/suw_glif_v3_nav_bar_color_light</item>
+ <!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
+ <item name="android:navigationBarDividerColor" tools:ignore="NewApi">@color/suw_glif_v3_nav_bar_divider_color_light</item>
+ <!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
+ <item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
+ </style>
+</resources>
diff --git a/library/gingerbread/res/values/styles.xml b/library/gingerbread/res/values/styles.xml
index 6e525ef..241f037 100644
--- a/library/gingerbread/res/values/styles.xml
+++ b/library/gingerbread/res/values/styles.xml
@@ -20,6 +20,7 @@
<!-- General styles -->
<style name="SuwThemeMaterial" parent="Theme.AppCompat.NoActionBar">
+ <item name="android:colorBackground">@color/suw_color_background_dark</item>
<item name="android:indeterminateTint" tools:ignore="NewApi">@color/suw_progress_bar_color_dark</item>
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
@@ -38,6 +39,8 @@
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonFontFamily">sans-serif</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>
@@ -51,6 +54,7 @@
</style>
<style name="SuwThemeMaterial.Light" parent="Theme.AppCompat.Light.NoActionBar">
+ <item name="android:colorBackground">@color/suw_color_background_light</item>
<item name="android:indeterminateTint" tools:ignore="NewApi">@color/suw_progress_bar_color_light</item>
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
@@ -69,6 +73,8 @@
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonFontFamily">sans-serif</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>
@@ -98,15 +104,19 @@
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="colorAccent">@color/suw_color_accent_glif_dark</item>
- <item name="colorPrimary">@color/suw_color_accent_glif_dark</item>
+ <item name="colorPrimary">?attr/colorAccent</item>
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
+ <item name="suwButtonFontFamily">sans-serif</item>
<item name="suwColorPrimary">?attr/colorPrimary</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="suwGlifIconStyle">@style/SuwGlifIcon</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>
@@ -133,15 +143,19 @@
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="colorAccent">@color/suw_color_accent_glif_light</item>
- <item name="colorPrimary">@color/suw_color_accent_glif_light</item>
+ <item name="colorPrimary">?attr/colorAccent</item>
<item name="listPreferredItemPaddingLeft">?attr/suwMarginSides</item>
<item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
+ <item name="suwButtonFontFamily">sans-serif</item>
<item name="suwColorPrimary">?attr/colorPrimary</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="suwGlifIconStyle">@style/SuwGlifIcon</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>
@@ -151,6 +165,21 @@
<item name="textAppearanceListItemSmall">@style/TextAppearance.SuwGlifItemSummary</item>
</style>
+ <style name="SuwThemeGlifV3" parent="SuwThemeGlifV2">
+ <item name="colorAccent">@color/suw_color_accent_glif_v3</item>
+ <item name="suwButtonAllCaps">false</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
+ <item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
+ </style>
+
+ <style name="SuwBaseThemeGlifV3.Light" parent="SuwThemeGlifV2.Light">
+ <item name="colorAccent">@color/suw_color_accent_glif_v3</item>
+ <item name="suwButtonAllCaps">false</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
+ <item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
+ </style>
+ <style name="SuwThemeGlifV3.Light" parent="SuwBaseThemeGlifV3.Light" />
+
<!-- Content styles -->
<style name="TextAppearance.SuwDescription" parent="TextAppearance.AppCompat.Medium">
@@ -197,11 +226,18 @@
ContextThemeWrapper. These self-referencing attributes make sure this is applied as
both to the button. -->
<item name="android:buttonStyle">@style/SuwGlifButton.Primary</item>
+ <item name="android:theme">@style/SuwGlifButton.Primary</item>
<item name="buttonStyle">@style/SuwGlifButton.Primary</item>
<!-- Values used in styles -->
+ <item name="android:fontFamily" tools:targetApi="jelly_bean">?attr/suwButtonFontFamily</item>
<item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
<item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
+ <item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">?attr/suwButtonAllCaps</item>
+ <item name="textAllCaps">?attr/suwButtonAllCaps</item>
+
+ <!-- Values used in themes -->
+ <item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
</style>
<style name="SuwGlifButton.Secondary" parent="Widget.AppCompat.Button.Borderless.Colored">
@@ -213,11 +249,15 @@
<item name="buttonStyle">@style/SuwGlifButton.Secondary</item>
<!-- Values used in styles -->
+ <item name="android:fontFamily" tools:targetApi="jelly_bean">?attr/suwButtonFontFamily</item>
<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>
+ <item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">?attr/suwButtonAllCaps</item>
+ <item name="textAllCaps">?attr/suwButtonAllCaps</item>
<!-- Values used in themes -->
+ <item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
<item name="android:colorControlHighlight" tools:targetApi="lollipop">@color/suw_flat_button_highlight</item>
<item name="colorControlHighlight">@color/suw_flat_button_highlight</item>
</style>
@@ -234,6 +274,13 @@
<item name="android:background">?attr/colorPrimary</item>
</style>
+ <style name="SuwBase.ProgressBarLarge" parent="@android:style/Widget.ProgressBar.Large" />
+
+ <style name="SuwFourColorIndeterminateProgressBar" parent="SuwBase.ProgressBarLarge">
+ <item name="android:layout_gravity">center</item>
+ <item name="android:indeterminate">true</item>
+ </style>
+
<!-- Navigation bar styles -->
<style name="SuwNavBarButtonStyle" parent="@android:style/Widget.Button">
@@ -258,4 +305,8 @@
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg_light</item>
</style>
+
+ <style name="SuwAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert" />
+
+ <style name="SuwAlertDialogTheme.Light" parent="Theme.AppCompat.Light.Dialog.Alert" />
</resources>
diff --git a/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java b/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
index be9916e..71d1bb6 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
@@ -138,6 +138,10 @@ public class ExpandableSwitchItem extends SwitchItem
}
tintCompoundDrawables(view);
+
+ // Expandable switch item has focusability on the expandable layout on the left, and the
+ // switch on the right, but not the item itself.
+ view.setFocusable(false);
}
@Override
diff --git a/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
index 1e663d6..9965aa0 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
@@ -19,6 +19,8 @@ package com.android.setupwizardlib.util;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
@@ -38,12 +40,11 @@ import java.util.List;
/**
* An accessibility delegate that allows {@link android.text.style.ClickableSpan} to be focused and
* clicked by accessibility services.
- * <p>
- * <strong>Note: </strong> From Android O on, there is native support for ClickableSpan
- * accessibility, so this class is not needed (and indeed has no effect.)
- * </p>
*
- * <p />Sample usage:
+ * <p><strong>Note:</strong> This class is a no-op on Android O or above since there is native
+ * support for ClickableSpan accessibility.
+ *
+ * <p>Sample usage:
* <pre>
* LinkAccessibilityHelper mAccessibilityHelper;
*
@@ -68,294 +69,260 @@ public class LinkAccessibilityHelper extends AccessibilityDelegateCompat {
private static final String TAG = "LinkAccessibilityHelper";
- private final TextView mView;
- private final Rect mTempRect = new Rect();
- private final ExploreByTouchHelper mExploreByTouchHelper;
+ private final AccessibilityDelegateCompat mDelegate;
public LinkAccessibilityHelper(TextView view) {
- 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
- protected int getVirtualViewAt(float x, float y) {
- return LinkAccessibilityHelper.this.getVirtualViewAt(x, y);
- }
-
- @Override
- protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
- LinkAccessibilityHelper.this.getVisibleVirtualViews(virtualViewIds);
- }
-
- @Override
- protected void onPopulateEventForVirtualView(int virtualViewId,
- AccessibilityEvent event) {
- LinkAccessibilityHelper
- .this.onPopulateEventForVirtualView(virtualViewId, event);
- }
-
- @Override
- protected void onPopulateNodeForVirtualView(int virtualViewId,
- AccessibilityNodeInfoCompat infoCompat) {
- LinkAccessibilityHelper
- .this.onPopulateNodeForVirtualView(virtualViewId, infoCompat);
-
- }
+ this(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ // Platform support was added in O. This helper will be no-op
+ ? new AccessibilityDelegateCompat()
+ // Pre-O, we extend ExploreByTouchHelper to expose a virtual view hierarchy
+ : new PreOLinkAccessibilityHelper(view));
+ }
- @Override
- protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
- Bundle arguments) {
- return LinkAccessibilityHelper.this
- .onPerformActionForVirtualView(virtualViewId, action, arguments);
- }
- };
- } else {
- mExploreByTouchHelper = null;
- }
- mView = view;
+ @VisibleForTesting
+ LinkAccessibilityHelper(@NonNull AccessibilityDelegateCompat delegate) {
+ mDelegate = delegate;
}
@Override
public void sendAccessibilityEvent(View host, int eventType) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.sendAccessibilityEvent(host, eventType);
- } else {
- super.sendAccessibilityEvent(host, eventType);
- }
+ mDelegate.sendAccessibilityEvent(host, eventType);
}
@Override
public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.sendAccessibilityEventUnchecked(host, event);
- } else {
- super.sendAccessibilityEventUnchecked(host, event);
- }
+ mDelegate.sendAccessibilityEventUnchecked(host, event);
}
@Override
public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.dispatchPopulateAccessibilityEvent(host, event)
- : super.dispatchPopulateAccessibilityEvent(host, event);
+ return mDelegate.dispatchPopulateAccessibilityEvent(host, event);
}
@Override
public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.onPopulateAccessibilityEvent(host, event);
- } else {
- super.onPopulateAccessibilityEvent(host, event);
- }
+ mDelegate.onPopulateAccessibilityEvent(host, event);
}
@Override
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.onInitializeAccessibilityEvent(host, event);
- } else {
- super.onInitializeAccessibilityEvent(host, event);
- }
+ mDelegate.onInitializeAccessibilityEvent(host, event);
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
- if (mExploreByTouchHelper != null) {
- mExploreByTouchHelper.onInitializeAccessibilityNodeInfo(host, info);
- } else {
- super.onInitializeAccessibilityNodeInfo(host, info);
- }
+ mDelegate.onInitializeAccessibilityNodeInfo(host, info);
}
@Override
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
AccessibilityEvent event) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.onRequestSendAccessibilityEvent(host, child, event)
- : super.onRequestSendAccessibilityEvent(host, child, event);
+ return mDelegate.onRequestSendAccessibilityEvent(host, child, event);
}
@Override
public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.getAccessibilityNodeProvider(host)
- : super.getAccessibilityNodeProvider(host);
+ return mDelegate.getAccessibilityNodeProvider(host);
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
- return (mExploreByTouchHelper != null)
- ? mExploreByTouchHelper.performAccessibilityAction(host, action, args)
- : super.performAccessibilityAction(host, action, args);
+ return mDelegate.performAccessibilityAction(host, action, args);
}
/**
- * Delegated to {@link ExploreByTouchHelper}
+ * Dispatches hover event to the virtual view hierarchy. This method should be called in
+ * {@link View#dispatchHoverEvent(MotionEvent)}.
+ *
+ * @see ExploreByTouchHelper#dispatchHoverEvent(MotionEvent)
*/
public final boolean dispatchHoverEvent(MotionEvent event) {
- return (mExploreByTouchHelper != null) ? mExploreByTouchHelper.dispatchHoverEvent(event)
- : false;
+ return mDelegate instanceof ExploreByTouchHelper
+ && ((ExploreByTouchHelper) mDelegate).dispatchHoverEvent(event);
}
- protected int getVirtualViewAt(float x, float y) {
- final CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- final Spanned spannedText = (Spanned) text;
- final int offset = getOffsetForPosition(mView, x, y);
- ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class);
- if (linkSpans.length == 1) {
- ClickableSpan linkSpan = linkSpans[0];
- return spannedText.getSpanStart(linkSpan);
- }
+ @VisibleForTesting
+ static class PreOLinkAccessibilityHelper extends ExploreByTouchHelper {
+
+ private final Rect mTempRect = new Rect();
+ private final TextView mView;
+
+ PreOLinkAccessibilityHelper(TextView view) {
+ super(view);
+ mView = view;
}
- return ExploreByTouchHelper.INVALID_ID;
- }
- protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
- final CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- final Spanned spannedText = (Spanned) text;
- ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(),
- ClickableSpan.class);
- for (ClickableSpan span : linkSpans) {
- virtualViewIds.add(spannedText.getSpanStart(span));
+ @Override
+ protected int getVirtualViewAt(float x, float y) {
+ final CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
+ final Spanned spannedText = (Spanned) text;
+ final int offset = getOffsetForPosition(mView, x, y);
+ ClickableSpan[] linkSpans =
+ spannedText.getSpans(offset, offset, ClickableSpan.class);
+ if (linkSpans.length == 1) {
+ ClickableSpan linkSpan = linkSpans[0];
+ return spannedText.getSpanStart(linkSpan);
+ }
}
+ return ExploreByTouchHelper.INVALID_ID;
}
- }
- protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
- final ClickableSpan span = getSpanForOffset(virtualViewId);
- if (span != null) {
- event.setContentDescription(getTextForSpan(span));
- } else {
- Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
- event.setContentDescription(mView.getText());
+ @Override
+ protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+ final CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
+ final Spanned spannedText = (Spanned) text;
+ ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(),
+ ClickableSpan.class);
+ for (ClickableSpan span : linkSpans) {
+ virtualViewIds.add(spannedText.getSpanStart(span));
+ }
+ }
}
- }
- protected void onPopulateNodeForVirtualView(int virtualViewId,
- AccessibilityNodeInfoCompat info) {
- final ClickableSpan span = getSpanForOffset(virtualViewId);
- if (span != null) {
- info.setContentDescription(getTextForSpan(span));
- } else {
- Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
- info.setContentDescription(mView.getText());
- }
- info.setFocusable(true);
- info.setClickable(true);
- getBoundsForSpan(span, mTempRect);
- if (mTempRect.isEmpty()) {
- Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
- mTempRect.set(0, 0, 1, 1);
+ @Override
+ protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+ final ClickableSpan span = getSpanForOffset(virtualViewId);
+ if (span != null) {
+ event.setContentDescription(getTextForSpan(span));
+ } else {
+ Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+ event.setContentDescription(mView.getText());
+ }
}
- info.setBoundsInParent(mTempRect);
- info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- }
- protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
- Bundle arguments) {
- if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
- ClickableSpan span = getSpanForOffset(virtualViewId);
+ @Override
+ protected void onPopulateNodeForVirtualView(
+ int virtualViewId,
+ AccessibilityNodeInfoCompat info) {
+ final ClickableSpan span = getSpanForOffset(virtualViewId);
if (span != null) {
- span.onClick(mView);
- return true;
+ info.setContentDescription(getTextForSpan(span));
} else {
Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+ info.setContentDescription(mView.getText());
}
+ info.setFocusable(true);
+ info.setClickable(true);
+ getBoundsForSpan(span, mTempRect);
+ if (mTempRect.isEmpty()) {
+ Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
+ mTempRect.set(0, 0, 1, 1);
+ }
+ info.setBoundsInParent(mTempRect);
+ info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
}
- return false;
- }
- private ClickableSpan getSpanForOffset(int offset) {
- CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- Spanned spannedText = (Spanned) text;
- ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
- if (spans.length == 1) {
- return spans[0];
+ @Override
+ protected boolean onPerformActionForVirtualView(
+ int virtualViewId,
+ int action,
+ Bundle arguments) {
+ if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
+ ClickableSpan span = getSpanForOffset(virtualViewId);
+ if (span != null) {
+ span.onClick(mView);
+ return true;
+ } else {
+ Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+ }
}
+ return false;
}
- return null;
- }
- private CharSequence getTextForSpan(ClickableSpan span) {
- CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- Spanned spannedText = (Spanned) text;
- return spannedText.subSequence(spannedText.getSpanStart(span),
- spannedText.getSpanEnd(span));
+ private ClickableSpan getSpanForOffset(int offset) {
+ CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
+ Spanned spannedText = (Spanned) text;
+ ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
+ if (spans.length == 1) {
+ return spans[0];
+ }
+ }
+ return null;
}
- return text;
- }
- // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for the
- // section on the first line.
- private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
- CharSequence text = mView.getText();
- outRect.setEmpty();
- if (text instanceof Spanned) {
- final Layout layout = mView.getLayout();
- if (layout != null) {
+ private CharSequence getTextForSpan(ClickableSpan span) {
+ CharSequence text = mView.getText();
+ if (text instanceof Spanned) {
Spanned spannedText = (Spanned) text;
- final int spanStart = spannedText.getSpanStart(span);
- final int spanEnd = spannedText.getSpanEnd(span);
- final float xStart = layout.getPrimaryHorizontal(spanStart);
- final float xEnd = layout.getPrimaryHorizontal(spanEnd);
- final int lineStart = layout.getLineForOffset(spanStart);
- final int lineEnd = layout.getLineForOffset(spanEnd);
- layout.getLineBounds(lineStart, outRect);
- if (lineEnd == lineStart) {
- // If the span is on a single line, adjust both the left and right bounds
- // so outrect is exactly bounding the span.
- outRect.left = (int) Math.min(xStart, xEnd);
- outRect.right = (int) Math.max(xStart, xEnd);
- } else {
- // If the span wraps across multiple lines, only use the first line (as returned
- // by layout.getLineBounds above), and adjust the "start" of outrect to where
- // the span starts, leaving the "end" of outrect at the end of the line.
- // ("start" being left for LTR, and right for RTL)
- if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
- outRect.right = (int) xStart;
+ return spannedText.subSequence(
+ spannedText.getSpanStart(span),
+ spannedText.getSpanEnd(span));
+ }
+ return text;
+ }
+
+ // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for
+ // the section on the first line.
+ private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
+ CharSequence text = mView.getText();
+ outRect.setEmpty();
+ if (text instanceof Spanned) {
+ final Layout layout = mView.getLayout();
+ if (layout != null) {
+ Spanned spannedText = (Spanned) text;
+ final int spanStart = spannedText.getSpanStart(span);
+ final int spanEnd = spannedText.getSpanEnd(span);
+ final float xStart = layout.getPrimaryHorizontal(spanStart);
+ final float xEnd = layout.getPrimaryHorizontal(spanEnd);
+ final int lineStart = layout.getLineForOffset(spanStart);
+ final int lineEnd = layout.getLineForOffset(spanEnd);
+ layout.getLineBounds(lineStart, outRect);
+ if (lineEnd == lineStart) {
+ // If the span is on a single line, adjust both the left and right bounds
+ // so outrect is exactly bounding the span.
+ outRect.left = (int) Math.min(xStart, xEnd);
+ outRect.right = (int) Math.max(xStart, xEnd);
} else {
- outRect.left = (int) xStart;
+ // If the span wraps across multiple lines, only use the first line (as
+ // returned by layout.getLineBounds above), and adjust the "start" of
+ // outrect to where the span starts, leaving the "end" of outrect at the end
+ // of the line. ("start" being left for LTR, and right for RTL)
+ if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
+ outRect.right = (int) xStart;
+ } else {
+ outRect.left = (int) xStart;
+ }
}
- }
- // Offset for padding
- outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
+ // Offset for padding
+ outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
+ }
}
+ return outRect;
}
- return outRect;
- }
- // Compat implementation of TextView#getOffsetForPosition().
+ // Compat implementation of TextView#getOffsetForPosition().
- private static int getOffsetForPosition(TextView view, float x, float y) {
- if (view.getLayout() == null) return -1;
- final int line = getLineAtCoordinate(view, y);
- return getOffsetAtCoordinate(view, line, x);
- }
+ private static int getOffsetForPosition(TextView view, float x, float y) {
+ if (view.getLayout() == null) return -1;
+ final int line = getLineAtCoordinate(view, y);
+ return getOffsetAtCoordinate(view, line, x);
+ }
- private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
- x -= view.getTotalPaddingLeft();
- // Clamp the position to inside of the view.
- x = Math.max(0.0f, x);
- x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
- x += view.getScrollX();
- return x;
- }
+ private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
+ x -= view.getTotalPaddingLeft();
+ // Clamp the position to inside of the view.
+ x = Math.max(0.0f, x);
+ x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
+ x += view.getScrollX();
+ return x;
+ }
- private static int getLineAtCoordinate(TextView view, float y) {
- y -= view.getTotalPaddingTop();
- // Clamp the position to inside of the view.
- y = Math.max(0.0f, y);
- y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
- y += view.getScrollY();
- return view.getLayout().getLineForVertical((int) y);
- }
+ private static int getLineAtCoordinate(TextView view, float y) {
+ y -= view.getTotalPaddingTop();
+ // Clamp the position to inside of the view.
+ y = Math.max(0.0f, y);
+ y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
+ y += view.getScrollY();
+ return view.getLayout().getLineForVertical((int) y);
+ }
- private static int getOffsetAtCoordinate(TextView view, int line, float x) {
- x = convertToLocalHorizontalCoordinate(view, x);
- return view.getLayout().getOffsetForHorizontal(line, x);
+ private static int getOffsetAtCoordinate(TextView view, int line, float x) {
+ x = convertToLocalHorizontalCoordinate(view, x);
+ return view.getLayout().getOffsetForHorizontal(line, x);
+ }
}
}
diff --git a/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
index 5172c47..d7a3c2e 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
@@ -16,6 +16,7 @@
package com.android.setupwizardlib.view;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
@@ -30,6 +31,7 @@ import android.widget.Button;
* Button for navigation bar, which includes tinting of its compound drawables to be used for dark
* and light themes.
*/
+@SuppressLint("AppCompatCustomView")
public class NavigationBarButton extends Button {
public NavigationBarButton(Context context) {
diff --git a/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
index e6bc9da..1b1f82e 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
@@ -25,7 +25,7 @@ import android.support.v7.widget.AppCompatTextView;
import android.text.Annotation;
import android.text.SpannableString;
import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
+import android.text.method.MovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.TextAppearanceSpan;
import android.util.AttributeSet;
@@ -36,6 +36,7 @@ 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;
+import com.android.setupwizardlib.view.TouchableMovementMethod.TouchableLinkMovementMethod;
/**
* An extension of TextView that automatically replaces the annotation tags as specified in
@@ -121,7 +122,7 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
// 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());
+ setMovementMethod(TouchableLinkMovementMethod.getInstance());
} else {
setMovementMethod(null);
}
@@ -130,6 +131,17 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
// as individual TextViews consume touch events and thereby reducing the focus window
// shown by Talkback. Disable focus if there are no links
setFocusable(hasLinks);
+ // Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
+ // focusable in touch mode, we may be focused when the screen is first shown, and starting
+ // a screen halfway scrolled down is confusing to the user.
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ setRevealOnFocusHint(false);
+ // setRevealOnFocusHint is a new API added in SDK 25. For lower SDK versions, do not
+ // call setFocusableInTouchMode. We won't get touch effect on those earlier versions,
+ // but the link will still work, and will prevent the scroll view from starting halfway
+ // down the page.
+ setFocusableInTouchMode(hasLinks);
+ }
}
private boolean hasLinks(CharSequence text) {
@@ -142,6 +154,25 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
}
@Override
+ @SuppressWarnings("ClickableViewAccessibility") // super.onTouchEvent is called
+ public boolean onTouchEvent(MotionEvent event) {
+ // Since View#onTouchEvent always return true if the view is clickable (which is the case
+ // when a TextView has a movement method), override the implementation to allow the movement
+ // method, if it implements TouchableMovementMethod, to say that the touch is not handled,
+ // allowing the event to bubble up to the parent view.
+ boolean superResult = super.onTouchEvent(event);
+ MovementMethod movementMethod = getMovementMethod();
+ if (movementMethod instanceof TouchableMovementMethod) {
+ TouchableMovementMethod touchableMovementMethod =
+ (TouchableMovementMethod) movementMethod;
+ if (touchableMovementMethod.getLastTouchEvent() == event) {
+ return touchableMovementMethod.isLastTouchEventHandled();
+ }
+ }
+ return superResult;
+ }
+
+ @Override
protected boolean dispatchHoverEvent(MotionEvent event) {
if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) {
return true;
diff --git a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
index 74d3be6..b97905c 100644
--- a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
+++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
@@ -18,6 +18,7 @@ package com.android.setupwizardlib.items;
import static org.junit.Assert.assertTrue;
+import android.support.annotation.StyleRes;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.rule.UiThreadTestRule;
@@ -29,7 +30,6 @@ import android.widget.LinearLayout;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.test.util.DrawingTestHelper;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,40 +38,61 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ButtonItemDrawingTest {
- private static final int GOOGLE_BLUE = 0xff4285f4;
+ private static final int GLIF_ACCENT_COLOR = 0xff4285f4;
+ private static final int GLIF_V3_ACCENT_COLOR = 0xff1a73e8;
// These tests need to be run on UI thread because button uses ValueAnimator
@Rule
public UiThreadTestRule mUiThreadTestRule = new UiThreadTestRule();
- private ViewGroup mParent;
+ @Test
+ @UiThreadTest
+ public void drawButton_glif_shouldHaveAccentColoredButton()
+ throws InstantiationException, IllegalAccessException {
+ Button button = createButton(R.style.SuwThemeGlif_Light);
+
+ DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
+ drawingTestHelper.drawView(button);
- @Before
- public void setUp() throws Exception {
- mParent = new LinearLayout(
- DrawingTestHelper.createCanvasActivity(R.style.SuwThemeGlif_Light));
+ int accentPixelCount =
+ countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_ACCENT_COLOR);
+ assertTrue("> 10 pixels should be #4285f4. Found " + accentPixelCount,
+ accentPixelCount > 10);
}
@Test
@UiThreadTest
- public void testColoredButtonTheme() {
+ public void drawButton_glifV3_shouldHaveAccentColoredButton()
+ throws InstantiationException, IllegalAccessException {
+ Button button = createButton(R.style.SuwThemeGlifV3_Light);
+
+ DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
+ drawingTestHelper.drawView(button);
+
+ int accentPixelCount =
+ countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_V3_ACCENT_COLOR);
+ assertTrue("> 10 pixels should be #1a73e8. Found " + accentPixelCount,
+ accentPixelCount > 10);
+ }
+
+ private Button createButton(@StyleRes int theme)
+ throws InstantiationException, IllegalAccessException {
+ final ViewGroup parent = new LinearLayout(DrawingTestHelper.createCanvasActivity(theme));
TestButtonItem item = new TestButtonItem();
item.setTheme(R.style.SuwButtonItem_Colored);
item.setText("foobar");
- final Button button = item.createButton(mParent);
-
- DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
- drawingTestHelper.drawView(button);
+ return item.createButton(parent);
+ }
- int googleBluePixelCount = 0;
- for (int pixel : drawingTestHelper.getPixels()) {
- if (pixel == GOOGLE_BLUE) {
- googleBluePixelCount++;
+ private int countPixelsWithColor(int[] pixels, int color) {
+ int count = 0;
+ for (int pixel : pixels) {
+ if (pixel == color) {
+ count++;
}
}
- assertTrue("> 10 pixels should be Google blue. Found " + googleBluePixelCount,
- googleBluePixelCount > 10);
+ return count;
}
private static class TestButtonItem extends ButtonItem {
diff --git a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java
index 844e73e..6228e6f 100644
--- a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java
+++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java
@@ -14,29 +14,35 @@
* limitations under the License.
*/
-package com.android.setupwizardlib.test;
+package com.android.setupwizardlib.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.graphics.Rect;
-import android.os.Build;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.text.BidiFormatter;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import android.support.v4.widget.ExploreByTouchHelper;
import android.text.SpannableStringBuilder;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.setupwizardlib.span.LinkSpan;
-import com.android.setupwizardlib.util.LinkAccessibilityHelper;
+import com.android.setupwizardlib.util.LinkAccessibilityHelper.PreOLinkAccessibilityHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,13 +58,12 @@ public class LinkAccessibilityHelperTest {
private static final LinkSpan LINK_SPAN = new LinkSpan("foobar");
private TextView mTextView;
- private TestLinkAccessibilityHelper mHelper;
+ private TestPreOLinkAccessibilityHelper mHelper;
private DisplayMetrics mDisplayMetrics;
@Test
public void testGetVirtualViewAt() {
- 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);
@@ -66,7 +71,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testGetVirtualViewAtHost() {
- 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",
@@ -75,7 +79,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testGetVisibleVirtualViews() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
List<Integer> virtualViewIds = new ArrayList<>();
mHelper.getVisibleVirtualViews(virtualViewIds);
@@ -86,7 +89,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testOnPopulateEventForVirtualView() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
AccessibilityEvent event = AccessibilityEvent.obtain();
mHelper.onPopulateEventForVirtualView(1, event);
@@ -100,7 +102,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testOnPopulateEventForVirtualViewHost() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
AccessibilityEvent event = AccessibilityEvent.obtain();
mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event);
@@ -113,7 +114,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testOnPopulateNodeForVirtualView() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
initTextView();
AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
mHelper.onPopulateNodeForVirtualView(1, info);
@@ -132,7 +132,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testNullLayout() {
- 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);
@@ -150,7 +149,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testRtlLayout() {
- 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);
@@ -170,7 +168,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testMultilineLink() {
- 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.");
@@ -192,7 +189,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testRtlMultilineLink() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
+ "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
+ "דפים המחשב מיזמים ב.";
@@ -216,7 +212,6 @@ public class LinkAccessibilityHelperTest {
@Test
public void testBidiMultilineLink() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return;
String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
+ "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
+ "דפים המחשב מיזמים ב.";
@@ -243,6 +238,70 @@ public class LinkAccessibilityHelperTest {
info.recycle();
}
+ @Test
+ public void testMethodDelegation() {
+ initTextView();
+ ExploreByTouchHelper delegate = mock(TestPreOLinkAccessibilityHelper.class);
+ LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate);
+
+ AccessibilityEvent accessibilityEvent =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
+ helper.sendAccessibilityEvent(mTextView, AccessibilityEvent.TYPE_VIEW_CLICKED);
+ verify(delegate).sendAccessibilityEvent(
+ same(mTextView),
+ eq(AccessibilityEvent.TYPE_VIEW_CLICKED));
+
+ helper.sendAccessibilityEventUnchecked(mTextView, accessibilityEvent);
+ verify(delegate).sendAccessibilityEventUnchecked(same(mTextView), same(accessibilityEvent));
+
+ helper.performAccessibilityAction(
+ mTextView,
+ AccessibilityActionCompat.ACTION_CLICK.getId(),
+ Bundle.EMPTY);
+ verify(delegate).performAccessibilityAction(
+ same(mTextView),
+ eq(AccessibilityActionCompat.ACTION_CLICK.getId()),
+ eq(Bundle.EMPTY));
+
+ helper.dispatchPopulateAccessibilityEvent(
+ mTextView,
+ accessibilityEvent);
+ verify(delegate).dispatchPopulateAccessibilityEvent(
+ same(mTextView),
+ same(accessibilityEvent));
+
+ MotionEvent motionEvent = MotionEvent.obtain(0, 0, 0, 0, 0, 0);
+ helper.dispatchHoverEvent(motionEvent);
+ verify(delegate).dispatchHoverEvent(eq(motionEvent));
+
+ helper.getAccessibilityNodeProvider(mTextView);
+ verify(delegate).getAccessibilityNodeProvider(same(mTextView));
+
+ helper.onInitializeAccessibilityEvent(mTextView, accessibilityEvent);
+ verify(delegate).onInitializeAccessibilityEvent(
+ same(mTextView),
+ eq(accessibilityEvent));
+
+ AccessibilityNodeInfoCompat accessibilityNodeInfo = AccessibilityNodeInfoCompat.obtain();
+ helper.onInitializeAccessibilityNodeInfo(mTextView, accessibilityNodeInfo);
+ verify(delegate).onInitializeAccessibilityNodeInfo(
+ same(mTextView),
+ same(accessibilityNodeInfo));
+
+ helper.onPopulateAccessibilityEvent(mTextView, accessibilityEvent);
+ verify(delegate).onPopulateAccessibilityEvent(
+ same(mTextView),
+ same(accessibilityEvent));
+
+ FrameLayout parent = new FrameLayout(InstrumentationRegistry.getTargetContext());
+ helper.onRequestSendAccessibilityEvent(parent, mTextView, accessibilityEvent);
+ verify(delegate).onRequestSendAccessibilityEvent(
+ same(parent),
+ same(mTextView),
+ same(accessibilityEvent));
+ }
+
private void initTextView() {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
@@ -254,7 +313,7 @@ public class LinkAccessibilityHelperTest {
mTextView.setSingleLine(false);
mTextView.setText(text);
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
- mHelper = new TestLinkAccessibilityHelper(mTextView);
+ mHelper = new TestPreOLinkAccessibilityHelper(mTextView);
int measureExactly500dp = View.MeasureSpec.makeMeasureSpec(dp2Px(500),
View.MeasureSpec.EXACTLY);
@@ -270,9 +329,9 @@ public class LinkAccessibilityHelperTest {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics);
}
- private static class TestLinkAccessibilityHelper extends LinkAccessibilityHelper {
+ public static class TestPreOLinkAccessibilityHelper extends PreOLinkAccessibilityHelper {
- TestLinkAccessibilityHelper(TextView view) {
+ TestPreOLinkAccessibilityHelper(TextView view) {
super(view);
}
diff --git a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
index 0ae0737..6192061 100644
--- a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
+++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
@@ -16,35 +16,29 @@
package com.android.setupwizardlib.items;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.not;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.robolectric.RuntimeEnvironment.application;
-import android.support.v7.widget.SwitchCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.TextView;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
+import com.android.setupwizardlib.view.CheckableLinearLayout;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
-import java.util.ArrayList;
-
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
public class ExpandableSwitchItemTest {
private TextView mSummaryView;
@@ -71,6 +65,14 @@ public class ExpandableSwitchItemTest {
assertEquals("Should be collapsed initially", "TestSummary", mItem.getSummary());
assertEquals("Summary view should display collapsed summary",
"TestSummary", mSummaryView.getText());
+
+ assertFalse("Expandable switch item itself should not be focusable", view.isFocusable());
+
+ View switchContent = view.findViewById(R.id.suw_items_expandable_switch_content);
+ assertThat(switchContent).isInstanceOf(CheckableLinearLayout.class);
+ assertThat(switchContent.isFocusable())
+ .named("expandable content focusable")
+ .isTrue();
}
@Test
@@ -132,57 +134,25 @@ public class ExpandableSwitchItemTest {
mItem.onBindView(view);
final View titleView = view.findViewById(R.id.suw_items_title);
- assertThat("state_checked should not be set initially",
- toArrayList(titleView.getDrawableState()),
- not(hasItem(android.R.attr.state_checked)));
+ assertThat(titleView.getDrawableState()).asList().named("Drawable state")
+ .doesNotContain(android.R.attr.state_checked);
mItem.setExpanded(true);
mItem.onBindView(view);
- assertThat("state_checked should not be set initially",
- toArrayList(titleView.getDrawableState()),
- hasItem(android.R.attr.state_checked));
+ assertThat(titleView.getDrawableState()).asList().named("Drawable state")
+ .contains(android.R.attr.state_checked);
mItem.setExpanded(false);
mItem.onBindView(view);
- assertThat("state_checked should not be set initially",
- toArrayList(titleView.getDrawableState()),
- not(hasItem(android.R.attr.state_checked)));
- }
-
- private ArrayList<Integer> toArrayList(int[] array) {
- ArrayList<Integer> arrayList = new ArrayList<>(array.length);
- for (int i : array) {
- arrayList.add(i);
- }
- return arrayList;
+ assertThat(titleView.getDrawableState()).asList().named("Drawable state")
+ .doesNotContain(android.R.attr.state_checked);
}
private ViewGroup createLayout() {
- ViewGroup root = new FrameLayout(application);
-
- ViewGroup content = new FrameLayout(application);
- content.setId(R.id.suw_items_expandable_switch_content);
- root.addView(content);
-
- TextView titleView = new TextView(application);
- titleView.setId(R.id.suw_items_title);
- content.addView(titleView);
-
- mSummaryView = new TextView(application);
- mSummaryView.setId(R.id.suw_items_summary);
- content.addView(mSummaryView);
-
- FrameLayout iconContainer = new FrameLayout(application);
- iconContainer.setId(R.id.suw_items_icon_container);
- content.addView(iconContainer);
-
- ImageView iconView = new ImageView(application);
- iconView.setId(R.id.suw_items_icon);
- iconContainer.addView(iconView);
-
- SwitchCompat switchView = new SwitchCompat(application);
- switchView.setId(R.id.suw_items_switch);
- root.addView(switchView);
+ ViewGroup root =
+ (ViewGroup) LayoutInflater.from(application)
+ .inflate(R.layout.suw_items_expandable_switch, null);
+ mSummaryView = root.findViewById(R.id.suw_items_summary);
return root;
}
diff --git a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
index d391d80..fa5bbba 100644
--- a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
+++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
@@ -31,7 +31,6 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
@@ -40,7 +39,7 @@ import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
public class SwitchItemTest {
private SwitchCompat mSwitch;
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
index 43e7f03..7a08235 100644
--- a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java
+++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java
@@ -25,7 +25,6 @@ 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;
@@ -35,7 +34,7 @@ import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = Config.ALL_SDKS)
+@Config(sdk = Config.ALL_SDKS)
public class DimensionConsistencyTest {
// Visual height of the framework switch widget
diff --git a/library/lint.xml b/library/lint.xml
index ca22c65..625b20d 100644
--- a/library/lint.xml
+++ b/library/lint.xml
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
+ <!-- SUW lib prefixes styleable resources -->
+ <issue id="CustomViewStyleable" severity="ignore" />
<issue id="ExtraTranslation" severity="ignore" />
<issue id="GradleDependency" severity="ignore" />
<issue id="MissingTranslation" severity="ignore" />
+ <!-- Stop lint from complaining about SDK version checks in the "platform" variant -->
+ <issue id="ObsoleteSdkInt" severity="ignore" />
<issue id="RtlEnabled" severity="ignore" />
</lint>
diff --git a/library/main/res/drawable-v21/suw_edit_text_bg_shape.xml b/library/main/res/drawable-v21/suw_edit_text_bg_shape.xml
new file mode 100644
index 0000000..ad55ec6
--- /dev/null
+++ b/library/main/res/drawable-v21/suw_edit_text_bg_shape.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:topLeftRadius="4dp" android:topRightRadius="4dp"/>
+ <solid android:color="?attr/suwEditTextBackgroundColor"/>
+</shape> \ No newline at end of file
diff --git a/library/main/res/drawable-v21/suw_edittext_bg.xml b/library/main/res/drawable-v21/suw_edittext_bg.xml
new file mode 100644
index 0000000..b69c10b
--- /dev/null
+++ b/library/main/res/drawable-v21/suw_edittext_bg.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false">
+ <layer-list>
+ <item android:drawable="@drawable/suw_edit_text_bg_shape" android:bottom="1dp"/>
+ <item android:gravity="bottom">
+ <shape>
+ <size android:height="1dp"/>
+ <solid android:color="?android:attr/textColorSecondary"/>
+ </shape>
+ </item>
+ </layer-list>
+ </item>
+ <item android:state_focused="false" android:state_pressed="false">
+ <layer-list>
+ <item android:drawable="@drawable/suw_edit_text_bg_shape" android:bottom="1dp" />
+ <item android:gravity="bottom">
+ <shape>
+ <size android:height="1dp"/>
+ <solid android:color="?android:attr/textColorSecondary"/>
+ </shape>
+ </item>
+ </layer-list>
+ </item>
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/suw_edit_text_bg_shape" android:bottom="2dp" />
+ <item android:gravity="bottom">
+ <shape>
+ <size android:height="2dp"/>
+ <solid android:color="?android:attr/colorAccent"/>
+ </shape>
+ </item>
+ </layer-list>
+ </item>
+</selector>
diff --git a/library/main/res/drawable-v21/suw_fourcolor_progress_bar.xml b/library/main/res/drawable-v21/suw_fourcolor_progress_bar.xml
new file mode 100644
index 0000000..2ac35ee
--- /dev/null
+++ b/library/main/res/drawable-v21/suw_fourcolor_progress_bar.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+
+
+<!-- Asset for 4 color indeterminate progress bar, which is a ring with 4 shades of blue -->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="MissingPrefix">
+ <!-- Ignore MissingPrefix: aapt:attr tags take the name attribute without "android:" prefix -->
+ <!-- TODO(yukl): Update to a newer version of lint which properly handles this case -->
+ <aapt:attr name="android:drawable">
+ <vector android:width="823dp" android:height="823dp" android:viewportHeight="823"
+ android:viewportWidth="823">
+ <group android:name="blue1" android:translateX="411.5" android:translateY="411.5">
+ <path android:name="blue1_path"
+ android:pathData="M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
+ android:strokeAlpha="1" android:strokeColor="#4688f1"
+ android:strokeLineCap="round" android:strokeLineJoin="round"
+ android:strokeWidth="27" />
+ </group>
+ <group android:name="blue2" android:translateX="411.5" android:translateY="411.5">
+ <path android:name="blue2_path"
+ android:pathData=" M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
+ android:strokeAlpha="1" android:strokeColor="#7dacf4"
+ android:strokeLineCap="round" android:strokeLineJoin="round"
+ android:strokeWidth="28" />
+ </group>
+ <group android:name="blue3" android:translateX="411.5" android:translateY="411.5">
+ <path android:name="blue3_path"
+ android:pathData=" M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
+ android:strokeAlpha="1" android:strokeColor="#c7dbfb"
+ android:strokeLineCap="round" android:strokeLineJoin="round"
+ android:strokeWidth="29" />
+ </group>
+ <group android:name="blue4" android:translateX="411.5" android:translateY="411.5">
+ <path android:name="blue4_path"
+ android:pathData=" M0 -395 C218,-395 395,-218 395,0 C395,218 218,395 0,395 C-218,395 -395,218 -395,0 C-395,-218 -218,-395 0,-395c "
+ android:strokeAlpha="1" android:strokeColor="#e8f0fd"
+ android:strokeLineCap="round" android:strokeLineJoin="round"
+ android:strokeWidth="30" />
+ </group>
+ </vector>
+ </aapt:attr>
+ <target android:name="blue1_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathStart"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 l.21,0 c.571,0 .194,.755 .79,1" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue1_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue1">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="rotation"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 c0.829,0.228 0.2,0.915 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue2_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathStart"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 l.21,0 c.571,0 .145,.831 .79,1" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue2_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue2">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="rotation"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 c0.792,0.233 0.2,0.915 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue3_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathStart"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 l.21,0 c.6138,0 .007,.883 .79,1" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue3_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue3">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:duration="1983" android:propertyName="rotation"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M0,0 c0.762,0.225 0.2,0.915 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="blue4_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathStart"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 l.21,0 c.572,0 0,1 .79,1" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue4_path">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="trimPathEnd"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 c0.606,0.315 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+ <target android:name="blue4">
+ <aapt:attr name="android:animation">
+ <objectAnimator android:duration="1983" android:propertyName="rotation"
+ android:repeatCount="infinite" android:valueFrom="0" android:valueTo="355"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M0,0 c0.606,0.172 0.2,0.915 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/library/main/res/layout/suw_glif_blank_template_content.xml b/library/main/res/layout/suw_glif_blank_template_content.xml
index ed81126..6d864cd 100644
--- a/library/main/res/layout/suw_glif_blank_template_content.xml
+++ b/library/main/res/layout/suw_glif_blank_template_content.xml
@@ -21,6 +21,11 @@
android:layout_height="match_parent"
android:orientation="vertical">
+ <ViewStub
+ android:id="@+id/suw_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<FrameLayout
android:id="@+id/suw_layout_content"
android:layout_width="match_parent"
diff --git a/library/main/res/layout/suw_glif_header.xml b/library/main/res/layout/suw_glif_header.xml
index b090f79..420e989 100644
--- a/library/main/res/layout/suw_glif_header.xml
+++ b/library/main/res/layout/suw_glif_header.xml
@@ -23,10 +23,11 @@
<ImageView
android:id="@+id/suw_layout_icon"
- style="@style/SuwGlifIcon"
+ style="?attr/suwGlifIconStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:contentDescription="@null" />
+ android:contentDescription="@null"
+ android:visibility="gone" />
<TextView
android:id="@+id/suw_layout_title"
diff --git a/library/main/res/layout/suw_glif_list_template_content.xml b/library/main/res/layout/suw_glif_list_template_content.xml
index d0c5cc4..58ca178 100644
--- a/library/main/res/layout/suw_glif_list_template_content.xml
+++ b/library/main/res/layout/suw_glif_list_template_content.xml
@@ -22,6 +22,11 @@
android:layout_height="match_parent"
android:orientation="vertical">
+ <ViewStub
+ android:id="@+id/suw_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
versions. -->
<com.android.setupwizardlib.view.StickyHeaderListView
diff --git a/library/main/res/layout/suw_glif_loading_screen.xml b/library/main/res/layout/suw_glif_loading_screen.xml
new file mode 100644
index 0000000..676ab34
--- /dev/null
+++ b/library/main/res/layout/suw_glif_loading_screen.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.setupwizardlib.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/setup_wizard_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:ignore="UnusedResources">
+ <!-- Ignore UnusedResources: can be used by clients -->
+
+ <com.android.setupwizardlib.view.FillContentLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <ProgressBar
+ android:id="@+id/suw_large_progress_bar"
+ style="@style/SuwFourColorIndeterminateProgressBar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ </com.android.setupwizardlib.view.FillContentLayout>
+
+</com.android.setupwizardlib.GlifLayout>
diff --git a/library/main/res/layout/suw_glif_template_content.xml b/library/main/res/layout/suw_glif_template_content.xml
index 0fe35a0..5226f63 100644
--- a/library/main/res/layout/suw_glif_template_content.xml
+++ b/library/main/res/layout/suw_glif_template_content.xml
@@ -22,6 +22,11 @@
android:layout_height="match_parent"
android:orientation="vertical">
+ <ViewStub
+ android:id="@+id/suw_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
versions. -->
<com.android.setupwizardlib.view.BottomScrollView
diff --git a/library/main/res/values-en-rCA/strings.xml b/library/main/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..5260500
--- /dev/null
+++ b/library/main/res/values-en-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<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">"Next"</string>
+ <string name="suw_back_button_label" msgid="1460929053642711025">"Back"</string>
+ <string name="suw_more_button_label" msgid="7769076059705546563">"More"</string>
+</resources>
diff --git a/library/main/res/values-en-rXC/strings.xml b/library/main/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..693af6b
--- /dev/null
+++ b/library/main/res/values-en-rXC/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎Next‎‏‎‎‏‎"</string>
+ <string name="suw_back_button_label" msgid="1460929053642711025">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎Back‎‏‎‎‏‎"</string>
+ <string name="suw_more_button_label" msgid="7769076059705546563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎More‎‏‎‎‏‎"</string>
+</resources>
diff --git a/library/main/res/values-v11/styles.xml b/library/main/res/values-v11/styles.xml
new file mode 100644
index 0000000..6903577
--- /dev/null
+++ b/library/main/res/values-v11/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+
+<resources>
+
+ <style name="SuwBase.ProgressBarLarge" parent="@android:style/Widget.Holo.ProgressBar.Large" />
+
+</resources>
diff --git a/library/main/res/values-v21/styles.xml b/library/main/res/values-v21/styles.xml
index ab6f887..d2c27f6 100644
--- a/library/main/res/values-v21/styles.xml
+++ b/library/main/res/values-v21/styles.xml
@@ -42,6 +42,16 @@
<item name="android:fontFamily">sans-serif-medium</item>
</style>
+ <style name="SuwBase.ProgressBarLarge" parent="@android:style/Widget.Material.ProgressBar.Large" />
+
+ <style name="SuwFourColorIndeterminateProgressBar" parent="SuwBase.ProgressBarLarge">
+ <item name="android:layout_gravity">center</item>
+ <item name="android:indeterminate">true</item>
+ <item name="android:indeterminateDrawable">@drawable/suw_fourcolor_progress_bar</item>
+ <item name="android:indeterminateTint">@null</item>
+ <item name="android:indeterminateTintMode">@null</item>
+ </style>
+
<!-- Items styles -->
<style name="SuwItemContainer">
@@ -83,4 +93,11 @@
<item name="suwNavBarButtonBackground">@drawable/suw_navbar_btn_bg</item>
</style>
+ <style name="SuwEditText" parent="@android:style/Widget.Material.EditText">
+ <item name="android:background">@drawable/suw_edittext_bg</item>
+ <item name="android:minHeight">@dimen/suw_edit_text_min_height</item>
+ <item name="android:paddingLeft">@dimen/suw_edit_text_padding_horizontal</item>
+ <item name="android:paddingRight">@dimen/suw_edit_text_padding_horizontal</item>
+ </style>
+
</resources>
diff --git a/library/main/res/values-v22/styles.xml b/library/main/res/values-v22/styles.xml
new file mode 100644
index 0000000..7d2d219
--- /dev/null
+++ b/library/main/res/values-v22/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<resources>
+
+ <style name="SuwAlertDialogTheme" parent="android:Theme.DeviceDefault.Dialog.Alert" />
+
+ <style name="SuwAlertDialogTheme.Light" parent="android:Theme.DeviceDefault.Light.Dialog.Alert" />
+</resources> \ No newline at end of file
diff --git a/library/main/res/values/attrs.xml b/library/main/res/values/attrs.xml
index 36d5fb7..b3fcfe9 100644
--- a/library/main/res/values/attrs.xml
+++ b/library/main/res/values/attrs.xml
@@ -20,6 +20,7 @@
<!-- Theme attributes -->
<attr name="suwLayoutTheme" format="reference" />
<attr name="suwMarginSides" format="dimension|reference" />
+ <attr name="suwEditTextBackgroundColor" format="color" />
<!-- Subset of values in "gravity" in frameworks/base/core/res/res/values/attrs.xml. Only
horizontal values are listed here as the header does not support vertical gravity. -->
@@ -37,13 +38,17 @@
<!-- Push object to the end of its container, not changing its size. -->
<flag name="end" value="0x00800005" />
</attr>
+ <attr name="suwGlifIconStyle" format="reference" />
+ <attr name="suwButtonAllCaps" format="boolean" />
+ <attr name="suwButtonCornerRadius" format="dimension" />
+ <attr name="suwButtonFontFamily" format="string|reference" />
<attr name="suwCardBackground" format="color|reference" />
- <attr name="suwFillContentLayoutStyle" format="reference" />
<attr name="suwDividerCondition">
<enum name="either" value="0" />
<enum name="both" value="1" />
</attr>
+ <attr name="suwFillContentLayoutStyle" format="reference" />
<attr name="suwListItemIconColor" format="color" />
<attr name="suwNavBarBackgroundColor" format="color" />
<attr name="suwNavBarButtonBackground" format="color|reference" />
@@ -102,6 +107,8 @@
<attr name="suwBackgroundBaseColor" format="color" />
<attr name="suwColorPrimary" />
<attr name="suwFooter" format="reference" />
+ <attr name="suwLayoutFullscreen" format="boolean" />
+ <attr name="suwStickyHeader" format="reference" />
</declare-styleable>
<declare-styleable name="SuwStatusBarBackgroundLayout">
diff --git a/library/main/res/values/colors.xml b/library/main/res/values/colors.xml
index cd57a8a..005d1c6 100644
--- a/library/main/res/values/colors.xml
+++ b/library/main/res/values/colors.xml
@@ -21,6 +21,8 @@
<color name="suw_color_accent_dark">#ff448aff</color>
<color name="suw_color_accent_light">#ff3367d6</color>
+ <color name="suw_color_background_dark">#ff303030</color>
+ <color name="suw_color_background_light">#fffafafa</color>
<color name="suw_link_color_dark">#ff448aff</color>
<color name="suw_link_color_light">#ff3367d6</color>
<color name="suw_list_item_icon_color_dark">#b3ffffff</color>
@@ -40,7 +42,11 @@
<!-- GLIF colors -->
<color name="suw_color_accent_glif_dark">#ff4285f4</color>
<color name="suw_color_accent_glif_light">#ff4285f4</color>
+ <color name="suw_color_accent_glif_v3">#ff1a73e8</color>
<color name="suw_glif_background_color_dark">#ff000000</color>
<color name="suw_glif_background_color_light">#ffffffff</color>
+ <color name="suw_glif_edit_text_bg_light_color">#fff1f3f4</color>
+ <color name="suw_glif_v3_nav_bar_color_light">#ffffffff</color>
+ <color name="suw_glif_v3_nav_bar_divider_color_light">#1f000000</color>
</resources>
diff --git a/library/main/res/values/config.xml b/library/main/res/values/config.xml
index a81b177..c11bf41 100644
--- a/library/main/res/values/config.xml
+++ b/library/main/res/values/config.xml
@@ -27,4 +27,8 @@
ButtonBarLayout -->
<item name="suw_original_weight" type="id" />
+ <!-- Secondary font for use with headings, title, and other non-body text -->
+ <string name="suwFontSecondary" translatable="false">google-sans</string>
+ <string name="suwFontSecondaryMedium" translatable="false">google-sans-medium</string>
+
</resources>
diff --git a/library/main/res/values/dimens.xml b/library/main/res/values/dimens.xml
index f29484a..1a8b516 100644
--- a/library/main/res/values/dimens.xml
+++ b/library/main/res/values/dimens.xml
@@ -20,16 +20,21 @@
<!-- General -->
<dimen name="suw_layout_margin_sides">40dp</dimen>
+ <dimen name="suw_glif_button_corner_radius">2dp</dimen>
<!-- Calculated by (suw_glif_margin_sides - 4dp internal padding of button) -->
<dimen name="suw_glif_button_margin_end">20dp</dimen>
<!-- 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>
+ <!-- Negative of suw_glif_button_padding -->
+ <dimen name="suw_glif_negative_button_padding">-16dp</dimen>
<dimen name="suw_glif_footer_padding_vertical">8dp</dimen>
- <dimen name="suw_glif_footer_min_height">80dp</dimen>
+ <dimen name="suw_glif_footer_min_height">72dp</dimen>
<dimen name="suw_glif_margin_sides">24dp</dimen>
<dimen name="suw_glif_margin_top">48dp</dimen>
+ <dimen name="suw_glif_v3_button_corner_radius">4dp</dimen>
+
<!-- Content styles -->
<dimen name="suw_check_box_line_spacing_extra">4sp</dimen>
<dimen name="suw_check_box_margin_bottom">12dp</dimen>
@@ -135,4 +140,8 @@
<dimen name="suw_progress_bar_margin_vertical">-7dp</dimen>
<dimen name="suw_glif_progress_bar_margin_vertical">7dp</dimen>
+ <!-- Edit Text dimensions -->
+ <dimen name="suw_edit_text_min_height">56dp</dimen>
+ <dimen name="suw_edit_text_padding_horizontal">12dp</dimen>
+
</resources>
diff --git a/library/main/res/values/styles.xml b/library/main/res/values/styles.xml
index bd3b60f..fa2a080 100644
--- a/library/main/res/values/styles.xml
+++ b/library/main/res/values/styles.xml
@@ -30,11 +30,11 @@
<item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item>
<item name="suwGlifHeaderGravity">center_horizontal</item>
<item name="suwScrollIndicators">top|bottom</item>
+ <item name="suwEditTextBackgroundColor">@color/suw_glif_edit_text_bg_light_color</item> <!-- TODO: Change color -->
+ <item name="android:editTextStyle">@style/SuwEditText</item>
+ <item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SuwAlertDialogTheme</item>
</style>
- <!-- 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>
@@ -46,11 +46,11 @@
<item name="suwDividerInsetStartNoIcon">?attr/suwMarginSides</item>
<item name="suwGlifHeaderGravity">center_horizontal</item>
<item name="suwScrollIndicators">top|bottom</item>
+ <item name="suwEditTextBackgroundColor">@color/suw_glif_edit_text_bg_light_color</item>
+ <item name="android:editTextStyle">@style/SuwEditText</item>
+ <item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SuwAlertDialogTheme.Light</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>
@@ -86,11 +86,10 @@
<item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
</style>
- <style name="TextAppearance.SuwDescription.Light" parent="TextAppearance.SuwDescription">
- <item name="android:fontFamily" tools:ignore="NewApi">sans-serif-light</item>
- </style>
-
- <style name="TextAppearance.SuwDescription.Secondary" parent="TextAppearance.SuwDescription">
+ <!-- Ignore UnusedResources: Used by clients -->
+ <style name="TextAppearance.SuwDescription.Secondary"
+ parent="TextAppearance.SuwDescription"
+ tools:ignore="UnusedResources">
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
@@ -196,14 +195,19 @@
<item name="android:buttonStyle">@style/SuwGlifButton.Tertiary</item>
<item name="android:theme">@style/SuwGlifButton.Tertiary</item>
- <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:layout_marginLeft">@dimen/suw_glif_negative_button_padding</item>
+ <item name="android:layout_marginRight">@dimen/suw_glif_negative_button_padding</item>
+ <!-- Always lowercase instead of reading attr/suwButtonAllCaps, since this is a tertiary
+ button -->
<item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">false</item>
</style>
- <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary" />
+ <!-- Ignore UnusedResources: used by clients -->
+ <style name="SuwGlifButton.Tertiary"
+ parent="SuwGlifButton.BaseTertiary"
+ tools:ignore="UnusedResources" />
<!-- The start and end paddings are asymmetric because start buttons are borderless buttons
which aligns the text label. -->
@@ -266,7 +270,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:fontFamily" tools:targetApi="jelly_bean">@string/suwFontSecondary</item>
<item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
@@ -309,4 +313,9 @@
<item name="suwNavBarTextColor">?android:attr/textColorPrimary</item>
</style>
+
+ <style name="SuwEditText" parent="@android:style/Widget.EditText">
+ <item name="android:minHeight">@dimen/suw_edit_text_min_height</item>
+ </style>
+
</resources>
diff --git a/library/main/src/com/android/setupwizardlib/GlifLayout.java b/library/main/src/com/android/setupwizardlib/GlifLayout.java
index f4d52a5..e1d9d70 100644
--- a/library/main/src/com/android/setupwizardlib/GlifLayout.java
+++ b/library/main/src/com/android/setupwizardlib/GlifLayout.java
@@ -77,6 +77,8 @@ public class GlifLayout extends TemplateLayout {
@Nullable
private ColorStateList mBackgroundBaseColor;
+ private boolean mLayoutFullscreen = true;
+
public GlifLayout(Context context) {
this(context, 0, 0);
}
@@ -139,7 +141,18 @@ public class GlifLayout extends TemplateLayout {
inflateFooter(footer);
}
+ final int stickyHeader = a.getResourceId(R.styleable.SuwGlifLayout_suwStickyHeader, 0);
+ if (stickyHeader != 0) {
+ inflateStickyHeader(stickyHeader);
+ }
+
+ mLayoutFullscreen = a.getBoolean(R.styleable.SuwGlifLayout_suwLayoutFullscreen, true);
+
a.recycle();
+
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && mLayoutFullscreen) {
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
}
@Override
@@ -160,17 +173,31 @@ public class GlifLayout extends TemplateLayout {
/**
* Sets the footer of the layout, which is at the bottom of the content area outside the
- * scrolling container. The footer can only be inflated once per layout.
+ * scrolling container. The footer can only be inflated once per instance of this layout.
*
* @param footer The layout to be inflated as footer.
* @return The root of the inflated footer view.
*/
public View inflateFooter(@LayoutRes int footer) {
- ViewStub footerStub = (ViewStub) findManagedViewById(R.id.suw_layout_footer);
+ ViewStub footerStub = findManagedViewById(R.id.suw_layout_footer);
footerStub.setLayoutResource(footer);
return footerStub.inflate();
}
+ /**
+ * Sets the sticky header (i.e. header that doesn't scroll) of the layout, which is at the top
+ * of the content area outside of the scrolling container. The header can only be inflated once
+ * per instance of this layout.
+ *
+ * @param header The layout to be inflated as the header.
+ * @return The root of the inflated header view.
+ */
+ public View inflateStickyHeader(@LayoutRes int header) {
+ ViewStub stickyHeaderStub = findManagedViewById(R.id.suw_layout_sticky_header);
+ stickyHeaderStub.setLayoutResource(header);
+ return stickyHeaderStub.inflate();
+ }
+
public ScrollView getScrollView() {
final View view = findManagedViewById(R.id.suw_scroll_view);
return view instanceof ScrollView ? (ScrollView) view : null;
@@ -280,9 +307,6 @@ public class GlifLayout extends TemplateLayout {
patternBg.setBackgroundDrawable(background);
}
}
- if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
- }
}
public boolean isProgressBarShown() {
diff --git a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
index 51c1a49..c1d968a 100644
--- a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
+++ b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
@@ -23,7 +23,6 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
@@ -96,7 +95,6 @@ public class GlifPatternDrawable extends Drawable {
private int mColor;
private Paint mTempPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private ColorFilter mColorFilter;
public GlifPatternDrawable(int color) {
setColor(color);
@@ -140,17 +138,10 @@ public class GlifPatternDrawable extends Drawable {
canvas.clipRect(bounds);
scaleCanvasToBounds(canvas, bitmap, bounds);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
- && canvas.isHardwareAccelerated()) {
- mTempPaint.setColorFilter(mColorFilter);
- canvas.drawBitmap(bitmap, 0, 0, mTempPaint);
- } else {
- // Software renderer doesn't work properly with ColorMatrix filter on ALPHA_8 bitmaps.
- canvas.drawColor(Color.BLACK);
- mTempPaint.setColor(Color.WHITE);
- canvas.drawBitmap(bitmap, 0, 0, mTempPaint);
- canvas.drawColor(mColor);
- }
+ canvas.drawColor(Color.BLACK);
+ mTempPaint.setColor(Color.WHITE);
+ canvas.drawBitmap(bitmap, 0, 0, mTempPaint);
+ canvas.drawColor(mColor);
canvas.restore();
}
@@ -299,12 +290,6 @@ public class GlifPatternDrawable extends Drawable {
final int g = Color.green(color);
final int b = Color.blue(color);
mColor = Color.argb(COLOR_ALPHA_INT, r, g, b);
- mColorFilter = new ColorMatrixColorFilter(new float[] {
- 0, 0, 0, 1 - COLOR_ALPHA, r * COLOR_ALPHA,
- 0, 0, 0, 1 - COLOR_ALPHA, g * COLOR_ALPHA,
- 0, 0, 0, 1 - COLOR_ALPHA, b * COLOR_ALPHA,
- 0, 0, 0, 0, 255
- });
invalidateSelf();
}
diff --git a/library/main/src/com/android/setupwizardlib/TemplateLayout.java b/library/main/src/com/android/setupwizardlib/TemplateLayout.java
index 771592f..d270091 100644
--- a/library/main/src/com/android/setupwizardlib/TemplateLayout.java
+++ b/library/main/src/com/android/setupwizardlib/TemplateLayout.java
@@ -103,7 +103,9 @@ public class TemplateLayout extends FrameLayout {
* by this view but not currently added to the view hierarchy. e.g. recycler view or list view
* headers that are not currently shown.
*/
- public View findManagedViewById(int id) {
+ // Returning generic type is the common pattern used for findViewBy* methods
+ @SuppressWarnings("TypeParameterUnusedInFormals")
+ public <T extends View> T findManagedViewById(int id) {
return findViewById(id);
}
diff --git a/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java b/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java
index 8325232..f438691 100644
--- a/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java
+++ b/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java
@@ -39,6 +39,7 @@ public final class ConsecutiveTapsGestureDetector {
private final View mView;
private final OnConsecutiveTapsListener mListener;
private final int mConsecutiveTapTouchSlopSquare;
+ private final int mConsecutiveTapTimeout;
private int mConsecutiveTapsCounter = 0;
private MotionEvent mPreviousTapEvent;
@@ -54,6 +55,7 @@ public final class ConsecutiveTapsGestureDetector {
mView = view;
int doubleTapSlop = ViewConfiguration.get(mView.getContext()).getScaledDoubleTapSlop();
mConsecutiveTapTouchSlopSquare = doubleTapSlop * doubleTapSlop;
+ mConsecutiveTapTimeout = ViewConfiguration.getDoubleTapTimeout();
}
/**
@@ -109,6 +111,8 @@ public final class ConsecutiveTapsGestureDetector {
double deltaX = mPreviousTapEvent.getX() - currentTapEvent.getX();
double deltaY = mPreviousTapEvent.getY() - currentTapEvent.getY();
- return (deltaX * deltaX + deltaY * deltaY <= mConsecutiveTapTouchSlopSquare);
+ long deltaTime = currentTapEvent.getEventTime() - mPreviousTapEvent.getEventTime();
+ return (deltaX * deltaX + deltaY * deltaY <= mConsecutiveTapTouchSlopSquare)
+ && deltaTime < mConsecutiveTapTimeout;
}
}
diff --git a/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java b/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java
index 55bbe75..06ce4ac 100644
--- a/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java
+++ b/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java
@@ -83,6 +83,7 @@ public class ButtonBarItem extends AbstractItem implements ItemInflater.ItemPare
return mVisible;
}
+ @Override
public int getViewId() {
return getId();
}
diff --git a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
index a5f0424..26a3d16 100644
--- a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
+++ b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
@@ -21,10 +21,13 @@ import android.content.ContextWrapper;
import android.graphics.Typeface;
import android.os.Build;
import android.support.annotation.Nullable;
+import android.text.Selection;
+import android.text.Spannable;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.View;
+import android.widget.TextView;
/**
* A clickable span that will listen for click events and send it back to the context. To use this
@@ -86,11 +89,19 @@ public class LinkSpan extends ClickableSpan {
public void onClick(View view) {
if (dispatchClick(view)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ // Prevent the touch event from bubbling up to the parent views.
view.cancelPendingInputEvents();
}
} else {
Log.w(TAG, "Dropping click event. No listener attached.");
}
+ if (view instanceof TextView) {
+ // Remove the highlight effect when the click happens by clearing the selection
+ CharSequence text = ((TextView) view).getText();
+ if (text instanceof Spannable) {
+ Selection.setSelection((Spannable) text, 0);
+ }
+ }
}
private boolean dispatchClick(View view) {
diff --git a/library/main/src/com/android/setupwizardlib/template/IconMixin.java b/library/main/src/com/android/setupwizardlib/template/IconMixin.java
index 46c23f0..5386c92 100644
--- a/library/main/src/com/android/setupwizardlib/template/IconMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/IconMixin.java
@@ -19,7 +19,9 @@ package com.android.setupwizardlib.template;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.ImageView;
import com.android.setupwizardlib.R;
@@ -44,8 +46,8 @@ public class IconMixin implements Mixin {
final TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.SuwIconMixin, defStyleAttr, 0);
- final Drawable icon = a.getDrawable(R.styleable.SuwIconMixin_android_icon);
- if (icon != null) {
+ final @DrawableRes int icon = a.getResourceId(R.styleable.SuwIconMixin_android_icon, 0);
+ if (icon != 0) {
setIcon(icon);
}
@@ -61,6 +63,22 @@ public class IconMixin implements Mixin {
final ImageView iconView = getView();
if (iconView != null) {
iconView.setImageDrawable(icon);
+ iconView.setVisibility(icon != null ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ /**
+ * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
+ *
+ * @param icon A drawable icon resource.
+ */
+ public void setIcon(@DrawableRes int icon) {
+ final ImageView iconView = getView();
+ if (iconView != null) {
+ // Note: setImageResource on the ImageView is overridden in AppCompatImageView for
+ // support lib users, which enables vector drawable compat to work on versions pre-L.
+ iconView.setImageResource(icon);
+ iconView.setVisibility(icon != 0 ? View.VISIBLE : View.GONE);
}
}
@@ -73,6 +91,24 @@ public class IconMixin implements Mixin {
}
/**
+ * Sets the content description of the icon view
+ */
+ public void setContentDescription(CharSequence description) {
+ final ImageView iconView = getView();
+ if (iconView != null) {
+ iconView.setContentDescription(description);
+ }
+ }
+
+ /**
+ * @return The content description of the icon view
+ */
+ public CharSequence getContentDescription() {
+ final ImageView iconView = getView();
+ return iconView != null ? iconView.getContentDescription() : null;
+ }
+
+ /**
* @return The ImageView responsible for displaying the icon.
*/
protected ImageView getView() {
diff --git a/library/main/src/com/android/setupwizardlib/util/Partner.java b/library/main/src/com/android/setupwizardlib/util/Partner.java
index 67f5546..3a603ee 100644
--- a/library/main/src/com/android/setupwizardlib/util/Partner.java
+++ b/library/main/src/com/android/setupwizardlib/util/Partner.java
@@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.support.annotation.AnyRes;
+import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
@@ -76,6 +77,15 @@ public class Partner {
}
/**
+ * Convenience method to get color from partner overlay, or if not available, the color from
+ * the original context.
+ */
+ public static int getColor(Context context, @ColorRes int id) {
+ final ResourceEntry resourceEntry = getResourceEntry(context, id);
+ return resourceEntry.resources.getColor(resourceEntry.id);
+ }
+
+ /**
* Convenience method to get a CharSequence from partner overlay, or if not available, the text
* from the original context.
*/
diff --git a/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java b/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java
index 1c5f3d3..b31e82e 100644
--- a/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java
+++ b/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java
@@ -24,6 +24,7 @@ import android.content.res.TypedArray;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
+import android.support.annotation.RequiresPermission;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -183,12 +184,25 @@ public class SystemBarHelper {
}
}
+ /**
+ * Sets whether the back button on the software navigation bar is visible. This only works if
+ * you have the STATUS_BAR permission. Otherwise framework will filter out this flag and this
+ * method call will not have any effect.
+ *
+ * <p>IMPORTANT: Do not assume that users have no way to go back when the back button is hidden.
+ * Many devices have physical back buttons, and accessibility services like TalkBack may have
+ * gestures mapped to back. Please use onBackPressed, onKeyDown, or other similar ways to
+ * make sure back button events are still handled (or ignored) properly.
+ */
+ @RequiresPermission("android.permission.STATUS_BAR")
public static void setBackButtonVisible(final Window window, final boolean visible) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
if (visible) {
removeVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
+ removeImmersiveFlagsFromDecorView(window, STATUS_BAR_DISABLE_BACK);
} else {
addVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
+ addImmersiveFlagsToDecorView(window, STATUS_BAR_DISABLE_BACK);
}
}
}
@@ -217,7 +231,7 @@ public class SystemBarHelper {
* {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} only takes effect when it is added to a view
* instead of the window.
*/
- @TargetApi(VERSION_CODES.LOLLIPOP)
+ @TargetApi(VERSION_CODES.HONEYCOMB)
private static void addImmersiveFlagsToDecorView(final Window window, final int vis) {
getDecorView(window, new OnDecorViewInstalledListener() {
@Override
@@ -227,7 +241,7 @@ public class SystemBarHelper {
});
}
- @TargetApi(VERSION_CODES.LOLLIPOP)
+ @TargetApi(VERSION_CODES.HONEYCOMB)
private static void removeImmersiveFlagsFromDecorView(final Window window, final int vis) {
getDecorView(window, new OnDecorViewInstalledListener() {
@Override
diff --git a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
index a93694c..cf9ddac 100644
--- a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
+++ b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
@@ -27,6 +27,8 @@ import android.support.annotation.VisibleForTesting;
import com.android.setupwizardlib.R;
+import java.util.Arrays;
+
public class WizardManagerHelper {
private static final String ACTION_NEXT = "com.android.wizard.NEXT";
@@ -45,6 +47,8 @@ public class WizardManagerHelper {
static final String EXTRA_IS_FIRST_RUN = "firstRun";
@VisibleForTesting
static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
+ @VisibleForTesting
+ static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
public static final String EXTRA_THEME = "theme";
public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
@@ -76,22 +80,22 @@ public class WizardManagerHelper {
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_V2_LIGHT = "glif_v2_light";
/**
- * @deprecated Use {@link #THEME_GLIF_V2_LIGHT} instead.
+ * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the
+ * theme used in setup wizard for P.
*/
- @Deprecated
- public static final String THEME_GLIF_PIXEL_LIGHT = THEME_GLIF_V2_LIGHT;
+ public static final String THEME_GLIF_V3 = "glif_v3";
+
+ /**
+ * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
+ * setup wizard for P.
+ */
+ public static final String THEME_GLIF_V3_LIGHT = "glif_v3_light";
/**
* Get an intent that will invoke the next step of setup wizard.
@@ -140,13 +144,14 @@ public class WizardManagerHelper {
*/
public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) {
dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE));
- 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));
+ for (String key : Arrays.asList(
+ EXTRA_IS_FIRST_RUN, EXTRA_IS_DEFERRED_SETUP, EXTRA_IS_PRE_DEFERRED_SETUP)) {
+ dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
+ }
+
+ for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) {
+ dstIntent.putExtra(key, srcIntent.getStringExtra(key));
+ }
}
/**
@@ -213,6 +218,18 @@ public class WizardManagerHelper {
}
/**
+ * Checks whether an intent is running in "pre-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 "pre-deferred" setup wizard.
+ */
+ public static boolean isPreDeferredSetupWizard(Intent originalIntent) {
+ return originalIntent != null
+ && originalIntent.getBooleanExtra(EXTRA_IS_PRE_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.
@@ -236,10 +253,12 @@ 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_V2_LIGHT.equals(theme)) {
+ || THEME_GLIF_LIGHT.equals(theme) || THEME_GLIF_V2_LIGHT.equals(theme)
+ || THEME_GLIF_V3_LIGHT.equals(theme)) {
return true;
} else if (THEME_HOLO.equals(theme) || THEME_MATERIAL.equals(theme)
- || THEME_GLIF.equals(theme) || THEME_GLIF_V2.equals(theme)) {
+ || THEME_GLIF.equals(theme) || THEME_GLIF_V2.equals(theme)
+ || THEME_GLIF_V3.equals(theme)) {
return false;
} else {
return def;
@@ -284,6 +303,10 @@ public class WizardManagerHelper {
public static @StyleRes int getThemeRes(String theme, @StyleRes int defaultTheme) {
if (theme != null) {
switch (theme) {
+ case THEME_GLIF_V3_LIGHT:
+ return R.style.SuwThemeGlifV3_Light;
+ case THEME_GLIF_V3:
+ return R.style.SuwThemeGlifV3;
case THEME_GLIF_V2_LIGHT:
return R.style.SuwThemeGlifV2_Light;
case THEME_GLIF_V2:
diff --git a/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java b/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java
index 3c678f8..bd0aead 100644
--- a/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java
+++ b/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java
@@ -58,6 +58,10 @@ public class CheckableLinearLayout extends LinearLayout implements Checkable {
super(context, attrs, defStyleAttr, defStyleRes);
}
+ {
+ setFocusable(true);
+ }
+
@Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mChecked) {
diff --git a/library/main/src/com/android/setupwizardlib/view/Illustration.java b/library/main/src/com/android/setupwizardlib/view/Illustration.java
index b576174..c6968f8 100644
--- a/library/main/src/com/android/setupwizardlib/view/Illustration.java
+++ b/library/main/src/com/android/setupwizardlib/view/Illustration.java
@@ -137,7 +137,8 @@ public class Illustration extends FrameLayout {
if (mAspectRatio != 0.0f) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int illustrationHeight = (int) (parentWidth / mAspectRatio);
- illustrationHeight -= illustrationHeight % mBaselineGridSize;
+ illustrationHeight =
+ (int) (illustrationHeight - (illustrationHeight % mBaselineGridSize));
setPadding(0, illustrationHeight, 0, 0);
}
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
diff --git a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
index 9c79eb5..e5c2fb1 100644
--- a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
+++ b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
@@ -23,9 +23,11 @@ import android.graphics.SurfaceTexture;
import android.graphics.drawable.Animatable;
import android.media.MediaPlayer;
import android.os.Build.VERSION_CODES;
+import android.support.annotation.Nullable;
import android.support.annotation.RawRes;
import android.support.annotation.VisibleForTesting;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
@@ -51,8 +53,11 @@ public class IllustrationVideoView extends TextureView implements Animatable,
MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnInfoListener {
+ private static final String TAG = "IllustrationVideoView";
+
protected float mAspectRatio = 1.0f; // initial guess until we know
+ @Nullable // Can be null when media player fails to initialize
protected MediaPlayer mMediaPlayer;
private @RawRes int mVideoResId = 0;
@@ -129,15 +134,20 @@ public class IllustrationVideoView extends TextureView implements Animatable,
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 (mMediaPlayer != null) {
+ 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();
+ }
+ } else {
+ Log.wtf(TAG, "Unable to initialize media player for video view");
}
if (getWindowVisibility() == View.VISIBLE) {
start();
diff --git a/library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java b/library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java
new file mode 100644
index 0000000..10e91f4
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.text.Selection;
+import android.text.Spannable;
+import android.text.method.LinkMovementMethod;
+import android.text.method.MovementMethod;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+/**
+ * A movement method that tracks the last result of whether touch events are handled. This is
+ * used to patch the return value of {@link TextView#onTouchEvent} so that it consumes the touch
+ * events only when the movement method says the event is consumed.
+ */
+public interface TouchableMovementMethod {
+
+ /**
+ * @return The last touch event received in {@link MovementMethod#onTouchEvent}
+ */
+ MotionEvent getLastTouchEvent();
+
+ /**
+ * @return The return value of the last {@link MovementMethod#onTouchEvent}, or whether the
+ * last touch event should be considered handled by the text view
+ */
+ boolean isLastTouchEventHandled();
+
+ /**
+ * An extension of LinkMovementMethod that tracks whether the event is handled when it is
+ * touched.
+ */
+ class TouchableLinkMovementMethod extends LinkMovementMethod
+ implements TouchableMovementMethod {
+
+ public static TouchableLinkMovementMethod getInstance() {
+ return new TouchableLinkMovementMethod();
+ }
+
+ boolean mLastEventResult = false;
+ MotionEvent mLastEvent;
+
+ @Override
+ public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
+ mLastEvent = event;
+ boolean result = super.onTouchEvent(widget, buffer, event);
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ // Unfortunately, LinkMovementMethod extends ScrollMovementMethod, and it always
+ // consume the down event. So here we use the selection instead as a hint of whether
+ // the down event landed on a link.
+ mLastEventResult = Selection.getSelectionStart(buffer) != -1;
+ } else {
+ mLastEventResult = result;
+ }
+ return result;
+ }
+
+ @Override
+ public MotionEvent getLastTouchEvent() {
+ return mLastEvent;
+ }
+
+ @Override
+ public boolean isLastTouchEventHandled() {
+ return mLastEventResult;
+ }
+ }
+}
diff --git a/library/platform/res/values-v23/styles.xml b/library/platform/res/values-v27/styles.xml
index 9fff5f1..6e36919 100644
--- a/library/platform/res/values-v23/styles.xml
+++ b/library/platform/res/values-v27/styles.xml
@@ -15,9 +15,10 @@
limitations under the License.
-->
+<!-- TODO(yukl): Bump this file to v28 once we can properly test that -->
<!-- These styles are only included in the platform build, to make sure that they do not
override the corresponding styles in the compatibility build. -->
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
<!-- General styles -->
@@ -26,6 +27,7 @@
Theme.AppCompat. -->
<style name="SuwThemeMaterial" parent="android:Theme.Material.NoActionBar">
<item name="android:colorAccent">@color/suw_color_accent_dark</item>
+ <item name="android:colorBackground">@color/suw_color_background_dark</item>
<item name="android:indeterminateTint">@color/suw_progress_bar_color_dark</item>
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode">src_in</item>
@@ -34,12 +36,14 @@
<item name="android:listPreferredItemPaddingStart">?attr/suwMarginSides</item>
<item name="android:navigationBarColor">@android:color/black</item>
<item name="android:statusBarColor">@android:color/black</item>
- <item name="android:textAppearanceListItemSmall">@android:style/TextAppearance.Material.Body1</item>
+ <item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwItemSummary</item>
<item name="android:textColorLink">@color/suw_link_color_dark</item>
<item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
<item name="android:windowDisablePreview">true</item>
<item name="android:windowSoftInputMode">adjustResize</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonFontFamily">sans-serif</item>
<item name="suwCardBackground">@drawable/suw_card_bg</item>
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
<item name="suwDividerInsetEnd">0dp</item>
@@ -54,6 +58,7 @@
<style name="SuwThemeMaterial.Light" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:colorAccent">@color/suw_color_accent_light</item>
+ <item name="android:colorBackground">@color/suw_color_background_light</item>
<item name="android:indeterminateTint">@color/suw_progress_bar_color_light</item>
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode">src_in</item>
@@ -62,12 +67,14 @@
<item name="android:listPreferredItemPaddingStart">?attr/suwMarginSides</item>
<item name="android:navigationBarColor">@android:color/black</item>
<item name="android:statusBarColor">@android:color/black</item>
- <item name="android:textAppearanceListItemSmall">@android:style/TextAppearance.Material.Body1</item>
+ <item name="android:textAppearanceListItemSmall">@style/TextAppearance.SuwItemSummary</item>
<item name="android:textColorLink">@color/suw_link_color_light</item>
<item name="android:windowAnimationStyle">@style/Animation.SuwWindowAnimation</item>
<item name="android:windowDisablePreview">true</item>
<item name="android:windowSoftInputMode">adjustResize</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonFontFamily">sans-serif</item>
<item name="suwCardBackground">@drawable/suw_card_bg</item>
<item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
<item name="suwDividerInsetEnd">0dp</item>
@@ -84,7 +91,7 @@
<style name="SuwThemeGlif" parent="android:Theme.Material.NoActionBar">
<item name="android:colorAccent">@color/suw_color_accent_glif_dark</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:colorPrimary">?android:attr/colorAccent</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>
@@ -100,12 +107,16 @@
<item name="android:windowDisablePreview">true</item>
<item name="android:windowSoftInputMode">adjustResize</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
+ <item name="suwButtonFontFamily">sans-serif</item>
<item name="suwColorPrimary">?android:attr/colorPrimary</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="suwGlifIconStyle">@style/SuwGlifIcon</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>
@@ -116,7 +127,7 @@
<style name="SuwThemeGlif.Light" parent="android:Theme.Material.Light.NoActionBar">
<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:colorPrimary">?android:attr/colorAccent</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>
@@ -132,12 +143,16 @@
<item name="android:windowDisablePreview">true</item>
<item name="android:windowSoftInputMode">adjustResize</item>
+ <item name="suwButtonAllCaps">true</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
+ <item name="suwButtonFontFamily">sans-serif</item>
<item name="suwColorPrimary">?android:attr/colorPrimary</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="suwGlifIconStyle">@style/SuwGlifIcon</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>
@@ -145,6 +160,27 @@
<item name="suwScrollIndicators">bottom</item>
</style>
+ <style name="SuwThemeGlifV3" parent="SuwThemeGlifV2">
+ <item name="android:colorAccent">@color/suw_color_accent_glif_v3</item>
+
+ <item name="suwButtonAllCaps">false</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
+ <item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
+ </style>
+
+ <style name="SuwThemeGlifV3.Light" parent="SuwThemeGlifV2.Light">
+ <item name="android:colorAccent">@color/suw_color_accent_glif_v3</item>
+ <item name="android:navigationBarColor">@color/suw_glif_v3_nav_bar_color_light</item>
+ <!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
+ <item name="android:navigationBarDividerColor" tools:ignore="NewApi">@color/suw_glif_v3_nav_bar_divider_color_light</item>
+ <!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
+ <item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
+
+ <item name="suwButtonAllCaps">false</item>
+ <item name="suwButtonCornerRadius">@dimen/suw_glif_v3_button_corner_radius</item>
+ <item name="suwButtonFontFamily">@string/suwFontSecondaryMedium</item>
+ </style>
+
<!-- Button styles -->
<style name="SuwGlifButton.Primary" parent="android:Widget.Material.Button.Colored">
@@ -154,8 +190,13 @@
<item name="android:buttonStyle">@style/SuwGlifButton.Primary</item>
<!-- Values used in styles -->
+ <item name="android:fontFamily">?attr/suwButtonFontFamily</item>
<item name="android:paddingLeft">@dimen/suw_glif_button_padding</item>
<item name="android:paddingRight">@dimen/suw_glif_button_padding</item>
+ <item name="android:textAllCaps">?attr/suwButtonAllCaps</item>
+
+ <!-- Values used in themes -->
+ <item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
</style>
<style name="SuwGlifButton.Secondary" parent="android:Widget.Material.Button.Borderless.Colored">
@@ -166,11 +207,14 @@
<item name="android:theme">@style/SuwGlifButton.Secondary</item>
<!-- Values used in styles -->
+ <item name="android:fontFamily">?attr/suwButtonFontFamily</item>
<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>
+ <item name="android:textAllCaps">?attr/suwButtonAllCaps</item>
<!-- Values used in themes -->
+ <item name="android:buttonCornerRadius" tools:ignore="NewApi">?attr/suwButtonCornerRadius</item>
<item name="android:colorControlHighlight">@color/suw_flat_button_highlight</item>
</style>
diff --git a/library/platform/src/com/android/setupwizardlib/view/RichTextView.java b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
index 5a78561..fa68a68 100644
--- a/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
+++ b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
@@ -20,16 +20,18 @@ import android.content.Context;
import android.text.Annotation;
import android.text.SpannableString;
import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
+import android.text.method.MovementMethod;
import android.text.style.ClickableSpan;
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.view.TouchableMovementMethod.TouchableLinkMovementMethod;
/**
* An extension of TextView that automatically replaces the annotation tags as specified in
@@ -112,7 +114,7 @@ public class RichTextView extends TextView implements OnLinkClickListener {
// 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());
+ setMovementMethod(TouchableLinkMovementMethod.getInstance());
} else {
setMovementMethod(null);
}
@@ -121,6 +123,11 @@ public class RichTextView extends TextView implements OnLinkClickListener {
// as individual TextViews consume touch events and thereby reducing the focus window
// shown by Talkback. Disable focus if there are no links
setFocusable(hasLinks);
+ // Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
+ // focusable in touch mode, we may be focused when the screen is first shown, and starting
+ // a screen halfway scrolled down is confusing to the user.
+ setRevealOnFocusHint(false);
+ setFocusableInTouchMode(hasLinks);
}
private boolean hasLinks(CharSequence text) {
@@ -132,6 +139,25 @@ public class RichTextView extends TextView implements OnLinkClickListener {
return false;
}
+ @Override
+ @SuppressWarnings("ClickableViewAccessibility") // super.onTouchEvent is called
+ public boolean onTouchEvent(MotionEvent event) {
+ // Since View#onTouchEvent always return true if the view is clickable (which is the case
+ // when a TextView has a movement method), override the implementation to allow the movement
+ // method, if it implements TouchableMovementMethod, to say that the touch is not handled,
+ // allowing the event to bubble up to the parent view.
+ boolean superResult = super.onTouchEvent(event);
+ MovementMethod movementMethod = getMovementMethod();
+ if (movementMethod instanceof TouchableMovementMethod) {
+ TouchableMovementMethod touchableMovementMethod =
+ (TouchableMovementMethod) movementMethod;
+ if (touchableMovementMethod.getLastTouchEvent() == event) {
+ return touchableMovementMethod.isLastTouchEventHandled();
+ }
+ }
+ return superResult;
+ }
+
public void setOnLinkClickListener(OnLinkClickListener listener) {
mOnLinkClickListener = listener;
}
diff --git a/library/recyclerview/res/layout/suw_glif_recycler_template_content.xml b/library/recyclerview/res/layout/suw_glif_recycler_template_content.xml
index e8d209b..c16f85a 100644
--- a/library/recyclerview/res/layout/suw_glif_recycler_template_content.xml
+++ b/library/recyclerview/res/layout/suw_glif_recycler_template_content.xml
@@ -23,6 +23,11 @@
android:layout_height="match_parent"
android:orientation="vertical">
+ <ViewStub
+ android:id="@+id/suw_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
versions. -->
<com.android.setupwizardlib.view.HeaderRecyclerView
diff --git a/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
index 75b1c7a..e8a021d 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
@@ -107,10 +107,12 @@ public class GlifRecyclerLayout extends GlifLayout {
}
@Override
- public View findManagedViewById(int id) {
+ // Returning generic type is the common pattern used for findViewBy* methods
+ @SuppressWarnings("TypeParameterUnusedInFormals")
+ public <T extends View> T findManagedViewById(int id) {
final View header = mRecyclerMixin.getHeader();
if (header != null) {
- final View view = header.findViewById(id);
+ final T view = header.findViewById(id);
if (view != null) {
return view;
}
diff --git a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
index 5ff825d..6f3aed4 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
@@ -127,10 +127,12 @@ public class SetupWizardRecyclerLayout extends SetupWizardLayout {
}
@Override
- public View findManagedViewById(int id) {
+ // Returning generic type is the common pattern used for findViewBy* methods
+ @SuppressWarnings("TypeParameterUnusedInFormals")
+ public <T extends View> T findManagedViewById(int id) {
final View header = mRecyclerMixin.getHeader();
if (header != null) {
- final View view = header.findViewById(id);
+ final T view = header.findViewById(id);
if (view != null) {
return view;
}
diff --git a/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
index 78280a6..0703c17 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
@@ -143,9 +143,9 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder>
@Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
final IItem item = getItem(position);
- item.onBindView(holder.itemView);
holder.setEnabled(item.isEnabled());
holder.setItem(item);
+ item.onBindView(holder.itemView);
}
@Override
diff --git a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
index b509389..5912f7f 100644
--- a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
+++ b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
@@ -27,7 +27,6 @@ import static org.robolectric.RuntimeEnvironment.application;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnScrollListener;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import org.junit.Before;
@@ -38,7 +37,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
@RunWith(SuwLibRobolectricTestRunner.class)
public class RecyclerViewScrollHandlingDelegateTest {
diff --git a/library/rules.gradle b/library/rules.gradle
index 6b0acce..9baa390 100644
--- a/library/rules.gradle
+++ b/library/rules.gradle
@@ -13,6 +13,23 @@ android {
publishNonDefault true
+ flavorDimensions 'compat'
+
+ productFlavors {
+ // DEPRECATED: Platform version that will not include the compatibility libraries
+ platformDeprecated {
+ dimension 'compat'
+ // TODO(yukl): Bump this file to v28 once we can properly test that
+ minSdkVersion 27
+ }
+
+ // Provides backwards compatibility for Gingerbread or above, using support libraries.
+ gingerbreadCompat {
+ dimension 'compat'
+ minSdkVersion 9
+ }
+ }
+
sourceSets {
main {
manifest.srcFile 'main/AndroidManifest.xml'
@@ -21,43 +38,7 @@ android {
res.srcDirs = ['main/res']
}
- productFlavors {
- // Platform version that will not include the compatibility libraries
- platform {
- minSdkVersion 23
-
- dependencies {
- // Read the dependencies from the "deps" map in the extra properties.
- //
- // For builds in the Android tree we want to build the dependencies from source
- // for reproducible builds, for example in build.gradle define something like
- // this:
- // ext {
- // deps = ['project-name': project(':project-path')]
- // }
- //
- // For standalone project clients, since the source may not be available, we
- // fetch the dependencies from maven. For example in standalone.gradle define
- // something like this:
- // ext {
- // deps = ['project-name': 'com.example.group:project-name:1.0.0']
- // }
- //
- platformCompile deps['support-annotations']
- }
- }
-
- // Provides backwards compatibility for Gingerbread or above, using support libraries.
- gingerbreadCompat {
- minSdkVersion 9
- dependencies {
- gingerbreadCompatCompile deps['support-appcompat-v7']
- gingerbreadCompatCompile deps['support-recyclerview-v7']
- }
- }
- }
-
- platform {
+ platformDeprecated {
java.srcDirs = ['platform/src']
res.srcDirs = ['platform/res']
}
@@ -68,3 +49,26 @@ android {
}
}
}
+
+dependencies {
+ // Read the dependencies from the "deps" map in the extra properties.
+ //
+ // For builds in the Android tree we want to build the dependencies from source
+ // for reproducible builds, for example in build.gradle define something like
+ // this:
+ // ext {
+ // deps = ['project-name': project(':project-path')]
+ // }
+ //
+ // For standalone project clients, since the source may not be available, we
+ // fetch the dependencies from maven. For example in standalone.gradle define
+ // something like this:
+ // ext {
+ // deps = ['project-name': 'com.example.group:project-name:1.0.0']
+ // }
+ //
+ platformDeprecatedCompile deps['support-annotations']
+
+ gingerbreadCompatCompile deps['support-appcompat-v7']
+ gingerbreadCompatCompile deps['support-recyclerview-v7']
+}
diff --git a/library/self.gradle b/library/self.gradle
index 6a405e2..a4bff4e 100644
--- a/library/self.gradle
+++ b/library/self.gradle
@@ -5,6 +5,18 @@ apply from: 'standalone-rules.gradle'
apply from: '../tools/gradle/dist-library-instrumentation-tests.gradle'
apply from: '../tools/gradle/dist-unit-tests.gradle'
+apply plugin: 'net.ltgt.errorprone'
+
+buildscript {
+ repositories {
+ maven { url "$rootDir/prebuilts/tools/common/m2/repository" }
+ }
+
+ dependencies {
+ classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
+ }
+}
+
// Add targets for tests
android.sourceSets {
androidTest {
@@ -13,16 +25,17 @@ android.sourceSets {
res.srcDirs = ['test/instrumentation/res']
dependencies {
- androidTestCompile 'com.android.support.test:rules:0.5'
- androidTestCompile 'com.android.support.test:runner:0.5'
- androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
- androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
- androidTestCompile 'junit:junit:4.+'
- androidTestCompile 'org.mockito:mockito-core:1.9.5'
+ androidTestImplementation 'com.android.support.test:rules:1.0.0'
+ androidTestImplementation 'com.android.support.test:runner:1.0.0'
+ androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
+ androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
+ androidTestImplementation 'com.google.truth:truth:0.31'
+ androidTestImplementation 'junit:junit:4.+'
+ androidTestImplementation 'org.mockito:mockito-core:1.9.5'
}
}
- androidTestPlatform {
+ androidTestPlatformDeprecated {
java.srcDirs = ['platform/test/src']
}
@@ -38,12 +51,13 @@ android.sourceSets {
java.srcDirs = ['test/robotest/src']
dependencies {
- testCompile 'org.robolectric:robolectric:3.+'
- testCompile 'org.robolectric:shadows-core:3.+'
- testCompile 'junit:junit:4.+'
- testCompile 'org.mockito:mockito-core:1.9.5'
+ testImplementation 'org.robolectric:robolectric:3.6.1'
+ testImplementation 'org.robolectric:shadows-framework:3.6.1'
+ testImplementation 'junit:junit:4.+'
+ testImplementation 'com.google.truth:truth:0.31'
+ testImplementation 'org.mockito:mockito-core:1.9.5'
// Workaround for https://github.com/robolectric/robolectric/issues/2566
- testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
+ testImplementation 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
}
}
@@ -51,6 +65,9 @@ android.sourceSets {
java.srcDirs = ['gingerbread/test/robotest/src', 'recyclerview/test/robotest/src']
}
}
+
+android.testOptions.unitTests.includeAndroidResources = true
+
android.defaultConfig.testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
android.lintOptions {
abortOnError true
@@ -64,14 +81,3 @@ android.lintOptions {
android.libraryVariants.all { variant ->
variant.assemble.dependsOn(tasks.findByName('lint'))
}
-
-// 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/standalone.gradle b/library/standalone.gradle
index a2119ac..132b908 100644
--- a/library/standalone.gradle
+++ b/library/standalone.gradle
@@ -16,5 +16,5 @@
apply from: 'standalone-rules.gradle'
-android.compileSdkVersion 25
-android.buildToolsVersion '25.0.0'
+android.compileSdkVersion 26
+android.buildToolsVersion '26.0.0'
diff --git a/library/test/instrumentation/AndroidManifest.xml b/library/test/instrumentation/AndroidManifest.xml
index 44d923f..0cbc9c1 100644
--- a/library/test/instrumentation/AndroidManifest.xml
+++ b/library/test/instrumentation/AndroidManifest.xml
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.setupwizardlib.test">
- <uses-sdk tools:overrideLibrary="android.support.test,android.app,android.support.test.rule" />
+ <uses-sdk tools:overrideLibrary="android.support.test.rules,android.support.test.runner" />
<application>
<activity android:name=".util.DrawingTestActivity" />
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java
index a1f2b54..5a36f4a 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java
@@ -16,6 +16,8 @@
package com.android.setupwizardlib.template;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.mockito.Matchers.eq;
@@ -27,10 +29,12 @@ import android.content.res.XmlResourceParser;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Xml;
+import android.view.View;
import android.widget.ImageView;
import com.android.setupwizardlib.TemplateLayout;
@@ -74,6 +78,26 @@ public class IconMixinTest {
mixin.setIcon(drawable);
assertSame(drawable, mIconView.getDrawable());
+ assertEquals(View.VISIBLE, mIconView.getVisibility());
+ }
+
+ @Test
+ public void setIcon_resourceId_shouldSetIcon() {
+ int icon = android.R.drawable.ic_menu_add;
+ IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+ mixin.setIcon(icon);
+
+ Drawable drawable = mIconView.getDrawable();
+ assertThat(drawable).isInstanceOf(BitmapDrawable.class);
+ assertEquals(View.VISIBLE, mIconView.getVisibility());
+ }
+
+ @Test
+ public void setIcon_shouldSetVisibilityToGone_whenIconIsNull() {
+ IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+ mixin.setIcon(null);
+
+ assertEquals(View.GONE, mIconView.getVisibility());
}
@Test
@@ -101,5 +125,20 @@ public class IconMixinTest {
.getDrawable(android.R.drawable.ic_menu_add);
final BitmapDrawable actual = (BitmapDrawable) mIconView.getDrawable();
assertEquals(expected.getBitmap(), actual.getBitmap());
+ assertEquals(View.VISIBLE, mIconView.getVisibility());
+ }
+
+ @Test
+ public void setContentDescription_shouldSetContentDescriptionOnIconView() {
+ IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+ mixin.setContentDescription("hello world");
+ assertThat(mIconView.getContentDescription()).isEqualTo("hello world");
+ }
+
+ @Test
+ public void getContentDescription_shouldReturnContentDescriptionFromView() {
+ IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+ mIconView.setContentDescription("aloha");
+ assertThat(mixin.getContentDescription()).isEqualTo("aloha");
}
}
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java
index 99c87fb..37ac41a 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java
@@ -137,7 +137,7 @@ public class GlifPatternDrawableTest {
assertEquals("Matrices should match", expected, canvas.getMatrix());
}
- @SmallTest
+ @Test
public void testScaleToCanvasMaxSize() {
final Canvas canvas = new Canvas();
final Matrix expected = new Matrix(canvas.getMatrix());
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java
index 0ebf7cb..98c28f6 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java
@@ -16,6 +16,8 @@
package com.android.setupwizardlib.test;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import android.annotation.SuppressLint;
@@ -31,6 +33,7 @@ import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.rule.UiThreadTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
@@ -131,7 +134,9 @@ public class SystemBarHelperTest {
@Test
public void testShowSystemBarsWindow() {
final Window window = createWindowWithSystemUiVisibility(0x456);
- SystemBarHelper.showSystemBars(window, InstrumentationRegistry.getContext());
+ Context context = new ContextThemeWrapper(
+ InstrumentationRegistry.getContext(), android.R.style.Theme);
+ SystemBarHelper.showSystemBars(window, context);
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
assertEquals(
"DEFAULT_IMMERSIVE_FLAGS should be removed from window's systemUiVisibility",
@@ -191,11 +196,15 @@ public class SystemBarHelperTest {
@UiThreadTest
@Test
public void testSetBackButtonVisibleTrue() {
- final Window window = createWindowWithSystemUiVisibility(0x456);
+ final Window window = createWindowWithSystemUiVisibility(STATUS_BAR_DISABLE_BACK | 0x456);
SystemBarHelper.setBackButtonVisible(window, true);
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
- assertEquals("View visibility should be 0x456", 0x456,
- window.getAttributes().systemUiVisibility);
+ assertThat(window.getAttributes().systemUiVisibility)
+ .named("window sysUiVisibility")
+ .isEqualTo(0x456);
+ assertThat(window.getDecorView().getSystemUiVisibility())
+ .named("decor view sysUiVisibility")
+ .isEqualTo(0x456);
}
}
@@ -205,8 +214,12 @@ public class SystemBarHelperTest {
final Window window = createWindowWithSystemUiVisibility(0x456);
SystemBarHelper.setBackButtonVisible(window, false);
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
- assertEquals("STATUS_BAR_DISABLE_BACK should be added to systemUiVisibility",
- 0x456 | STATUS_BAR_DISABLE_BACK, window.getAttributes().systemUiVisibility);
+ assertThat(window.getAttributes().systemUiVisibility)
+ .named("window sysUiVisibility")
+ .isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
+ assertThat(window.getDecorView().getSystemUiVisibility())
+ .named("decor view sysUiVisibility")
+ .isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
}
}
diff --git a/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java b/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java
index d46409d..d2d1ee0 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java
@@ -32,6 +32,8 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.support.annotation.IdRes;
import android.view.ContextThemeWrapper;
import android.view.View;
@@ -53,7 +55,7 @@ import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
public class GlifLayoutTest {
private Context mContext;
@@ -266,6 +268,74 @@ public class GlifLayoutTest {
assertNotNull(layout.findViewById(android.R.id.text1));
}
+ @Test
+ public void inflateStickyHeader_shouldAddViewToLayout() {
+ GlifLayout layout = new GlifLayout(mContext);
+
+ final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
+ assertEquals(android.R.id.text1, view.getId());
+ assertNotNull(layout.findViewById(android.R.id.text1));
+ }
+
+ @Config(qualifiers = "sw600dp")
+ @Test
+ public void inflateStickyHeader_whenOnTablets_shouldAddViewToLayout() {
+ inflateStickyHeader_shouldAddViewToLayout();
+ }
+
+ @Test
+ public void inflateStickyHeader_whenInXml_shouldAddViewToLayout() {
+ GlifLayout layout = new GlifLayout(
+ mContext,
+ Robolectric.buildAttributeSet()
+ .addAttribute(R.attr.suwStickyHeader, "@android:layout/simple_list_item_1")
+ .build());
+
+ assertNotNull(layout.findViewById(android.R.id.text1));
+ }
+
+ @Test
+ public void inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout() {
+ GlifLayout layout = new GlifLayout(mContext, R.layout.suw_glif_blank_template);
+
+ final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
+ assertEquals(android.R.id.text1, view.getId());
+ assertNotNull(layout.findViewById(android.R.id.text1));
+ }
+
+ @Config(qualifiers = "sw600dp")
+ @Test
+ public void inflateStickyHeader_whenOnBlankTemplateTablet_shouldAddViewToLayout() {
+ inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout();
+ }
+
+ @Config(minSdk = Config.OLDEST_SDK, maxSdk = Config.NEWEST_SDK)
+ @Test
+ public void createFromXml_shouldSetLayoutFullscreen_whenLayoutFullscreenIsNotSet() {
+ GlifLayout layout = new GlifLayout(
+ mContext,
+ Robolectric.buildAttributeSet()
+ .build());
+ if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ assertEquals(
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
+ layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+ }
+
+ @Test
+ public void createFromXml_shouldNotSetLayoutFullscreen_whenLayoutFullscreenIsFalse() {
+ GlifLayout layout = new GlifLayout(
+ mContext,
+ Robolectric.buildAttributeSet()
+ .addAttribute(R.attr.suwLayoutFullscreen, "false")
+ .build());
+
+ assertEquals(
+ 0,
+ layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+
private Drawable getPhoneBackground(GlifLayout layout) {
final StatusBarBackgroundLayout patternBg =
(StatusBarBackgroundLayout) layout.findManagedViewById(R.id.suw_pattern_bg);
diff --git a/library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java b/library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java
new file mode 100644
index 0000000..aa2cce3
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 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.gesture;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@RunWith(SuwLibRobolectricTestRunner.class)
+public class ConsecutiveTapsGestureDetectorTest {
+
+ @Mock
+ private ConsecutiveTapsGestureDetector.OnConsecutiveTapsListener mListener;
+
+ private ConsecutiveTapsGestureDetector mDetector;
+ private int mSlop;
+ private int mTapTimeout;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ View view = new View(application);
+ view.measure(500, 500);
+ view.layout(0, 0, 500, 500);
+ mDetector = new ConsecutiveTapsGestureDetector(mListener, view);
+
+ mSlop = ViewConfiguration.get(application).getScaledDoubleTapSlop();
+ mTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+ }
+
+ @Test
+ public void onTouchEvent_shouldTriggerCallbackOnFourTaps() {
+ InOrder inOrder = inOrder(mListener);
+
+ tap(0, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+
+ tap(100, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+
+ tap(200, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(3));
+
+ tap(300, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(4));
+ }
+
+ @Test
+ public void onTouchEvent_tapOnDifferentLocation_shouldResetCounter() {
+ InOrder inOrder = inOrder(mListener);
+
+ tap(0, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+
+ tap(100, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+
+ tap(200, 25f + mSlop * 2, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+
+ tap(300, 25f + mSlop * 2, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+ }
+
+ @Test
+ public void onTouchEvent_tapAfterTimeout_shouldResetCounter() {
+ InOrder inOrder = inOrder(mListener);
+
+ tap(0, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+
+ tap(100, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+
+ tap(200 + mTapTimeout, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+
+ tap(300 + mTapTimeout, 25f, 25f);
+ inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+ }
+
+ private void tap(int timeMillis, float x, float y) {
+ mDetector.onTouchEvent(
+ MotionEvent.obtain(timeMillis, timeMillis, MotionEvent.ACTION_DOWN, x, y, 0));
+ mDetector.onTouchEvent(
+ MotionEvent.obtain(timeMillis, timeMillis + 10, MotionEvent.ACTION_UP, x, y, 0));
+ }
+}
diff --git a/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java b/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java
index 93b9a6d..40e5da8 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java
@@ -39,7 +39,6 @@ import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.items.ButtonItem.OnClickListener;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
@@ -50,9 +49,7 @@ import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(
- constants = BuildConfig.class,
- sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
public class ButtonItemTest {
private ViewGroup mParent;
diff --git a/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java b/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java
index a61b750..ecaec71 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import org.junit.Before;
@@ -36,9 +35,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(
- constants = BuildConfig.class,
- sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
public class ItemGroupTest {
private static final Item CHILD_1 = new EqualsItem("Child 1");
diff --git a/library/test/robotest/src/com/android/setupwizardlib/robolectric/PatchedGradleManifestFactory.java b/library/test/robotest/src/com/android/setupwizardlib/robolectric/PatchedGradleManifestFactory.java
deleted file mode 100644
index 64c63e7..0000000
--- a/library/test/robotest/src/com/android/setupwizardlib/robolectric/PatchedGradleManifestFactory.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.robolectric;
-
-import org.robolectric.annotation.Config;
-import org.robolectric.internal.GradleManifestFactory;
-import org.robolectric.internal.ManifestIdentifier;
-import org.robolectric.res.FileFsFile;
-import org.robolectric.util.Logger;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.io.File;
-import java.net.URL;
-
-/**
- * Modified GradleManifestFactory to patch an issue where some build variants have merged
- * resources under res/merged/variant/type while others have it under bundles/variant/type/res.
- *
- * The change is that in the .exists() checks below we check for specific folders for the build
- * variant rather than checking existence at the parent directory.
- */
-class PatchedGradleManifestFactory extends GradleManifestFactory {
-
- @Override
- public ManifestIdentifier identify(Config config) {
- if (config.constants() == Void.class) {
- Logger.error("Field 'constants' not specified in @Config annotation");
- Logger.error("This is required when using Robolectric with Gradle!");
- throw new RuntimeException("No 'constants' field in @Config annotation!");
- }
-
- final String buildOutputDir = getBuildOutputDir(config);
- final String type = getType(config);
- final String flavor = getFlavor(config);
- final String abiSplit = getAbiSplit(config);
- final String packageName = config.packageName().isEmpty()
- ? config.constants().getPackage().getName()
- : config.packageName();
-
- final FileFsFile res;
- final FileFsFile assets;
- final FileFsFile manifest;
-
- if (FileFsFile.from(buildOutputDir, "data-binding-layout-out", flavor, type).exists()) {
- // Android gradle plugin 1.5.0+ puts the merged layouts in data-binding-layout-out.
- // https://github.com/robolectric/robolectric/issues/2143
- res = FileFsFile.from(buildOutputDir, "data-binding-layout-out", flavor, type);
- } else if (FileFsFile.from(buildOutputDir, "res", "merged", flavor, type).exists()) {
- // res/merged added in Android Gradle plugin 1.3-beta1
- res = FileFsFile.from(buildOutputDir, "res", "merged", flavor, type);
- } else if (FileFsFile.from(buildOutputDir, "res", flavor, type).exists()) {
- res = FileFsFile.from(buildOutputDir, "res", flavor, type);
- } else {
- res = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "res");
- }
-
- if (FileFsFile.from(buildOutputDir, "assets", flavor, type).exists()) {
- assets = FileFsFile.from(buildOutputDir, "assets", flavor, type);
- } else {
- assets = FileFsFile.from(buildOutputDir, "bundles", flavor, type, "assets");
- }
-
- String manifestName = config.manifest();
- URL manifestUrl = getClass().getClassLoader().getResource(manifestName);
- if (manifestUrl != null && manifestUrl.getProtocol().equals("file")) {
- manifest = FileFsFile.from(manifestUrl.getPath());
- } else if (FileFsFile.from(buildOutputDir, "manifests", "full", flavor, abiSplit, type,
- manifestName).exists()) {
- manifest = FileFsFile.from(
- buildOutputDir, "manifests", "full", flavor, abiSplit, type, manifestName);
- } else if (FileFsFile.from(buildOutputDir, "manifests", "aapt", flavor, abiSplit, type,
- manifestName).exists()) {
- // Android gradle plugin 2.2.0+ can put library manifest files inside of "aapt"
- // instead of "full"
- manifest = FileFsFile.from(buildOutputDir, "manifests", "aapt", flavor, abiSplit,
- type, manifestName);
- } else {
- manifest = FileFsFile.from(buildOutputDir, "bundles", flavor, abiSplit, type,
- manifestName);
- }
-
- return new ManifestIdentifier(manifest, res, assets, packageName, null);
- }
-
- private static String getBuildOutputDir(Config config) {
- return config.buildDir() + File.separator + "intermediates";
- }
-
- private static String getType(Config config) {
- try {
- return ReflectionHelpers.getStaticField(config.constants(), "BUILD_TYPE");
- } catch (Throwable e) {
- return null;
- }
- }
-
- private static String getFlavor(Config config) {
- try {
- return ReflectionHelpers.getStaticField(config.constants(), "FLAVOR");
- } catch (Throwable e) {
- return null;
- }
- }
-
- private static String getAbiSplit(Config config) {
- try {
- return config.abiSplit();
- } catch (Throwable e) {
- return null;
- }
- }
-}
diff --git a/library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java b/library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java
index 509201a..61baa23 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java
@@ -20,42 +20,13 @@ import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.internal.ManifestFactory;
public class SuwLibRobolectricTestRunner extends RobolectricTestRunner {
- private String mModuleRootPath;
-
public SuwLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
- // Hack to determine the module root path in the build folder (e.g. out/gradle/setup-wizard-lib)
- private void updateModuleRootPath(Config config) {
- String moduleRoot = config.constants().getResource("").toString()
- .replace("file:", "").replace("jar:", "");
- mModuleRootPath =
- moduleRoot.substring(0, moduleRoot.lastIndexOf("/build")) + "/setup-wizard-lib";
- }
-
- /**
- * Return the default config used to run Robolectric tests.
- */
- @Override
- protected Config buildGlobalConfig() {
- Config parent = super.buildGlobalConfig();
- updateModuleRootPath(parent);
- return new Config.Builder(parent)
- .setBuildDir(mModuleRootPath + "/build")
- .build();
- }
-
- @Override
- protected ManifestFactory getManifestFactory(Config config) {
- return new PatchedGradleManifestFactory();
- }
-
@Override
protected void runChild(FrameworkMethod method, RunNotifier notifier) {
System.out.println("===== Running " + method + " =====");
diff --git a/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowLog.java b/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowLog.java
new file mode 100644
index 0000000..f1d37c8
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowLog.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.shadow;
+
+import android.util.Log;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(Log.class)
+public class ShadowLog extends org.robolectric.shadows.ShadowLog {
+
+ public static boolean sWtfIsFatal = true;
+
+ public static class TerribleFailure extends RuntimeException {
+
+ public TerribleFailure(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+ }
+
+ @Implementation
+ public static void wtf(String tag, String msg) {
+ org.robolectric.shadows.ShadowLog.wtf(tag, msg);
+ if (sWtfIsFatal) {
+ throw new TerribleFailure(msg, null);
+ }
+ }
+
+ @Implementation
+ public static void wtf(String tag, String msg, Throwable throwable) {
+ org.robolectric.shadows.ShadowLog.wtf(tag, msg, throwable);
+ if (sWtfIsFatal) {
+ throw new TerribleFailure(msg, throwable);
+ }
+ }
+}
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 f86e057..3aafa7d 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
@@ -16,26 +16,28 @@
package com.android.setupwizardlib.span;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertSame;
import static org.robolectric.RuntimeEnvironment.application;
import android.content.Context;
import android.content.ContextWrapper;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
import android.widget.TextView;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class)
public class LinkSpanTest {
@Test
- public void testOnClick() {
+ public void onClick_shouldCallListenerOnContext() {
final TestContext context = new TestContext(application);
final TextView textView = new TextView(context);
final LinkSpan linkSpan = new LinkSpan("test_id");
@@ -46,7 +48,7 @@ public class LinkSpanTest {
}
@Test
- public void testNonImplementingContext() {
+ public void onClick_contextDoesNotImplementOnClickListener_shouldBeNoOp() {
final TextView textView = new TextView(application);
final LinkSpan linkSpan = new LinkSpan("test_id");
@@ -57,7 +59,7 @@ public class LinkSpanTest {
}
@Test
- public void testWrappedListener() {
+ public void onClick_contextWrapsOnClickListener_shouldCallWrappedListener() {
final TestContext context = new TestContext(application);
final Context wrapperContext = new ContextWrapper(context);
final TextView textView = new TextView(wrapperContext);
@@ -68,6 +70,28 @@ public class LinkSpanTest {
assertSame("Clicked LinkSpan should be passed to setup", linkSpan, context.clickedSpan);
}
+ @Test
+ public void onClick_shouldClearSelection() {
+ final TestContext context = new TestContext(application);
+ final TextView textView = new TextView(context);
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ textView.setFocusable(true);
+ textView.setFocusableInTouchMode(true);
+ final LinkSpan linkSpan = new LinkSpan("test_id");
+
+ SpannableStringBuilder text = new SpannableStringBuilder("Lorem ipsum dolor sit");
+ textView.setText(text);
+ text.setSpan(linkSpan, /* start= */ 0, /* end= */ 5, /* flags= */ 0);
+ // Simulate the touch effect set by TextView when touched.
+ Selection.setSelection(text, /* start= */ 0, /* end= */ 5);
+
+ linkSpan.onClick(textView);
+
+ assertThat(Selection.getSelectionStart(textView.getText())).isEqualTo(0);
+ assertThat(Selection.getSelectionEnd(textView.getText())).isEqualTo(0);
+ }
+
+ @SuppressWarnings("deprecation")
private static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
public LinkSpan clickedSpan = null;
diff --git a/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java b/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java
index fa81dc0..ec3622d 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java
@@ -31,7 +31,6 @@ import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import org.junit.Before;
@@ -42,7 +41,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
@RunWith(SuwLibRobolectricTestRunner.class)
public class ListViewScrollHandlingDelegateTest {
diff --git a/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java b/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java
index 8e39c43..c641449 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java
@@ -34,7 +34,6 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.TemplateLayout;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import com.android.setupwizardlib.template.RequireScrollMixin.OnRequireScrollStateChangedListener;
@@ -48,7 +47,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
@RunWith(SuwLibRobolectricTestRunner.class)
public class RequireScrollMixinTest {
diff --git a/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java b/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java
index f77e256..429445c 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java
@@ -23,7 +23,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.robolectric.RuntimeEnvironment.application;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import com.android.setupwizardlib.view.BottomScrollView;
import com.android.setupwizardlib.view.BottomScrollView.BottomScrollListener;
@@ -36,7 +35,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
@RunWith(SuwLibRobolectricTestRunner.class)
public class ScrollViewScrollHandlingDelegateTest {
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java
index 2be64e1..c10c122 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java
@@ -26,7 +26,6 @@ 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;
@@ -36,7 +35,7 @@ import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = Config.ALL_SDKS)
+@Config(sdk = Config.ALL_SDKS)
public class GlifDimensionTest {
private Context mContext;
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
index aea2c03..46df9d6 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
@@ -16,8 +16,10 @@
package com.android.setupwizardlib.util;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.robolectric.RuntimeEnvironment.application;
import android.annotation.TargetApi;
@@ -29,8 +31,8 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.ContextThemeWrapper;
import android.widget.Button;
+import android.widget.ProgressBar;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
@@ -41,7 +43,7 @@ import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
public class GlifStyleTest {
private Context mContext;
@@ -58,9 +60,8 @@ public class GlifStyleTest {
Robolectric.buildAttributeSet()
.setStyleAttribute("@style/SuwGlifButton.Tertiary")
.build());
- assertNull("Background of tertiary button should be null", button.getBackground());
- assertNull("Tertiary button should have no transformation method",
- button.getTransformationMethod());
+ assertThat(button.getBackground()).named("background").isNotNull();
+ assertThat(button.getTransformationMethod()).named("transformation method").isNull();
if (VERSION.SDK_INT < VERSION_CODES.M) {
// Robolectric resolved the wrong theme attribute on versions >= M
// https://github.com/robolectric/robolectric/issues/2940
@@ -76,6 +77,15 @@ public class GlifStyleTest {
assertEquals(0x00000000, activity.getWindow().getStatusBarColor());
}
+ @Test
+ public void glifLoadingScreen_shouldHaveProgressBar() {
+ GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
+ activity.setContentView(R.layout.suw_glif_loading_screen);
+
+ assertTrue("Progress bar should exist",
+ activity.findViewById(R.id.suw_large_progress_bar) instanceof ProgressBar);
+ }
+
private static class GlifThemeActivity extends Activity {
@Override
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java
new file mode 100644
index 0000000..613f2aa
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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 com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.setupwizardlib.R;
+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(minSdk = Config.OLDEST_SDK, maxSdk = Config.NEWEST_SDK)
+public class GlifV3StyleTest {
+
+ @Test
+ public void activityWithGlifV3Theme_shouldUseLightNavBarOnV27OrAbove() {
+ GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
+ if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
+ assertEquals(
+ activity.getWindow().getNavigationBarColor(),
+ Color.WHITE);
+ int vis = activity.getWindow().getDecorView().getSystemUiVisibility();
+ assertTrue((vis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0);
+ } else if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ assertEquals(
+ activity.getWindow().getNavigationBarColor(),
+ Color.BLACK);
+ }
+ // Nav bar color is not customizable pre-L
+ }
+
+ @Test
+ public void buttonWithGlifV3_shouldBeGoogleSans() {
+ GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
+ Button button = new Button(
+ activity,
+ Robolectric.buildAttributeSet()
+ .setStyleAttribute("@style/SuwGlifButton.Primary")
+ .build());
+ assertThat(button.getTypeface()).isEqualTo(Typeface.create("google-sans", 0));
+ // Button should not be all caps
+ assertThat(button.getTransformationMethod()).isNull();
+ }
+
+ private static class GlifThemeActivity extends Activity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ setTheme(R.style.SuwThemeGlifV3_Light);
+ super.onCreate(savedInstanceState);
+ }
+ }
+}
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java
index f47eef1..f8e71be 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java
@@ -31,29 +31,33 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import com.android.setupwizardlib.util.Partner.ResourceEntry;
+import com.android.setupwizardlib.util.PartnerTest.ShadowApplicationPackageManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
-import org.robolectric.res.builder.DefaultPackageManager;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowResources;
import java.util.Arrays;
import java.util.Collections;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@Config(
+ sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK },
+ shadows = ShadowApplicationPackageManager.class)
public class PartnerTest {
private static final String ACTION_PARTNER_CUSTOMIZATION =
@@ -62,7 +66,7 @@ public class PartnerTest {
private Context mContext;
private Resources mPartnerResources;
- private TestPackageManager mPackageManager;
+ private ShadowApplicationPackageManager mPackageManager;
@Before
public void setUp() throws Exception {
@@ -71,8 +75,9 @@ public class PartnerTest {
mContext = spy(application);
mPartnerResources = spy(ShadowResources.getSystem());
- mPackageManager = new TestPackageManager();
- RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
+ mPackageManager =
+ (ShadowApplicationPackageManager) Shadows.shadowOf(application.getPackageManager());
+ mPackageManager.partnerResources = mPartnerResources;
}
@Test
@@ -127,6 +132,20 @@ public class PartnerTest {
}
@Test
+ public void getColor_shouldReturnPartnerValueIfPresent() {
+ final int expectedPartnerColor = 1111;
+ doReturn(12345).when(mPartnerResources)
+ .getIdentifier(eq("suw_color_accent_dark"), eq("color"), anyString());
+ doReturn(expectedPartnerColor).when(mPartnerResources).getColor(eq(12345));
+ mPackageManager.addResolveInfoForIntent(
+ new Intent(ACTION_PARTNER_CUSTOMIZATION),
+ Arrays.asList(createResolveInfo("test.partner.package", true, true)));
+ final int foundColor = Partner.getColor(mContext, R.color.suw_color_accent_dark);
+ assertEquals("Partner color should be overlayed to: " + expectedPartnerColor,
+ expectedPartnerColor, foundColor);
+ }
+
+ @Test
public void testLoadDefaultValue() {
mPackageManager.addResolveInfoForIntent(
new Intent(ACTION_PARTNER_CUSTOMIZATION),
@@ -173,13 +192,18 @@ public class PartnerTest {
return info;
}
- private class TestPackageManager extends DefaultPackageManager {
+ @Implements(className = "android.app.ApplicationPackageManager")
+ public static class ShadowApplicationPackageManager extends
+ org.robolectric.shadows.ShadowApplicationPackageManager {
+
+ public Resources partnerResources;
+ @Implementation
@Override
public Resources getResourcesForApplication(ApplicationInfo app)
throws NameNotFoundException {
if (app != null && "test.partner.package".equals(app.packageName)) {
- return mPartnerResources;
+ return partnerResources;
} else {
return super.getResourcesForApplication(app);
}
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 4c460c8..0d15ef4 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
@@ -31,7 +31,6 @@ import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.support.annotation.StyleRes;
-import com.android.setupwizardlib.BuildConfig;
import com.android.setupwizardlib.R;
import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
@@ -39,8 +38,12 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = Config.NEWEST_SDK)
+@Config(sdk = Config.NEWEST_SDK)
public class WizardManagerHelperTest {
@Test
@@ -88,83 +91,73 @@ public class WizardManagerHelperTest {
}
@Test
- public void testIsSetupWizardFalse() {
- final Intent intent = new Intent();
- intent.putExtra("firstRun", false);
- assertFalse("Is setup wizard should be true",
- WizardManagerHelper.isSetupWizardIntent(intent));
- }
-
- @Test
- public void testHoloIsNotLightTheme() {
- final Intent intent = new Intent();
- intent.putExtra("theme", "holo");
- assertFalse("Theme holo should not be light theme",
- WizardManagerHelper.isLightTheme(intent, true));
- }
-
- @Test
- public void testHoloLightIsLightTheme() {
- final Intent intent = new Intent();
- intent.putExtra("theme", "holo_light");
- assertTrue("Theme holo_light should be light theme",
- WizardManagerHelper.isLightTheme(intent, false));
- }
-
- @Test
- public void testMaterialIsNotLightTheme() {
- final Intent intent = new Intent();
- intent.putExtra("theme", "material");
- assertFalse("Theme material should not be light theme",
- WizardManagerHelper.isLightTheme(intent, true));
- }
-
- @Test
- public void testMaterialLightIsLightTheme() {
+ public void testIsPreDeferredSetupTrue() {
final Intent intent = new Intent();
- intent.putExtra("theme", "material_light");
- assertTrue("Theme material_light should be light theme",
- WizardManagerHelper.isLightTheme(intent, false));
- }
-
- @Test
- public void testGlifIsDarkTheme() {
- final Intent intent = new Intent();
- intent.putExtra("theme", "glif");
- assertFalse("Theme glif should be dark theme",
- WizardManagerHelper.isLightTheme(intent, false));
- assertFalse("Theme glif should be dark theme",
- WizardManagerHelper.isLightTheme(intent, true));
+ intent.putExtra("preDeferredSetup", true);
+ assertTrue("Is pre-deferred setup wizard should be true",
+ WizardManagerHelper.isPreDeferredSetupWizard(intent));
}
@Test
- public void testGlifLightIsLightTheme() {
+ public void testIsSetupWizardFalse() {
final Intent intent = new Intent();
- intent.putExtra("theme", "glif_light");
- assertTrue("Theme glif_light should be light theme",
- WizardManagerHelper.isLightTheme(intent, false));
- assertTrue("Theme glif_light should be light theme",
- WizardManagerHelper.isLightTheme(intent, true));
+ intent.putExtra("firstRun", false);
+ assertFalse("Is setup wizard should be true",
+ WizardManagerHelper.isSetupWizardIntent(intent));
}
@Test
- public void testGlifV2IsDarkTheme() {
- final Intent intent = new Intent();
- intent.putExtra("theme", "glif_v2");
- assertFalse("Theme glif_v2 should be dark theme",
- WizardManagerHelper.isLightTheme(intent, false));
- assertFalse("Theme glif_v2 should be dark theme",
- WizardManagerHelper.isLightTheme(intent, true));
+ public void isLightTheme_shouldReturnTrue_whenThemeIsLight() {
+ List<String> lightThemes = Arrays.asList(
+ "holo_light",
+ "material_light",
+ "glif_light",
+ "glif_v2_light",
+ "glif_v3_light"
+ );
+ ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
+ ArrayList<String> unexpectedStringThemes = new ArrayList<>();
+ for (final String theme : lightThemes) {
+ Intent intent = new Intent();
+ intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
+ if (!WizardManagerHelper.isLightTheme(intent, false)) {
+ unexpectedIntentThemes.add(theme);
+ }
+ if (!WizardManagerHelper.isLightTheme(theme, false)) {
+ unexpectedStringThemes.add(theme);
+ }
+ }
+ assertTrue("Intent themes " + unexpectedIntentThemes + " should be light",
+ unexpectedIntentThemes.isEmpty());
+ assertTrue("String themes " + unexpectedStringThemes + " should be light",
+ unexpectedStringThemes.isEmpty());
}
@Test
- public void testGlifV2LightIsLightTheme() {
- final Intent intent = new Intent();
- intent.putExtra("theme", "glif_v2_light");
- assertTrue("Theme glif_v2_light should be light theme",
- WizardManagerHelper.isLightTheme(intent, false));
- assertTrue("Theme glif_v2_light should be light theme",
- WizardManagerHelper.isLightTheme(intent, true));
+ public void isLightTheme_shouldReturnFalse_whenThemeIsNotLight() {
+ List<String> lightThemes = Arrays.asList(
+ "holo",
+ "material",
+ "glif",
+ "glif_v2",
+ "glif_v3"
+ );
+ ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
+ ArrayList<String> unexpectedStringThemes = new ArrayList<>();
+ for (final String theme : lightThemes) {
+ Intent intent = new Intent();
+ intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
+ if (WizardManagerHelper.isLightTheme(intent, true)) {
+ unexpectedIntentThemes.add(theme);
+ }
+ if (WizardManagerHelper.isLightTheme(theme, true)) {
+ unexpectedStringThemes.add(theme);
+ }
+ }
+ assertTrue("Intent themes " + unexpectedIntentThemes + " should not be light",
+ unexpectedIntentThemes.isEmpty());
+ assertTrue("String themes " + unexpectedStringThemes + " should not be light",
+ unexpectedStringThemes.isEmpty());
}
@Test
@@ -187,19 +180,15 @@ public class WizardManagerHelperTest {
}
@Test
- public void testIsLightThemeString() {
- assertTrue("isLightTheme should return true for material_light",
- WizardManagerHelper.isLightTheme("material_light", false));
- assertFalse("isLightTheme should return false for material",
- WizardManagerHelper.isLightTheme("material", false));
- assertTrue("isLightTheme should return true for holo_light",
- WizardManagerHelper.isLightTheme("holo_light", false));
- assertFalse("isLightTheme should return false for holo",
- WizardManagerHelper.isLightTheme("holo", false));
- assertTrue("isLightTheme should return default value true",
- WizardManagerHelper.isLightTheme("abracadabra", true));
- assertFalse("isLightTheme should return default value false",
- WizardManagerHelper.isLightTheme("abracadabra", false));
+ public void testGetThemeResGlifV3Light() {
+ assertEquals(R.style.SuwThemeGlifV3_Light,
+ WizardManagerHelper.getThemeRes("glif_v3_light", 0));
+ }
+
+ @Test
+ public void testGetThemeResGlifV3() {
+ assertEquals(R.style.SuwThemeGlifV3,
+ WizardManagerHelper.getThemeRes("glif_v3", 0));
}
@Test
@@ -266,6 +255,7 @@ public class WizardManagerHelperTest {
.putExtra(WizardManagerHelper.EXTRA_WIZARD_BUNDLE, wizardBundle)
.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true)
.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true)
+ .putExtra(WizardManagerHelper.EXTRA_IS_PRE_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");
@@ -284,6 +274,8 @@ public class WizardManagerHelperTest {
intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, false));
assertTrue("EXTRA_IS_DEFERRED_SETUP should be copied",
intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false));
+ assertTrue("EXTRA_IS_PRE_DEFERRED_SETUP should be copied",
+ intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_PRE_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
index f1332c0..ae4f3d1 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java
@@ -22,7 +22,6 @@ 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;
@@ -31,7 +30,7 @@ import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
public class FillContentLayoutTest {
@Test
diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
index ffa228d..21822a4 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
@@ -18,6 +18,7 @@ package com.android.setupwizardlib.view;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -31,9 +32,10 @@ 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.shadow.ShadowLog;
+import com.android.setupwizardlib.shadow.ShadowLog.TerribleFailure;
import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowMockMediaPlayer;
import com.android.setupwizardlib.view.IllustrationVideoViewTest.ShadowSurface;
@@ -48,15 +50,15 @@ 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.shadow.api.Shadow;
import org.robolectric.shadows.ShadowMediaPlayer;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SuwLibRobolectricTestRunner.class)
@Config(
- constants = BuildConfig.class,
sdk = Config.NEWEST_SDK,
shadows = {
+ ShadowLog.class,
ShadowMockMediaPlayer.class,
ShadowSurface.class
})
@@ -78,6 +80,17 @@ public class IllustrationVideoViewTest {
}
@Test
+ public void nullMediaPlayer_shouldThrowWtf() {
+ ShadowMockMediaPlayer.sMediaPlayer = null;
+ try {
+ createDefaultView();
+ fail("WTF should be thrown for null media player");
+ } catch (TerribleFailure e) {
+ // pass
+ }
+ }
+
+ @Test
public void testPausedWhenWindowFocusLost() {
createDefaultView();
mView.start();
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java
index 5f3eb9f..f77de68 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/RichTextViewTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java
@@ -14,39 +14,45 @@
* limitations under the License.
*/
-package com.android.setupwizardlib.test;
+package com.android.setupwizardlib.view;
+
+import static com.google.common.truth.Truth.assertThat;
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.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.robolectric.RuntimeEnvironment.application;
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;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.text.Annotation;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TextAppearanceSpan;
+import android.view.MotionEvent;
+import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
import com.android.setupwizardlib.span.LinkSpan;
import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
-import com.android.setupwizardlib.view.RichTextView;
+import com.android.setupwizardlib.view.TouchableMovementMethod.TouchableLinkMovementMethod;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
import java.util.Arrays;
-@RunWith(AndroidJUnit4.class)
-@SmallTest
+@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
public class RichTextViewTest {
@Test
@@ -55,12 +61,14 @@ public class RichTextViewTest {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText(ssb);
final CharSequence text = textView.getText();
assertTrue("Text should be spanned", text instanceof Spanned);
+ assertThat(textView.getMovementMethod()).isInstanceOf(TouchableLinkMovementMethod.class);
+
Object[] spans = ((Spanned) text).getSpans(0, text.length(), Annotation.class);
assertEquals("Annotation should be removed " + Arrays.toString(spans), 0, spans.length);
@@ -77,7 +85,7 @@ public class RichTextViewTest {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText(ssb);
OnLinkClickListener listener = mock(OnLinkClickListener.class);
@@ -99,7 +107,7 @@ public class RichTextViewTest {
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- TestContext context = spy(new TestContext(InstrumentationRegistry.getTargetContext()));
+ TestContext context = spy(new TestContext(application));
RichTextView textView = new RichTextView(context);
textView.setText(ssb);
@@ -111,12 +119,50 @@ public class RichTextViewTest {
}
@Test
+ public void onTouchEvent_clickOnLinks_shouldReturnTrue() {
+ Annotation link = new Annotation("link", "foobar");
+ SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+ ssb.setSpan(link, 0, 2, 0 /* flags */);
+
+ RichTextView textView = new RichTextView(application);
+ textView.setText(ssb);
+
+ TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
+ textView.setMovementMethod(mockMovementMethod);
+
+ MotionEvent motionEvent =
+ MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
+ doReturn(true).when(mockMovementMethod).isLastTouchEventHandled();
+ assertThat(textView.onTouchEvent(motionEvent)).isTrue();
+ }
+
+ @Test
+ public void onTouchEvent_clickOutsideLinks_shouldReturnFalse() {
+ Annotation link = new Annotation("link", "foobar");
+ SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+ ssb.setSpan(link, 0, 2, 0 /* flags */);
+
+ RichTextView textView = new RichTextView(application);
+ textView.setText(ssb);
+
+ TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
+ textView.setMovementMethod(mockMovementMethod);
+
+ MotionEvent motionEvent =
+ MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
+ doReturn(false).when(mockMovementMethod).isLastTouchEventHandled();
+ assertThat(textView.onTouchEvent(motionEvent)).isFalse();
+ }
+
+ @Test
public void testTextStyle() {
Annotation link = new Annotation("textAppearance", "foobar");
SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
ssb.setSpan(link, 1, 2, 0 /* flags */);
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText(ssb);
final CharSequence text = textView.getText();
@@ -137,7 +183,7 @@ public class RichTextViewTest {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Linked");
spannableStringBuilder.setSpan(testLink, 0, 3, 0);
- RichTextView view = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView view = new RichTextView(application);
view.setText(spannableStringBuilder);
assertTrue("TextView should be focusable since it contains spans", view.isFocusable());
@@ -147,7 +193,7 @@ public class RichTextViewTest {
@SuppressLint("SetTextI18n") // It's OK. This is just a test.
@Test
public void testTextContainingNoLinksAreNotFocusable() {
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText("Thou shall not be focusable!");
assertFalse("TextView should not be focusable since it does not contain any span",
@@ -160,16 +206,23 @@ public class RichTextViewTest {
@SuppressLint("SetTextI18n") // It's OK. This is just a test.
@Test
public void testRichTextViewFocusChangesWithTextChange() {
- RichTextView textView = new RichTextView(InstrumentationRegistry.getContext());
+ RichTextView textView = new RichTextView(application);
textView.setText("Thou shall not be focusable!");
assertFalse(textView.isFocusable());
+ assertFalse(textView.isFocusableInTouchMode());
SpannableStringBuilder spannableStringBuilder =
new SpannableStringBuilder("I am focusable");
spannableStringBuilder.setSpan(new Annotation("link", "focus:on_me"), 0, 1, 0);
textView.setText(spannableStringBuilder);
assertTrue(textView.isFocusable());
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ assertTrue(textView.isFocusableInTouchMode());
+ assertFalse(textView.getRevealOnFocusHint());
+ } else {
+ assertFalse(textView.isFocusableInTouchMode());
+ }
}
public static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
diff --git a/navigationbar/res/values-en-rCA/strings.xml b/navigationbar/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..b06dc86
--- /dev/null
+++ b/navigationbar/res/values-en-rCA/strings.xml
@@ -0,0 +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">"Next"</string>
+ <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Back"</string>
+</resources>
diff --git a/navigationbar/res/values-en-rXC/strings.xml b/navigationbar/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..5c7c658
--- /dev/null
+++ b/navigationbar/res/values-en-rXC/strings.xml
@@ -0,0 +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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‎Next‎‏‎‎‏‎"</string>
+ <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎Back‎‏‎‎‏‎"</string>
+</resources>
diff --git a/tools/build_for_build_server.sh b/tools/build_for_build_server.sh
index a90ae67..7a8c942 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 buildProjectFull test
+./gradlew buildProjectFull test coverage
diff --git a/tools/gradle/android.properties b/tools/gradle/android.properties
index 75de660..f62de66 100644
--- a/tools/gradle/android.properties
+++ b/tools/gradle/android.properties
@@ -1,6 +1,6 @@
// Set the default SDK and build tools version for all apps
-compileSdkVersion 25
-buildToolsVersion = '25.0.0'
+compileSdkVersion 28
+buildToolsVersion = '28.0.0'
// enable Java7
compileOptions.sourceCompatibility JavaVersion.VERSION_1_7
diff --git a/tools/gradle/dist-unit-tests.gradle b/tools/gradle/dist-unit-tests.gradle
index faae260..aaecd35 100644
--- a/tools/gradle/dist-unit-tests.gradle
+++ b/tools/gradle/dist-unit-tests.gradle
@@ -10,6 +10,7 @@
*/
apply plugin: 'dist'
+apply plugin: 'jacoco'
// If unit tests are run as part of the build, dist the test XML reports to host-test-reports/*.zip
android.unitTestVariants.all { variant ->
@@ -28,11 +29,80 @@ android.unitTestVariants.all { variant ->
archiveName = task.name + 'Result.zip'
destinationDir = junitReport.destination.parentFile
}
- zipTask.mustRunAfter task
+ task.finalizedBy zipTask
// Copy the test reports to dist/host-test-reports
// The file path and format should match GradleHostBasedTest class in TradeFed.
- tasks.dist.dependsOn zipTask
+ tasks.dist.mustRunAfter zipTask
dist.file zipTask.archivePath.path, "host-test-reports/${zipTask.archiveName}"
}
}
+
+/*
+ * The section below adds code coverage to all the unitTest targets. By default, the jacoco plugin
+ * only adds to the 'java' plugin and not the Android ones.
+ *
+ * For each unitTest task "fooUnitTest", a new target "fooUnitTestCoverage" will be generated for
+ * to generate the jacoco report.
+ */
+android.testOptions.unitTests.all {
+ // Fix robolectric tests reporting 0 coverage on newer versions of the plugin.
+ jacoco {
+ includeNoLocationClasses = true
+ }
+}
+
+// Define the main coverage task if it does not exist. This task generates coverage report for all
+// unit tests.
+def coverageTask = tasks.findByName('coverage') ?: tasks.create('coverage') {
+ group = "Reporting"
+ description = "Generate Jacoco coverage reports"
+}
+
+android.unitTestVariants.all { variant ->
+ def testTaskName = "test${variant.name.capitalize()}"
+ def testTask = tasks.findByName(testTaskName)
+
+ // Create coverage task of form 'testFlavorCoverageUnitTestCoverage' depending on
+ // 'testFlavorCoverageUnitTest'
+ def jacocoTask = tasks.create("${testTaskName}Coverage", JacocoReport) {
+ group = "Reporting"
+ description = "Generate a Jacoco coverage report for robolectric tests on ${variant.name}."
+
+ classDirectories = fileTree(
+ dir: "${project.buildDir}/intermediates/classes/" +
+ "${variant.productFlavors[0].name}/${variant.buildType.name}",
+ excludes: ['**/R.class',
+ '**/R$*.class',
+ '**/BuildConfig.*',
+ '**/Manifest*.*']
+ )
+
+ sourceDirectories = files(variant.testedVariant.sourceSets.collect { it.java.srcDirs })
+ executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
+
+ reports {
+ xml.enabled = true
+ html.enabled = true
+ }
+ }
+ jacocoTask.dependsOn testTask
+
+ // Create a zip file of the HTML coverage reports
+ def zipTask = tasks.create("zipResultsOf${jacocoTask.name.capitalize()}", Zip) {
+ from jacocoTask.reports.html.destination
+ archiveName = "${testTaskName}HtmlCoverage.zip"
+ destinationDir = jacocoTask.reports.html.destination.parentFile
+ }
+ jacocoTask.finalizedBy zipTask
+
+ // Copy the coverage reports to dist/host-test-coverage
+ // The file path and format should match JacocoLogForwarder class in TradeFed.
+ tasks.dist.mustRunAfter jacocoTask
+ dist.file jacocoTask.reports.xml.destination.path, "host-test-coverage/${jacocoTask.name}.xml"
+
+ tasks.dist.mustRunAfter zipTask
+ dist.file zipTask.archivePath.path, "host-test-coverage/${zipTask.archiveName}"
+
+ coverageTask.dependsOn(jacocoTask)
+}
diff --git a/tools/root.mk b/tools/root.mk
new file mode 100644
index 0000000..11597ea
--- /dev/null
+++ b/tools/root.mk
@@ -0,0 +1,2 @@
+default:
+ TARGET_BUILD_APPS=setup-wizard-lib ./gradlew