summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2021-08-14 06:31:01 +0000
committerXin Li <delphij@google.com>2021-08-14 06:31:01 +0000
commite595fd650a23892286e18f2216e017ff5092a74e (patch)
treefa0429fae76936b9bc212aa5ddd323759bc005fc
parent0aa6eb462b2449c29d1cae10abe122e3367d9f9e (diff)
parenta364928354280dc412543f6b42319a6df367d213 (diff)
downloadsetupdesign-e595fd650a23892286e18f2216e017ff5092a74e.tar.gz
Merge sc-dev-plus-aosp-without-vendor@7634622temp_sam_202323961
Merged-In: I1ab0e42437ef8c2acdf7e93863c21197b4731e5f Change-Id: I38da9f192d57688b1b05bb2bf41ce33f87a60807
-rw-r--r--Android.bp1
-rw-r--r--exempting_lint_checks.txt49
-rw-r--r--grandfathered_lint_checks.txt0
-rw-r--r--lint-baseline.xml34
-rw-r--r--lottie_loading_layout/Android.bp31
-rw-r--r--lottie_loading_layout/AndroidManifest.xml25
-rw-r--r--lottie_loading_layout/res/layout-land-v31/sud_glif_loading_template_content.xml100
-rw-r--r--lottie_loading_layout/res/layout-v31/sud_glif_loading_template_content.xml92
-rw-r--r--lottie_loading_layout/res/layout-v31/sud_loading_illustration_layout.xml39
-rw-r--r--lottie_loading_layout/res/layout-v31/sud_loading_lottie_layout.xml34
-rw-r--r--lottie_loading_layout/res/layout/sud_glif_loading_template_card.xml49
-rw-r--r--lottie_loading_layout/res/layout/sud_glif_loading_template_compat.xml26
-rw-r--r--lottie_loading_layout/res/layout/sud_glif_loading_template_content.xml80
-rw-r--r--lottie_loading_layout/res/layout/sud_loading_illustration_layout.xml46
-rw-r--r--lottie_loading_layout/res/layout/sud_loading_lottie_layout.xml41
-rw-r--r--lottie_loading_layout/res/values-sw600dp/layouts.xml20
-rw-r--r--lottie_loading_layout/res/values/attrs.xml24
-rw-r--r--lottie_loading_layout/res/values/layouts.xml20
-rw-r--r--lottie_loading_layout/src/com/google/android/setupdesign/GlifLoadingLayout.java864
-rw-r--r--lottie_loading_layout/src/com/google/android/setupdesign/LoadingFooterBarMixin.java44
-rw-r--r--main/AndroidManifest.xml10
-rw-r--r--main/res/anim-v31/sud_interpolator.xml19
-rw-r--r--main/res/anim-v31/sud_slide_back_in.xml22
-rw-r--r--main/res/anim-v31/sud_slide_back_out.xml22
-rw-r--r--main/res/anim-v31/sud_slide_next_in.xml22
-rw-r--r--main/res/anim-v31/sud_slide_next_out.xml22
-rw-r--r--main/res/anim/sud_pre_p_activity_close_enter.xml23
-rw-r--r--main/res/anim/sud_pre_p_activity_close_exit.xml30
-rw-r--r--main/res/anim/sud_pre_p_activity_open_enter.xml30
-rw-r--r--main/res/anim/sud_pre_p_activity_open_exit.xml24
-rw-r--r--main/res/anim/sud_stay.xml23
-rw-r--r--main/res/color/sud_switch_track_on.xml26
-rw-r--r--main/res/drawable-v21/sud_switch_thumb_on.xml31
-rw-r--r--main/res/drawable/sud_dialog_background_dark.xml30
-rw-r--r--main/res/drawable/sud_dialog_background_light.xml30
-rw-r--r--main/res/drawable/sud_scroll_bar_dark.xml22
-rw-r--r--main/res/drawable/sud_scroll_bar_light.xml22
-rw-r--r--main/res/drawable/sud_switch_thumb_off.xml31
-rw-r--r--main/res/drawable/sud_switch_thumb_on.xml32
-rw-r--r--main/res/drawable/sud_switch_thumb_selector.xml21
-rw-r--r--main/res/drawable/sud_switch_track_off_background.xml24
-rw-r--r--main/res/drawable/sud_switch_track_on_background.xml24
-rw-r--r--main/res/drawable/sud_switch_track_selector.xml21
-rw-r--r--main/res/interpolator/sud_accelerate_quart.xml19
-rw-r--r--main/res/interpolator/sud_decelerate_quart.xml19
-rw-r--r--main/res/layout-land-v31/sud_glif_blank_template_content.xml67
-rw-r--r--main/res/layout-land-v31/sud_glif_list_template_content.xml80
-rw-r--r--main/res/layout-land-v31/sud_glif_preference_recycler_view.xml25
-rw-r--r--main/res/layout-land-v31/sud_glif_preference_template_content.xml78
-rw-r--r--main/res/layout-land-v31/sud_glif_recycler_template_content.xml81
-rw-r--r--main/res/layout-land-v31/sud_glif_template_content.xml100
-rw-r--r--main/res/layout-v31/sud_glif_preference_template_compact.xml26
-rw-r--r--main/res/layout-v31/sud_glif_preference_template_content.xml27
-rw-r--r--main/res/layout/sud_content_info.xml61
-rw-r--r--main/res/layout/sud_glif_blank_template_content.xml1
-rw-r--r--main/res/layout/sud_glif_header.xml47
-rw-r--r--main/res/layout/sud_glif_list_template_content.xml1
-rw-r--r--main/res/layout/sud_glif_recycler_template_content.xml1
-rw-r--r--main/res/layout/sud_glif_template_content.xml8
-rw-r--r--main/res/layout/sud_progress_illustration_layout.xml53
-rw-r--r--main/res/values-land-v31/dimens.xml27
-rw-r--r--main/res/values-land/dimens.xml12
-rw-r--r--main/res/values-night-v31/colors.xml33
-rw-r--r--main/res/values-night/colors.xml31
-rw-r--r--main/res/values-night/styles.xml2
-rw-r--r--main/res/values-sw600dp/dimens.xml7
-rw-r--r--main/res/values-v21/styles.xml4
-rw-r--r--main/res/values-v27/styles.xml9
-rw-r--r--main/res/values-v31/colors.xml80
-rw-r--r--main/res/values-v31/config.xml22
-rw-r--r--main/res/values-v31/layouts.xml22
-rw-r--r--main/res/values-v31/styles.xml120
-rw-r--r--main/res/values/attrs.xml50
-rw-r--r--main/res/values/colors.xml100
-rw-r--r--main/res/values/dimens.xml67
-rw-r--r--main/res/values/layouts.xml1
-rw-r--r--main/res/values/styles.xml244
-rw-r--r--main/src/com/google/android/setupdesign/DividerItemDecoration.java4
-rw-r--r--main/src/com/google/android/setupdesign/GlifLayout.java196
-rw-r--r--main/src/com/google/android/setupdesign/GlifListLayout.java10
-rw-r--r--main/src/com/google/android/setupdesign/GlifRecyclerLayout.java10
-rw-r--r--main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java2
-rw-r--r--main/src/com/google/android/setupdesign/SetupWizardLayout.java6
-rw-r--r--main/src/com/google/android/setupdesign/SetupWizardListLayout.java4
-rw-r--r--main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java4
-rw-r--r--main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java6
-rw-r--r--main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java5
-rw-r--r--main/src/com/google/android/setupdesign/items/DescriptionItem.java47
-rw-r--r--main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java3
-rw-r--r--main/src/com/google/android/setupdesign/items/Item.java16
-rw-r--r--main/src/com/google/android/setupdesign/items/ItemHierarchy.java2
-rw-r--r--main/src/com/google/android/setupdesign/items/ItemViewHolder.java6
-rw-r--r--main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java15
-rw-r--r--main/src/com/google/android/setupdesign/items/ReflectionInflater.java4
-rw-r--r--main/src/com/google/android/setupdesign/items/SimpleInflater.java2
-rw-r--r--main/src/com/google/android/setupdesign/span/LinkSpan.java2
-rw-r--r--main/src/com/google/android/setupdesign/template/DescriptionMixin.java194
-rw-r--r--main/src/com/google/android/setupdesign/template/HeaderMixin.java185
-rw-r--r--main/src/com/google/android/setupdesign/template/IconMixin.java93
-rw-r--r--main/src/com/google/android/setupdesign/template/IllustrationProgressMixin.java193
-rw-r--r--main/src/com/google/android/setupdesign/template/ListMixin.java63
-rw-r--r--main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java4
-rw-r--r--main/src/com/google/android/setupdesign/template/ProgressBarMixin.java67
-rw-r--r--main/src/com/google/android/setupdesign/template/RecyclerMixin.java54
-rw-r--r--main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java4
-rw-r--r--main/src/com/google/android/setupdesign/template/RequireScrollMixin.java6
-rw-r--r--main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java4
-rw-r--r--main/src/com/google/android/setupdesign/transition/TransitionHelper.java669
-rw-r--r--main/src/com/google/android/setupdesign/transition/support/TransitionHelper.java130
-rw-r--r--main/src/com/google/android/setupdesign/util/ButtonStyler.java45
-rw-r--r--main/src/com/google/android/setupdesign/util/ContentStyler.java161
-rw-r--r--main/src/com/google/android/setupdesign/util/DescriptionStyler.java34
-rw-r--r--main/src/com/google/android/setupdesign/util/DynamicColorPalette.java89
-rw-r--r--main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java217
-rw-r--r--main/src/com/google/android/setupdesign/util/ItemStyler.java173
-rw-r--r--main/src/com/google/android/setupdesign/util/LayoutStyler.java147
-rw-r--r--main/src/com/google/android/setupdesign/util/Partner.java38
-rw-r--r--main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java156
-rw-r--r--main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java85
-rw-r--r--main/src/com/google/android/setupdesign/util/ThemeHelper.java152
-rw-r--r--main/src/com/google/android/setupdesign/util/ThemeResolver.java17
-rw-r--r--main/src/com/google/android/setupdesign/view/BottomScrollView.java3
-rw-r--r--main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java2
-rw-r--r--main/src/com/google/android/setupdesign/view/FillContentLayout.java4
-rw-r--r--main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java4
-rw-r--r--main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java164
-rw-r--r--main/src/com/google/android/setupdesign/view/IconUniformityAppImageViewBindable.java36
-rw-r--r--main/src/com/google/android/setupdesign/view/Illustration.java4
-rw-r--r--main/src/com/google/android/setupdesign/view/IllustrationVideoView.java63
-rw-r--r--main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java39
-rw-r--r--main/src/com/google/android/setupdesign/view/NavigationBar.java6
-rw-r--r--main/src/com/google/android/setupdesign/view/NavigationBarButton.java6
-rw-r--r--main/src/com/google/android/setupdesign/view/RichTextView.java4
-rw-r--r--main/src/com/google/android/setupdesign/view/StickyHeaderListView.java4
-rw-r--r--main/src/com/google/android/setupdesign/widget/CardBackgroundDrawable.java104
135 files changed, 7210 insertions, 262 deletions
diff --git a/Android.bp b/Android.bp
index 1852976..4d416d3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,7 @@ android_library {
"androidx.legacy_legacy-support-core-ui",
"androidx.appcompat_appcompat",
"androidx.recyclerview_recyclerview",
+ "com.google.android.material_material",
"setupcompat",
"setupdesign-strings",
],
diff --git a/exempting_lint_checks.txt b/exempting_lint_checks.txt
new file mode 100644
index 0000000..201fd64
--- /dev/null
+++ b/exempting_lint_checks.txt
@@ -0,0 +1,49 @@
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java: AnnotateVersionCheck: private static final boolean ON_L_PLUS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
diff --git a/grandfathered_lint_checks.txt b/grandfathered_lint_checks.txt
deleted file mode 100644
index e69de29..0000000
--- a/grandfathered_lint_checks.txt
+++ /dev/null
diff --git a/lint-baseline.xml b/lint-baseline.xml
index dbe190c..c369cea 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -8,7 +8,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="external/setupdesign/main/res/values/styles.xml"
- line="229"
+ line="291"
column="15"/>
</issue>
@@ -19,7 +19,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="external/setupdesign/main/res/values/styles.xml"
- line="230"
+ line="292"
column="15"/>
</issue>
@@ -30,7 +30,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="external/setupdesign/main/res/values/styles.xml"
- line="239"
+ line="302"
column="15"/>
</issue>
@@ -41,7 +41,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="external/setupdesign/main/res/values/styles.xml"
- line="240"
+ line="303"
column="15"/>
</issue>
@@ -52,7 +52,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="external/setupdesign/main/res/values/styles.xml"
- line="460"
+ line="523"
column="15"/>
</issue>
@@ -63,7 +63,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="external/setupdesign/main/res/values/styles.xml"
- line="556"
+ line="619"
column="15"/>
</issue>
@@ -78,4 +78,26 @@
column="9"/>
</issue>
+ <issue
+ id="NewApi"
+ message="`@android:interpolator/linear_out_slow_in` requires API level 21 (current min is 14)"
+ errorLine1=" android:interpolator=&quot;@android:interpolator/linear_out_slow_in&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="external/setupdesign/main/res/anim/sud_pre_p_activity_close_enter.xml"
+ line="21"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="`@android:interpolator/fast_out_slow_in` requires API level 21 (current min is 14)"
+ errorLine1=" android:interpolator=&quot;@android:interpolator/fast_out_slow_in&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="external/setupdesign/main/res/anim/sud_pre_p_activity_open_exit.xml"
+ line="22"
+ column="9"/>
+ </issue>
+
</issues>
diff --git a/lottie_loading_layout/Android.bp b/lottie_loading_layout/Android.bp
new file mode 100644
index 0000000..6d41812
--- /dev/null
+++ b/lottie_loading_layout/Android.bp
@@ -0,0 +1,31 @@
+//
+// Build the setup design - lottie_loading_layout.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_setupdesign_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["external_setupdesign_license"],
+}
+
+android_library {
+ name: "setupdesign-lottie-loading-layout",
+ manifest: "AndroidManifest.xml",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "lottie",
+ "setupcompat",
+ "setupdesign",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ min_sdk_version: "16",
+ sdk_version: "current"
+}
diff --git a/lottie_loading_layout/AndroidManifest.xml b/lottie_loading_layout/AndroidManifest.xml
new file mode 100644
index 0000000..3cf1948
--- /dev/null
+++ b/lottie_loading_layout/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.setupdesign.lottieloadinglayout">
+
+ <uses-sdk
+ android:minSdkVersion="14"
+ android:targetSdkVersion="30" />
+
+</manifest>
diff --git a/lottie_loading_layout/res/layout-land-v31/sud_glif_loading_template_content.xml b/lottie_loading_layout/res/layout-land-v31/sud_glif_loading_template_content.xml
new file mode 100644
index 0000000..65e20a9
--- /dev/null
+++ b/lottie_loading_layout/res/layout-land-v31/sud_glif_loading_template_content.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_header_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_header_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators">
+
+ <include layout="@layout/sud_glif_header" />
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_content_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/sud_layout_loading_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ViewStub
+ android:id="@+id/sud_loading_layout_lottie_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:inflatedId="@+id/sud_layout_lottie_illustration"
+ android:layout="@layout/sud_loading_lottie_layout" />
+
+ <ViewStub
+ android:id="@+id/sud_loading_layout_illustration_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:inflatedId="@+id/sud_layout_progress_illustration"
+ android:layout="@layout/sud_loading_illustration_layout" />
+
+ <FrameLayout
+ android:id="@+id/sud_layout_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout-v31/sud_glif_loading_template_content.xml b/lottie_loading_layout/res/layout-v31/sud_glif_loading_template_content.xml
new file mode 100644
index 0000000..8415324
--- /dev/null
+++ b/lottie_loading_layout/res/layout-v31/sud_glif_loading_template_content.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
+ versions. -->
+ <com.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_header_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/sudLoadingHeaderHeight"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators"
+ tools:ignore="UnusedAttribute">
+
+ <include layout="@layout/sud_glif_header" />
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_loading_layout_lottie_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:inflatedId="@+id/sud_layout_lottie_illustration"
+ android:layout="@layout/sud_loading_lottie_layout" />
+
+ <ViewStub
+ android:id="@+id/sud_loading_layout_illustration_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:inflatedId="@+id/sud_layout_progress_illustration"
+ android:layout="@layout/sud_loading_illustration_layout" />
+
+ <FrameLayout
+ android:id="@+id/sud_layout_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/lottie_loading_layout/res/layout-v31/sud_loading_illustration_layout.xml b/lottie_loading_layout/res/layout-v31/sud_loading_illustration_layout.xml
new file mode 100644
index 0000000..0f60d02
--- /dev/null
+++ b/lottie_loading_layout/res/layout-v31/sud_loading_illustration_layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/SudLoadingContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ProgressBar
+ android:id="@+id/sud_progress_bar"
+ android:visibility="gone"
+ style="@style/SudFourColorIndeterminateProgressBar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <com.google.android.setupdesign.view.IllustrationVideoView
+ android:id="@+id/sud_progress_illustration"
+ android:visibility="gone"
+ style="@style/SudContentIllustration"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/lottie_loading_layout/res/layout-v31/sud_loading_lottie_layout.xml b/lottie_loading_layout/res/layout-v31/sud_loading_lottie_layout.xml
new file mode 100644
index 0000000..a38b791
--- /dev/null
+++ b/lottie_loading_layout/res/layout-v31/sud_loading_lottie_layout.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ style="@style/SudLoadingContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/sud_lottie_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ style="@style/SudContentIllustration"
+ app:lottie_autoPlay="false"
+ app:lottie_loop="true" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout/sud_glif_loading_template_card.xml b/lottie_loading_layout/res/layout/sud_glif_loading_template_card.xml
new file mode 100644
index 0000000..2db095c
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_glif_loading_template_card.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/suc_layout_status"
+ style="@style/SudGlifCardBackground"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <com.google.android.setupdesign.view.IntrinsicSizeFrameLayout
+ style="@style/SudGlifCardContainer"
+ android:layout_width="@dimen/sud_glif_card_width"
+ android:layout_height="wrap_content"
+ android:height="@dimen/sud_glif_card_height">
+
+ <include layout="@layout/sud_glif_loading_template_content" />
+
+ </com.google.android.setupdesign.view.IntrinsicSizeFrameLayout>
+
+ <View
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout/sud_glif_loading_template_compat.xml b/lottie_loading_layout/res/layout/sud_glif_loading_template_compat.xml
new file mode 100644
index 0000000..86003b9
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_glif_loading_template_compat.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.google.android.setupcompat.view.StatusBarBackgroundLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/suc_layout_status"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/sud_glif_loading_template_content" />
+
+</com.google.android.setupcompat.view.StatusBarBackgroundLayout> \ No newline at end of file
diff --git a/lottie_loading_layout/res/layout/sud_glif_loading_template_content.xml b/lottie_loading_layout/res/layout/sud_glif_loading_template_content.xml
new file mode 100644
index 0000000..e70edc1
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_glif_loading_template_content.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_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.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators"
+ tools:ignore="UnusedAttribute">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include layout="@layout/sud_glif_header" />
+
+ <ViewStub
+ android:id="@+id/sud_loading_layout_illustration_stub"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:inflatedId="@+id/sud_layout_progress_illustration"
+ android:layout="@layout/sud_loading_illustration_layout" />
+
+ <ViewStub
+ android:id="@+id/sud_loading_layout_lottie_stub"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:inflatedId="@+id/sud_layout_lottie_illustration"
+ android:layout="@layout/sud_loading_lottie_layout" />
+
+ <FrameLayout
+ android:id="@+id/sud_layout_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout/sud_loading_illustration_layout.xml b/lottie_loading_layout/res/layout/sud_loading_illustration_layout.xml
new file mode 100644
index 0000000..772598e
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_loading_illustration_layout.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/SudContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.google.android.setupdesign.view.FillContentLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ProgressBar
+ android:id="@+id/sud_progress_bar"
+ android:visibility="gone"
+ style="@style/SudFourColorIndeterminateProgressBar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <com.google.android.setupdesign.view.IllustrationVideoView
+ android:id="@+id/sud_progress_illustration"
+ android:visibility="gone"
+ style="@style/SudContentIllustration"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </com.google.android.setupdesign.view.FillContentLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/lottie_loading_layout/res/layout/sud_loading_lottie_layout.xml b/lottie_loading_layout/res/layout/sud_loading_lottie_layout.xml
new file mode 100644
index 0000000..6e00f98
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_loading_lottie_layout.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ style="@style/SudContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.google.android.setupdesign.view.FillContentLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/sud_lottie_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ style="@style/SudContentIllustration"
+ app:lottie_autoPlay="false"
+ app:lottie_loop="true" />
+
+ </com.google.android.setupdesign.view.FillContentLayout>
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/values-sw600dp/layouts.xml b/lottie_loading_layout/res/values-sw600dp/layouts.xml
new file mode 100644
index 0000000..9fe5404
--- /dev/null
+++ b/lottie_loading_layout/res/values-sw600dp/layouts.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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:tools="http://schemas.android.com/tools">
+ <item name="sud_glif_loading_template" type="layout" tools:ignore="UnusedResources">@layout/sud_glif_loading_template_card</item>
+</resources>
diff --git a/lottie_loading_layout/res/values/attrs.xml b/lottie_loading_layout/res/values/attrs.xml
new file mode 100644
index 0000000..57fcac7
--- /dev/null
+++ b/lottie_loading_layout/res/values/attrs.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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>
+ <declare-styleable name="SudGlifLoadingLayout">
+ <attr name="sudIllustrationType" format="string" />
+ <attr name="sudLottieRes" format="reference" />
+ <attr name="sudUsePartnerHeavyTheme" />
+ </declare-styleable>
+</resources> \ No newline at end of file
diff --git a/lottie_loading_layout/res/values/layouts.xml b/lottie_loading_layout/res/values/layouts.xml
new file mode 100644
index 0000000..483f09d
--- /dev/null
+++ b/lottie_loading_layout/res/values/layouts.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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:tools="http://schemas.android.com/tools">
+ <item name="sud_glif_loading_template" type="layout" tools:ignore="UnusedResources">@layout/sud_glif_loading_template_compat</item>
+</resources> \ No newline at end of file
diff --git a/lottie_loading_layout/src/com/google/android/setupdesign/GlifLoadingLayout.java b/lottie_loading_layout/src/com/google/android/setupdesign/GlifLoadingLayout.java
new file mode 100644
index 0000000..d239e3d
--- /dev/null
+++ b/lottie_loading_layout/src/com/google/android/setupdesign/GlifLoadingLayout.java
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2021 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.google.android.setupdesign;
+
+import static com.google.android.setupcompat.partnerconfig.Util.isNightMode;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings.Global;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RawRes;
+import androidx.annotation.StringDef;
+import androidx.annotation.VisibleForTesting;
+import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieDrawable;
+import com.airbnb.lottie.LottieProperty;
+import com.airbnb.lottie.SimpleColorFilter;
+import com.airbnb.lottie.model.KeyPath;
+import com.airbnb.lottie.value.LottieValueCallback;
+import com.airbnb.lottie.value.SimpleLottieValueCallback;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig.ResourceType;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.partnerconfig.ResourceEntry;
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.util.BuildCompatUtils;
+import com.google.android.setupdesign.lottieloadinglayout.R;
+import com.google.android.setupdesign.view.IllustrationVideoView;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A GLIF themed layout with a {@link com.airbnb.lottie.LottieAnimationView} to showing lottie
+ * illustration and a substitute {@link com.google.android.setupdesign.view.IllustrationVideoView}
+ * to showing mp4 illustration. {@code app:sudIllustrationType} can also be used to specify one of
+ * the set including "default", "account", "connection", "update", and "final_hold". {@code
+ * app:sudLottieRes} can assign the json file of Lottie resource.
+ */
+public class GlifLoadingLayout extends GlifLayout {
+
+ private static final String TAG = "GlifLoadingLayout";
+ View inflatedView;
+
+ @VisibleForTesting @IllustrationType String illustrationType = IllustrationType.DEFAULT;
+ @VisibleForTesting LottieAnimationConfig animationConfig = LottieAnimationConfig.CONFIG_DEFAULT;
+
+ @VisibleForTesting @RawRes int customLottieResource = 0;
+
+ @VisibleForTesting Map<KeyPath, SimpleColorFilter> customizationMap = new HashMap<>();
+
+ @VisibleForTesting
+ public List<LottieAnimationFinishListener> animationFinishListeners = new ArrayList<>();
+
+ public GlifLoadingLayout(Context context) {
+ this(context, 0, 0);
+ }
+
+ public GlifLoadingLayout(Context context, int template) {
+ this(context, template, 0);
+ }
+
+ public GlifLoadingLayout(Context context, int template, int containerId) {
+ super(context, template, containerId);
+ init(null, R.attr.sudLayoutTheme);
+ }
+
+ public GlifLoadingLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(attrs, R.attr.sudLayoutTheme);
+ }
+
+ @TargetApi(VERSION_CODES.HONEYCOMB)
+ public GlifLoadingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(attrs, defStyleAttr);
+ }
+
+ private void init(AttributeSet attrs, int defStyleAttr) {
+ registerMixin(FooterBarMixin.class, new LoadingFooterBarMixin(this, attrs, defStyleAttr));
+
+ TypedArray a =
+ getContext()
+ .obtainStyledAttributes(attrs, R.styleable.SudGlifLoadingLayout, defStyleAttr, 0);
+ customLottieResource = a.getResourceId(R.styleable.SudGlifLoadingLayout_sudLottieRes, 0);
+ String illustrationType = a.getString(R.styleable.SudGlifLoadingLayout_sudIllustrationType);
+ boolean usePartnerHeavyTheme =
+ a.getBoolean(R.styleable.SudGlifLoadingLayout_sudUsePartnerHeavyTheme, false);
+ a.recycle();
+
+ if (customLottieResource != 0) {
+ inflateLottieView();
+ ViewGroup container = findContainer(0);
+ container.setVisibility(View.VISIBLE);
+ } else {
+ if (illustrationType != null) {
+ setIllustrationType(illustrationType);
+ }
+
+ if (BuildCompatUtils.isAtLeastS()) {
+ inflateLottieView();
+ } else {
+ inflateIllustrationStub();
+ }
+ }
+
+ boolean applyPartnerHeavyThemeResource = shouldApplyPartnerResource() && usePartnerHeavyTheme;
+ if (applyPartnerHeavyThemeResource) {
+ View view = findManagedViewById(R.id.sud_layout_loading_content);
+ if (view != null) {
+ applyPartnerCustomizationContentPaddingTopStyle(view);
+ }
+ }
+
+ updateHeaderHeight();
+ updateLandscapeMiddleHorizontalSpacing();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (inflatedView instanceof LinearLayout) {
+ updateContentPadding((LinearLayout) inflatedView);
+ }
+ }
+
+ public void setIllustrationType(@IllustrationType String type) {
+ if (customLottieResource != 0) {
+ throw new IllegalStateException(
+ "custom illustration already applied, should not set illustration.");
+ }
+
+ if (!illustrationType.equals(type)) {
+ illustrationType = type;
+ customizationMap.clear();
+ }
+
+ switch (type) {
+ case IllustrationType.ACCOUNT:
+ animationConfig = LottieAnimationConfig.CONFIG_ACCOUNT;
+ break;
+
+ case IllustrationType.CONNECTION:
+ animationConfig = LottieAnimationConfig.CONFIG_CONNECTION;
+ break;
+
+ case IllustrationType.UPDATE:
+ animationConfig = LottieAnimationConfig.CONFIG_UPDATE;
+ break;
+
+ case IllustrationType.FINAL_HOLD:
+ animationConfig = LottieAnimationConfig.CONFIG_FINAL_HOLD;
+ break;
+
+ default:
+ animationConfig = LottieAnimationConfig.CONFIG_DEFAULT;
+ break;
+ }
+
+ updateAnimationView();
+ }
+
+ // TODO: [GlifLoadingLayout] Should add testcase. LottieAnimationView was auto
+ // generated not able to mock. So we have no idea how to detected is the api pass to
+ // LottiAnimationView correctly.
+ public boolean setAnimation(InputStream inputStream, String keyCache) {
+ LottieAnimationView lottieAnimationView = findLottieAnimationView();
+ if (lottieAnimationView != null) {
+ lottieAnimationView.setAnimation(inputStream, keyCache);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean setAnimation(String assetName) {
+ LottieAnimationView lottieAnimationView = findLottieAnimationView();
+ if (lottieAnimationView != null) {
+ lottieAnimationView.setAnimation(assetName);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean setAnimation(@RawRes int rawRes) {
+ LottieAnimationView lottieAnimationView = findLottieAnimationView();
+ if (lottieAnimationView != null) {
+ lottieAnimationView.setAnimation(rawRes);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void updateAnimationView() {
+ if (BuildCompatUtils.isAtLeastS()) {
+ setLottieResource();
+ } else {
+ setIllustrationResource();
+ }
+ }
+
+ /**
+ * Call this when your activity is done and should be closed. The activity will be finished while
+ * animation finished.
+ */
+ public void finish(@NonNull Activity activity) {
+ if (activity == null) {
+ throw new NullPointerException("activity should not be null");
+ }
+ registerAnimationFinishRunnable(activity::finish, /* allowFinishWithMaximumDuration= */ true);
+ }
+
+ /**
+ * Launch a new activity after the animation finished.
+ *
+ * @param activity The activity which is GlifLoadingLayout attached to.
+ * @param intent The intent to start.
+ * @param options Additional options for how the Activity should be started. See {@link
+ * android.content.Context#startActivity(Intent, Bundle)} for more details.
+ * @param finish Finish the activity after startActivity
+ * @see Activity#startActivity(Intent)
+ * @see Activity#startActivityForResult
+ */
+ public void startActivity(
+ @NonNull Activity activity,
+ @NonNull Intent intent,
+ @Nullable Bundle options,
+ boolean finish) {
+ if (activity == null) {
+ throw new NullPointerException("activity should not be null");
+ }
+
+ if (intent == null) {
+ throw new NullPointerException("intent should not be null");
+ }
+
+ registerAnimationFinishRunnable(
+ () -> {
+ if (options == null || Build.VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN) {
+ activity.startActivity(intent);
+ } else {
+ activity.startActivity(intent, options);
+ }
+
+ if (finish) {
+ activity.finish();
+ }
+ },
+ /* allowFinishWithMaximumDuration= */ true);
+ }
+
+ /**
+ * Waiting for the animation finished and launch an activity for which you would like a result
+ * when it finished.
+ *
+ * @param activity The activity which the GlifLoadingLayout attached to.
+ * @param intent The intent to start.
+ * @param requestCode If >= 0, this code will be returned in onActivityResult() when the activity
+ * exits.
+ * @param options Additional options for how the Activity should be started.
+ * @param finish Finish the activity after startActivityForResult. The onActivityResult might not
+ * be called because the activity already finished.
+ * <p>See {@link android.content.Context#startActivity(Intent, Bundle)}
+ * Context.startActivity(Intent, Bundle)} for more details.
+ */
+ public void startActivityForResult(
+ @NonNull Activity activity,
+ @NonNull Intent intent,
+ int requestCode,
+ @Nullable Bundle options,
+ boolean finish) {
+ if (activity == null) {
+ throw new NullPointerException("activity should not be null");
+ }
+
+ if (intent == null) {
+ throw new NullPointerException("intent should not be null");
+ }
+
+ registerAnimationFinishRunnable(
+ () -> {
+ if (options == null || Build.VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN) {
+ activity.startActivityForResult(intent, requestCode);
+ } else {
+ activity.startActivityForResult(intent, requestCode, options);
+ }
+
+ if (finish) {
+ activity.finish();
+ }
+ },
+ /* allowFinishWithMaximumDuration= */ true);
+ }
+
+ private void updateHeaderHeight() {
+ View headerView = findManagedViewById(R.id.sud_header_scroll_view);
+ if (headerView != null
+ && PartnerConfigHelper.get(getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_HEADER_HEIGHT)) {
+ float configHeaderHeight =
+ PartnerConfigHelper.get(getContext())
+ .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_HEADER_HEIGHT);
+ headerView.getLayoutParams().height = (int) configHeaderHeight;
+ }
+ }
+
+ private void updateContentPadding(LinearLayout linearLayout) {
+ int paddingTop = linearLayout.getPaddingTop();
+ int paddingLeft = linearLayout.getPaddingLeft();
+ int paddingRight = linearLayout.getPaddingRight();
+ int paddingBottom = linearLayout.getPaddingBottom();
+
+ if (PartnerConfigHelper.get(getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_TOP)) {
+ float configPaddingTop =
+ PartnerConfigHelper.get(getContext())
+ .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_TOP);
+ if (configPaddingTop >= 0) {
+ paddingTop = (int) configPaddingTop;
+ }
+ }
+
+ if (PartnerConfigHelper.get(getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_START)) {
+ float configPaddingLeft =
+ PartnerConfigHelper.get(getContext())
+ .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_START);
+ if (configPaddingLeft >= 0) {
+ paddingLeft = (int) configPaddingLeft;
+ }
+ }
+
+ if (PartnerConfigHelper.get(getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_END)) {
+ float configPaddingRight =
+ PartnerConfigHelper.get(getContext())
+ .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_END);
+ if (configPaddingRight >= 0) {
+ paddingRight = (int) configPaddingRight;
+ }
+ }
+
+ if (PartnerConfigHelper.get(getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_BOTTOM)) {
+ float configPaddingBottom =
+ PartnerConfigHelper.get(getContext())
+ .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_BOTTOM);
+ if (configPaddingBottom >= 0) {
+ FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class);
+ if (footerBarMixin == null || footerBarMixin.getButtonContainer() == null) {
+ paddingBottom = (int) configPaddingBottom;
+ } else {
+ paddingBottom =
+ (int) configPaddingBottom
+ - (int)
+ Math.min(
+ configPaddingBottom,
+ getButtonContainerHeight(footerBarMixin.getButtonContainer()));
+ }
+ }
+ }
+
+ linearLayout.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+ }
+
+ private static final int getButtonContainerHeight(View view) {
+ view.measure(
+ MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(), MeasureSpec.EXACTLY));
+ return view.getMeasuredHeight();
+ }
+
+ private void inflateLottieView() {
+ final View lottieLayout = peekLottieLayout();
+ if (lottieLayout == null) {
+ ViewStub viewStub = findManagedViewById(R.id.sud_loading_layout_lottie_stub);
+ if (viewStub != null) {
+ inflatedView = viewStub.inflate();
+ if (inflatedView instanceof LinearLayout) {
+ updateContentPadding((LinearLayout) inflatedView);
+ }
+ setLottieResource();
+ }
+ }
+ }
+
+ private void inflateIllustrationStub() {
+ final View progressLayout = peekProgressIllustrationLayout();
+ if (progressLayout == null) {
+ ViewStub viewStub = findManagedViewById(R.id.sud_loading_layout_illustration_stub);
+ if (viewStub != null) {
+ inflatedView = viewStub.inflate();
+ if (inflatedView instanceof LinearLayout) {
+ updateContentPadding((LinearLayout) inflatedView);
+ }
+ setIllustrationResource();
+ }
+ }
+ }
+
+ private void setLottieResource() {
+ LottieAnimationView lottieView = findViewById(R.id.sud_lottie_view);
+ if (lottieView == null) {
+ Log.w(TAG, "Lottie view not found, skip set resource. Wait for layout inflated.");
+ return;
+ }
+ if (customLottieResource != 0) {
+ InputStream inputRaw = getResources().openRawResource(customLottieResource);
+ lottieView.setAnimation(inputRaw, null);
+ lottieView.playAnimation();
+ } else {
+ PartnerConfigHelper partnerConfigHelper = PartnerConfigHelper.get(getContext());
+ ResourceEntry resourceEntry =
+ partnerConfigHelper.getIllustrationResourceEntry(
+ getContext(), animationConfig.getLottieConfig());
+
+ if (resourceEntry != null) {
+ InputStream inputRaw =
+ resourceEntry.getResources().openRawResource(resourceEntry.getResourceId());
+ lottieView.setAnimation(inputRaw, null);
+ lottieView.playAnimation();
+ setLottieLayoutVisibility(View.VISIBLE);
+ setIllustrationLayoutVisibility(View.GONE);
+ applyThemeCustomization();
+ } else {
+ setLottieLayoutVisibility(View.GONE);
+ setIllustrationLayoutVisibility(View.VISIBLE);
+ inflateIllustrationStub();
+ }
+ }
+ }
+
+ private void setIllustrationLayoutVisibility(int visibility) {
+ View illustrationLayout = findViewById(R.id.sud_layout_progress_illustration);
+ if (illustrationLayout != null) {
+ illustrationLayout.setVisibility(visibility);
+ }
+ }
+
+ private void setLottieLayoutVisibility(int visibility) {
+ View lottieLayout = findViewById(R.id.sud_layout_lottie_illustration);
+ if (lottieLayout != null) {
+ lottieLayout.setVisibility(visibility);
+ }
+ }
+
+ @VisibleForTesting
+ boolean isLottieLayoutVisible() {
+ View lottieLayout = findViewById(R.id.sud_layout_lottie_illustration);
+ return lottieLayout != null && lottieLayout.getVisibility() == View.VISIBLE;
+ }
+
+ private void setIllustrationResource() {
+ View illustrationLayout = findViewById(R.id.sud_layout_progress_illustration);
+ if (illustrationLayout == null) {
+ Log.i(TAG, "Illustration stub not inflated, skip set resource");
+ return;
+ }
+
+ IllustrationVideoView illustrationVideoView =
+ findManagedViewById(R.id.sud_progress_illustration);
+ ProgressBar progressBar = findManagedViewById(R.id.sud_progress_bar);
+
+ PartnerConfigHelper partnerConfigHelper = PartnerConfigHelper.get(getContext());
+ ResourceEntry resourceEntry =
+ partnerConfigHelper.getIllustrationResourceEntry(
+ getContext(), animationConfig.getIllustrationConfig());
+
+ if (resourceEntry != null) {
+ progressBar.setVisibility(GONE);
+ illustrationVideoView.setVisibility(VISIBLE);
+ illustrationVideoView.setVideoResourceEntry(resourceEntry);
+ } else {
+ progressBar.setVisibility(VISIBLE);
+ illustrationVideoView.setVisibility(GONE);
+ }
+ }
+
+ private LottieAnimationView findLottieAnimationView() {
+ return findViewById(R.id.sud_lottie_view);
+ }
+
+ private IllustrationVideoView findIllustrationVideoView() {
+ return findManagedViewById(R.id.sud_progress_illustration);
+ }
+
+ public void playAnimation() {
+ LottieAnimationView lottieAnimationView = findLottieAnimationView();
+ if (lottieAnimationView != null) {
+ lottieAnimationView.setRepeatCount(LottieDrawable.INFINITE);
+ lottieAnimationView.playAnimation();
+ }
+ }
+
+ /** Returns whether the layout is waiting for animation finish or not. */
+ public boolean isFinishing() {
+ LottieAnimationView lottieAnimationView = findLottieAnimationView();
+ if (lottieAnimationView != null) {
+ return !animationFinishListeners.isEmpty() && lottieAnimationView.getRepeatCount() == 0;
+ } else {
+ return false;
+ }
+ }
+
+ @AnimationType
+ public int getAnimationType() {
+ if (findLottieAnimationView() != null && isLottieLayoutVisible()) {
+ return AnimationType.LOTTIE;
+ } else if (findIllustrationVideoView() != null) {
+ return AnimationType.ILLUSTRATION;
+ } else {
+ return AnimationType.PROGRESS_BAR;
+ }
+ }
+
+ // TODO: Should add testcase with mocked LottieAnimationView.
+ /** Add an animator listener to {@link LottieAnimationView}. */
+ public void addAnimatorListener(Animator.AnimatorListener listener) {
+ LottieAnimationView animationView = findLottieAnimationView();
+ if (animationView != null) {
+ animationView.addAnimatorListener(listener);
+ }
+ }
+
+ /** Remove the listener from {@link LottieAnimationView}. */
+ public void removeAnimatorListener(AnimatorListener listener) {
+ LottieAnimationView animationView = findLottieAnimationView();
+ if (animationView != null) {
+ animationView.removeAnimatorListener(listener);
+ }
+ }
+
+ /** Remove all {@link AnimatorListener} from {@link LottieAnimationView}. */
+ public void removeAllAnimatorListener() {
+ LottieAnimationView animationView = findLottieAnimationView();
+ if (animationView != null) {
+ animationView.removeAllAnimatorListeners();
+ }
+ }
+
+ /** Add a value callback with property {@link LottieProperty.COLOR_FILTER}. */
+ public void addColorCallback(KeyPath keyPath, LottieValueCallback<ColorFilter> callback) {
+ LottieAnimationView animationView = findLottieAnimationView();
+ if (animationView != null) {
+ animationView.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback);
+ }
+ }
+
+ /** Add a simple value callback with property {@link LottieProperty.COLOR_FILTER}. */
+ public void addColorCallback(KeyPath keyPath, SimpleLottieValueCallback<ColorFilter> callback) {
+ LottieAnimationView animationView = findLottieAnimationView();
+ if (animationView != null) {
+ animationView.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback);
+ }
+ }
+
+ @VisibleForTesting
+ protected void loadCustomization() {
+ if (customizationMap.isEmpty()) {
+ PartnerConfigHelper helper = PartnerConfigHelper.get(getContext());
+ List<String> lists =
+ helper.getStringArray(
+ getContext(),
+ isNightMode(getResources().getConfiguration())
+ ? animationConfig.getDarkThemeCustomization()
+ : animationConfig.getLightThemeCustomization());
+ for (String item : lists) {
+ String[] splitItem = item.split(":");
+ if (splitItem.length == 2) {
+ customizationMap.put(
+ new KeyPath(splitItem[0]), new SimpleColorFilter(Color.parseColor(splitItem[1])));
+ } else {
+ Log.w(TAG, "incorrect format customization, value=" + item);
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ protected void applyThemeCustomization() {
+ LottieAnimationView animationView = findLottieAnimationView();
+ if (animationView != null) {
+ loadCustomization();
+ for (KeyPath keyPath : customizationMap.keySet()) {
+ animationView.addValueCallback(
+ keyPath,
+ LottieProperty.COLOR_FILTER,
+ new LottieValueCallback<>(customizationMap.get(keyPath)));
+ }
+ }
+ }
+
+ @Nullable
+ private View peekLottieLayout() {
+ return findViewById(R.id.sud_layout_lottie_illustration);
+ }
+
+ @Nullable
+ private View peekProgressIllustrationLayout() {
+ return findViewById(R.id.sud_layout_progress_illustration);
+ }
+
+ @Override
+ protected View onInflateTemplate(LayoutInflater inflater, int template) {
+ if (template == 0) {
+ template = R.layout.sud_glif_loading_template;
+ }
+ return inflateTemplate(inflater, R.style.SudThemeGlif_Light, template);
+ }
+
+ @Override
+ protected ViewGroup findContainer(int containerId) {
+ if (containerId == 0) {
+ containerId = R.id.sud_layout_content;
+ }
+ return super.findContainer(containerId);
+ }
+
+ /** The progress config used to maps to different animation */
+ public enum LottieAnimationConfig {
+ CONFIG_DEFAULT(
+ PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_DEFAULT,
+ PartnerConfig.CONFIG_LOADING_LOTTIE_DEFAULT,
+ PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_DEFAULT,
+ PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_DEFAULT),
+ CONFIG_ACCOUNT(
+ PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_ACCOUNT,
+ PartnerConfig.CONFIG_LOADING_LOTTIE_ACCOUNT,
+ PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_ACCOUNT,
+ PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_ACCOUNT),
+ CONFIG_CONNECTION(
+ PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_CONNECTION,
+ PartnerConfig.CONFIG_LOADING_LOTTIE_CONNECTION,
+ PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_CONNECTION,
+ PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_CONNECTION),
+ CONFIG_UPDATE(
+ PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_UPDATE,
+ PartnerConfig.CONFIG_LOADING_LOTTIE_UPDATE,
+ PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_UPDATE,
+ PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_UPDATE),
+ CONFIG_FINAL_HOLD(
+ PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_FINAL_HOLD,
+ PartnerConfig.CONFIG_LOADING_LOTTIE_FINAL_HOLD,
+ PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_FINAL_HOLD,
+ PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_FINAL_HOLD);
+
+ private final PartnerConfig illustrationConfig;
+ private final PartnerConfig lottieConfig;
+ private final PartnerConfig lightThemeCustomization;
+ private final PartnerConfig darkThemeCustomization;
+
+ LottieAnimationConfig(
+ PartnerConfig illustrationConfig,
+ PartnerConfig lottieConfig,
+ PartnerConfig lightThemeCustomization,
+ PartnerConfig darkThemeCustomization) {
+ if (illustrationConfig.getResourceType() != ResourceType.ILLUSTRATION
+ || lottieConfig.getResourceType() != ResourceType.ILLUSTRATION) {
+ throw new IllegalArgumentException(
+ "Illustration progress only allow illustration resource");
+ }
+ this.illustrationConfig = illustrationConfig;
+ this.lottieConfig = lottieConfig;
+ this.lightThemeCustomization = lightThemeCustomization;
+ this.darkThemeCustomization = darkThemeCustomization;
+ }
+
+ PartnerConfig getIllustrationConfig() {
+ return illustrationConfig;
+ }
+
+ PartnerConfig getLottieConfig() {
+ return lottieConfig;
+ }
+
+ PartnerConfig getLightThemeCustomization() {
+ return lightThemeCustomization;
+ }
+
+ PartnerConfig getDarkThemeCustomization() {
+ return darkThemeCustomization;
+ }
+ }
+
+ /**
+ * Register the {@link Runnable} as a callback class that will be perform when animation finished.
+ */
+ public void registerAnimationFinishRunnable(Runnable runnable) {
+ registerAnimationFinishRunnable(runnable, /* allowFinishWithMaximumDuration= */ false);
+ }
+
+ /**
+ * Register the {@link Runnable} as a callback class that will be perform when animation finished.
+ * {@code allowFinishWithMaximumDuration} to allow the animation finish advanced by {@link
+ * PartnerConfig#CONFIG_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS} config. The {@code runnable}
+ * will be performed if the Lottie animation finish played and the duration of Lottie animation
+ * less than @link PartnerConfig#CONFIG_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS} config.
+ */
+ public void registerAnimationFinishRunnable(
+ Runnable runnable, boolean allowFinishWithMaximumDuration) {
+ if (allowFinishWithMaximumDuration) {
+ int delayMs =
+ PartnerConfigHelper.get(getContext())
+ .getInteger(
+ getContext(), PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS, 0);
+ animationFinishListeners.add(new LottieAnimationFinishListener(this, runnable, delayMs));
+ } else {
+ animationFinishListeners.add(
+ new LottieAnimationFinishListener(this, runnable, /* finishWithMinimumDuration= */ 0L));
+ }
+ }
+
+ /** The listener that to indicate the playing status for lottie animation. */
+ @VisibleForTesting
+ public static class LottieAnimationFinishListener {
+
+ private final Handler handler;
+ private final Runnable runnable;
+ private final GlifLoadingLayout glifLoadingLayout;
+ private final LottieAnimationView lottieAnimationView;
+
+ @VisibleForTesting
+ AnimatorListener animatorListener =
+ new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onAnimationFinished();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // Do nothing.
+ }
+ };
+
+ @VisibleForTesting
+ LottieAnimationFinishListener(
+ GlifLoadingLayout glifLoadingLayout, Runnable runnable, long finishWithMinimumDuration) {
+ if (runnable == null) {
+ throw new NullPointerException("Runnable can not be null");
+ }
+ this.glifLoadingLayout = glifLoadingLayout;
+ this.runnable = runnable;
+ this.handler = new Handler(Looper.getMainLooper());
+ this.lottieAnimationView = glifLoadingLayout.findLottieAnimationView();
+
+ if (glifLoadingLayout.isLottieLayoutVisible() && !isZeroAnimatorDurationScale()) {
+ lottieAnimationView.setRepeatCount(0);
+ lottieAnimationView.addAnimatorListener(animatorListener);
+ if (finishWithMinimumDuration > 0) {
+ handler.postDelayed(this::onAnimationFinished, finishWithMinimumDuration);
+ }
+ } else {
+ onAnimationFinished();
+ }
+ }
+
+ @VisibleForTesting
+ boolean isZeroAnimatorDurationScale() {
+ try {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+ return Global.getFloat(
+ glifLoadingLayout.getContext().getContentResolver(), Global.ANIMATOR_DURATION_SCALE)
+ == 0f;
+ } else {
+ return false;
+ }
+
+ } catch (SettingNotFoundException e) {
+ return false;
+ }
+ }
+
+ @VisibleForTesting
+ public void onAnimationFinished() {
+ handler.removeCallbacks(runnable);
+ runnable.run();
+ if (lottieAnimationView != null) {
+ lottieAnimationView.removeAnimatorListener(animatorListener);
+ }
+ glifLoadingLayout.animationFinishListeners.remove(this);
+ }
+ }
+
+ /** Annotates the state for the illustration. */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ IllustrationType.ACCOUNT,
+ IllustrationType.CONNECTION,
+ IllustrationType.DEFAULT,
+ IllustrationType.UPDATE,
+ IllustrationType.FINAL_HOLD
+ })
+ public @interface IllustrationType {
+ String DEFAULT = "default";
+ String ACCOUNT = "account";
+ String CONNECTION = "connection";
+ String UPDATE = "update";
+ String FINAL_HOLD = "final_hold";
+ }
+
+ /** Annotates the type for the illustration. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({AnimationType.LOTTIE, AnimationType.ILLUSTRATION, AnimationType.PROGRESS_BAR})
+ public @interface AnimationType {
+ int LOTTIE = 1;
+ int ILLUSTRATION = 2;
+ int PROGRESS_BAR = 3;
+ }
+}
diff --git a/lottie_loading_layout/src/com/google/android/setupdesign/LoadingFooterBarMixin.java b/lottie_loading_layout/src/com/google/android/setupdesign/LoadingFooterBarMixin.java
new file mode 100644
index 0000000..fcc2fff
--- /dev/null
+++ b/lottie_loading_layout/src/com/google/android/setupdesign/LoadingFooterBarMixin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.google.android.setupdesign;
+
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.template.FooterBarMixin;
+
+/** A {@link Mixin} to get the container of footer bar for usage. */
+public class LoadingFooterBarMixin extends FooterBarMixin {
+
+ /**
+ * Creates a mixin for managing buttons on the footer.
+ *
+ * @param layout The {@link TemplateLayout} containing this mixin.
+ * @param attrs XML attributes given to the layout.
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+ */
+ public LoadingFooterBarMixin(
+ TemplateLayout layout, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(layout, attrs, defStyleAttr);
+ }
+
+ @Override
+ public LinearLayout getButtonContainer() {
+ return super.getButtonContainer();
+ }
+}
diff --git a/main/AndroidManifest.xml b/main/AndroidManifest.xml
index 245db97..984a73b 100644
--- a/main/AndroidManifest.xml
+++ b/main/AndroidManifest.xml
@@ -15,7 +15,15 @@
limitations under the License.
-->
-<manifest package="com.google.android.setupdesign">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.google.android.setupdesign">
<!-- Set in the BUILD or gradle file -->
<uses-sdk />
+ <!-- after SDK 30, package need to declare its visible packages. -->
+ <queries tools:node="merge">
+ <intent>
+ <action android:name="com.android.setupwizard.action.PARTNER_CUSTOMIZATION" />
+ </intent>
+ </queries>
</manifest>
diff --git a/main/res/anim-v31/sud_interpolator.xml b/main/res/anim-v31/sud_interpolator.xml
new file mode 100644
index 0000000..bec77f0
--- /dev/null
+++ b/main/res/anim-v31/sud_interpolator.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1" />
diff --git a/main/res/anim-v31/sud_slide_back_in.xml b/main/res/anim-v31/sud_slide_back_in.xml
new file mode 100644
index 0000000..77d4cd5
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_back_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/sudTransitionDuration"
+ android:fromXDelta="-100%"
+ android:toXDelta="0%"
+ android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim-v31/sud_slide_back_out.xml b/main/res/anim-v31/sud_slide_back_out.xml
new file mode 100644
index 0000000..8ae16d4
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_back_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/sudTransitionDuration"
+ android:fromXDelta="0%"
+ android:toXDelta="100%"
+ android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim-v31/sud_slide_next_in.xml b/main/res/anim-v31/sud_slide_next_in.xml
new file mode 100644
index 0000000..006d9b4
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_next_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/sudTransitionDuration"
+ android:fromXDelta="100%"
+ android:toXDelta="0%"
+ android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim-v31/sud_slide_next_out.xml b/main/res/anim-v31/sud_slide_next_out.xml
new file mode 100644
index 0000000..341bbad
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_next_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@integer/sudTransitionDuration"
+ android:fromXDelta="0%"
+ android:toXDelta="-100%"
+ android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim/sud_pre_p_activity_close_enter.xml b/main/res/anim/sud_pre_p_activity_close_enter.xml
new file mode 100644
index 0000000..b8a7654
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_close_enter.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:zAdjustment="normal">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="250"/>
+</set>
diff --git a/main/res/anim/sud_pre_p_activity_close_exit.xml b/main/res/anim/sud_pre_p_activity_close_exit.xml
new file mode 100644
index 0000000..c813937
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_close_exit.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false" android:zAdjustment="top">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@android:interpolator/linear"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="100"
+ android:duration="150"/>
+ <translate android:fromYDelta="0%" android:toYDelta="8%"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/sud_accelerate_quart"
+ android:duration="250"/>
+</set>
diff --git a/main/res/anim/sud_pre_p_activity_open_enter.xml b/main/res/anim/sud_pre_p_activity_open_enter.xml
new file mode 100644
index 0000000..63acc28
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_open_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:interpolator="@interpolator/sud_decelerate_quart"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="200"/>
+ <translate android:fromYDelta="8%" android:toYDelta="0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/decelerate_quint"
+ android:duration="350"/>
+</set>
diff --git a/main/res/anim/sud_pre_p_activity_open_exit.xml b/main/res/anim/sud_pre_p_activity_open_exit.xml
new file mode 100644
index 0000000..950e32c
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_open_exit.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="normal">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="217"/>
+</set>
diff --git a/main/res/anim/sud_stay.xml b/main/res/anim/sud_stay.xml
new file mode 100644
index 0000000..285e7af
--- /dev/null
+++ b/main/res/anim/sud_stay.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<!-- This is needed to keep the exiting window shown while the entering window fades in -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="@android:integer/config_longAnimTime"
+ android:fromAlpha="1.0"
+ android:interpolator="@android:interpolator/decelerate_quad"
+ android:toAlpha="1.0" />
diff --git a/main/res/color/sud_switch_track_on.xml b/main/res/color/sud_switch_track_on.xml
new file mode 100644
index 0000000..a95a1ee
--- /dev/null
+++ b/main/res/color/sud_switch_track_on.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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"
+ xmlns:tools="http://schemas.android.com/tools">
+ <item android:state_enabled="false"
+ android:color="?android:attr/colorForeground"
+ android:alpha="?android:attr/disabledAlpha" />
+ <item android:state_checked="true"
+ android:color="?android:attr/colorControlActivated" tools:ignore="NewApi"
+ android:alpha="?android:attr/disabledAlpha" />
+ <item android:color="?android:attr/colorForeground" />
+</selector>
diff --git a/main/res/drawable-v21/sud_switch_thumb_on.xml b/main/res/drawable-v21/sud_switch_thumb_on.xml
new file mode 100644
index 0000000..224f181
--- /dev/null
+++ b/main/res/drawable-v21/sud_switch_thumb_on.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/sud_switch_thumb_margin"
+ android:left="@dimen/sud_switch_thumb_margin"
+ android:right="@dimen/sud_switch_thumb_margin"
+ android:bottom="@dimen/sud_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/sud_switch_thumb_size"
+ android:width="@dimen/sud_switch_thumb_size" />
+ <solid android:color="?android:attr/colorControlActivated" />
+ </shape>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/main/res/drawable/sud_dialog_background_dark.xml b/main/res/drawable/sud_dialog_background_dark.xml
new file mode 100644
index 0000000..914cfec
--- /dev/null
+++ b/main/res/drawable/sud_dialog_background_dark.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<!-- The purpose of this file is to set the radius for datetime dialog which is
+ copy from abc_dialog_material_background.xml of
+ Base.V7.Theme.AppCompat.Light.Dialog. -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ android:insetLeft="16dp"
+ android:insetTop="16dp"
+ android:insetRight="16dp"
+ android:insetBottom="16dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="?attr/dialogCornerRadius" />
+ <solid android:color="@color/sud_glif_window_bg_dark_color" />
+ </shape>
+</inset>
diff --git a/main/res/drawable/sud_dialog_background_light.xml b/main/res/drawable/sud_dialog_background_light.xml
new file mode 100644
index 0000000..0302783
--- /dev/null
+++ b/main/res/drawable/sud_dialog_background_light.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<!-- The purpose of this file is to set the radius for datetime dialog which is
+ copy from abc_dialog_material_background.xml of
+ Base.V7.Theme.AppCompat.Light.Dialog. -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ android:insetLeft="16dp"
+ android:insetTop="16dp"
+ android:insetRight="16dp"
+ android:insetBottom="16dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="?attr/dialogCornerRadius" />
+ <solid android:color="@color/sud_glif_window_bg_light_color" />
+ </shape>
+</inset>
diff --git a/main/res/drawable/sud_scroll_bar_dark.xml b/main/res/drawable/sud_scroll_bar_dark.xml
new file mode 100644
index 0000000..fbfce55
--- /dev/null
+++ b/main/res/drawable/sud_scroll_bar_dark.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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"
+ android:shape="rectangle">
+ <solid android:color="#33FFFFFF" />
+ <corners android:radius="2dp" />
+</shape>
diff --git a/main/res/drawable/sud_scroll_bar_light.xml b/main/res/drawable/sud_scroll_bar_light.xml
new file mode 100644
index 0000000..ad32634
--- /dev/null
+++ b/main/res/drawable/sud_scroll_bar_light.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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"
+ android:shape="rectangle">
+ <solid android:color="#33000000" />
+ <corners android:radius="2dp" />
+</shape>
diff --git a/main/res/drawable/sud_switch_thumb_off.xml b/main/res/drawable/sud_switch_thumb_off.xml
new file mode 100644
index 0000000..6b4f115
--- /dev/null
+++ b/main/res/drawable/sud_switch_thumb_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/sud_switch_thumb_margin"
+ android:left="@dimen/sud_switch_thumb_margin"
+ android:right="@dimen/sud_switch_thumb_margin"
+ android:bottom="@dimen/sud_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/sud_switch_thumb_size"
+ android:width="@dimen/sud_switch_thumb_size" />
+ <solid android:color="@color/sud_switch_thumb_off" />
+ </shape>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/main/res/drawable/sud_switch_thumb_on.xml b/main/res/drawable/sud_switch_thumb_on.xml
new file mode 100644
index 0000000..cc1cf06
--- /dev/null
+++ b/main/res/drawable/sud_switch_thumb_on.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+ <item
+ android:top="@dimen/sud_switch_thumb_margin"
+ android:left="@dimen/sud_switch_thumb_margin"
+ android:right="@dimen/sud_switch_thumb_margin"
+ android:bottom="@dimen/sud_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/sud_switch_thumb_size"
+ android:width="@dimen/sud_switch_thumb_size" />
+ <solid android:color="?android:attr/colorAccent" tools:ignore="NewApi" />
+ </shape>
+ </item>
+</layer-list>
diff --git a/main/res/drawable/sud_switch_thumb_selector.xml b/main/res/drawable/sud_switch_thumb_selector.xml
new file mode 100644
index 0000000..b30ffb0
--- /dev/null
+++ b/main/res/drawable/sud_switch_thumb_selector.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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:drawable="@drawable/sud_switch_thumb_on" android:state_checked="true" />
+ <item android:drawable="@drawable/sud_switch_thumb_off" android:state_checked="false" />
+</selector> \ No newline at end of file
diff --git a/main/res/drawable/sud_switch_track_off_background.xml b/main/res/drawable/sud_switch_track_off_background.xml
new file mode 100644
index 0000000..d15cb9b
--- /dev/null
+++ b/main/res/drawable/sud_switch_track_off_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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"
+ android:shape="rectangle"
+ android:width="@dimen/sud_switch_track_width"
+ android:height="@dimen/sud_switch_track_height">
+ <solid android:color="@color/sud_switch_track_off" />
+ <corners android:radius="@dimen/sud_switch_track_radius" />
+</shape> \ No newline at end of file
diff --git a/main/res/drawable/sud_switch_track_on_background.xml b/main/res/drawable/sud_switch_track_on_background.xml
new file mode 100644
index 0000000..ff40595
--- /dev/null
+++ b/main/res/drawable/sud_switch_track_on_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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"
+ android:shape="rectangle"
+ android:width="@dimen/sud_switch_track_width"
+ android:height="@dimen/sud_switch_track_height">
+ <solid android:color="@color/sud_switch_track_on" />
+ <corners android:radius="@dimen/sud_switch_track_radius" />
+</shape> \ No newline at end of file
diff --git a/main/res/drawable/sud_switch_track_selector.xml b/main/res/drawable/sud_switch_track_selector.xml
new file mode 100644
index 0000000..11cee5c
--- /dev/null
+++ b/main/res/drawable/sud_switch_track_selector.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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:drawable="@drawable/sud_switch_track_on_background" android:state_checked="true" />
+ <item android:drawable="@drawable/sud_switch_track_off_background" android:state_checked="false" />
+</selector> \ No newline at end of file
diff --git a/main/res/interpolator/sud_accelerate_quart.xml b/main/res/interpolator/sud_accelerate_quart.xml
new file mode 100644
index 0000000..b8a6ebf
--- /dev/null
+++ b/main/res/interpolator/sud_accelerate_quart.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:factor="2.0" />
diff --git a/main/res/interpolator/sud_decelerate_quart.xml b/main/res/interpolator/sud_decelerate_quart.xml
new file mode 100644
index 0000000..06d7fe8
--- /dev/null
+++ b/main/res/interpolator/sud_decelerate_quart.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:factor="2.0" />
diff --git a/main/res/layout-land-v31/sud_glif_blank_template_content.xml b/main/res/layout-land-v31/sud_glif_blank_template_content.xml
new file mode 100644
index 0000000..dd4d52d
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_blank_template_content.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_header_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_content_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/sud_layout_content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_list_template_content.xml b/main/res/layout-land-v31/sud_glif_list_template_content.xml
new file mode 100644
index 0000000..478ac5f
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_list_template_content.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_header_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_header_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators">
+
+ <include layout="@layout/sud_glif_header" />
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_content_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+ android:orientation="vertical">
+
+ <com.google.android.setupdesign.view.StickyHeaderListView
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:scrollIndicators="?attr/sudScrollIndicators" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_preference_recycler_view.xml b/main/res/layout-land-v31/sud_glif_preference_recycler_view.xml
new file mode 100644
index 0000000..53fac8f
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_preference_recycler_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.google.android.setupdesign.view.HeaderRecyclerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/sud_recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:scrollbars="vertical" />
diff --git a/main/res/layout-land-v31/sud_glif_preference_template_content.xml b/main/res/layout-land-v31/sud_glif_preference_template_content.xml
new file mode 100644
index 0000000..c03d2fe
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_preference_template_content.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_header_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_header_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators">
+
+ <include layout="@layout/sud_glif_header" />
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_content_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/sud_layout_content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_recycler_template_content.xml b/main/res/layout-land-v31/sud_glif_recycler_template_content.xml
new file mode 100644
index 0000000..4840caf
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_recycler_template_content.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_header_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_header_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators">
+
+ <include layout="@layout/sud_glif_header" />
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_content_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+ android:orientation="vertical">
+
+ <com.google.android.setupdesign.view.HeaderRecyclerView
+ android:id="@+id/sud_recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:scrollIndicators="?attr/sudScrollIndicators" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_template_content.xml b/main/res/layout-land-v31/sud_glif_template_content.xml
new file mode 100644
index 0000000..d1b6b92
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_template_content.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_header_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+ android:orientation="vertical">
+
+ <ViewStub
+ android:id="@+id/sud_layout_sticky_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_header_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators">
+
+ <include layout="@layout/sud_glif_header" />
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sud_landscape_content_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+ android:orientation="vertical">
+
+ <com.google.android.setupdesign.view.BottomScrollView
+ android:id="@+id/sud_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollIndicators="?attr/sudScrollIndicators">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/sud_layout_content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <ViewStub
+ android:id="@+id/sud_layout_illustration_progress_stub"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inflatedId="@+id/sud_layout_progress_illustration"
+ android:layout="@layout/sud_progress_illustration_layout" />
+ </LinearLayout>
+
+ </com.google.android.setupdesign.view.BottomScrollView>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ViewStub
+ android:id="@+id/suc_layout_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-v31/sud_glif_preference_template_compact.xml b/main/res/layout-v31/sud_glif_preference_template_compact.xml
new file mode 100644
index 0000000..a111e70
--- /dev/null
+++ b/main/res/layout-v31/sud_glif_preference_template_compact.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.google.android.setupcompat.view.StatusBarBackgroundLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/suc_layout_status"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/sud_glif_preference_template_content" />
+
+</com.google.android.setupcompat.view.StatusBarBackgroundLayout> \ No newline at end of file
diff --git a/main/res/layout-v31/sud_glif_preference_template_content.xml b/main/res/layout-v31/sud_glif_preference_template_content.xml
new file mode 100644
index 0000000..c618139
--- /dev/null
+++ b/main/res/layout-v31/sud_glif_preference_template_content.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sud_layout_template_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/sud_glif_blank_template_content" />
+
+</LinearLayout>
diff --git a/main/res/layout/sud_content_info.xml b/main/res/layout/sud_content_info.xml
new file mode 100644
index 0000000..288c3af
--- /dev/null
+++ b/main/res/layout/sud_content_info.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/sud_content_info_container"
+ style="@style/SudInfoContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="false"
+ android:orientation="horizontal">
+
+ <FrameLayout
+ android:id="@+id/sud_content_info_icon_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:gravity="start">
+
+ <ImageView
+ android:id="@+id/sud_content_info_icon"
+ android:layout_width="@dimen/sud_content_info_icon_size"
+ android:layout_height="@dimen/sud_content_info_icon_size"
+ android:layout_marginEnd="@dimen/sud_content_info_icon_margin_end"
+ tools:ignore="ContentDescription" />
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <com.google.android.setupdesign.view.RichTextView
+ android:id="@+id/sud_content_info_description"
+ style="@style/SudInfoDescription"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start"
+ android:textAlignment="viewStart"
+ tools:ignore="UnusedAttribute" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/main/res/layout/sud_glif_blank_template_content.xml b/main/res/layout/sud_glif_blank_template_content.xml
index 1eaae13..887655d 100644
--- a/main/res/layout/sud_glif_blank_template_content.xml
+++ b/main/res/layout/sud_glif_blank_template_content.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sud_layout_template_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
diff --git a/main/res/layout/sud_glif_header.xml b/main/res/layout/sud_glif_header.xml
index f5a1113..50feeb2 100644
--- a/main/res/layout/sud_glif_header.xml
+++ b/main/res/layout/sud_glif_header.xml
@@ -22,13 +22,25 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <ImageView
- android:id="@+id/sud_layout_icon"
- style="?attr/sudGlifIconStyle"
- android:layout_width="wrap_content"
+ <FrameLayout
+ android:id="@+id/sud_layout_icon_container"
+ style="@style/SudGlifIconContainer"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:contentDescription="@null"
- android:visibility="gone" />
+ android:visibility="gone" >
+ <ImageView
+ android:id="@+id/sud_layout_icon"
+ style="?attr/sudGlifIconStyle"
+ android:layout_marginLeft="0dp"
+ android:layout_marginRight="0dp"
+ android:layout_marginTop="0dp"
+ android:layout_marginBottom="0dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@null"
+ android:layout_gravity="?attr/sudGlifHeaderGravity"
+ android:visibility="gone" />
+ </FrameLayout>
<TextView
android:id="@+id/suc_layout_title"
@@ -41,10 +53,29 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/sud_glif_progress_bar_margin_vertical"
- android:layout_marginLeft="?attr/sudMarginSides"
- android:layout_marginRight="?attr/sudMarginSides"
+ android:layout_marginLeft="?attr/sudMarginStart"
+ android:layout_marginRight="?attr/sudMarginStart"
android:layout_marginTop="@dimen/sud_glif_progress_bar_margin_vertical"
android:inflatedId="@+id/sud_layout_progress"
android:layout="@layout/sud_progress_bar" />
+ <com.google.android.setupdesign.view.RichTextView
+ android:id="@+id/sud_layout_subtitle"
+ style="@style/SudGlifDescription"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <ProgressBar
+ android:id="@+id/sud_glif_progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="?attr/sudMarginStart"
+ android:layout_marginRight="?attr/sudMarginStart"
+ android:layout_marginBottom="@dimen/sud_progress_bar_margin_vertical"
+ android:layout_marginTop="@dimen/sud_progress_bar_margin_vertical"
+ android:visibility="gone"
+ android:indeterminate="true" />
+
</LinearLayout>
diff --git a/main/res/layout/sud_glif_list_template_content.xml b/main/res/layout/sud_glif_list_template_content.xml
index 1a6b4cd..09c56d2 100644
--- a/main/res/layout/sud_glif_list_template_content.xml
+++ b/main/res/layout/sud_glif_list_template_content.xml
@@ -18,6 +18,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/sud_layout_template_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
diff --git a/main/res/layout/sud_glif_recycler_template_content.xml b/main/res/layout/sud_glif_recycler_template_content.xml
index 9ca640b..c2cccf0 100644
--- a/main/res/layout/sud_glif_recycler_template_content.xml
+++ b/main/res/layout/sud_glif_recycler_template_content.xml
@@ -19,6 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/sud_layout_template_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
diff --git a/main/res/layout/sud_glif_template_content.xml b/main/res/layout/sud_glif_template_content.xml
index a72861e..fa898eb 100644
--- a/main/res/layout/sud_glif_template_content.xml
+++ b/main/res/layout/sud_glif_template_content.xml
@@ -18,6 +18,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/sud_layout_template_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -45,6 +46,13 @@
<include layout="@layout/sud_glif_header" />
+ <ViewStub
+ android:id="@+id/sud_layout_illustration_progress_stub"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inflatedId="@+id/sud_layout_progress_illustration"
+ android:layout="@layout/sud_progress_illustration_layout" />
+
<FrameLayout
android:id="@+id/sud_layout_content"
android:layout_width="match_parent"
diff --git a/main/res/layout/sud_progress_illustration_layout.xml b/main/res/layout/sud_progress_illustration_layout.xml
new file mode 100644
index 0000000..3b850f4
--- /dev/null
+++ b/main/res/layout/sud_progress_illustration_layout.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/SudContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/sud_layout_description"
+ android:visibility="invisible"
+ style="@style/SudDescription.Glif"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.google.android.setupdesign.view.FillContentLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ProgressBar
+ android:id="@+id/sud_progress_bar"
+ android:visibility="gone"
+ style="@style/SudFourColorIndeterminateProgressBar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <com.google.android.setupdesign.view.IllustrationVideoView
+ android:id="@+id/sud_progress_illustration"
+ android:visibility="gone"
+ style="@style/SudContentIllustration"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </com.google.android.setupdesign.view.FillContentLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/main/res/values-land-v31/dimens.xml b/main/res/values-land-v31/dimens.xml
new file mode 100644
index 0000000..8b8d10d
--- /dev/null
+++ b/main/res/values-land-v31/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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>
+
+ <!-- General -->
+ <dimen name="sud_glif_footer_padding_start">16dp</dimen>
+ <!-- Calculated by (sud_glif_margin_end - 4dp internal padding of button) -->
+ <dimen name="sud_glif_footer_padding_end">28dp</dimen>
+ <dimen name="sud_glif_margin_start">32dp</dimen>
+ <dimen name="sud_glif_margin_end">32dp</dimen>
+
+</resources>
diff --git a/main/res/values-land/dimens.xml b/main/res/values-land/dimens.xml
index 88a97be..49ed20d 100644
--- a/main/res/values-land/dimens.xml
+++ b/main/res/values-land/dimens.xml
@@ -17,6 +17,11 @@
<resources>
+ <!-- General -->
+ <dimen name="sud_glif_button_min_height">36dp</dimen>
+ <dimen name="sud_glif_footer_min_height">52dp</dimen>
+ <dimen name="sud_glif_footer_padding_vertical">0dp</dimen>
+
<!-- Card layout (for tablets) -->
<dimen name="sud_card_title_padding_end">32dp</dimen>
<dimen name="sud_card_title_padding_start">56dp</dimen>
@@ -33,4 +38,11 @@
<!-- Illustration -->
<item name="sud_illustration_aspect_ratio" format="float" type="dimen">0</item>
+ <!-- TODO: Add testcase for testing padding value in landscape mode -->
+ <!-- Loading content styles -->
+ <dimen name="sud_content_loading_frame_padding_top">0dp</dimen>
+ <dimen name="sud_content_loading_frame_padding_start">0dp</dimen>
+ <dimen name="sud_content_loading_frame_padding_end">0dp</dimen>
+ <dimen name="sud_content_loading_frame_padding_bottom">24dp</dimen>
+
</resources>
diff --git a/main/res/values-night-v31/colors.xml b/main/res/values-night-v31/colors.xml
new file mode 100644
index 0000000..f929595
--- /dev/null
+++ b/main/res/values-night-v31/colors.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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>
+ <!-- Accent color -->
+ <color name="sud_dynamic_color_accent_glif_v3">@color/sud_dynamic_color_accent_glif_v3_dark</color>
+
+
+ <color name="sud_system_primary_text">@color/sud_system_neutral1_50</color>
+ <color name="sud_system_secondary_text">@color/sud_system_neutral2_200</color>
+ <color name="sud_system_tertiary_text_inactive">@color/sud_system_neutral2_400</color>
+ <color name="sud_system_error_warning">@color/sud_error_warning_default_dark</color>
+ <color name="sud_system_background_surface">@color/sud_system_neutral1_900</color>
+ <color name="sud_system_accent_icon_text_button">@color/sud_system_accent1_100</color>
+ <color name="sud_system_button_surface">@color/sud_system_accent1_100</color>
+ <color name="sud_system_button_text">@color/sud_system_neutral1_900</color>
+ <color name="sud_system_dividing_line">@color/sud_system_neutral2_300</color>
+
+</resources> \ No newline at end of file
diff --git a/main/res/values-night/colors.xml b/main/res/values-night/colors.xml
new file mode 100644
index 0000000..1062ea7
--- /dev/null
+++ b/main/res/values-night/colors.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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>
+
+ <color name="sud_system_primary_text">@color/sud_primary_default_text_dark</color>
+ <color name="sud_system_secondary_text">@color/sud_secondary_default_text_dark</color>
+ <color name="sud_system_tertiary_text_inactive">@color/sud_inactive_default_dark</color>
+ <color name="sud_system_error_warning">@color/sud_error_warning_default_dark</color>
+ <color name="sud_system_success_done">@color/success_color_device_default_dark</color>
+ <color name="sud_system_fallback_accent">@color/fallback_color_device_default_dark</color>
+
+ <color name="sud_portal_pending_progress">@color/sud_portal_pending_progress_dark</color>
+
+
+ <color name="sud_uniformity_backdrop_color">#2A2B2E</color>
+</resources> \ No newline at end of file
diff --git a/main/res/values-night/styles.xml b/main/res/values-night/styles.xml
index ca94c12..d609b4c 100644
--- a/main/res/values-night/styles.xml
+++ b/main/res/values-night/styles.xml
@@ -22,5 +22,7 @@
<style name="SudThemeGlif.DayNight" parent="SudThemeGlif" />
<style name="SudThemeGlifV2.DayNight" parent="SudThemeGlifV2" />
<style name="SudThemeGlifV3.DayNight" parent="SudThemeGlifV3" />
+ <style name="SudDynamicColorThemeGlifV3.DayNight" parent="SudDynamicColorThemeGlifV3" />
+ <style name="SudFullDynamicColorThemeGlifV3.DayNight" parent="SudFullDynamicColorThemeGlifV3" />
</resources>
diff --git a/main/res/values-sw600dp/dimens.xml b/main/res/values-sw600dp/dimens.xml
index bf9e1e9..b7802b1 100644
--- a/main/res/values-sw600dp/dimens.xml
+++ b/main/res/values-sw600dp/dimens.xml
@@ -18,11 +18,12 @@
<resources>
<!-- General -->
- <!-- Calculated by (sud_glif_margin_sides - 4dp internal padding of button) -->
+ <!-- Calculated by (sud_glif_margin_start - 4dp internal padding of button) -->
<dimen name="sud_glif_button_margin_end">36dp</dimen>
- <!-- Calculated by (sud_glif_margin_sides - sud_glif_button_padding) -->
+ <!-- Calculated by (sud_glif_margin_start - sud_glif_button_padding) -->
<dimen name="sud_glif_button_margin_start">24dp</dimen>
- <dimen name="sud_glif_margin_sides">40dp</dimen>
+ <dimen name="sud_glif_margin_start">40dp</dimen>
+ <dimen name="sud_glif_margin_end">40dp</dimen>
<!-- Decor view (illustration or the header without illustration) -->
<dimen name="sud_decor_padding_top">256dp</dimen>
diff --git a/main/res/values-v21/styles.xml b/main/res/values-v21/styles.xml
index 6a4df1b..c961745 100644
--- a/main/res/values-v21/styles.xml
+++ b/main/res/values-v21/styles.xml
@@ -54,10 +54,10 @@
<style name="SudItemContainer">
<item name="android:minHeight">?android:attr/listPreferredItemHeight</item>
- <item name="android:paddingBottom">@dimen/sud_items_padding_vertical</item>
+ <item name="android:paddingBottom">@dimen/sud_items_padding_bottom</item>
<item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
<item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
- <item name="android:paddingTop">@dimen/sud_items_padding_vertical</item>
+ <item name="android:paddingTop">@dimen/sud_items_padding_top</item>
</style>
<style name="SudItemTitle">
diff --git a/main/res/values-v27/styles.xml b/main/res/values-v27/styles.xml
index d3906b8..c24f538 100644
--- a/main/res/values-v27/styles.xml
+++ b/main/res/values-v27/styles.xml
@@ -27,5 +27,14 @@
<!-- 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="sucLightSystemNavBar" tools:ignore="NewApi">?android:attr/windowLightNavigationBar</item>
+ <item name="sucSystemNavBarDividerColor" tools:ignore="NewApi">?android:attr/navigationBarDividerColor</item>
+ </style>
+
+ <style name="SudThemeGlifV3" parent="SudBaseThemeGlifV3">
+ <item name="android:navigationBarColor">@color/sud_glif_v3_nav_bar_color_dark</item>
+ <item name="android:navigationBarDividerColor" tools:ignore="NewApi">@color/sud_glif_v3_nav_bar_divider_color_dark</item>
+ <item name="android:windowLightNavigationBar" tools:ignore="NewApi">false</item>
+ <item name="sucLightSystemNavBar" tools:ignore="NewApi">?android:attr/windowLightNavigationBar</item>
+ <item name="sucSystemNavBarDividerColor" tools:ignore="NewApi">?android:attr/navigationBarDividerColor</item>
</style>
</resources>
diff --git a/main/res/values-v31/colors.xml b/main/res/values-v31/colors.xml
new file mode 100644
index 0000000..91587ee
--- /dev/null
+++ b/main/res/values-v31/colors.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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>
+ <!-- Default color for BC -->
+
+ <color name="sud_color_accent_glif_v3_dark">#ff669df6</color>
+
+ <color name="sud_color_accent_glif_v3_light">#ff1a73e8</color>
+
+ <!-- Accent color -->
+ <color name="sud_dynamic_color_accent_glif_v3">@color/sud_dynamic_color_accent_glif_v3_light</color>
+ <color name="sud_dynamic_color_accent_glif_v3_dark">@color/sud_system_accent1_100</color>
+ <color name="sud_dynamic_color_accent_glif_v3_light">@color/sud_system_accent1_600</color>
+
+
+ <color name="sud_error_warning_default_dark">#F28B82</color>
+
+ <color name="sud_error_warning_default_light">#EA4335</color>
+
+
+
+ <color name="sud_system_accent1_100">@android:color/system_accent1_100</color>
+
+ <color name="sud_system_accent1_200">@android:color/system_accent1_200</color>
+
+ <color name="sud_system_accent1_600">@android:color/system_accent1_600</color>
+
+
+
+ <color name="sud_system_neutral1_0">@android:color/system_neutral1_0</color>
+
+ <color name="sud_system_neutral1_50">@android:color/system_neutral1_50</color>
+
+ <color name="sud_system_neutral1_800">@android:color/system_neutral1_800</color>
+
+ <color name="sud_system_neutral1_900">@android:color/system_neutral1_900</color>
+
+
+
+ <color name="sud_system_neutral2_50">@android:color/system_neutral1_50</color>
+
+ <color name="sud_system_neutral2_100">@android:color/system_neutral1_100</color>
+
+ <color name="sud_system_neutral2_200">@android:color/system_neutral2_200</color>
+
+ <color name="sud_system_neutral2_300">@android:color/system_accent2_300</color>
+
+ <color name="sud_system_neutral2_400">@android:color/system_neutral2_400</color>
+
+ <color name="sud_system_neutral2_500">@android:color/system_neutral2_500</color>
+
+ <color name="sud_system_neutral2_700">@android:color/system_neutral2_700</color>
+
+
+ <color name="sud_system_primary_text">@color/sud_system_neutral1_900</color>
+ <color name="sud_system_secondary_text">@color/sud_system_neutral2_700</color>
+ <color name="sud_system_tertiary_text_inactive">@color/sud_system_neutral2_500</color>
+ <color name="sud_system_error_warning">@color/sud_error_warning_default_light</color>
+ <color name="sud_system_background_surface">@color/sud_system_neutral1_50</color>
+ <color name="sud_system_accent_icon_text_button">@color/sud_system_accent1_600</color>
+ <color name="sud_system_button_surface">@color/sud_system_accent1_100</color>
+ <color name="sud_system_button_text">@color/sud_system_neutral1_900</color>
+ <color name="sud_system_dividing_line">@color/sud_system_neutral2_300</color>
+
+</resources> \ No newline at end of file
diff --git a/main/res/values-v31/config.xml b/main/res/values-v31/config.xml
new file mode 100644
index 0000000..7367e33
--- /dev/null
+++ b/main/res/values-v31/config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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>
+ <!-- The duration (in milliseconds) of activity transitions -->
+ <integer name="sudTransitionDuration">450</integer>
+
+</resources> \ No newline at end of file
diff --git a/main/res/values-v31/layouts.xml b/main/res/values-v31/layouts.xml
new file mode 100644
index 0000000..e6198e8
--- /dev/null
+++ b/main/res/values-v31/layouts.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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:tools="http://schemas.android.com/tools">
+
+ <item name="sud_glif_preference_template" type="layout">@layout/sud_glif_preference_template_compact</item>
+
+</resources>
diff --git a/main/res/values-v31/styles.xml b/main/res/values-v31/styles.xml
new file mode 100644
index 0000000..e1240e4
--- /dev/null
+++ b/main/res/values-v31/styles.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 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>
+ <!-- Main theme for dynamic color -->
+ <style name="SudDynamicColorThemeGlifV3" parent="SudThemeGlifV3">
+ <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+ <item name="android:textColorLink">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+ <item name="alertDialogTheme">@style/SudDynamicColorAlertDialogThemeCompat</item>
+ <item name="android:datePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme</item>
+ <item name="android:timePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme</item>
+ <item name="sucFullDynamicColor">false</item>
+ </style>
+
+ <style name="SudDynamicColorThemeGlifV3.Light" parent="SudThemeGlifV3.Light">
+ <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_light</item>
+ <item name="android:textColorLink">@color/sud_dynamic_color_accent_glif_v3_light</item>
+ <item name="alertDialogTheme">@style/SudDynamicColorAlertDialogThemeCompat.Light</item>
+ <item name="android:datePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme.Light</item>
+ <item name="android:timePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme.Light</item>
+ <item name="sucFullDynamicColor">false</item>
+ </style>
+
+ <style name="SudFullDynamicColorThemeGlifV3" parent="SudDynamicColorThemeGlifV3">
+ <item name="android:windowBackground">@android:color/system_neutral1_900</item>
+ <item name="android:colorForeground">@android:color/system_neutral1_50</item>
+ <item name="android:colorForegroundInverse">@android:color/system_neutral1_900</item>
+ <item name="android:colorBackground">@android:color/system_neutral1_900</item>
+ <item name="android:colorBackgroundCacheHint">@android:color/system_neutral1_900</item>
+ <item name="colorBackgroundFloating">@android:color/system_neutral1_900</item>
+ <item name="android:navigationBarColor">@android:color/system_neutral1_900</item>
+
+ <item name="android:textColorPrimary">@color/sud_system_primary_text</item>
+ <item name="android:textColorSecondary">@color/sud_system_secondary_text</item>
+ <item name="android:textColorTertiary">@color/sud_system_tertiary_text_inactive</item>
+ <item name="android:textColorPrimaryDisableOnly">@color/sud_system_tertiary_text_inactive</item>
+ <item name="android:textColorPrimaryInverseDisableOnly">@color/sud_system_tertiary_text_inactive</item>
+
+ <item name="colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="colorControlHighlight">@color/ripple_material_dark</item>
+ <item name="colorButtonNormal">@color/button_material_dark</item>
+ <item name="colorSwitchThumbNormal">@color/switch_thumb_material_dark</item>
+
+ <item name="alertDialogTheme">@style/SudFullDynamicColorAlertDialogThemeCompat</item>
+
+ <item name="sucFullDynamicColor">true</item>
+ </style>
+
+ <style name="SudFullDynamicColorThemeGlifV3.Light" parent="SudDynamicColorThemeGlifV3.Light">
+ <item name="android:windowBackground">@android:color/system_neutral1_50</item>
+ <item name="android:colorForeground">@android:color/system_neutral1_900</item>
+ <item name="android:colorForegroundInverse">@android:color/system_neutral1_50</item>
+ <item name="android:colorBackground">@android:color/system_neutral1_50</item>
+ <item name="android:colorBackgroundCacheHint">@@android:color/system_neutral1_50</item>
+ <item name="colorBackgroundFloating">@android:color/system_neutral1_50</item>
+ <item name="android:navigationBarColor">@android:color/system_neutral1_50</item>
+
+ <item name="android:textColorPrimary">@color/sud_system_primary_text</item>
+ <item name="android:textColorSecondary">@color/sud_system_secondary_text</item>
+ <item name="android:textColorTertiary">@color/sud_system_tertiary_text_inactive</item>
+ <item name="android:textColorPrimaryDisableOnly">@color/sud_system_tertiary_text_inactive</item>
+
+ <item name="colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="colorControlHighlight">@color/ripple_material_light</item>
+ <item name="colorButtonNormal">@color/button_material_light</item>
+ <item name="colorSwitchThumbNormal">@color/switch_thumb_material_light</item>
+
+ <item name="alertDialogTheme">@style/SudFullDynamicColorAlertDialogThemeCompat.Light</item>
+
+ <item name="sucFullDynamicColor">true</item>
+ </style>
+
+ <!-- Dynamic color theme for alert dialog -->
+ <style name="SudDynamicColorAlertDialogThemeCompat" parent="Theme.AppCompat.Dialog.Alert">
+ <item name="android:textAllCaps">false</item>
+ <item name="android:colorBackground">@color/sud_glif_v3_dialog_background_color_dark</item>
+ <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+ <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+ </style>
+
+ <style name="SudDynamicColorAlertDialogThemeCompat.Light" parent="Theme.AppCompat.Light.Dialog.Alert">
+ <item name="android:textAllCaps">false</item>
+ <item name="android:colorBackground">@color/sud_glif_v3_dialog_background_color_light</item>
+ <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_light</item>
+ <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+ </style>
+
+ <style name="SudFullDynamicColorAlertDialogThemeCompat" parent="SudDynamicColorAlertDialogThemeCompat">
+ <item name="android:colorBackground">@color/sud_system_neutral1_900</item>
+ </style>
+ <style name="SudFullDynamicColorAlertDialogThemeCompat.Light" parent="SudDynamicColorAlertDialogThemeCompat.Light">
+ <item name="android:colorBackground">@color/sud_system_neutral1_50</item>
+ </style>
+
+ <!-- Dynamic color theme for date time dialog -->
+ <style name="SudDynamicColorDateTimePickerDialogTheme" parent="SudDateTimePickerDialogTheme">
+ <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+ <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+ </style>
+
+ <style name="SudDynamicColorDateTimePickerDialogTheme.Light" parent="SudDateTimePickerDialogTheme.Light">
+ <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_light</item>
+ <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+ </style>
+
+</resources>
diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml
index 95c8bb5..c946912 100644
--- a/main/res/values/attrs.xml
+++ b/main/res/values/attrs.xml
@@ -19,7 +19,8 @@
<!-- Theme attributes -->
<attr name="sudLayoutTheme" format="reference" />
- <attr name="sudMarginSides" format="dimension|reference" />
+ <attr name="sudMarginStart" format="dimension|reference" />
+ <attr name="sudMarginEnd" format="dimension|reference" />
<attr name="sudEditTextBackgroundColor" format="color" />
<attr name="sudButtonHighlightAlpha" format="float" />
@@ -39,7 +40,24 @@
<!-- Push object to the end of its container, not changing its size. -->
<flag name="end" value="0x00800005" />
</attr>
+
+ <attr name="sudGlifSubtitleGravity">
+ <!-- Push object to the left of its container, not changing its size. -->
+ <flag name="left" value="0x03" />
+ <!-- Push object to the right of its container, not changing its size. -->
+ <flag name="right" value="0x05" />
+ <!-- Place object in the horizontal center of its container, not changing its size. -->
+ <flag name="center_horizontal" value="0x01" />
+ <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
+ <flag name="fill_horizontal" value="0x07" />
+ <!-- Push object to the beginning of its container, not changing its size. -->
+ <flag name="start" value="0x00800003" />
+ <!-- Push object to the end of its container, not changing its size. -->
+ <flag name="end" value="0x00800005" />
+ </attr>
+
<attr name="sudGlifIconStyle" format="reference" />
+ <attr name="sudGlifIconSize" format="dimension" />
<attr name="sudButtonAllCaps" format="boolean" />
<attr name="sudButtonCornerRadius" format="dimension" />
@@ -83,6 +101,9 @@
<flag name="end" value="0x20" />
</attr>
+ <!-- Custom the scroll bar indicator -->
+ <attr name="sudScrollBarThumb" format="reference" />
+
<!-- Custom view attributes -->
<attr name="sudColorPrimary" format="color" />
<attr name="sudHeader" format="reference" />
@@ -93,6 +114,8 @@
<attr name="sudDividerInsetStartNoIcon" format="dimension|reference" />
<attr name="sudItemDescriptionStyle" format="reference" />
<attr name="sudItemDescriptionTitleStyle" format="reference" />
+ <attr name="sudContentFramePaddingTop" format="dimension|reference" />
+ <attr name="sudContentFramePaddingBottom" format="dimension|reference" />
<attr name="sudHasStableIds" format="boolean|reference" />
@@ -110,6 +133,7 @@
<declare-styleable name="SudIllustrationVideoView">
<attr name="sudVideo" format="reference" />
+ <attr name="sudPauseVideoWhenFinished" format="boolean" />
</declare-styleable>
<declare-styleable name="SudGlifLayout">
@@ -128,6 +152,10 @@
<attr name="android:width" />
</declare-styleable>
+ <attr name="sudContentIllustrationMaxWidth" format="dimension" />
+ <attr name="sudContentIllustrationMaxHeight" format="dimension" />
+ <attr name="sudContentIllustrationPaddingTop" format="dimension" />
+ <attr name="sudContentIllustrationPaddingBottom" format="dimension" />
<declare-styleable name="SudFillContentLayout">
<attr name="android:maxHeight" />
<attr name="android:maxWidth" />
@@ -211,4 +239,24 @@
<attr name="sudCollapsedSummary" format="string" localization="suggested" />
<attr name="sudExpandedSummary" format="string" localization="suggested" />
</declare-styleable>
+
+ <declare-styleable name="SudDescriptionMixin">
+ <attr name="sudDescriptionText" format="string" localization="suggested" />
+ <attr name="sudDescriptionTextColor" format="reference|color" />
+ <attr name="sudGlifDescriptionMarginTop" format="dimension" />
+ <attr name="sudGlifDescriptionMarginBottom" format="dimension" />
+ </declare-styleable>
+
+ <declare-styleable name="SudProgressBarMixin">
+ <attr name="sudUseBottomProgressBar" format="boolean" />
+ </declare-styleable>
+
+ <declare-styleable name="SudGlifLoadingFramePadding">
+ <attr name="sudLoadingContentFramePaddingTop" format="dimension|reference" />
+ <attr name="sudLoadingContentFramePaddingStart" format="dimension|reference" />
+ <attr name="sudLoadingContentFramePaddingEnd" format="dimension|reference" />
+ <attr name="sudLoadingContentFramePaddingBottom" format="dimension|reference" />
+ </declare-styleable>
+
+ <attr name="sudLoadingHeaderHeight" format="dimension|reference" />
</resources>
diff --git a/main/res/values/colors.xml b/main/res/values/colors.xml
index a386eb5..798abc9 100644
--- a/main/res/values/colors.xml
+++ b/main/res/values/colors.xml
@@ -18,7 +18,6 @@
<resources>
<!-- General colors -->
-
<color name="sud_color_accent_dark">#ff448aff</color>
<color name="sud_color_accent_light">#ff3367d6</color>
<color name="sud_color_background_dark">#ff303030</color>
@@ -35,25 +34,116 @@
<color name="sud_flat_button_highlight">#1f000000</color>
<!-- Navigation bar colors -->
-
<color name="sud_navbar_bg_dark">#ff21272b</color>
<color name="sud_navbar_bg_light">#ffe4e7e9</color>
<!-- GLIF colors -->
<color name="sud_color_accent_glif_dark">#ff4285f4</color>
<color name="sud_color_accent_glif_light">#ff4285f4</color>
- <color name="sud_color_accent_glif_v3_light">#ff1a73e8</color>
<color name="sud_color_accent_glif_v3_dark">#ff8ab4f8</color>
+ <color name="sud_color_accent_glif_v3_light">#ff1a73e8</color>
<color name="sud_glif_background_color_dark">#ff000000</color>
<color name="sud_glif_background_color_light">#ffffffff</color>
<color name="sud_glif_edit_text_bg_dark_color">#ff202124</color>
<color name="sud_glif_edit_text_bg_light_color">#0a000000</color>
<color name="sud_glif_v3_dialog_background_color_dark">#ff3c4043</color>
+ <color name="sud_glif_v3_dialog_background_color_light">#fff1f3f4</color>
+ <color name="sud_glif_v3_nav_bar_color_dark">#ff000000</color>
<color name="sud_glif_v3_nav_bar_color_light">#ffffffff</color>
+ <color name="sud_glif_v3_nav_bar_divider_color_dark">#00000000</color>
<color name="sud_glif_v3_nav_bar_divider_color_light">#1f000000</color>
+ <color name="sud_glif_v3_text_color_dark">#ffffffff</color>
+ <color name="sud_glif_v3_text_color_light">#de000000</color>
+ <color name="sud_glif_window_bg_dark_color">#ff202124</color>
+ <color name="sud_glif_window_bg_light_color">#ffffffff</color>
<!-- Color for error text -->
- <color name="sud_color_error_text_light">#ffd93025</color> <!-- Google red 600 -->
- <color name="sud_color_error_text_dark">#fff28b82</color> <!-- Google red 300 -->
+
+ <color name="sud_color_error_text_dark">#fff28b82</color>
+
+ <color name="sud_color_error_text_light">#ffd93025</color>
+
+
+ <color name="sud_dynamic_color_accent_glif_v3_dark">@color/sud_color_accent_glif_v3_dark</color>
+ <color name="sud_dynamic_color_accent_glif_v3_light">@color/sud_color_accent_glif_v3_light</color>
+
+
+
+ <color name="sud_system_accent1_100">#8DF5E3</color>
+
+ <color name="sud_system_accent1_200">#71D8C7</color>
+
+ <color name="sud_system_accent1_600">#006C5F</color>
+
+
+
+ <color name="sud_system_neutral1_0">#ffffff</color>
+
+ <color name="sud_system_neutral1_50">#f0f0f0</color>
+
+ <color name="sud_system_neutral1_800">#303030</color>
+
+ <color name="sud_system_neutral1_900">#1b1b1b</color>
+
+
+
+ <color name="sud_system_neutral2_50">#f0f0f0</color>
+
+ <color name="sud_system_neutral2_100">#e2e2e2</color>
+
+ <color name="sud_system_neutral2_200">#c6c6c6</color>
+
+ <color name="sud_system_neutral2_300">#ababab</color>
+
+ <color name="sud_system_neutral2_400">#909090</color>
+
+ <color name="sud_system_neutral2_500">#757575</color>
+
+ <color name="sud_system_neutral2_700">#464646</color>
+
+
+ <color name="success_color_device_default_dark">#5BB974</color>
+
+ <color name="success_color_device_default_light">#1E8E3E</color>
+
+ <color name="fallback_color_device_default_dark">#669DF6</color>
+
+ <color name="fallback_color_device_default_light">#1A73E8</color>
+
+ <color name="sud_portal_pending_progress_light">#fdc69c</color>
+
+ <color name="sud_portal_pending_progress_dark">#b06000</color>
+
+ <color name="sud_primary_default_text_dark">#FFFFFF</color>
+
+ <color name="sud_primary_default_text_light">#202124</color>
+
+ <color name="sud_secondary_default_text_dark">#9AA0A6</color>
+
+ <color name="sud_secondary_default_text_light">#5F6368</color>
+
+ <color name="sud_inactive_default_dark">#5F6368</color>
+
+ <color name="sud_inactive_default_light">#9AA0A6</color>
+
+ <color name="sud_error_warning_default_dark">#EE675C</color>
+
+ <color name="sud_error_warning_default_light">#D93025</color>
+
+
+ <color name="sud_system_primary_text">@color/sud_primary_default_text_light</color>
+ <color name="sud_system_secondary_text">@color/sud_secondary_default_text_light</color>
+ <color name="sud_system_tertiary_text_inactive">@color/sud_inactive_default_light</color>
+ <color name="sud_system_error_warning">@color/sud_error_warning_default_light</color>
+ <color name="sud_system_success_done">@color/success_color_device_default_light</color>
+ <color name="sud_system_fallback_accent">@color/fallback_color_device_default_light</color>
+
+ <color name="sud_portal_pending_progress">@color/sud_portal_pending_progress_light</color>
+
+ <color name="sud_switch_track_off">#FF757575</color>
+ <color name="sud_switch_thumb_off">#BFFFFFFF</color>
+
+
+ <color name="sud_uniformity_backdrop_color">@android:color/white</color>
</resources>
diff --git a/main/res/values/dimens.xml b/main/res/values/dimens.xml
index 7508f25..64970c8 100644
--- a/main/res/values/dimens.xml
+++ b/main/res/values/dimens.xml
@@ -21,20 +21,29 @@
<dimen name="sud_layout_margin_sides">40dp</dimen>
<dimen name="sud_glif_button_corner_radius">2dp</dimen>
- <!-- Calculated by (sud_glif_margin_sides - 4dp internal padding of button) -->
+ <!-- Calculated by (sud_glif_margin_start - 4dp internal padding of button) -->
<dimen name="sud_glif_button_margin_end">20dp</dimen>
- <!-- Calculated by (sud_glif_margin_sides - sud_glif_button_padding) -->
+ <!-- Calculated by (sud_glif_margin_start - sud_glif_button_padding) -->
<dimen name="sud_glif_button_margin_start">8dp</dimen>
<dimen name="sud_glif_button_padding">16dp</dimen>
+ <dimen name="sud_glif_button_min_height">48dp</dimen>
<!-- Negative of sud_glif_button_padding -->
<dimen name="sud_glif_negative_button_padding">-16dp</dimen>
<dimen name="sud_glif_footer_padding_vertical">8dp</dimen>
+ <dimen name="sud_glif_footer_padding_start">8dp</dimen>
+ <!-- Calculated by (sud_glif_margin_end - 4dp internal padding of button) -->
+ <dimen name="sud_glif_footer_padding_end">20dp</dimen>
<dimen name="sud_glif_footer_min_height">72dp</dimen>
- <dimen name="sud_glif_margin_sides">24dp</dimen>
- <dimen name="sud_glif_margin_top">56dp</dimen>
+ <dimen name="sud_glif_margin_start">24dp</dimen>
+ <dimen name="sud_glif_margin_end">24dp</dimen>
+ <dimen name="sud_glif_icon_margin_top">56dp</dimen>
<dimen name="sud_glif_alert_dialog_corner_radius">8dp</dimen>
<dimen name="sud_glif_v3_button_corner_radius">4dp</dimen>
+ <dimen name="sud_glif_device_default_dialog_corner_radius">28dp</dimen>
+ <dimen name="sud_glif_land_header_area_weight">1</dimen>
+ <dimen name="sud_glif_land_content_area_weight">1</dimen>
+ <dimen name="sud_glif_land_middle_horizontal_spacing">48dp</dimen>
<!-- Content styles -->
<dimen name="sud_check_box_line_spacing_extra">4sp</dimen>
@@ -51,7 +60,11 @@
<dimen name="sud_description_margin_bottom_lists">24dp</dimen>
<dimen name="sud_description_line_spacing_extra">4sp</dimen>
<dimen name="sud_description_text_size">16sp</dimen>
+ <!-- This value is the margin bottom difference between DescriptionItem and DescriptionMixin. -->
+ <dimen name="sud_description_margin_top_extra">1dp</dimen>
+ <dimen name="sud_description_margin_bottom_extra">12dp</dimen>
+ <!-- TODO: Remove sud_description_glif_margin_xxx once all apps migrate to sud_glif_description_margin_xxx -->
<dimen name="sud_description_glif_margin_top">3dp</dimen>
<dimen name="sud_description_glif_margin_bottom_lists">24dp</dimen>
@@ -64,6 +77,22 @@
<dimen name="sud_content_illustration_min_width">172dp</dimen>
<dimen name="sud_content_illustration_padding_vertical">24dp</dimen>
+ <!-- Loading content styles -->
+ <dimen name="sud_content_loading_frame_padding_top">0dp</dimen>
+ <dimen name="sud_content_loading_frame_padding_start">40dp</dimen>
+ <dimen name="sud_content_loading_frame_padding_end">40dp</dimen>
+ <dimen name="sud_content_loading_frame_padding_bottom">144dp</dimen>
+
+ <dimen name="sud_loading_header_height">274dp</dimen>
+
+ <!-- Glif Content info text -->
+ <dimen name="sud_content_info_text_size">16sp</dimen>
+ <dimen name="sud_content_info_line_spacing_extra">3sp</dimen>
+ <dimen name="sud_content_info_icon_size">18dp</dimen>
+ <dimen name="sud_content_info_icon_margin_end">16dp</dimen>
+ <dimen name="sud_content_info_padding_top">0dp</dimen>
+ <dimen name="sud_content_info_padding_bottom">0dp</dimen>
+
<!-- Margin on the start to offset for margin in the drawable -->
<dimen name="sud_radio_button_margin_start">-6dp</dimen>
<dimen name="sud_radio_button_margin_top">0dp</dimen>
@@ -104,16 +133,26 @@
<dimen name="sud_glif_header_title_margin_top">16dp</dimen>
<dimen name="sud_glif_header_title_margin_bottom">2dp</dimen>
+ <dimen name="sud_header_container_margin_bottom">0dp</dimen>
- <dimen name="sud_glif_icon_max_height">32dp</dimen>
+ <!-- This value leverages sud_description_glif_margin_top -->
+ <dimen name="sud_glif_description_margin_top">3dp</dimen>
+ <dimen name="sud_glif_description_margin_bottom">12dp</dimen>
+ <dimen name="sud_glif_icon_max_height">32dp</dimen>
<!-- Illustration -->
<item name="sud_illustration_aspect_ratio" format="float" type="dimen">2.22</item>
<!-- Items -->
<dimen name="sud_items_icon_container_width">48dp</dimen>
+ <!-- TODO: Remove it when all attributes being used migrated into new one. -->
<dimen name="sud_items_padding_vertical">15dp</dimen>
<dimen name="sud_items_verbose_padding_vertical">20dp</dimen>
+ <dimen name="sud_items_title_text_size">16sp</dimen>
+ <dimen name="sud_items_summary_text_size">14sp</dimen>
+ <dimen name="sud_items_summary_margin_top">4dp</dimen>
+ <dimen name="sud_items_padding_top">15dp</dimen>
+ <dimen name="sud_items_padding_bottom">15dp</dimen>
<!-- Ignore UnusedResources: can be used by clients -->
<dimen name="sud_items_icon_divider_inset" tools:ignore="UnusedResources">88dp</dimen>
@@ -160,4 +199,22 @@
<!-- ExpandableSwithItem -->
<dimen name="sud_expand_arrow_drawable_padding">4dp</dimen>
+ <!-- SwitchBar -->
+ <!-- Minimum width of switch -->
+ <dimen name="sud_switch_min_width">52dp</dimen>
+ <!-- Margin of switch thumb -->
+ <dimen name="sud_switch_thumb_margin">4dp</dimen>
+ <!-- Size of switch thumb -->
+ <dimen name="sud_switch_thumb_size">20dp</dimen>
+ <!-- Width of switch track -->
+ <dimen name="sud_switch_track_width">52dp</dimen>
+ <!-- Height of switch track -->
+ <dimen name="sud_switch_track_height">28dp</dimen>
+ <!-- Radius of switch track -->
+ <dimen name="sud_switch_track_radius">35dp</dimen>
+
+ <!-- IconUniformityAppImageView -->
+ <!-- Set 0dp since we don't want shadow. -->
+ <dimen name="sud_icon_uniformity_elevation">0dp</dimen>
+
</resources>
diff --git a/main/res/values/layouts.xml b/main/res/values/layouts.xml
index cf37c37..910965f 100644
--- a/main/res/values/layouts.xml
+++ b/main/res/values/layouts.xml
@@ -53,4 +53,3 @@
<item name="sud_glif_preference_template" type="layout">@layout/sud_glif_blank_template_compact</item>
<item name="sud_glif_recycler_template" type="layout">@layout/sud_glif_recycler_template_compact</item>
</resources>
-
diff --git a/main/res/values/styles.xml b/main/res/values/styles.xml
index d3d6018..4725735 100644
--- a/main/res/values/styles.xml
+++ b/main/res/values/styles.xml
@@ -25,8 +25,8 @@
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
<item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
- <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
- <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+ <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+ <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
<item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
<item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item>
<item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -37,21 +37,33 @@
<item name="colorAccent">@color/sud_color_accent_dark</item>
<item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
- <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
- <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+ <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+ <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
<item name="sudButtonAllCaps">true</item>
<item name="sudButtonFontFamily">sans-serif</item>
<item name="sudButtonHighlightAlpha">0.24</item>
<item name="sudCardBackground">@drawable/sud_card_bg_dark</item>
+ <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+ <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+ <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+ <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+ <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+ <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
<item name="sudDividerInsetEnd">0dp</item>
<item name="sudDividerInsetStart">@dimen/sud_items_icon_divider_inset</item>
<item name="sudDividerInsetStartNoIcon">@dimen/sud_items_text_divider_inset</item>
<item name="sudItemDescriptionStyle">@style/SudItemContainer.Description</item>
<item name="sudItemDescriptionTitleStyle">@style/SudItemTitle</item>
<item name="sudListItemIconColor">@color/sud_list_item_icon_color_dark</item>
- <item name="sudMarginSides">@dimen/sud_layout_margin_sides</item>
+ <item name="sudMarginStart">@dimen/sud_layout_margin_sides</item>
+ <item name="sudMarginEnd">@dimen/sud_layout_margin_sides</item>
<item name="sudNavBarTheme">@style/SudNavBarThemeDark</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.SudItemSummary</item>
+ <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+ <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+ <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
</style>
<style name="SudThemeMaterial.Light" parent="Theme.AppCompat.Light.NoActionBar">
@@ -60,8 +72,8 @@
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
<item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
- <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
- <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+ <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+ <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
<item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
<item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item>
<item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -72,21 +84,33 @@
<item name="colorAccent">@color/sud_color_accent_light</item>
<item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
- <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
- <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+ <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+ <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
<item name="sudButtonAllCaps">true</item>
<item name="sudButtonFontFamily">sans-serif</item>
<item name="sudButtonHighlightAlpha">0.24</item>
<item name="sudCardBackground">@drawable/sud_card_bg_light</item>
+ <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+ <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+ <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+ <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+ <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+ <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
<item name="sudDividerInsetEnd">0dp</item>
<item name="sudDividerInsetStart">@dimen/sud_items_icon_divider_inset</item>
<item name="sudDividerInsetStartNoIcon">@dimen/sud_items_text_divider_inset</item>
<item name="sudItemDescriptionStyle">@style/SudItemContainer.Description</item>
<item name="sudItemDescriptionTitleStyle">@style/SudItemTitle</item>
<item name="sudListItemIconColor">@color/sud_list_item_icon_color_light</item>
- <item name="sudMarginSides">@dimen/sud_layout_margin_sides</item>
+ <item name="sudMarginStart">@dimen/sud_layout_margin_sides</item>
+ <item name="sudMarginEnd">@dimen/sud_layout_margin_sides</item>
<item name="sudNavBarTheme">@style/SudNavBarThemeLight</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.SudItemSummary</item>
+ <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+ <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+ <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
</style>
<style name="SudBaseThemeGlif" parent="Theme.AppCompat.NoActionBar">
@@ -95,8 +119,8 @@
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
<item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
- <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
- <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+ <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+ <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
<item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
<item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item>
<item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -105,27 +129,44 @@
<item name="android:windowDisablePreview">true</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="android:colorError" tools:targetApi="26">@color/sud_color_error_text_dark</item>
+ <item name="android:scrollbarThumbVertical">?attr/sudScrollBarThumb</item>
<item name="colorAccent">@color/sud_color_accent_glif_dark</item>
<item name="colorPrimary">?attr/colorAccent</item>
- <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
- <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+ <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+ <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
<item name="sudButtonAllCaps">true</item>
<item name="sudButtonCornerRadius">@dimen/sud_glif_button_corner_radius</item>
<item name="sudButtonFontFamily">sans-serif-medium</item>
<item name="sudButtonHighlightAlpha">0.24</item>
<item name="sudColorPrimary">?attr/colorPrimary</item>
+ <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+ <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+ <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+ <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+ <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+ <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
<item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
<item name="sudDividerInsetEnd">0dp</item>
<item name="sudDividerInsetStart">@dimen/sud_items_glif_icon_divider_inset</item>
<item name="sudDividerInsetStartNoIcon">@dimen/sud_items_glif_text_divider_inset</item>
<item name="sudGlifHeaderGravity">start</item>
+ <item name="sudGlifSubtitleGravity">start</item>
+ <item name="sucGlifHeaderMarginTop">@dimen/sud_glif_header_title_margin_top</item>
+ <item name="sudGlifDescriptionMarginTop">@dimen/sud_glif_description_margin_top</item>
+ <item name="sucGlifHeaderMarginBottom">@dimen/sud_glif_header_title_margin_bottom</item>
+ <item name="sudGlifDescriptionMarginBottom">@dimen/sud_glif_description_margin_bottom</item>
+ <item name="sucHeaderContainerMarginBottom">@dimen/sud_header_container_margin_bottom</item>
<item name="sudGlifIconStyle">@style/SudGlifIcon</item>
+ <item name="sucGlifIconMarginTop">@dimen/sud_glif_icon_margin_top</item>
+ <item name="sudGlifIconSize">@dimen/sud_glif_icon_max_height</item>
<item name="sudItemDescriptionStyle">@style/SudItemContainer.Description.Glif</item>
<item name="sudItemDescriptionTitleStyle">@style/SudItemTitle.GlifDescription</item>
<item name="sudListItemIconColor">@color/sud_list_item_icon_color_dark</item>
- <item name="sudMarginSides">@dimen/sud_glif_margin_sides</item>
+ <item name="sudMarginStart">@dimen/sud_glif_margin_start</item>
+ <item name="sudMarginEnd">@dimen/sud_glif_margin_end</item>
<item name="sudScrollIndicators">bottom</item>
+ <item name="sudScrollBarThumb">@drawable/sud_scroll_bar_dark</item>
<item name="textAppearanceListItem">@style/TextAppearance.SudGlifItemTitle</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.SudGlifItemSummary</item>
<item name="sucFooterBarButtonFontFamily">?attr/sudButtonFontFamily</item>
@@ -136,6 +177,16 @@
<item name="sucStatusBarBackground">?android:attr/colorBackground</item>
<item name="sucSystemNavBarBackgroundColor">@android:color/black</item>
<item name="sucFooterBarPaddingVertical">@dimen/sud_glif_footer_padding_vertical</item>
+ <item name="sucFooterBarPaddingStart">@dimen/sud_glif_footer_padding_start</item>
+ <item name="sucFooterBarPaddingEnd">@dimen/sud_glif_footer_padding_end</item>
+ <item name="sucFooterBarMinHeight">@dimen/sud_glif_footer_min_height</item>
+ <item name="sucFooterButtonPaddingStart">@dimen/sud_glif_button_padding</item>
+ <item name="sucFooterButtonPaddingEnd">@dimen/sud_glif_button_padding</item>
+ <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+ <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+ <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
</style>
<style name="SudThemeGlif" parent="SudBaseThemeGlif"/>
@@ -145,8 +196,8 @@
<!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
<item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
<item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
- <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
- <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+ <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+ <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
<item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
<item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item>
<item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -155,27 +206,44 @@
<item name="android:windowDisablePreview">true</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="android:colorError" tools:targetApi="26">@color/sud_color_error_text_light</item>
+ <item name="android:scrollbarThumbVertical">?attr/sudScrollBarThumb</item>
<item name="colorAccent">@color/sud_color_accent_glif_light</item>
<item name="colorPrimary">?attr/colorAccent</item>
- <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
- <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+ <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+ <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
<item name="sudButtonAllCaps">true</item>
<item name="sudButtonCornerRadius">@dimen/sud_glif_button_corner_radius</item>
<item name="sudButtonFontFamily">sans-serif-medium</item>
<item name="sudButtonHighlightAlpha">0.12</item>
<item name="sudColorPrimary">?attr/colorPrimary</item>
+ <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+ <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+ <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+ <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+ <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+ <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
<item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
<item name="sudDividerInsetEnd">0dp</item>
<item name="sudDividerInsetStart">@dimen/sud_items_glif_icon_divider_inset</item>
<item name="sudDividerInsetStartNoIcon">@dimen/sud_items_glif_text_divider_inset</item>
<item name="sudGlifHeaderGravity">start</item>
+ <item name="sudGlifSubtitleGravity">start</item>
+ <item name="sucGlifHeaderMarginTop">@dimen/sud_glif_header_title_margin_top</item>
+ <item name="sudGlifDescriptionMarginTop">@dimen/sud_glif_description_margin_top</item>
+ <item name="sucGlifHeaderMarginBottom">@dimen/sud_glif_header_title_margin_bottom</item>
+ <item name="sudGlifDescriptionMarginBottom">@dimen/sud_glif_description_margin_bottom</item>
+ <item name="sucHeaderContainerMarginBottom">@dimen/sud_header_container_margin_bottom</item>
<item name="sudGlifIconStyle">@style/SudGlifIcon</item>
+ <item name="sucGlifIconMarginTop">@dimen/sud_glif_icon_margin_top</item>
+ <item name="sudGlifIconSize">@dimen/sud_glif_icon_max_height</item>
<item name="sudItemDescriptionStyle">@style/SudItemContainer.Description.Glif</item>
<item name="sudItemDescriptionTitleStyle">@style/SudItemTitle.GlifDescription</item>
<item name="sudListItemIconColor">@color/sud_list_item_icon_color_light</item>
- <item name="sudMarginSides">@dimen/sud_glif_margin_sides</item>
+ <item name="sudMarginStart">@dimen/sud_glif_margin_start</item>
+ <item name="sudMarginEnd">@dimen/sud_glif_margin_end</item>
<item name="sudScrollIndicators">bottom</item>
+ <item name="sudScrollBarThumb">@drawable/sud_scroll_bar_light</item>
<item name="textAppearanceListItem">@style/TextAppearance.SudGlifItemTitle</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.SudGlifItemSummary</item>
<item name="sucFooterBarButtonFontFamily">?attr/sudButtonFontFamily</item>
@@ -186,6 +254,16 @@
<item name="sucStatusBarBackground">?android:attr/colorBackground</item>
<item name="sucSystemNavBarBackgroundColor">@android:color/black</item>
<item name="sucFooterBarPaddingVertical">@dimen/sud_glif_footer_padding_vertical</item>
+ <item name="sucFooterBarPaddingStart">@dimen/sud_glif_footer_padding_start</item>
+ <item name="sucFooterBarPaddingEnd">@dimen/sud_glif_footer_padding_end</item>
+ <item name="sucFooterBarMinHeight">@dimen/sud_glif_footer_min_height</item>
+ <item name="sucFooterButtonPaddingStart">@dimen/sud_glif_button_padding</item>
+ <item name="sucFooterButtonPaddingEnd">@dimen/sud_glif_button_padding</item>
+ <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+ <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+ <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
</style>
<style name="SudThemeGlif.Light" parent="SudBaseThemeGlif.Light"/>
@@ -195,13 +273,15 @@
<item name="sudBackgroundBaseColor">?android:attr/colorBackground</item>
<item name="sudBackgroundPatterned">false</item>
- <item name="sudDividerInsetEnd">?attr/sudMarginSides</item>
- <item name="sudDividerInsetStart">?attr/sudMarginSides</item>
- <item name="sudDividerInsetStartNoIcon">?attr/sudMarginSides</item>
+ <item name="sudDividerInsetEnd">?attr/sudMarginEnd</item>
+ <item name="sudDividerInsetStart">?attr/sudMarginStart</item>
+ <item name="sudDividerInsetStartNoIcon">?attr/sudMarginStart</item>
<item name="sudGlifHeaderGravity">center_horizontal</item>
+ <item name="sudGlifSubtitleGravity">center_horizontal</item>
<item name="sudScrollIndicators">top|bottom</item>
<item name="sudEditTextBackgroundColor">@color/sud_glif_edit_text_bg_dark_color</item>
<item name="android:editTextStyle">@style/SudEditText</item>
+ <item name="alertDialogTheme">@style/SudAlertDialogThemeCompat</item>
<item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SudAlertDialogTheme</item>
<item name="sucLightStatusBar" tools:targetApi="m">?android:attr/windowLightStatusBar</item>
</style>
@@ -212,18 +292,20 @@
<item name="sudBackgroundBaseColor">?android:attr/colorBackground</item>
<item name="sudBackgroundPatterned">false</item>
- <item name="sudDividerInsetEnd">?attr/sudMarginSides</item>
- <item name="sudDividerInsetStart">?attr/sudMarginSides</item>
- <item name="sudDividerInsetStartNoIcon">?attr/sudMarginSides</item>
+ <item name="sudDividerInsetEnd">?attr/sudMarginEnd</item>
+ <item name="sudDividerInsetStart">?attr/sudMarginStart</item>
+ <item name="sudDividerInsetStartNoIcon">?attr/sudMarginStart</item>
<item name="sudGlifHeaderGravity">center_horizontal</item>
+ <item name="sudGlifSubtitleGravity">center_horizontal</item>
<item name="sudScrollIndicators">top|bottom</item>
<item name="sudEditTextBackgroundColor">@color/sud_glif_edit_text_bg_light_color</item>
<item name="android:editTextStyle">@style/SudEditText</item>
+ <item name="alertDialogTheme">@style/SudAlertDialogThemeCompat.Light</item>
<item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SudAlertDialogTheme.Light</item>
<item name="sucLightStatusBar" tools:targetApi="m">?android:attr/windowLightStatusBar</item>
</style>
- <style name="SudThemeGlifV3" parent="SudThemeGlifV2">
+ <style name="SudBaseThemeGlifV3" parent="SudThemeGlifV2">
<item name="colorAccent">@color/sud_color_accent_glif_v3_dark</item>
<item name="colorBackgroundFloating">@color/sud_glif_v3_dialog_background_color_dark</item>
<item name="android:datePickerDialogTheme">@style/SudDateTimePickerDialogTheme</item>
@@ -233,6 +315,9 @@
<item name="sudButtonCornerRadius">@dimen/sud_glif_v3_button_corner_radius</item>
<item name="sudButtonFontFamily">@string/sudFontSecondaryMedium</item>
</style>
+ <style name="SudThemeGlifV3" parent="SudBaseThemeGlifV3" />
+ <style name="SudDynamicColorThemeGlifV3" parent="SudThemeGlifV3" />
+ <style name="SudFullDynamicColorThemeGlifV3" parent="SudDynamicColorThemeGlifV3" />
<style name="SudBaseThemeGlifV3.Light" parent="SudThemeGlifV2.Light">
<item name="colorAccent">@color/sud_color_accent_glif_v3_light</item>
@@ -244,6 +329,8 @@
<item name="sudButtonFontFamily">@string/sudFontSecondaryMedium</item>
</style>
<style name="SudThemeGlifV3.Light" parent="SudBaseThemeGlifV3.Light" />
+ <style name="SudDynamicColorThemeGlifV3.Light" parent="SudThemeGlifV3.Light" />
+ <style name="SudFullDynamicColorThemeGlifV3.Light" parent="SudDynamicColorThemeGlifV3.Light" />
<style name="Animation.SudWindowAnimation" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/sud_slide_next_in</item>
@@ -257,16 +344,38 @@
<style name="SudThemeGlif.DayNight" parent="SudThemeGlif.Light" />
<style name="SudThemeGlifV2.DayNight" parent="SudThemeGlifV2.Light" />
<style name="SudThemeGlifV3.DayNight" parent="SudThemeGlifV3.Light" />
+ <style name="SudDynamicColorThemeGlifV3.DayNight" parent="SudDynamicColorThemeGlifV3.Light" />
+ <style name="SudFullDynamicColorThemeGlifV3.DayNight" parent="SudFullDynamicColorThemeGlifV3.Light" />
<!-- Content styles -->
<!-- Ignore UnusedResources: Used by clients -->
<style name="SudContentFrame" tools:ignore="UnusedResources">
<item name="android:clipToPadding">false</item>
- <item name="android:paddingTop">@dimen/sud_content_frame_padding_top</item>
- <item name="android:paddingLeft">?attr/sudMarginSides</item>
- <item name="android:paddingRight">?attr/sudMarginSides</item>
- <item name="android:paddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+ <item name="android:paddingTop">?attr/sudContentFramePaddingTop</item>
+ <item name="android:paddingLeft">?attr/sudMarginStart</item>
+ <item name="android:paddingRight">?attr/sudMarginEnd</item>
+ <item name="android:paddingBottom">?attr/sudContentFramePaddingBottom</item>
+ </style>
+
+ <style name="SudLoadingContentFrame" tools:ignore="UnusedResources">
+ <item name="android:clipToPadding">false</item>
+ <item name="android:paddingTop">?attr/sudLoadingContentFramePaddingTop</item>
+ <item name="android:paddingLeft">?attr/sudLoadingContentFramePaddingStart</item>
+ <item name="android:paddingRight">?attr/sudLoadingContentFramePaddingEnd</item>
+ <item name="android:paddingBottom">?attr/sudLoadingContentFramePaddingBottom</item>
+ </style>
+
+ <!-- Content info -->
+
+ <style name="SudInfoContainer">
+ <item name="android:paddingTop">@dimen/sud_content_info_padding_top</item>
+ <item name="android:paddingBottom">@dimen/sud_content_info_padding_bottom</item>
+ </style>
+
+ <style name="SudInfoDescription">
+ <item name="android:textSize">@dimen/sud_content_info_text_size</item>
+ <item name="android:lineSpacingExtra">@dimen/sud_content_info_line_spacing_extra</item>
</style>
<!-- Ignore UnusedResources: Used by clients -->
@@ -338,10 +447,10 @@
<style name="SudFillContentLayout">
<item name="android:minWidth">@dimen/sud_content_illustration_min_width</item>
<item name="android:minHeight">@dimen/sud_content_illustration_min_height</item>
- <item name="android:maxWidth">@dimen/sud_content_illustration_max_width</item>
- <item name="android:maxHeight">@dimen/sud_content_illustration_max_height</item>
- <item name="android:paddingTop">@dimen/sud_content_illustration_padding_vertical</item>
- <item name="android:paddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+ <item name="android:maxWidth">?attr/sudContentIllustrationMaxWidth</item>
+ <item name="android:maxHeight">?attr/sudContentIllustrationMaxHeight</item>
+ <item name="android:paddingTop">?attr/sudContentIllustrationPaddingTop</item>
+ <item name="android:paddingBottom">?attr/sudContentIllustrationPaddingBottom</item>
</style>
<!-- Ignore UnusedResources: used by clients -->
@@ -404,8 +513,8 @@
<style name="SudHeaderTitle" parent="SudBaseHeaderTitle">
<item name="android:layout_marginBottom">@dimen/sud_header_title_margin_bottom</item>
- <item name="android:layout_marginLeft">?attr/sudMarginSides</item>
- <item name="android:layout_marginRight">?attr/sudMarginSides</item>
+ <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+ <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
<item name="android:lineSpacingExtra">@dimen/sud_header_title_line_spacing_extra</item>
<item name="android:paddingBottom">@dimen/sud_header_title_padding_bottom</item>
<item name="android:paddingTop">@dimen/sud_header_title_padding_top</item>
@@ -510,12 +619,12 @@
<style name="SudItemContainer">
<item name="android:minHeight">?android:attr/listPreferredItemHeight</item>
- <item name="android:paddingBottom">@dimen/sud_items_padding_vertical</item>
+ <item name="android:paddingBottom">@dimen/sud_items_padding_bottom</item>
<item name="android:paddingEnd" tools:ignore="NewApi">?attr/listPreferredItemPaddingRight</item>
<item name="android:paddingLeft">?attr/listPreferredItemPaddingLeft</item>
<item name="android:paddingRight">?attr/listPreferredItemPaddingRight</item>
<item name="android:paddingStart" tools:ignore="NewApi">?attr/listPreferredItemPaddingLeft</item>
- <item name="android:paddingTop">@dimen/sud_items_padding_vertical</item>
+ <item name="android:paddingTop">@dimen/sud_items_padding_top</item>
</style>
<style name="SudItemContainer.Description" parent="SudItemContainer">
@@ -577,24 +686,44 @@
<style name="SudGlifHeaderTitle" parent="SudBaseHeaderTitle">
<item name="android:gravity">?attr/sudGlifHeaderGravity</item>
- <item name="android:layout_marginBottom">@dimen/sud_glif_header_title_margin_bottom</item>
- <item name="android:layout_marginLeft">?attr/sudMarginSides</item>
- <item name="android:layout_marginRight">?attr/sudMarginSides</item>
- <item name="android:layout_marginTop">@dimen/sud_glif_header_title_margin_top</item>
+ <item name="android:layout_marginBottom">?attr/sucGlifHeaderMarginBottom</item>
+ <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+ <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+ <item name="android:layout_marginTop">?attr/sucGlifHeaderMarginTop</item>
<item name="android:fontFamily" tools:targetApi="jelly_bean">@string/sudFontSecondary</item>
<item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+
+ <style name="SudGlifDescription" parent="SudDescription.Glif">
+ <item name="android:layout_marginTop">?attr/sudGlifDescriptionMarginTop</item>
+ <item name="android:layout_marginBottom">?attr/sudGlifDescriptionMarginBottom</item>
+ <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+ <item name="android:layout_marginStart">?attr/sudMarginStart</item>
+ <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+ <item name="android:layout_marginEnd">?attr/sudMarginEnd</item>
+ <item name="android:fontFamily" tools:targetApi="jelly_bean">@string/sudFontSecondary</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
<style name="SudGlifHeaderContainer">
<item name="android:gravity">?attr/sudGlifHeaderGravity</item>
+ <item name="android:layout_marginBottom">?attr/sucHeaderContainerMarginBottom</item>
+ </style>
+
+ <style name="SudGlifIconContainer">
+ <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+ <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+ <item name="android:layout_marginTop">?attr/sucGlifIconMarginTop</item>
+ <item name="android:maxHeight">?attr/sudGlifIconSize</item>
</style>
<style name="SudGlifIcon">
- <item name="android:layout_marginLeft">?attr/sudMarginSides</item>
- <item name="android:layout_marginRight">?attr/sudMarginSides</item>
- <item name="android:layout_marginTop">@dimen/sud_glif_margin_top</item>
+ <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+ <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+ <item name="android:layout_marginTop">?attr/sucGlifIconMarginTop</item>
<item name="android:adjustViewBounds">true</item>
- <item name="android:maxHeight">@dimen/sud_glif_icon_max_height</item>
+ <item name="android:maxHeight">?attr/sudGlifIconSize</item>
<item name="android:scaleType">centerInside</item>
</style>
@@ -604,12 +733,12 @@
</style>
<style name="TextAppearance.SudGlifItemTitle" parent="android:TextAppearance">
- <item name="android:textSize">16sp</item>
+ <item name="android:textSize">@dimen/sud_items_title_text_size</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.SudGlifItemSummary" parent="android:TextAppearance">
- <item name="android:textSize">14sp</item>
+ <item name="android:textSize">@dimen/sud_items_summary_text_size</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
@@ -651,29 +780,42 @@
<item name="android:minHeight">@dimen/sud_edit_text_min_height</item>
</style>
- <style name="SudAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
+ <style name="SudAlertDialogThemeCompat" parent="Theme.AppCompat.Dialog.Alert">
<item name="android:textAllCaps">false</item>
<item name="android:colorBackground">@color/sud_glif_v3_dialog_background_color_dark</item>
<item name="colorAccent">@color/sud_color_accent_glif_v3_dark</item>
<item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
</style>
- <style name="SudAlertDialogTheme.Light" parent="Theme.AppCompat.Light.Dialog.Alert">
+ <style name="SudAlertDialogThemeCompat.Light" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:textAllCaps">false</item>
<item name="colorAccent">@color/sud_color_accent_glif_v3_light</item>
<item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
</style>
+ <style name="SudAlertDialogTheme" parent="SudAlertDialogThemeCompat"/>
+ <style name="SudAlertDialogTheme.Light" parent="SudAlertDialogThemeCompat.Light"/>
+
<style name="SudDateTimePickerDialogTheme" parent="Theme.AppCompat.Dialog">
<item name="android:textAllCaps">false</item>
<item name="colorAccent">@color/sud_color_accent_glif_v3_dark</item>
<item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
+ <item name="android:windowBackground">@drawable/sud_dialog_background_dark</item>
</style>
<style name="SudDateTimePickerDialogTheme.Light" parent="Theme.AppCompat.Light.Dialog">
<item name="android:textAllCaps">false</item>
<item name="colorAccent">@color/sud_color_accent_glif_v3_light</item>
<item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
+ <item name="android:windowBackground">@drawable/sud_dialog_background_light</item>
+ </style>
+
+ <style name="SudSwitchBarStyle">
+ <item name="android:layout_gravity">center_vertical</item>
+ <item name="track">@drawable/sud_switch_track_selector</item>
+ <item name="android:track">@drawable/sud_switch_track_selector</item>
+ <item name="android:thumb">@drawable/sud_switch_thumb_selector</item>
+ <item name="android:switchMinWidth">@dimen/sud_switch_min_width</item>
</style>
</resources>
diff --git a/main/src/com/google/android/setupdesign/DividerItemDecoration.java b/main/src/com/google/android/setupdesign/DividerItemDecoration.java
index fa0166f..df86b23 100644
--- a/main/src/com/google/android/setupdesign/DividerItemDecoration.java
+++ b/main/src/com/google/android/setupdesign/DividerItemDecoration.java
@@ -21,10 +21,10 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import androidx.annotation.IntDef;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
+import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -125,7 +125,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
}
}
- private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
+ protected boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
final int index = holder.getLayoutPosition();
final int lastItemIndex = parent.getAdapter().getItemCount() - 1;
diff --git a/main/src/com/google/android/setupdesign/GlifLayout.java b/main/src/com/google/android/setupdesign/GlifLayout.java
index de4f8c1..829e3d7 100644
--- a/main/src/com/google/android/setupdesign/GlifLayout.java
+++ b/main/src/com/google/android/setupdesign/GlifLayout.java
@@ -22,11 +22,8 @@ import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.ColorInt;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -35,16 +32,25 @@ import android.view.ViewStub;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
+import androidx.annotation.ColorInt;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import com.google.android.setupcompat.PartnerCustomizationLayout;
import com.google.android.setupcompat.partnerconfig.PartnerConfig;
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.template.StatusBarMixin;
+import com.google.android.setupdesign.template.DescriptionMixin;
import com.google.android.setupdesign.template.HeaderMixin;
import com.google.android.setupdesign.template.IconMixin;
+import com.google.android.setupdesign.template.IllustrationProgressMixin;
import com.google.android.setupdesign.template.ProgressBarMixin;
import com.google.android.setupdesign.template.RequireScrollMixin;
import com.google.android.setupdesign.template.ScrollViewScrollHandlingDelegate;
import com.google.android.setupdesign.util.DescriptionStyler;
+import com.google.android.setupdesign.util.LayoutStyler;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
/**
* Layout for the GLIF theme used in Setup Wizard for N.
@@ -67,8 +73,6 @@ import com.google.android.setupdesign.util.DescriptionStyler;
*/
public class GlifLayout extends PartnerCustomizationLayout {
- private static final String TAG = "GlifLayout";
-
private ColorStateList primaryColor;
private boolean backgroundPatterned = true;
@@ -105,6 +109,9 @@ public class GlifLayout extends PartnerCustomizationLayout {
// All the constructors delegate to this init method. The 3-argument constructor is not
// available in LinearLayout before v11, so call super with the exact same arguments.
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
TypedArray a =
getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
@@ -113,8 +120,10 @@ public class GlifLayout extends PartnerCustomizationLayout {
applyPartnerHeavyThemeResource = shouldApplyPartnerResource() && usePartnerHeavyTheme;
registerMixin(HeaderMixin.class, new HeaderMixin(this, attrs, defStyleAttr));
+ registerMixin(DescriptionMixin.class, new DescriptionMixin(this, attrs, defStyleAttr));
registerMixin(IconMixin.class, new IconMixin(this, attrs, defStyleAttr));
- registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
+ registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this, attrs, defStyleAttr));
+ registerMixin(IllustrationProgressMixin.class, new IllustrationProgressMixin(this));
final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
registerMixin(RequireScrollMixin.class, requireScrollMixin);
@@ -131,7 +140,21 @@ public class GlifLayout extends PartnerCustomizationLayout {
if (applyPartnerHeavyThemeResource) {
updateContentBackgroundColorWithPartnerConfig();
+
+ View view = findManagedViewById(R.id.sud_layout_content);
+ if (view != null) {
+ // The margin of content is defined by @style/SudContentFrame. The Setupdesign library
+ // cannot obtain the content resource ID of the client, so the value of the content margin
+ // cannot be adjusted through GlifLayout. If the margin sides are changed through the
+ // partner config, it can only be based on the increased or decreased value to adjust the
+ // value of pading. In this way, the value of content margin plus padding will be equal to
+ // the value of partner config.
+ LayoutStyler.applyPartnerCustomizationExtraPaddingStyle(view);
+
+ applyPartnerCustomizationContentPaddingTopStyle(view);
+ }
}
+ updateLandscapeMiddleHorizontalSpacing();
ColorStateList backgroundColor =
a.getColorStateList(R.styleable.SudGlifLayout_sudBackgroundBaseColor);
@@ -153,15 +176,79 @@ public class GlifLayout extends PartnerCustomizationLayout {
super.onFinishInflate();
getMixin(IconMixin.class).tryApplyPartnerCustomizationStyle();
getMixin(HeaderMixin.class).tryApplyPartnerCustomizationStyle();
+ getMixin(DescriptionMixin.class).tryApplyPartnerCustomizationStyle();
tryApplyPartnerCustomizationStyleToShortDescription();
}
+ // TODO: remove when all sud_layout_description has migrated to
+ // DescriptionMixin(sud_layout_subtitle)
private void tryApplyPartnerCustomizationStyleToShortDescription() {
- if (applyPartnerHeavyThemeResource) {
- TextView description =
- this.findManagedViewById(com.google.android.setupdesign.R.id.sud_layout_description);
- if (description != null) {
- DescriptionStyler.applyPartnerCustomizationStyle(description);
+ TextView description = this.findManagedViewById(R.id.sud_layout_description);
+ if (description != null) {
+ if (applyPartnerHeavyThemeResource) {
+ DescriptionStyler.applyPartnerCustomizationHeavyStyle(description);
+ } else if (shouldApplyPartnerResource()) {
+ DescriptionStyler.applyPartnerCustomizationLightStyle(description);
+ }
+ }
+ }
+
+ protected void updateLandscapeMiddleHorizontalSpacing() {
+ int horizontalSpacing =
+ getResources().getDimensionPixelSize(R.dimen.sud_glif_land_middle_horizontal_spacing);
+
+ View headerView = this.findManagedViewById(R.id.sud_landscape_header_area);
+ if (headerView != null) {
+ if (PartnerConfigHelper.get(getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END)) {
+ int layoutMarginEnd =
+ (int)
+ PartnerConfigHelper.get(getContext())
+ .getDimension(getContext(), PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+
+ int paddingEnd = (horizontalSpacing / 2) - layoutMarginEnd;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ headerView.setPadding(
+ headerView.getPaddingStart(),
+ headerView.getPaddingTop(),
+ paddingEnd,
+ headerView.getPaddingBottom());
+ } else {
+ headerView.setPadding(
+ headerView.getPaddingLeft(),
+ headerView.getPaddingTop(),
+ paddingEnd,
+ headerView.getPaddingBottom());
+ }
+ }
+ }
+
+ View contentView = this.findManagedViewById(R.id.sud_landscape_content_area);
+ if (contentView != null) {
+ if (PartnerConfigHelper.get(getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+ int layoutMarginStart =
+ (int)
+ PartnerConfigHelper.get(getContext())
+ .getDimension(getContext(), PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+
+ int paddingStart = 0;
+ if (headerView != null) {
+ paddingStart = (horizontalSpacing / 2) - layoutMarginStart;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ contentView.setPadding(
+ paddingStart,
+ contentView.getPaddingTop(),
+ contentView.getPaddingEnd(),
+ contentView.getPaddingBottom());
+ } else {
+ contentView.setPadding(
+ paddingStart,
+ contentView.getPaddingTop(),
+ contentView.getPaddingRight(),
+ contentView.getPaddingBottom());
+ }
}
}
}
@@ -187,8 +274,8 @@ public class GlifLayout extends PartnerCustomizationLayout {
* 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.
+ * @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.sud_layout_sticky_header);
@@ -217,6 +304,35 @@ public class GlifLayout extends PartnerCustomizationLayout {
return getMixin(HeaderMixin.class).getText();
}
+ public TextView getDescriptionTextView() {
+ return getMixin(DescriptionMixin.class).getTextView();
+ }
+
+ /**
+ * Sets the description text and also sets the text visibility to visible. This can also be set
+ * via the XML attribute {@code app:sudDescriptionText}.
+ *
+ * @param title The resource ID of the text to be set as description
+ */
+ public void setDescriptionText(@StringRes int title) {
+ getMixin(DescriptionMixin.class).setText(title);
+ }
+
+ /**
+ * Sets the description text and also sets the text visibility to visible. This can also be set
+ * via the XML attribute {@code app:sudDescriptionText}.
+ *
+ * @param title The text to be set as description
+ */
+ public void setDescriptionText(CharSequence title) {
+ getMixin(DescriptionMixin.class).setText(title);
+ }
+
+ /** Returns the current description text. */
+ public CharSequence getDescriptionText() {
+ return getMixin(DescriptionMixin.class).getText();
+ }
+
public void setHeaderColor(ColorStateList color) {
getMixin(HeaderMixin.class).setTextColor(color);
}
@@ -234,6 +350,24 @@ public class GlifLayout extends PartnerCustomizationLayout {
}
/**
+ * Sets the visibility of header area in landscape mode. These views inlcudes icon, header title
+ * and subtitle. It can make the content view become full screen when set false.
+ */
+ @TargetApi(Build.VERSION_CODES.S)
+ public void setLandscapeHeaderAreaVisible(boolean visible) {
+ View view = this.findManagedViewById(R.id.sud_landscape_header_area);
+ if (view == null) {
+ return;
+ }
+ if (visible) {
+ view.setVisibility(View.VISIBLE);
+ } else {
+ view.setVisibility(View.GONE);
+ }
+ updateLandscapeMiddleHorizontalSpacing();
+ }
+
+ /**
* Sets the primary color of this layout, which will be used to determine the color of the
* progress bar and the background pattern.
*/
@@ -253,7 +387,7 @@ public class GlifLayout extends PartnerCustomizationLayout {
* drawn with this color.
*
* @param color The color to use as the base color of the background. If {@code null}, {@link
- * #getPrimaryColor()} will be used.
+ * #getPrimaryColor()} will be used
*/
public void setBackgroundBaseColor(@Nullable ColorStateList color) {
backgroundBaseColor = color;
@@ -316,15 +450,45 @@ public class GlifLayout extends PartnerCustomizationLayout {
* Returns if the current layout/activity applies heavy partner customized configurations or not.
*/
public boolean shouldApplyPartnerHeavyThemeResource() {
- return applyPartnerHeavyThemeResource;
+
+ return applyPartnerHeavyThemeResource
+ || (shouldApplyPartnerResource()
+ && PartnerConfigHelper.shouldApplyExtendedPartnerConfig(getContext()));
}
/** Updates the background color of this layout with the partner-customizable background color. */
private void updateContentBackgroundColorWithPartnerConfig() {
+ // If full dynamic color enabled which means this activity is running outside of setup
+ // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3.
+ if (useFullDynamicColor()) {
+ return;
+ }
+
@ColorInt
int color =
PartnerConfigHelper.get(getContext())
.getColor(getContext(), PartnerConfig.CONFIG_LAYOUT_BACKGROUND_COLOR);
this.getRootView().setBackgroundColor(color);
}
+
+ @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+ protected static void applyPartnerCustomizationContentPaddingTopStyle(View view) {
+ Context context = view.getContext();
+ boolean partnerPaddingTopAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_PADDING_TOP);
+
+ if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(view)
+ && partnerPaddingTopAvailable) {
+ int paddingTop =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CONTENT_PADDING_TOP);
+
+ if (paddingTop != view.getPaddingTop()) {
+ view.setPadding(
+ view.getPaddingStart(), paddingTop, view.getPaddingEnd(), view.getPaddingBottom());
+ }
+ }
+ }
}
diff --git a/main/src/com/google/android/setupdesign/GlifListLayout.java b/main/src/com/google/android/setupdesign/GlifListLayout.java
index 31335cc..60f0343 100644
--- a/main/src/com/google/android/setupdesign/GlifListLayout.java
+++ b/main/src/com/google/android/setupdesign/GlifListLayout.java
@@ -63,12 +63,22 @@ public class GlifListLayout extends GlifLayout {
}
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
listMixin = new ListMixin(this, attrs, defStyleAttr);
registerMixin(ListMixin.class, listMixin);
final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
requireScrollMixin.setScrollHandlingDelegate(
new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
+
+ View view = this.findManagedViewById(R.id.sud_landscape_content_area);
+ if (view != null) {
+ applyPartnerCustomizationContentPaddingTopStyle(view);
+ }
+ updateLandscapeMiddleHorizontalSpacing();
}
@Override
diff --git a/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java b/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java
index c74cff0..5e75436 100644
--- a/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java
+++ b/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java
@@ -64,12 +64,22 @@ public class GlifRecyclerLayout extends GlifLayout {
}
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
recyclerMixin.parseAttributes(attrs, defStyleAttr);
registerMixin(RecyclerMixin.class, recyclerMixin);
final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
requireScrollMixin.setScrollHandlingDelegate(
new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView()));
+
+ View view = this.findManagedViewById(R.id.sud_landscape_content_area);
+ if (view != null) {
+ applyPartnerCustomizationContentPaddingTopStyle(view);
+ }
+ updateLandscapeMiddleHorizontalSpacing();
}
@Override
diff --git a/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java b/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java
index 2f3dd86..28f95fb 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java
@@ -17,9 +17,9 @@
package com.google.android.setupdesign;
import android.content.Context;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.ListAdapter;
+import androidx.annotation.Nullable;
import com.google.android.setupdesign.items.ItemAdapter;
/** @deprecated Use {@link SetupWizardListLayout} instead. */
diff --git a/main/src/com/google/android/setupdesign/SetupWizardLayout.java b/main/src/com/google/android/setupdesign/SetupWizardLayout.java
index 5797aa7..b91cec7 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardLayout.java
@@ -40,6 +40,7 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.template.SystemNavBarMixin;
+import com.google.android.setupdesign.template.DescriptionMixin;
import com.google.android.setupdesign.template.HeaderMixin;
import com.google.android.setupdesign.template.NavigationBarMixin;
import com.google.android.setupdesign.template.ProgressBarMixin;
@@ -80,10 +81,15 @@ public class SetupWizardLayout extends TemplateLayout {
// All the constructors delegate to this init method. The 3-argument constructor is not
// available in LinearLayout before v11, so call super with the exact same arguments.
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
registerMixin(SystemNavBarMixin.class, new SystemNavBarMixin(this, /* window= */ null));
registerMixin(
HeaderMixin.class,
new HeaderMixin(this, attrs, defStyleAttr));
+ registerMixin(DescriptionMixin.class, new DescriptionMixin(this, attrs, defStyleAttr));
registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
registerMixin(NavigationBarMixin.class, new NavigationBarMixin(this));
final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
diff --git a/main/src/com/google/android/setupdesign/SetupWizardListLayout.java b/main/src/com/google/android/setupdesign/SetupWizardListLayout.java
index 760602f..dcb35d2 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardListLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardListLayout.java
@@ -59,6 +59,10 @@ public class SetupWizardListLayout extends SetupWizardLayout {
}
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
listMixin = new ListMixin(this, attrs, defStyleAttr);
registerMixin(ListMixin.class, listMixin);
diff --git a/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java b/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java
index f0dc138..f2a7e3d 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java
@@ -60,6 +60,10 @@ public class SetupWizardRecyclerLayout extends SetupWizardLayout {
}
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
recyclerMixin.parseAttributes(attrs, defStyleAttr);
registerMixin(RecyclerMixin.class, recyclerMixin);
diff --git a/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java b/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java
index ed9daf3..1a55b25 100644
--- a/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java
+++ b/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java
@@ -19,12 +19,9 @@ package com.google.android.setupdesign.accessibility;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.ClickableSpan;
@@ -34,6 +31,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.customview.widget.ExploreByTouchHelper;
import java.util.List;
/**
diff --git a/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java b/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java
index 267e4ab..45c8e9e 100644
--- a/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java
+++ b/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.View;
import com.google.android.setupdesign.R;
import java.util.ArrayList;
@@ -33,13 +34,13 @@ public abstract class AbstractItemHierarchy implements ItemHierarchy {
/* non-static section */
private final ArrayList<Observer> observers = new ArrayList<>();
- private int id = 0;
+ private int id = View.NO_ID;
public AbstractItemHierarchy() {}
public AbstractItemHierarchy(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SudAbstractItem);
- id = a.getResourceId(R.styleable.SudAbstractItem_android_id, 0);
+ id = a.getResourceId(R.styleable.SudAbstractItem_android_id, View.NO_ID);
a.recycle();
}
diff --git a/main/src/com/google/android/setupdesign/items/DescriptionItem.java b/main/src/com/google/android/setupdesign/items/DescriptionItem.java
index 24fbf5f..36ccfb9 100644
--- a/main/src/com/google/android/setupdesign/items/DescriptionItem.java
+++ b/main/src/com/google/android/setupdesign/items/DescriptionItem.java
@@ -19,17 +19,24 @@ package com.google.android.setupdesign.items;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.FrameLayout;
import android.widget.TextView;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.R;
import com.google.android.setupdesign.util.DescriptionStyler;
/**
* Definition of an item in an {@link ItemHierarchy}. An item is usually defined in XML and inflated
* using {@link ItemInflater}.
+ *
+ * @deprecated Use {@link com.google.android.setupdesign.template.DescriptionMixin} instead.
*/
+@Deprecated
public class DescriptionItem extends Item {
- private boolean applyPartnerDescriptionStyle = false;
+ private boolean partnerDescriptionHeavyStyle = false;
+ private boolean partnerDescriptionLightStyle = false;
public DescriptionItem() {
super();
@@ -39,16 +46,40 @@ public class DescriptionItem extends Item {
super(context, attrs);
}
- public boolean shouldApplyPartnerDescriptionStyle() {
- return applyPartnerDescriptionStyle;
+ /**
+ * Returns true if the description of partner's layout should apply heavy style, this depends on
+ * if the layout fulfill conditions in {@code applyPartnerHeavyThemeResource} of {@link
+ * com.google.android.setupdesign.GlifLayout}
+ */
+ public boolean shouldApplyPartnerDescriptionHeavyStyle() {
+ return partnerDescriptionHeavyStyle;
+ }
+
+ /**
+ * Returns true if the description of partner's layout should apply light style, this depends on
+ * if the layout fulfill conditions in {@code shouldApplyPartnerResource} of {@link
+ * com.google.android.setupcompat.PartnerCustomizationLayout}
+ */
+ public boolean shouldApplyPartnerDescriptionLightStyle() {
+ return partnerDescriptionLightStyle;
}
/**
* Applies partner description style on the title of the item, i.e. the TextView with {@code
* R.id.sud_items_title}.
+ *
+ * @param layout A layout indicates if the description of partner's layout should apply heavy or
+ * light style
*/
- public void setApplyPartnerDescriptionStyle(boolean applyPartnerDescriptionStyle) {
- this.applyPartnerDescriptionStyle = applyPartnerDescriptionStyle;
+ public void setPartnerDescriptionStyle(FrameLayout layout) {
+ if (layout instanceof GlifLayout) {
+ this.partnerDescriptionHeavyStyle =
+ ((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource();
+ }
+ if (layout instanceof PartnerCustomizationLayout) {
+ this.partnerDescriptionLightStyle =
+ ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
+ }
notifyItemChanged();
}
@@ -56,8 +87,10 @@ public class DescriptionItem extends Item {
public void onBindView(View view) {
super.onBindView(view);
TextView label = (TextView) view.findViewById(R.id.sud_items_title);
- if (shouldApplyPartnerDescriptionStyle()) {
- DescriptionStyler.applyPartnerCustomizationStyle(label);
+ if (shouldApplyPartnerDescriptionHeavyStyle()) {
+ DescriptionStyler.applyPartnerCustomizationHeavyStyle(label);
+ } else if (shouldApplyPartnerDescriptionLightStyle()) {
+ DescriptionStyler.applyPartnerCustomizationLightStyle(label);
}
}
}
diff --git a/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java b/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java
index 42438d6..29eaf23 100644
--- a/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java
+++ b/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java
@@ -35,6 +35,7 @@ import android.view.View.OnClickListener;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.LayoutStyler;
import com.google.android.setupdesign.view.CheckableLinearLayout;
/**
@@ -179,6 +180,8 @@ public class ExpandableSwitchItem extends SwitchItem
// 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);
+
+ LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(content);
}
@Override
diff --git a/main/src/com/google/android/setupdesign/items/Item.java b/main/src/com/google/android/setupdesign/items/Item.java
index e5d173f..fd3f2eb 100644
--- a/main/src/com/google/android/setupdesign/items/Item.java
+++ b/main/src/com/google/android/setupdesign/items/Item.java
@@ -20,8 +20,6 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
-import androidx.annotation.ColorInt;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -29,7 +27,11 @@ import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.ItemStyler;
+import com.google.android.setupdesign.util.LayoutStyler;
/**
* Definition of an item in an {@link ItemHierarchy}. An item is usually defined in XML and inflated
@@ -200,6 +202,16 @@ public class Item extends AbstractItem {
}
view.setId(getViewId());
+
+ // ExpandableSwitchItem uses its child view to apply the style SudItemContainer. It is not
+ // possible to directly adjust the padding start/end of the item's layout here. It needs to
+ // get its child view to adjust it first, so skip the Layout padding adjustment.
+ // If the item view is a header layout, it doesn't need to adjust the layout padding start/end
+ // here. It will be adjusted by HeaderMixin.
+ if (!(this instanceof ExpandableSwitchItem) && view.getId() != R.id.sud_layout_header) {
+ LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(view);
+ }
+ ItemStyler.applyPartnerCustomizationItemStyle(view);
}
/**
diff --git a/main/src/com/google/android/setupdesign/items/ItemHierarchy.java b/main/src/com/google/android/setupdesign/items/ItemHierarchy.java
index b22e9ef..6a5b51f 100644
--- a/main/src/com/google/android/setupdesign/items/ItemHierarchy.java
+++ b/main/src/com/google/android/setupdesign/items/ItemHierarchy.java
@@ -71,7 +71,7 @@ public interface ItemHierarchy {
/**
* Get the item at position.
*
- * @param position An integer from 0 to {@link #getCount()}}, which indicates the position in this
+ * @param position An integer from 0 to {@link #getCount()}, which indicates the position in this
* item hierarchy to get the child item.
* @return A representation of the item at {@code position}. Must not be {@code null}.
*/
diff --git a/main/src/com/google/android/setupdesign/items/ItemViewHolder.java b/main/src/com/google/android/setupdesign/items/ItemViewHolder.java
index f79b2b6..3cffddd 100644
--- a/main/src/com/google/android/setupdesign/items/ItemViewHolder.java
+++ b/main/src/com/google/android/setupdesign/items/ItemViewHolder.java
@@ -20,7 +20,11 @@ import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import com.google.android.setupdesign.DividerItemDecoration;
-class ItemViewHolder extends RecyclerView.ViewHolder
+/**
+ * ViewHolder for the RecyclerItemAdapter that describes an item view and metadata about its place
+ * within the RecyclerView.
+ */
+public class ItemViewHolder extends RecyclerView.ViewHolder
implements DividerItemDecoration.DividedViewHolder {
private boolean isEnabled;
diff --git a/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java b/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java
index 3526bf5..dbbda29 100644
--- a/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java
+++ b/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java
@@ -21,12 +21,12 @@ import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
-import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.VisibleForTesting;
import com.google.android.setupcompat.partnerconfig.PartnerConfig;
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupdesign.R;
@@ -61,6 +61,7 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder>
private final ItemHierarchy itemHierarchy;
@VisibleForTesting public final boolean applyPartnerHeavyThemeResource;
+ @VisibleForTesting public final boolean useFullDynamicColor;
private OnItemSelectedListener listener;
public RecyclerItemAdapter(ItemHierarchy hierarchy) {
@@ -68,7 +69,15 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder>
}
public RecyclerItemAdapter(ItemHierarchy hierarchy, boolean applyPartnerHeavyThemeResource) {
+ this(hierarchy, applyPartnerHeavyThemeResource, /* useFullDynamicColor= */ false);
+ }
+
+ public RecyclerItemAdapter(
+ ItemHierarchy hierarchy,
+ boolean applyPartnerHeavyThemeResource,
+ boolean useFullDynamicColor) {
this.applyPartnerHeavyThemeResource = applyPartnerHeavyThemeResource;
+ this.useFullDynamicColor = useFullDynamicColor;
itemHierarchy = hierarchy;
itemHierarchy.registerObserver(this);
}
@@ -118,7 +127,9 @@ public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder>
} else {
background = view.getBackground();
if (background == null) {
- if (applyPartnerHeavyThemeResource) {
+ // If full dynamic color enabled which means this activity is running outside of setup
+ // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3.
+ if (applyPartnerHeavyThemeResource && !useFullDynamicColor) {
int color =
PartnerConfigHelper.get(view.getContext())
.getColor(view.getContext(), PartnerConfig.CONFIG_LAYOUT_BACKGROUND_COLOR);
diff --git a/main/src/com/google/android/setupdesign/items/ReflectionInflater.java b/main/src/com/google/android/setupdesign/items/ReflectionInflater.java
index 329d240..8123c18 100644
--- a/main/src/com/google/android/setupdesign/items/ReflectionInflater.java
+++ b/main/src/com/google/android/setupdesign/items/ReflectionInflater.java
@@ -17,10 +17,10 @@
package com.google.android.setupdesign.items;
import android.content.Context;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.view.InflateException;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.HashMap;
diff --git a/main/src/com/google/android/setupdesign/items/SimpleInflater.java b/main/src/com/google/android/setupdesign/items/SimpleInflater.java
index c7e370a..0c7d1d9 100644
--- a/main/src/com/google/android/setupdesign/items/SimpleInflater.java
+++ b/main/src/com/google/android/setupdesign/items/SimpleInflater.java
@@ -18,11 +18,11 @@ package com.google.android.setupdesign.items;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import android.view.InflateException;
+import androidx.annotation.NonNull;
import java.io.IOException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/main/src/com/google/android/setupdesign/span/LinkSpan.java b/main/src/com/google/android/setupdesign/span/LinkSpan.java
index 7f1f02b..7d91012 100644
--- a/main/src/com/google/android/setupdesign/span/LinkSpan.java
+++ b/main/src/com/google/android/setupdesign/span/LinkSpan.java
@@ -19,7 +19,6 @@ package com.google.android.setupdesign.span;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
-import androidx.annotation.Nullable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextPaint;
@@ -27,6 +26,7 @@ import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.Nullable;
/**
* A clickable span that will listen for click events and send it back to the context. To use this
diff --git a/main/src/com/google/android/setupdesign/template/DescriptionMixin.java b/main/src/com/google/android/setupdesign/template/DescriptionMixin.java
new file mode 100644
index 0000000..bbccf82
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/template/DescriptionMixin.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 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.google.android.setupdesign.template;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.template.Mixin;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.HeaderAreaStyler;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
+
+/**
+ * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the description
+ * text.
+ */
+public class DescriptionMixin implements Mixin {
+
+ private static final String TAG = "DescriptionMixin";
+ private final TemplateLayout templateLayout;
+
+ /**
+ * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the
+ * description.
+ *
+ * @param layout The layout this Mixin belongs to
+ * @param attrs XML attributes given to the layout
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout
+ */
+ public DescriptionMixin(
+ @NonNull TemplateLayout layout, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
+ templateLayout = layout;
+
+ final TypedArray a =
+ layout
+ .getContext()
+ .obtainStyledAttributes(attrs, R.styleable.SudDescriptionMixin, defStyleAttr, 0);
+
+ // Set the description text
+ final CharSequence descriptionText =
+ a.getText(R.styleable.SudDescriptionMixin_sudDescriptionText);
+ if (descriptionText != null) {
+ setText(descriptionText);
+ }
+ // Set the description text color
+ final ColorStateList descriptionTextColor =
+ a.getColorStateList(R.styleable.SudDescriptionMixin_sudDescriptionTextColor);
+ if (descriptionTextColor != null) {
+ setTextColor(descriptionTextColor);
+ }
+
+ a.recycle();
+ }
+
+ /**
+ * Applies the partner customizations to the description text (contains text alignment) and
+ * background, if apply heavy theme resource, it will apply all partner customizations, otherwise,
+ * only apply alignment style.
+ */
+ public void tryApplyPartnerCustomizationStyle() {
+ TextView description = templateLayout.findManagedViewById(R.id.sud_layout_subtitle);
+ boolean partnerHeavyThemeLayout = PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout);
+ if (partnerHeavyThemeLayout) {
+ if (description != null) {
+ HeaderAreaStyler.applyPartnerCustomizationDescriptionHeavyStyle(description);
+ }
+ } else if (templateLayout instanceof PartnerCustomizationLayout
+ && ((PartnerCustomizationLayout) templateLayout).shouldApplyPartnerResource()) {
+ if (description != null) {
+ HeaderAreaStyler.applyPartnerCustomizationDescriptionLightStyle(description);
+ }
+ }
+ }
+
+ /** Returns the TextView displaying the description. */
+ public TextView getTextView() {
+ return (TextView) templateLayout.findManagedViewById(R.id.sud_layout_subtitle);
+ }
+
+ /**
+ * Sets the description text and also sets the text visibility to visible. This can also be set
+ * via the XML attribute {@code app:sudDescriptionText}.
+ *
+ * @param title The resource ID of the text to be set as description
+ */
+ public void setText(@StringRes int title) {
+ final TextView titleView = getTextView();
+ if (titleView != null && title != 0) {
+ titleView.setText(title);
+ setVisibility(View.VISIBLE);
+ } else {
+ Log.w(TAG, "Fail to set text due to either invalid resource id or text view not found.");
+ }
+ }
+
+ /**
+ * Sets the description text and also sets the text visibility to visible. This can also be set
+ * via the XML attribute {@code app:sudDescriptionText}.
+ *
+ * @param title The text to be set as description
+ */
+ public void setText(CharSequence title) {
+ final TextView titleView = getTextView();
+ if (titleView != null) {
+ titleView.setText(title);
+ setVisibility(View.VISIBLE);
+ }
+ }
+
+ /** Returns the current description text. */
+ public CharSequence getText() {
+ final TextView titleView = getTextView();
+ return titleView != null ? titleView.getText() : null;
+ }
+
+ /** Sets the visibility of description text */
+ public void setVisibility(int visibility) {
+ final TextView titleView = getTextView();
+ if (titleView != null) {
+ titleView.setVisibility(visibility);
+ }
+ }
+
+ /**
+ * Sets the color of the description text. This can also be set via XML using {@code
+ * app:sudDescriptionTextColor}.
+ *
+ * @param color The text color of the description
+ */
+ public void setTextColor(ColorStateList color) {
+ final TextView titleView = getTextView();
+ if (titleView != null) {
+ titleView.setTextColor(color);
+ }
+ }
+
+ /** Returns the current text color of the description. */
+ public ColorStateList getTextColor() {
+ final TextView titleView = getTextView();
+ return titleView != null ? titleView.getTextColors() : null;
+ }
+
+ /**
+ * Call this method ONLY when a layout migrates from {@link
+ * com.google.android.setupdesign.items.DescriptionItem} to {@link DescriptionMixin}.
+ *
+ * <p>If a screen is migrated from {@link com.google.android.setupdesign.items.DescriptionItem} it
+ * will looks slightly different from the original UI. This method helps keeping the UI consistent
+ * with the original UI.
+ */
+ public void adjustLegacyDescriptionItem() {
+ final TextView titleView = getTextView();
+ final Context context = titleView.getContext();
+
+ final ViewGroup.LayoutParams lp = titleView.getLayoutParams();
+ if (lp instanceof ViewGroup.MarginLayoutParams) {
+ final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+ int extraBottomMargin =
+ (int) context.getResources().getDimension(R.dimen.sud_description_margin_bottom_extra);
+ int extraTopMargin =
+ (int) context.getResources().getDimension(R.dimen.sud_description_margin_top_extra);
+ mlp.setMargins(
+ mlp.leftMargin,
+ mlp.topMargin + extraTopMargin,
+ mlp.rightMargin,
+ mlp.bottomMargin + extraBottomMargin);
+ }
+ }
+}
diff --git a/main/src/com/google/android/setupdesign/template/HeaderMixin.java b/main/src/com/google/android/setupdesign/template/HeaderMixin.java
index 0a6eb9e..d040c94 100644
--- a/main/src/com/google/android/setupdesign/template/HeaderMixin.java
+++ b/main/src/com/google/android/setupdesign/template/HeaderMixin.java
@@ -16,19 +16,30 @@
package com.google.android.setupdesign.template;
+import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import androidx.annotation.AttrRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.template.Mixin;
import com.google.android.setupdesign.R;
import com.google.android.setupdesign.util.HeaderAreaStyler;
+import com.google.android.setupdesign.util.LayoutStyler;
import com.google.android.setupdesign.util.PartnerStyleHelper;
/**
@@ -37,11 +48,19 @@ import com.google.android.setupdesign.util.PartnerStyleHelper;
public class HeaderMixin implements Mixin {
private final TemplateLayout templateLayout;
+ @VisibleForTesting boolean autoTextSizeEnabled = false;
+ private float headerAutoSizeMaxTextSizeInPx;
+ private float headerAutoSizeMinTextSizeInPx;
+ private float headerAutoSizeLineExtraSpacingInPx;
+ private int headerAutoSizeMaxLineOfMaxSize;
+ private static final int AUTO_SIZE_DEFAULT_MAX_LINES = 6;
/**
- * @param layout The layout this Mixin belongs to.
- * @param attrs XML attributes given to the layout.
- * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+ * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the Header.
+ *
+ * @param layout The layout this Mixin belongs to
+ * @param attrs XML attributes given to the layout
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout
*/
public HeaderMixin(
@NonNull TemplateLayout layout, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
@@ -52,41 +71,103 @@ public class HeaderMixin implements Mixin {
.getContext()
.obtainStyledAttributes(attrs, R.styleable.SucHeaderMixin, defStyleAttr, 0);
- // Set the header text
final CharSequence headerText = a.getText(R.styleable.SucHeaderMixin_sucHeaderText);
+ final ColorStateList headerTextColor =
+ a.getColorStateList(R.styleable.SucHeaderMixin_sucHeaderTextColor);
+
+ a.recycle();
+
+ // overlay the Auto size config settings
+ updateAutoTextSizeWithPartnerConfig();
+
+ // Set the header text
if (headerText != null) {
setText(headerText);
}
// Set the header text color
- final ColorStateList headerTextColor =
- a.getColorStateList(R.styleable.SucHeaderMixin_sucHeaderTextColor);
if (headerTextColor != null) {
setTextColor(headerTextColor);
}
+ }
- a.recycle();
+ private void updateAutoTextSizeWithPartnerConfig() {
+ Context context = templateLayout.getContext();
+ if (!PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)
+ || !PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context)) {
+ autoTextSizeEnabled = false;
+ return;
+ }
+ // overridden by partner resource
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_ENABLED)) {
+ autoTextSizeEnabled =
+ PartnerConfigHelper.get(context)
+ .getBoolean(
+ context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_ENABLED, autoTextSizeEnabled);
+ }
+ if (!autoTextSizeEnabled) {
+ return;
+ }
+
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_TEXT_SIZE)) {
+ headerAutoSizeMaxTextSizeInPx =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_TEXT_SIZE);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MIN_TEXT_SIZE)) {
+ headerAutoSizeMinTextSizeInPx =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MIN_TEXT_SIZE);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_LINE_SPACING_EXTRA)) {
+ headerAutoSizeLineExtraSpacingInPx =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_LINE_SPACING_EXTRA);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_LINE_OF_MAX_SIZE)) {
+ headerAutoSizeMaxLineOfMaxSize =
+ PartnerConfigHelper.get(context)
+ .getInteger(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_LINE_OF_MAX_SIZE, 0);
+ }
+ if ((headerAutoSizeMaxLineOfMaxSize < 1)
+ || (headerAutoSizeMinTextSizeInPx <= 0)
+ || (headerAutoSizeMaxTextSizeInPx < headerAutoSizeMinTextSizeInPx)) {
+ Log.w("HeaderMixin", "Invalid configs, disable auto text size.");
+ autoTextSizeEnabled = false;
+ }
}
/**
- * Tries to apply the partner customizations to the header text and background if the layout of
- * this {@link HeaderMixin} is set to apply partner heavy theme resource.
+ * Applies the partner customizations to the header text (contains text alignment), background,
+ * and margin. If apply heavy theme resource, it will apply all partner customizations, otherwise,
+ * only apply alignment style. In addition, if only enable extended customized flag, the margin
+ * style will be applied.
*/
public void tryApplyPartnerCustomizationStyle() {
- if (!PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)) {
- return;
- }
-
TextView header = templateLayout.findManagedViewById(R.id.suc_layout_title);
- if (header != null) {
- HeaderAreaStyler.applyPartnerCustomizationHeaderStyle(header);
+ boolean partnerLightThemeLayout = PartnerStyleHelper.isPartnerLightThemeLayout(templateLayout);
+ boolean partnerHeavyThemeLayout = PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout);
+ if (partnerHeavyThemeLayout) {
+ View headerAreaView = templateLayout.findManagedViewById(R.id.sud_layout_header);
+ HeaderAreaStyler.applyPartnerCustomizationHeaderHeavyStyle(header);
+ HeaderAreaStyler.applyPartnerCustomizationHeaderAreaStyle((ViewGroup) headerAreaView);
+ LayoutStyler.applyPartnerCustomizationExtraPaddingStyle(headerAreaView);
+ // overlay the Auto size config settings
+ updateAutoTextSizeWithPartnerConfig();
+ } else if (partnerLightThemeLayout) {
+ HeaderAreaStyler.applyPartnerCustomizationHeaderLightStyle(header);
}
- LinearLayout headerLayout = templateLayout.findManagedViewById(R.id.sud_layout_header);
- if (headerLayout != null) {
- HeaderAreaStyler.applyPartnerCustomizationHeaderAreaStyle(headerLayout);
+ if (autoTextSizeEnabled) {
+ // Override the text size setting of the header
+ autoAdjustTextSize(header);
}
}
- /** @return The TextView displaying the header. */
+ /** Returns the TextView displaying the header. */
public TextView getTextView() {
return (TextView) templateLayout.findManagedViewById(R.id.suc_layout_title);
}
@@ -94,11 +175,15 @@ public class HeaderMixin implements Mixin {
/**
* Sets the header text. This can also be set via the XML attribute {@code app:sucHeaderText}.
*
- * @param title The resource ID of the text to be set as header.
+ * @param title The resource ID of the text to be set as header
*/
public void setText(int title) {
final TextView titleView = getTextView();
if (titleView != null) {
+ if (autoTextSizeEnabled) {
+ // Override the text size setting of the header
+ autoAdjustTextSize(titleView);
+ }
titleView.setText(title);
}
}
@@ -106,16 +191,58 @@ public class HeaderMixin implements Mixin {
/**
* Sets the header text. This can also be set via the XML attribute {@code app:sucHeaderText}.
*
- * @param title The text to be set as header.
+ * @param title The text to be set as header
*/
public void setText(CharSequence title) {
final TextView titleView = getTextView();
if (titleView != null) {
+ if (autoTextSizeEnabled) {
+ // Override the text size setting of the header
+ autoAdjustTextSize(titleView);
+ }
titleView.setText(title);
}
}
- /** @return The current header text. */
+ private void autoAdjustTextSize(TextView titleView) {
+ if (titleView == null) {
+ return;
+ }
+ // preset as the max size
+ titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, headerAutoSizeMaxTextSizeInPx);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ titleView.setLineHeight(
+ Math.round(headerAutoSizeLineExtraSpacingInPx + headerAutoSizeMaxTextSizeInPx));
+ }
+ titleView.setMaxLines(AUTO_SIZE_DEFAULT_MAX_LINES);
+
+ // reset text size if the line count for max text size > headerAutoSizeMaxLineOfMaxTextSize
+ titleView
+ .getViewTreeObserver()
+ .addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ // Remove listener to avoid this called every frame
+ titleView.getViewTreeObserver().removeOnPreDrawListener(this);
+ int lineCount = titleView.getLineCount();
+ if (lineCount > headerAutoSizeMaxLineOfMaxSize) {
+ // reset text size
+ titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, headerAutoSizeMinTextSizeInPx);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ titleView.setLineHeight(
+ Math.round(
+ headerAutoSizeLineExtraSpacingInPx + headerAutoSizeMinTextSizeInPx));
+ }
+ titleView.invalidate();
+ return false; // false to skip this frame
+ }
+ return true;
+ }
+ });
+ }
+
+ /** Returns the current header text. */
public CharSequence getText() {
final TextView titleView = getTextView();
return titleView != null ? titleView.getText() : null;
@@ -133,7 +260,7 @@ public class HeaderMixin implements Mixin {
* Sets the color of the header text. This can also be set via XML using {@code
* app:sucHeaderTextColor}.
*
- * @param color The text color of the header.
+ * @param color The text color of the header
*/
public void setTextColor(ColorStateList color) {
final TextView titleView = getTextView();
@@ -142,7 +269,11 @@ public class HeaderMixin implements Mixin {
}
}
- /** Sets the background color of the header's parent LinearLayout */
+ /**
+ * Sets the background color of the header's parent LinearLayout.
+ *
+ * @param color The background color of the header's parent LinearLayout
+ */
public void setBackgroundColor(int color) {
final TextView titleView = getTextView();
if (titleView != null) {
diff --git a/main/src/com/google/android/setupdesign/template/IconMixin.java b/main/src/com/google/android/setupdesign/template/IconMixin.java
index dca934f..75e624a 100644
--- a/main/src/com/google/android/setupdesign/template/IconMixin.java
+++ b/main/src/com/google/android/setupdesign/template/IconMixin.java
@@ -22,12 +22,13 @@ import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.ColorInt;
-import androidx.annotation.DrawableRes;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.annotation.ColorInt;
+import androidx.annotation.DrawableRes;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.template.Mixin;
import com.google.android.setupdesign.R;
@@ -44,15 +45,17 @@ public class IconMixin implements Mixin {
private final int originalHeight;
private final ImageView.ScaleType originalScaleType;
-
+ private final Context context;
/**
- * @param layout The template layout that this Mixin is a part of.
- * @param attrs XML attributes given to the layout.
- * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+ * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the Icon.
+ *
+ * @param layout The template layout that this Mixin is a part of
+ * @param attrs XML attributes given to the layout
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout
*/
public IconMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
templateLayout = layout;
- final Context context = layout.getContext();
+ context = layout.getContext();
ImageView iconView = getView();
if (iconView != null) {
@@ -65,14 +68,17 @@ public class IconMixin implements Mixin {
}
final TypedArray a =
- context.obtainStyledAttributes(attrs, R.styleable.SudIconMixin, defStyleAttr, 0);
+ context.obtainStyledAttributes(
+ attrs, R.styleable.SudIconMixin, defStyleAttr, /* defStyleRes= */ 0);
- final @DrawableRes int icon = a.getResourceId(R.styleable.SudIconMixin_android_icon, 0);
+ final @DrawableRes int icon =
+ a.getResourceId(R.styleable.SudIconMixin_android_icon, /* defValue= */ 0);
if (icon != 0) {
setIcon(icon);
}
- final boolean upscaleIcon = a.getBoolean(R.styleable.SudIconMixin_sudUpscaleIcon, false);
+ final boolean upscaleIcon =
+ a.getBoolean(R.styleable.SudIconMixin_sudUpscaleIcon, /* defValue= */ false);
setUpscaleIcon(upscaleIcon);
final @ColorInt int iconTint =
@@ -84,38 +90,40 @@ public class IconMixin implements Mixin {
a.recycle();
}
- /**
- * Tries to apply the partner customization to the header icon if the layout of this {@link
- * IconMixin} is set to apply partner heavy theme resource.
- */
+ /** Tries to apply the partner customization to the header icon. */
public void tryApplyPartnerCustomizationStyle() {
- if (!PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)) {
- return;
- }
-
- ImageView iconImage = templateLayout.findManagedViewById(R.id.sud_layout_icon);
- if (iconImage != null) {
- HeaderAreaStyler.applyPartnerCustomizationIconStyle(iconImage);
+ if (PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)) {
+ // apply partner heavy configs
+ HeaderAreaStyler.applyPartnerCustomizationIconStyle(getView(), getContainerView());
+ } else if (PartnerStyleHelper.isPartnerLightThemeLayout(templateLayout)) {
+ // apply partner light configs
+ HeaderAreaStyler.applyPartnerCustomizationIconStyle(getView());
}
}
/**
* Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
*
- * @param icon A drawable icon.
+ * @param icon A drawable icon
*/
public void setIcon(Drawable icon) {
final ImageView iconView = getView();
if (iconView != null) {
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ if (icon != null) {
+ icon.applyTheme(context.getTheme());
+ }
+ }
iconView.setImageDrawable(icon);
iconView.setVisibility(icon != null ? View.VISIBLE : View.GONE);
+ setIconContainerVisibility(iconView.getVisibility());
}
}
/**
* Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
*
- * @param icon A drawable icon resource.
+ * @param icon A drawable icon resource
*/
public void setIcon(@DrawableRes int icon) {
final ImageView iconView = getView();
@@ -124,6 +132,7 @@ public class IconMixin implements Mixin {
// 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);
+ setIconContainerVisibility(iconView.getVisibility());
}
}
@@ -133,7 +142,11 @@ public class IconMixin implements Mixin {
return iconView != null ? iconView.getDrawable() : null;
}
- /** Forces the icon view to be as big as desired in the style. */
+ /**
+ * Forces the icon view to be as big as desired in the style.
+ *
+ * @param shouldUpscaleIcon If scale icon view as desired
+ */
public void setUpscaleIcon(boolean shouldUpscaleIcon) {
final int maxHeight;
final ImageView iconView = getView();
@@ -150,7 +163,11 @@ public class IconMixin implements Mixin {
}
}
- /** Tints the icon on this layout to the given color. */
+ /**
+ * Tints the icon on this layout to the given color.
+ *
+ * @param tint The color to tint the icon
+ */
public void setIconTint(@ColorInt int tint) {
final ImageView iconView = getView();
if (iconView != null) {
@@ -158,7 +175,11 @@ public class IconMixin implements Mixin {
}
}
- /** Sets the content description of the icon view */
+ /**
+ * Sets the content description of the icon view.
+ *
+ * @param description The description char
+ */
public void setContentDescription(CharSequence description) {
final ImageView iconView = getView();
if (iconView != null) {
@@ -172,16 +193,32 @@ public class IconMixin implements Mixin {
return iconView != null ? iconView.getContentDescription() : null;
}
- /** Sets the visibiltiy of the icon view */
+ /**
+ * Sets the visibility of the icon view.
+ *
+ * @param visibility Set it visible or not
+ */
public void setVisibility(int visibility) {
final ImageView iconView = getView();
if (iconView != null) {
iconView.setVisibility(visibility);
+ setIconContainerVisibility(visibility);
}
}
- /** @return The ImageView responsible for displaying the icon. */
+ /** Returns the ImageView responsible for displaying the icon. */
protected ImageView getView() {
return (ImageView) templateLayout.findManagedViewById(R.id.sud_layout_icon);
}
+
+ /** Returns the container of the ImageView responsible for displaying the icon. */
+ protected FrameLayout getContainerView() {
+ return (FrameLayout) templateLayout.findManagedViewById(R.id.sud_layout_icon_container);
+ }
+
+ private void setIconContainerVisibility(int visibility) {
+ if (getContainerView() != null) {
+ getContainerView().setVisibility(visibility);
+ }
+ }
}
diff --git a/main/src/com/google/android/setupdesign/template/IllustrationProgressMixin.java b/main/src/com/google/android/setupdesign/template/IllustrationProgressMixin.java
new file mode 100644
index 0000000..23b9fa0
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/template/IllustrationProgressMixin.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 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.google.android.setupdesign.template;
+
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig.ResourceType;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.partnerconfig.ResourceEntry;
+import com.google.android.setupcompat.template.Mixin;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.view.IllustrationVideoView;
+
+// TODO: remove this mixin after migrate to new GlifLoadingLayout
+/**
+ * A {@link Mixin} for showing a progress illustration.
+ *
+ * @deprecated Will be replaced by GlifLoadingLayout.
+ */
+@TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH)
+@Deprecated
+public class IllustrationProgressMixin implements Mixin {
+
+ private final GlifLayout glifLayout;
+ private final Context context;
+
+ private ProgressConfig progressConfig = ProgressConfig.CONFIG_DEFAULT;
+ private String progressDescription;
+
+ public IllustrationProgressMixin(GlifLayout layout) {
+ this.glifLayout = layout;
+ context = layout.getContext();
+ }
+
+ /**
+ * Sets whether the progress layout is shown. If the progress layout has not been inflated from
+ * the stub, this method will inflate the progress layout.
+ *
+ * @param shown True to show the progress layout, false to set the layout visibiltiy to {@code
+ * GONE}
+ */
+ public void setShown(boolean shown) {
+ if (!shown) {
+ View view = peekProgressIllustrationLayout();
+ if (view != null) {
+ view.setVisibility(GONE);
+ }
+ } else {
+ View view = getProgressIllustrationLayout();
+ if (view != null) {
+ view.setVisibility(VISIBLE);
+
+ if (progressDescription != null) {
+ TextView descriptionView = view.findViewById(R.id.sud_layout_description);
+ if (descriptionView != null) {
+ descriptionView.setVisibility(VISIBLE);
+ descriptionView.setText(progressDescription);
+ }
+ }
+ }
+ }
+ }
+
+ /** Returns true if the progress layout is currently shown. */
+ public boolean isShown() {
+ View view = peekProgressIllustrationLayout();
+ return view != null && view.getVisibility() == VISIBLE;
+ }
+
+ /**
+ * Sets the type of progress illustration.
+ *
+ * @param config {@link ProgressConfig}
+ */
+ public void setProgressConfig(ProgressConfig config) {
+ this.progressConfig = config;
+
+ // When ViewStub not inflated, do nothing. It will set illustration resource when inflate
+ // layout.
+ if (peekProgressIllustrationLayout() != null) {
+ setIllustrationResource();
+ }
+ }
+
+ /**
+ * Sets the description text of progress
+ * @param description the description text.
+ */
+ public void setProgressIllustrationDescription(String description) {
+ progressDescription = description;
+
+ if (isShown()) {
+ final View progressLayout = getProgressIllustrationLayout();
+ if (progressLayout != null) {
+ TextView descriptionView = progressLayout.findViewById(R.id.sud_layout_description);
+ if (description != null) {
+ descriptionView.setVisibility(VISIBLE);
+ descriptionView.setText(description);
+ } else {
+ descriptionView.setVisibility(INVISIBLE);
+ descriptionView.setText(description);
+ }
+ }
+ }
+ }
+
+ @Nullable private View getProgressIllustrationLayout() {
+ final View progressLayout = peekProgressIllustrationLayout();
+ if (progressLayout == null) {
+ final ViewStub viewStub =
+ glifLayout.findManagedViewById(R.id.sud_layout_illustration_progress_stub);
+
+ if (viewStub != null) {
+ viewStub.inflate();
+ setIllustrationResource();
+ }
+ }
+
+ return peekProgressIllustrationLayout();
+ }
+
+ private void setIllustrationResource() {
+ IllustrationVideoView illustrationVideoView =
+ glifLayout.findManagedViewById(R.id.sud_progress_illustration);
+ ProgressBar progressBar = glifLayout.findManagedViewById(R.id.sud_progress_bar);
+
+ PartnerConfigHelper partnerConfigHelper = PartnerConfigHelper.get(context);
+ ResourceEntry resourceEntry =
+ partnerConfigHelper.getIllustrationResourceEntry(
+ context, progressConfig.getPartnerConfig());
+
+ if (resourceEntry != null) {
+ progressBar.setVisibility(GONE);
+ illustrationVideoView.setVisibility(VISIBLE);
+ illustrationVideoView.setVideoResourceEntry(resourceEntry);
+ } else {
+ progressBar.setVisibility(VISIBLE);
+ illustrationVideoView.setVisibility(GONE);
+ }
+ }
+
+ @Nullable private View peekProgressIllustrationLayout() {
+ return glifLayout.findViewById(R.id.sud_layout_progress_illustration);
+ }
+
+ /** The progress config used to maps to different animation */
+ public enum ProgressConfig {
+ CONFIG_DEFAULT(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_DEFAULT),
+ CONFIG_ACCOUNT(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_ACCOUNT),
+ CONFIG_CONNECTION(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_CONNECTION),
+ CONFIG_UPDATE(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_UPDATE);
+
+ private final PartnerConfig config;
+
+ ProgressConfig(PartnerConfig config) {
+ if (config.getResourceType() != ResourceType.ILLUSTRATION) {
+ throw new IllegalArgumentException(
+ "Illustration progress only allow illustration resource");
+ }
+ this.config = config;
+ }
+
+ PartnerConfig getPartnerConfig() {
+ return config;
+ }
+ }
+}
diff --git a/main/src/com/google/android/setupdesign/template/ListMixin.java b/main/src/com/google/android/setupdesign/template/ListMixin.java
index 38928aa..5963a79 100644
--- a/main/src/com/google/android/setupdesign/template/ListMixin.java
+++ b/main/src/com/google/android/setupdesign/template/ListMixin.java
@@ -21,21 +21,24 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.AttrRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.template.Mixin;
import com.google.android.setupdesign.R;
import com.google.android.setupdesign.items.ItemAdapter;
import com.google.android.setupdesign.items.ItemGroup;
import com.google.android.setupdesign.items.ItemInflater;
import com.google.android.setupdesign.util.DrawableLayoutDirectionHelper;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
/** A {@link Mixin} for interacting with ListViews. */
public class ListMixin implements Mixin {
@@ -64,18 +67,56 @@ public class ListMixin implements Mixin {
final ItemGroup inflated = (ItemGroup) new ItemInflater(context).inflate(entries);
setAdapter(new ItemAdapter(inflated));
}
- int dividerInset = a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInset, -1);
- if (dividerInset != -1) {
- setDividerInset(dividerInset);
- } else {
- int dividerInsetStart =
- a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetStart, 0);
- int dividerInsetEnd = a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetEnd, 0);
- setDividerInsets(dividerInsetStart, dividerInsetEnd);
+
+ boolean isDividerDisplay = isDividerShown(context);
+ if (isDividerDisplay) {
+ int dividerInset = a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInset, -1);
+ if (dividerInset != -1) {
+ setDividerInset(dividerInset);
+ } else {
+ int dividerInsetStart =
+ a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetStart, 0);
+ int dividerInsetEnd =
+ a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetEnd, 0);
+
+ if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(templateLayout)) {
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+ dividerInsetStart =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END)) {
+ dividerInsetEnd =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+ }
+ }
+ setDividerInsets(dividerInsetStart, dividerInsetEnd);
+ }
}
a.recycle();
}
+ private boolean isDividerShown(Context context) {
+ if (PartnerStyleHelper.shouldApplyPartnerResource(templateLayout)) {
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN)) {
+ boolean isDividerDisplayed =
+ PartnerConfigHelper.get(context)
+ .getBoolean(context, PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN, true);
+ if (!isDividerDisplayed) {
+ getListView().setDivider(null);
+ return isDividerDisplayed;
+ }
+ }
+ }
+ return true;
+ }
+
/**
* @return The list view contained in the layout, as marked by {@code @android:id/list}. This will
* return {@code null} if the list doesn't exist in the layout.
diff --git a/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java b/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java
index 2040c6b..3c7fdfd 100644
--- a/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java
+++ b/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java
@@ -16,12 +16,12 @@
package com.google.android.setupdesign.template;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.Log;
import android.widget.AbsListView;
import android.widget.ListAdapter;
import android.widget.ListView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupdesign.template.RequireScrollMixin.ScrollHandlingDelegate;
/**
diff --git a/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java b/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java
index 7e55d51..da1b997 100644
--- a/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java
+++ b/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java
@@ -17,12 +17,16 @@
package com.google.android.setupdesign.template;
import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
import android.os.Build;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.Nullable;
+import android.util.AttributeSet;
import android.view.View;
import android.view.ViewStub;
import android.widget.ProgressBar;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.template.Mixin;
import com.google.android.setupdesign.R;
@@ -31,17 +35,64 @@ import com.google.android.setupdesign.R;
public class ProgressBarMixin implements Mixin {
private final TemplateLayout templateLayout;
+ private final boolean useBottomProgressBar;
@Nullable private ColorStateList color;
/** @param layout The layout this mixin belongs to. */
- public ProgressBarMixin(TemplateLayout layout) {
+ public ProgressBarMixin(@NonNull TemplateLayout layout) {
+ this(layout, null, 0);
+ }
+
+ /**
+ * Constructor that allow using the bottom progress bar.
+ *
+ * @param layout The layout this mixin belongs to.
+ * @param useBottomProgressBar Whether to use bottom progress
+ */
+ public ProgressBarMixin(@NonNull TemplateLayout layout, boolean useBottomProgressBar) {
+ templateLayout = layout;
+ this.useBottomProgressBar = useBottomProgressBar;
+ }
+
+ /**
+ * Constructor that provide styled attribute information in this Context's theme.
+ *
+ * @param layout The {@link TemplateLayout} containing this mixin.
+ * @param attrs XML attributes given to the layout.
+ * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+ */
+ public ProgressBarMixin(
+ @NonNull TemplateLayout layout, AttributeSet attrs, @AttrRes int defStyleAttr) {
templateLayout = layout;
+
+ boolean useBottomProgressBar = false;
+ if (attrs != null) {
+ final TypedArray a =
+ layout
+ .getContext()
+ .obtainStyledAttributes(attrs, R.styleable.SudProgressBarMixin, defStyleAttr, 0);
+
+ if (a.hasValue(R.styleable.SudProgressBarMixin_sudUseBottomProgressBar)) {
+ // Set whether we use bottom progress bar or not
+ useBottomProgressBar =
+ a.getBoolean(R.styleable.SudProgressBarMixin_sudUseBottomProgressBar, false);
+ }
+
+ a.recycle();
+
+ // To avoid bottom progressbar bouncing, change the view state from GONE to INVISIBLE
+ setShown(false);
+ }
+
+ this.useBottomProgressBar = useBottomProgressBar;
}
/** @return True if the progress bar is currently shown. */
public boolean isShown() {
- final View progressBar = templateLayout.findManagedViewById(R.id.sud_layout_progress);
+ final View progressBar =
+ templateLayout.findManagedViewById(
+ useBottomProgressBar ? R.id.sud_glif_progress_bar : R.id.sud_layout_progress);
return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
}
@@ -60,7 +111,7 @@ public class ProgressBarMixin implements Mixin {
} else {
View progressBar = peekProgressBar();
if (progressBar != null) {
- progressBar.setVisibility(View.GONE);
+ progressBar.setVisibility(useBottomProgressBar ? View.INVISIBLE : View.GONE);
}
}
}
@@ -73,8 +124,8 @@ public class ProgressBarMixin implements Mixin {
* progress bar built-in.
*/
private ProgressBar getProgressBar() {
- final View progressBar = peekProgressBar();
- if (progressBar == null) {
+ final View progressBarView = peekProgressBar();
+ if (progressBarView == null && !useBottomProgressBar) {
final ViewStub progressBarStub =
(ViewStub) templateLayout.findManagedViewById(R.id.sud_layout_progress_stub);
if (progressBarStub != null) {
@@ -94,7 +145,9 @@ public class ProgressBarMixin implements Mixin {
* or if the template does not contain a progress bar.
*/
public ProgressBar peekProgressBar() {
- return (ProgressBar) templateLayout.findManagedViewById(R.id.sud_layout_progress);
+ return (ProgressBar)
+ templateLayout.findManagedViewById(
+ useBottomProgressBar ? R.id.sud_glif_progress_bar : R.id.sud_layout_progress);
}
/** Sets the color of the indeterminate progress bar. This method is a no-op on SDK < 21. */
diff --git a/main/src/com/google/android/setupdesign/template/RecyclerMixin.java b/main/src/com/google/android/setupdesign/template/RecyclerMixin.java
index 11e9144..b327060 100644
--- a/main/src/com/google/android/setupdesign/template/RecyclerMixin.java
+++ b/main/src/com/google/android/setupdesign/template/RecyclerMixin.java
@@ -21,15 +21,17 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import android.util.AttributeSet;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.template.Mixin;
import com.google.android.setupdesign.DividerItemDecoration;
import com.google.android.setupdesign.GlifLayout;
@@ -38,6 +40,7 @@ import com.google.android.setupdesign.items.ItemHierarchy;
import com.google.android.setupdesign.items.ItemInflater;
import com.google.android.setupdesign.items.RecyclerItemAdapter;
import com.google.android.setupdesign.util.DrawableLayoutDirectionHelper;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
import com.google.android.setupdesign.view.HeaderRecyclerView;
import com.google.android.setupdesign.view.HeaderRecyclerView.HeaderAdapter;
@@ -64,6 +67,7 @@ public class RecyclerMixin implements Mixin {
private int dividerInsetStart;
private int dividerInsetEnd;
+ private boolean isDividerDisplay = true;
/**
* Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this
@@ -86,7 +90,22 @@ public class RecyclerMixin implements Mixin {
header = ((HeaderRecyclerView) recyclerView).getHeader();
}
- this.recyclerView.addItemDecoration(dividerDecoration);
+ isDividerDisplay = isShowItemsDivider();
+ if (isDividerDisplay) {
+ this.recyclerView.addItemDecoration(dividerDecoration);
+ }
+ }
+
+ private boolean isShowItemsDivider() {
+ // Skips to add item decoration if config flag is false.
+ if (PartnerStyleHelper.shouldApplyPartnerResource(templateLayout)) {
+ if (PartnerConfigHelper.get(recyclerView.getContext())
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN)) {
+ return PartnerConfigHelper.get(recyclerView.getContext())
+ .getBoolean(recyclerView.getContext(), PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN, true);
+ }
+ }
+ return true;
}
/**
@@ -108,16 +127,24 @@ public class RecyclerMixin implements Mixin {
final ItemHierarchy inflated = new ItemInflater(context).inflate(entries);
boolean applyPartnerHeavyThemeResource = false;
+ boolean useFullDynamicColor = false;
if (templateLayout instanceof GlifLayout) {
applyPartnerHeavyThemeResource =
((GlifLayout) templateLayout).shouldApplyPartnerHeavyThemeResource();
+ useFullDynamicColor = ((GlifLayout) templateLayout).useFullDynamicColor();
}
final RecyclerItemAdapter adapter =
- new RecyclerItemAdapter(inflated, applyPartnerHeavyThemeResource);
+ new RecyclerItemAdapter(inflated, applyPartnerHeavyThemeResource, useFullDynamicColor);
adapter.setHasStableIds(a.getBoolean(R.styleable.SudRecyclerMixin_sudHasStableIds, false));
setAdapter(adapter);
}
+
+ if (!isDividerDisplay) {
+ a.recycle();
+ return;
+ }
+
int dividerInset = a.getDimensionPixelSize(R.styleable.SudRecyclerMixin_sudDividerInset, -1);
if (dividerInset != -1) {
setDividerInset(dividerInset);
@@ -126,6 +153,23 @@ public class RecyclerMixin implements Mixin {
a.getDimensionPixelSize(R.styleable.SudRecyclerMixin_sudDividerInsetStart, 0);
int dividerInsetEnd =
a.getDimensionPixelSize(R.styleable.SudRecyclerMixin_sudDividerInsetEnd, 0);
+
+ if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(templateLayout)) {
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+ dividerInsetStart =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END)) {
+ dividerInsetEnd =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+ }
+ }
setDividerInsets(dividerInsetStart, dividerInsetEnd);
}
@@ -172,7 +216,7 @@ public class RecyclerMixin implements Mixin {
* @return The adapter, or {@code null} if the recycler view has no adapter.
*/
public Adapter<? extends ViewHolder> getAdapter() {
- @SuppressWarnings("unchecked") // RecyclerView.getAdapter returns raw type :(
+ // RecyclerView.getAdapter returns raw type :(
final RecyclerView.Adapter<? extends ViewHolder> adapter = recyclerView.getAdapter();
if (adapter instanceof HeaderAdapter) {
return ((HeaderAdapter<? extends ViewHolder>) adapter).getWrappedAdapter();
diff --git a/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java b/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java
index 71094cf..b7a8d4a 100644
--- a/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java
+++ b/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java
@@ -16,10 +16,10 @@
package com.google.android.setupdesign.template;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupdesign.template.RequireScrollMixin.ScrollHandlingDelegate;
/**
diff --git a/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java b/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java
index f1c7cbe..62c503c 100644
--- a/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java
+++ b/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java
@@ -19,12 +19,12 @@ package com.google.android.setupdesign.template;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.template.Mixin;
diff --git a/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java b/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java
index 0fbe5ce..852656f 100644
--- a/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java
+++ b/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java
@@ -16,10 +16,10 @@
package com.google.android.setupdesign.template;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.Log;
import android.widget.ScrollView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupdesign.template.RequireScrollMixin.ScrollHandlingDelegate;
import com.google.android.setupdesign.view.BottomScrollView;
import com.google.android.setupdesign.view.BottomScrollView.BottomScrollListener;
diff --git a/main/src/com/google/android/setupdesign/transition/TransitionHelper.java b/main/src/com/google/android/setupdesign/transition/TransitionHelper.java
new file mode 100644
index 0000000..ec2c480
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/transition/TransitionHelper.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2020 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.google.android.setupdesign.transition;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.Fragment;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.Window;
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.material.transition.platform.MaterialSharedAxis;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.BuildCompatUtils;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.ThemeHelper;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Helper class for apply the transition to the pages which uses platform version. */
+public class TransitionHelper {
+
+ private static final String TAG = "TransitionHelper";
+
+ /*
+ * In Setup Wizard, all Just-a-sec style screens (i.e. screens that has an indeterminate
+ * progress bar and automatically finishes itself), should do a cross-fade when entering or
+ * exiting the screen. For all other screens, the transition should be a slide-in-from-right
+ * or customized.
+ *
+ * We use two different ways to override the transitions. The first is calling
+ * overridePendingTransition in code, and the second is using windowAnimationStyle in the theme.
+ * They have the following priority when framework is figuring out what transition to use:
+ * 1. overridePendingTransition, entering activity (highest priority)
+ * 2. overridePendingTransition, exiting activity
+ * 3. windowAnimationStyle, entering activity
+ * 4. windowAnimationStyle, exiting activity
+ *
+ * This is why, in general, overridePendingTransition is used to specify the fade animation,
+ * while windowAnimationStyle is used to specify the slide transition. This way fade animation
+ * will take priority over the slide animation.
+ *
+ * Below are types of animation when switching activities. These are return values for
+ * {@link #getTransition()}. Each of these values represents 4 animations: (backward exit,
+ * backward enter, forward exit, forward enter).
+ *
+ * We override the transition in the following flow
+ * +--------------+-------------------------+--------------------------+
+ * | | going forward | going backward |
+ * +--------------+-------------------------+--------------------------+
+ * | old activity | startActivity(OnResult) | onActivityResult |
+ * +--------------+-------------------------+--------------------------+
+ * | new activity | onStart | finish (RESULT_CANCELED) |
+ * +--------------+-------------------------+--------------------------+
+ */
+
+ /** The constant of transition type. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TRANSITION_NONE,
+ TRANSITION_NO_OVERRIDE,
+ TRANSITION_FRAMEWORK_DEFAULT,
+ TRANSITION_SLIDE,
+ TRANSITION_FADE,
+ TRANSITION_FRAMEWORK_DEFAULT_PRE_P,
+ TRANSITION_CAPTIVE,
+ })
+ public @interface TransitionType {}
+
+ /** No transition, as in overridePendingTransition(0, 0). */
+ public static final int TRANSITION_NONE = -1;
+
+ /**
+ * No override. If this is specified as the transition, overridePendingTransition will not be
+ * called.
+ */
+ public static final int TRANSITION_NO_OVERRIDE = 0;
+
+ /**
+ * Override the transition to the framework default. This values are read from {@link
+ * android.R.style#Animation_Activity}.
+ */
+ public static final int TRANSITION_FRAMEWORK_DEFAULT = 1;
+
+ /** Override the transition to a slide-in-from-right (or from-left for RTL locales). */
+ public static final int TRANSITION_SLIDE = 2;
+
+ /**
+ * Override the transition to fade in the new activity, while keeping the old activity. Setup
+ * wizard does not use cross fade to avoid the bright-dim-bright effect when transitioning between
+ * two screens that look similar.
+ */
+ public static final int TRANSITION_FADE = 3;
+
+ /** Override the transition to the old framework default pre P. */
+ public static final int TRANSITION_FRAMEWORK_DEFAULT_PRE_P = 4;
+
+ /**
+ * Override the transition to the specific transition and the transition type will depends on the
+ * partner resource.
+ */
+ // TODO: Add new partner resource to determine which transition type would be apply.
+ public static final int TRANSITION_CAPTIVE = 5;
+
+ /**
+ * No override. If this is specified as the transition, the enter/exit transition of the window
+ * will not be set and keep original behavior.
+ */
+ public static final int CONFIG_TRANSITION_NONE = 0;
+
+ /** Override the transition to the specific type that will depend on the partner resource. */
+ public static final int CONFIG_TRANSITION_SHARED_X_AXIS = 1;
+
+ /**
+ * Passed in an intent as EXTRA_ACTIVITY_OPTIONS. This is the {@link ActivityOptions} of the
+ * transition used in {@link Activity#startActivity} or {@link Activity#startActivityForResult}.
+ */
+ public static final String EXTRA_ACTIVITY_OPTIONS = "sud:activity_options";
+
+ /** A flag to avoid the {@link Activity#finish} been called more than once. */
+ @VisibleForTesting static boolean isFinishCalled = false;
+
+ /** A flag to avoid the {@link Activity#startActivity} called more than once. */
+ @VisibleForTesting static boolean isStartActivity = false;
+
+ /** A flag to avoid the {@link Activity#startActivityForResult} called more than once. */
+ @VisibleForTesting static boolean isStartActivityForResult = false;
+
+ private TransitionHelper() {}
+
+ /**
+ * Apply the transition for going forward which is decided by partner resource {@link
+ * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+ * The default transition that will be applied is {@link #TRANSITION_SLIDE}. The timing to apply
+ * the transition is going forward from the previous activity to this, or going forward from this
+ * activity to the next.
+ *
+ * <p>For example, in the flow below, the forward transitions will be applied to all arrows
+ * pointing to the right. Previous screen --> This screen --> Next screen
+ */
+ @TargetApi(VERSION_CODES.LOLLIPOP)
+ public static void applyForwardTransition(Activity activity) {
+ applyForwardTransition(activity, TRANSITION_CAPTIVE);
+ }
+
+ /**
+ * Apply the transition for going forward which is decided by partner resource {@link
+ * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+ * The default transition that will be applied is {@link #CONFIG_TRANSITION_NONE}. The timing to
+ * apply the transition is going forward from the previous {@link Fragment} to this, or going
+ * forward from this {@link Fragment} to the next.
+ */
+ @TargetApi(VERSION_CODES.M)
+ public static void applyForwardTransition(Fragment fragment) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ if (getConfigTransitionType(fragment.getContext()) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ MaterialSharedAxis exitTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+ fragment.setExitTransition(exitTransition);
+
+ MaterialSharedAxis enterTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+ fragment.setEnterTransition(enterTransition);
+ } else {
+ Log.w(TAG, "Not apply the forward transition for platform fragment.");
+ }
+ } else {
+ Log.w(
+ TAG,
+ "Not apply the forward transition for platform fragment. The API is supported from"
+ + " Android Sdk "
+ + VERSION_CODES.M);
+ }
+ }
+
+ /**
+ * Apply the transition for going forward. This is applied when going forward from the previous
+ * activity to this, or going forward from this activity to the next.
+ *
+ * <p>For example, in the flow below, the forward transitions will be applied to all arrows
+ * pointing to the right. Previous screen --> This screen --> Next screen
+ */
+ @TargetApi(VERSION_CODES.LOLLIPOP)
+ public static void applyForwardTransition(Activity activity, @TransitionType int transitionId) {
+ if (transitionId == TRANSITION_SLIDE) {
+ activity.overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
+ } else if (transitionId == TRANSITION_FADE) {
+ activity.overridePendingTransition(android.R.anim.fade_in, R.anim.sud_stay);
+ } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT) {
+ TypedArray typedArray =
+ activity.obtainStyledAttributes(
+ android.R.style.Animation_Activity,
+ new int[] {
+ android.R.attr.activityOpenEnterAnimation, android.R.attr.activityOpenExitAnimation
+ });
+ activity.overridePendingTransition(
+ typedArray.getResourceId(/* index= */ 0, /* defValue= */ 0),
+ typedArray.getResourceId(/* index= */ 1, /* defValue= */ 0));
+ typedArray.recycle();
+ } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT_PRE_P) {
+ activity.overridePendingTransition(
+ R.anim.sud_pre_p_activity_open_enter, R.anim.sud_pre_p_activity_open_exit);
+ } else if (transitionId == TRANSITION_NONE) {
+ // For TRANSITION_NONE, turn off the transition
+ activity.overridePendingTransition(/* enterAnim= */ 0, /* exitAnim= */ 0);
+ } else if (transitionId == TRANSITION_CAPTIVE) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ // 1. Do not change the transition behavior by default
+ // 2. If the flag present, apply the transition from transition type
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ Window window = activity.getWindow();
+ if (window != null) {
+ MaterialSharedAxis exitTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+ window.setExitTransition(exitTransition);
+
+ window.setAllowEnterTransitionOverlap(true);
+
+ MaterialSharedAxis enterTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+ window.setEnterTransition(enterTransition);
+ } else {
+ Log.w(TAG, "applyForwardTransition: Invalid window=" + window);
+ }
+ }
+ } else {
+ Log.w(TAG, "This API is supported from Android Sdk " + VERSION_CODES.LOLLIPOP);
+ }
+ }
+ // For TRANSITION_NO_OVERRIDE or other values, do not override the transition
+ }
+
+ /**
+ * Apply the transition for going backward which is decided by partner resource {@link
+ * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+ * The default transition that will be applied is {@link #TRANSITION_SLIDE}. The timing to apply
+ * the transition is going backward from the next activity to this, or going backward from this
+ * activity to the previous.
+ *
+ * <p>For example, in the flow below, the backward transitions will be applied to all arrows
+ * pointing to the left. Previous screen <-- This screen <-- Next screen
+ */
+ @TargetApi(VERSION_CODES.LOLLIPOP)
+ public static void applyBackwardTransition(Activity activity) {
+ applyBackwardTransition(activity, TRANSITION_CAPTIVE);
+ }
+
+ /**
+ * Apply the transition for going backward which is decided by partner resource {@link
+ * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+ * The default transition that will be applied is {@link #CONFIG_TRANSITION_NONE}. The timing to
+ * apply the transition is going backward from the next {@link Fragment} to this, or going
+ * backward from this {@link Fragment} to the previous.
+ */
+ @TargetApi(VERSION_CODES.M)
+ public static void applyBackwardTransition(Fragment fragment) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ if (getConfigTransitionType(fragment.getContext()) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ MaterialSharedAxis returnTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+ fragment.setReturnTransition(returnTransition);
+
+ MaterialSharedAxis reenterTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+ fragment.setReenterTransition(reenterTransition);
+ } else {
+ Log.w(TAG, "Not apply the backward transition for platform fragment.");
+ }
+ } else {
+ Log.w(
+ TAG,
+ "Not apply the backward transition for platform fragment. The API is supported from"
+ + " Android Sdk "
+ + VERSION_CODES.M);
+ }
+ }
+
+ /**
+ * Apply the transition for going backward. This is applied when going backward from the next
+ * activity to this, or going backward from this activity to the previous.
+ *
+ * <p>For example, in the flow below, the backward transitions will be applied to all arrows
+ * pointing to the left. Previous screen <-- This screen <-- Next screen
+ */
+ @TargetApi(VERSION_CODES.LOLLIPOP)
+ public static void applyBackwardTransition(Activity activity, @TransitionType int transitionId) {
+ if (transitionId == TRANSITION_SLIDE) {
+ activity.overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out);
+ } else if (transitionId == TRANSITION_FADE) {
+ activity.overridePendingTransition(R.anim.sud_stay, android.R.anim.fade_out);
+ } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT) {
+ TypedArray typedArray =
+ activity.obtainStyledAttributes(
+ android.R.style.Animation_Activity,
+ new int[] {
+ android.R.attr.activityCloseEnterAnimation,
+ android.R.attr.activityCloseExitAnimation
+ });
+ activity.overridePendingTransition(
+ typedArray.getResourceId(/* index= */ 0, /* defValue= */ 0),
+ typedArray.getResourceId(/* index= */ 1, /* defValue= */ 0));
+ typedArray.recycle();
+ } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT_PRE_P) {
+ activity.overridePendingTransition(
+ R.anim.sud_pre_p_activity_close_enter, R.anim.sud_pre_p_activity_close_exit);
+ } else if (transitionId == TRANSITION_NONE) {
+ // For TRANSITION_NONE, turn off the transition
+ activity.overridePendingTransition(/* enterAnim= */ 0, /* exitAnim= */ 0);
+ } else if (transitionId == TRANSITION_CAPTIVE) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ // 1. Do not change the transition behavior by default
+ // 2. If the flag present, apply the transition from transition type
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ Window window = activity.getWindow();
+ if (window != null) {
+ MaterialSharedAxis reenterTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+ window.setReenterTransition(reenterTransition);
+
+ MaterialSharedAxis returnTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+ window.setReturnTransition(returnTransition);
+ } else {
+ Log.w(TAG, "applyBackwardTransition: Invalid window=" + window);
+ }
+ }
+ } else {
+ Log.w(TAG, "This API is supported from Android Sdk " + VERSION_CODES.LOLLIPOP);
+ }
+ }
+ // For TRANSITION_NO_OVERRIDE or other values, do not override the transition
+ }
+
+ /**
+ * A wrapper method, create an {@link android.app.ActivityOptions} to transition between
+ * activities as the {@link ActivityOptions} parameter of {@link Activity#startActivity}.
+ *
+ * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null.
+ * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run
+ * the given Intent.
+ */
+ public static void startActivityWithTransition(Activity activity, Intent intent) {
+ startActivityWithTransition(activity, intent, /* overrideActivityOptions= */ null);
+ }
+
+ /**
+ * A wrapper method, create an {@link android.app.ActivityOptions} to transition between
+ * activities as the {@link ActivityOptions} parameter of {@link Activity#startActivity}.
+ *
+ * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null.
+ * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run
+ * the given Intent.
+ */
+ public static void startActivityWithTransition(
+ Activity activity, Intent intent, Bundle overrideActivityOptions) {
+ if (activity == null) {
+ throw new IllegalArgumentException("Invalid activity=" + activity);
+ }
+
+ if (intent == null) {
+ throw new IllegalArgumentException("Invalid intent=" + intent);
+ }
+
+ if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) {
+ Log.e(
+ TAG,
+ "The transition won't take effect since the WindowManager does not allow override new"
+ + " task transitions");
+ }
+
+ if (!isStartActivity) {
+ isStartActivity = true;
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ if (activity.getWindow() != null
+ && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+ Log.w(
+ TAG,
+ "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+ }
+
+ Bundle bundleActivityOptions;
+ if (overrideActivityOptions != null) {
+ bundleActivityOptions = overrideActivityOptions;
+ } else {
+ bundleActivityOptions = makeActivityOptions(activity, intent);
+ }
+ intent.putExtra(EXTRA_ACTIVITY_OPTIONS, (Parcelable) bundleActivityOptions);
+ activity.startActivity(intent, bundleActivityOptions);
+ } else {
+ Log.w(
+ TAG,
+ "Fallback to using startActivity due to the"
+ + " ActivityOptions#makeSceneTransitionAnimation is supported from Android Sdk "
+ + VERSION_CODES.LOLLIPOP);
+ startActivityWithTransitionInternal(activity, intent, overrideActivityOptions);
+ }
+ } else {
+ startActivityWithTransitionInternal(activity, intent, overrideActivityOptions);
+ }
+ }
+ isStartActivity = false;
+ }
+
+ private static void startActivityWithTransitionInternal(
+ Activity activity, Intent intent, Bundle overrideActivityOptions) {
+ try {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS
+ && overrideActivityOptions != null) {
+ activity.startActivity(intent, overrideActivityOptions);
+ } else {
+ activity.startActivity(intent);
+ }
+ } else {
+ Log.w(
+ TAG,
+ "Fallback to using startActivity(Intent) due to the startActivity(Intent, Bundle) is"
+ + " supported from Android Sdk "
+ + VERSION_CODES.JELLY_BEAN);
+ activity.startActivity(intent);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity not found when startActivity with transition.");
+ isStartActivity = false;
+ throw e;
+ }
+ }
+
+ public static void startActivityForResultWithTransition(
+ Activity activity, Intent intent, int requestCode) {
+ startActivityForResultWithTransition(
+ activity, intent, requestCode, /* overrideActivityOptions= */ null);
+ }
+
+ /**
+ * A wrapper method, create an {@link android.app.ActivityOptions} to transition between
+ * activities as the {@code activityOptions} parameter of {@link Activity#startActivityForResult}.
+ *
+ * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null.
+ * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run
+ * the given Intent.
+ */
+ public static void startActivityForResultWithTransition(
+ Activity activity, Intent intent, int requestCode, Bundle overrideActivityOptions) {
+ if (activity == null) {
+ throw new IllegalArgumentException("Invalid activity=" + activity);
+ }
+
+ if (intent == null) {
+ throw new IllegalArgumentException("Invalid intent=" + intent);
+ }
+
+ if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) {
+ Log.e(
+ TAG,
+ "The transition won't take effect since the WindowManager does not allow override new"
+ + " task transitions");
+ }
+
+ if (!isStartActivityForResult) {
+ isStartActivityForResult = true;
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ if (activity.getWindow() != null
+ && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+ Log.w(
+ TAG,
+ "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+ }
+
+ Bundle bundleActivityOptions;
+ if (overrideActivityOptions != null) {
+ bundleActivityOptions = overrideActivityOptions;
+ } else {
+ bundleActivityOptions = makeActivityOptions(activity, intent);
+ }
+ intent.putExtra(EXTRA_ACTIVITY_OPTIONS, (Parcelable) bundleActivityOptions);
+ activity.startActivityForResult(intent, requestCode, bundleActivityOptions);
+ } else {
+ Log.w(
+ TAG,
+ "Fallback to using startActivityForResult API due to the"
+ + " ActivityOptions#makeSceneTransitionAnimation is supported from Android Sdk "
+ + VERSION_CODES.LOLLIPOP);
+ startActivityForResultWithTransitionInternal(
+ activity, intent, requestCode, overrideActivityOptions);
+ }
+ } else {
+ startActivityForResultWithTransitionInternal(
+ activity, intent, requestCode, overrideActivityOptions);
+ }
+ isStartActivityForResult = false;
+ }
+ }
+
+ private static void startActivityForResultWithTransitionInternal(
+ Activity activity, Intent intent, int requestCode, Bundle overrideActivityOptions) {
+ try {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS
+ && overrideActivityOptions != null) {
+ activity.startActivityForResult(intent, requestCode, overrideActivityOptions);
+ } else {
+ activity.startActivityForResult(intent, requestCode);
+ }
+ } else {
+ Log.w(
+ TAG,
+ "Fallback to using startActivityForResult(Intent) due to the"
+ + " startActivityForResult(Intent,int) is supported from Android Sdk "
+ + VERSION_CODES.JELLY_BEAN);
+ activity.startActivityForResult(intent, requestCode);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity not found when startActivityForResult with transition.");
+ isStartActivityForResult = false;
+ throw e;
+ }
+ }
+
+ /**
+ * A wrapper method, calling {@link Activity#finishAfterTransition()} to trigger exit transition
+ * when running in Android S and the transition type {link #CONFIG_TRANSITION_SHARED_X_AXIS}.
+ *
+ * @throws IllegalArgumentException is thrown when {@code activity} is null.
+ */
+ public static void finishActivity(Activity activity) {
+ if (activity == null) {
+ throw new IllegalArgumentException("Invalid activity=" + activity);
+ }
+
+ // Avoids finish been called more than once.
+ if (!isFinishCalled) {
+ isFinishCalled = true;
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
+ && getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ activity.finishAfterTransition();
+ } else {
+ Log.w(
+ TAG,
+ "Fallback to using Activity#finish() due to the"
+ + " Activity#finishAfterTransition() is supported from Android Sdk "
+ + VERSION_CODES.LOLLIPOP);
+ activity.finish();
+ }
+ }
+ isFinishCalled = false;
+ }
+
+ /**
+ * Returns the transition type from the {@link PartnerConfig#CONFIG_TRANSITION_TYPE} partner
+ * resource on Android S, otherwise returns {@link #CONFIG_TRANSITION_NONE}.
+ */
+ public static int getConfigTransitionType(Context context) {
+ return BuildCompatUtils.isAtLeastS() && ThemeHelper.shouldApplyExtendedPartnerConfig(context)
+ ? PartnerConfigHelper.get(context)
+ .getInteger(context, PartnerConfig.CONFIG_TRANSITION_TYPE, CONFIG_TRANSITION_NONE)
+ : CONFIG_TRANSITION_NONE;
+ }
+
+ /**
+ * A wrapper method, create a {@link Bundle} from {@link ActivityOptions} to transition between
+ * Activities using cross-Activity scene animations. This {@link Bundle} that can be used with
+ * {@link Context#startActivity(Intent, Bundle)} and related methods.
+ *
+ * <p>Example usage:
+ *
+ * <pre>{@code
+ * Intent intent = new Intent("com.example.NEXT_ACTIVITY");
+ * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent, null);
+ * }</pre>
+ *
+ * <p>Unexpected usage:
+ *
+ * <pre>{@code
+ * Intent intent = new Intent("com.example.NEXT_ACTIVITY");
+ * Intent intent2 = new Intent("com.example.NEXT_ACTIVITY");
+ * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent2, null);
+ * }</pre>
+ */
+ @Nullable
+ public static Bundle makeActivityOptions(Activity activity, Intent intent) {
+ return makeActivityOptions(activity, intent, false);
+ }
+
+ /**
+ * A wrapper method, create a {@link Bundle} from {@link ActivityOptions} to transition between
+ * Activities using cross-Activity scene animations. This {@link Bundle} that can be used with
+ * {@link Context#startActivity(Intent, Bundle)} and related methods. When this {@code activity}
+ * is a no UI activity(the activity doesn't inflate any layouts), you will need to pass the bundle
+ * coming from previous UI activity as the {@link ActivityOptions}, otherwise, the transition
+ * won't be take effect. The {@code overrideActivityOptionsFromIntent} is supporting this purpose
+ * to return the {@link ActivityOptions} instead of creating from this no UI activity while the
+ * transition is apply {@link #CONFIG_TRANSITION_SHARED_X_AXIS} config. Moreover, the
+ * startActivity*WithTransition relative methods and {@link #makeActivityOptions} will put {@link
+ * ActivityOptions} to the {@code intent} by default, you can get the {@link ActivityOptions}
+ * which makes from previous activity by accessing {@link #EXTRA_ACTIVITY_OPTIONS} extra from
+ * {@link Activity#getIntent()}.
+ *
+ * <p>Example usage of a no UI activity:
+ *
+ * <pre>{@code
+ * Intent intent = new Intent("com.example.NEXT_ACTIVITY");
+ * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent, true);
+ * }</pre>
+ */
+ @Nullable
+ public static Bundle makeActivityOptions(
+ Activity activity, Intent intent, boolean overrideActivityOptionsFromIntent) {
+ Bundle resultBundle = null;
+ if (activity == null || intent == null) {
+ return resultBundle;
+ }
+
+ if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) {
+ Log.e(
+ TAG,
+ "The transition won't take effect since the WindowManager does not allow override new"
+ + " task transitions");
+ }
+
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ if (activity.getWindow() != null
+ && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+ Log.w(
+ TAG,
+ "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+ }
+
+ if (overrideActivityOptionsFromIntent && activity.getIntent() != null) {
+ resultBundle = activity.getIntent().getBundleExtra(EXTRA_ACTIVITY_OPTIONS);
+ } else {
+ resultBundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle();
+ }
+ intent.putExtra(EXTRA_ACTIVITY_OPTIONS, (Parcelable) resultBundle);
+ return resultBundle;
+ }
+ }
+
+ return resultBundle;
+ }
+}
diff --git a/main/src/com/google/android/setupdesign/transition/support/TransitionHelper.java b/main/src/com/google/android/setupdesign/transition/support/TransitionHelper.java
new file mode 100644
index 0000000..8fd9d6e
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/transition/support/TransitionHelper.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 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.google.android.setupdesign.transition.support;
+
+import static com.google.android.setupdesign.transition.TransitionHelper.CONFIG_TRANSITION_SHARED_X_AXIS;
+import static com.google.android.setupdesign.transition.TransitionHelper.getConfigTransitionType;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import androidx.fragment.app.Fragment;
+import android.util.Log;
+import android.view.Window;
+import androidx.annotation.Nullable;
+import androidx.core.app.ActivityOptionsCompat;
+import com.google.android.material.transition.platform.MaterialSharedAxis;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+
+/** Helper class for apply the transition to the pages which uses support library. */
+public class TransitionHelper {
+
+ private static final String TAG = "TransitionHelper";
+
+ private TransitionHelper() {}
+
+ /**
+ * Apply the transition for going forward which is decided by partner resource {@link
+ * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+ * The default transition that will be applied is {@link
+ * com.google.android.setupdesign.transition.TransitionHelper#CONFIG_TRANSITION_NONE}. The timing
+ * to apply the transition is going forward from the previous {@link Fragment} to this, or going
+ * forward from this {@link Fragment} to the next.
+ */
+ @TargetApi(VERSION_CODES.M)
+ public static void applyForwardTransition(Fragment fragment) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ if (CONFIG_TRANSITION_SHARED_X_AXIS == getConfigTransitionType(fragment.getContext())) {
+ MaterialSharedAxis exitTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+ fragment.setExitTransition(exitTransition);
+
+ MaterialSharedAxis enterTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+ fragment.setEnterTransition(enterTransition);
+ } else {
+ Log.w(TAG, "Not apply the forward transition for support lib's fragment.");
+ }
+ } else {
+ Log.w(
+ TAG,
+ "Not apply the forward transition for support lib's fragment. The API is supported from"
+ + " Android Sdk "
+ + VERSION_CODES.M);
+ }
+ }
+
+ /**
+ * Apply the transition for going backward which is decided by partner resource {@link
+ * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+ * The default transition that will be applied is {@link
+ * com.google.android.setupdesign.transition.TransitionHelper#CONFIG_TRANSITION_NONE}. The timing
+ * to apply the transition is going backward from the next {@link Fragment} to this, or going
+ * backward from this {@link Fragment} to the previous.
+ */
+ @TargetApi(VERSION_CODES.M)
+ public static void applyBackwardTransition(Fragment fragment) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ if (CONFIG_TRANSITION_SHARED_X_AXIS == getConfigTransitionType(fragment.getContext())) {
+ MaterialSharedAxis returnTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+ fragment.setReturnTransition(returnTransition);
+
+ MaterialSharedAxis reenterTransition =
+ new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+ fragment.setReenterTransition(reenterTransition);
+ } else {
+ Log.w(TAG, "Not apply the backward transition for support lib's fragment.");
+ }
+ } else {
+ Log.w(
+ TAG,
+ "Not apply the backward transition for support lib's fragment. The API is supported from"
+ + " Android Sdk "
+ + VERSION_CODES.M);
+ }
+ }
+
+ /**
+ * A wrapper method, create an {@link ActivityOptionsCompat} to transition between activities as
+ * the {@link ActivityOptionsCompat} parameter of {@link
+ * androidx.activity.result.ActivityResultLauncher#launch(I, ActivityOptionsCompat)} method.
+ */
+ @Nullable
+ public static ActivityOptionsCompat makeActivityOptionsCompat(Activity activity) {
+ ActivityOptionsCompat activityOptionsCompat = null;
+ if (activity == null) {
+ return activityOptionsCompat;
+ }
+
+ if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ if (activity.getWindow() != null
+ && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+ Log.w(
+ TAG,
+ "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+ }
+
+ activityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity);
+ }
+ }
+
+ return activityOptionsCompat;
+ }
+}
diff --git a/main/src/com/google/android/setupdesign/util/ButtonStyler.java b/main/src/com/google/android/setupdesign/util/ButtonStyler.java
new file mode 100644
index 0000000..8eb92af
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/ButtonStyler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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.google.android.setupdesign.util;
+
+import android.content.Context;
+import android.widget.Button;
+import com.google.android.setupcompat.template.FooterButtonStyleUtils;
+
+/** Utils for buttons in GlifLayout content area to get the style as footer buttons. */
+public class ButtonStyler {
+
+ /** Apply the partner primary button style to given {@code button}. */
+ public static void applyPartnerCustomizationPrimaryButtonStyle(Context context, Button button) {
+ if (button == null || context == null) {
+ return;
+ }
+ FooterButtonStyleUtils.applyPrimaryButtonPartnerResource(
+ context, button, ThemeHelper.shouldApplyDynamicColor(context));
+ }
+
+ /** Apply the partner secondary button style to given {@code button}. */
+ public static void applyPartnerCustomizationSecondaryButtonStyle(Context context, Button button) {
+ if (button == null || context == null) {
+ return;
+ }
+ FooterButtonStyleUtils.applySecondaryButtonPartnerResource(
+ context, button, ThemeHelper.shouldApplyDynamicColor(context));
+ }
+
+ private ButtonStyler() {}
+}
diff --git a/main/src/com/google/android/setupdesign/util/ContentStyler.java b/main/src/com/google/android/setupdesign/util/ContentStyler.java
index 0ab49b8..d390780 100644
--- a/main/src/com/google/android/setupdesign/util/ContentStyler.java
+++ b/main/src/com/google/android/setupdesign/util/ContentStyler.java
@@ -17,19 +17,33 @@
package com.google.android.setupdesign.util;
import android.content.Context;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import com.google.android.setupcompat.partnerconfig.PartnerConfig;
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupdesign.R;
import com.google.android.setupdesign.util.TextViewPartnerStyler.TextPartnerConfigs;
import java.util.Locale;
/**
- * Applies the partner style of content to the given TextView {@code contentText}. The user needs to
- * check if the {@code contentText} should apply partner heavy theme before calling this method.
+ * Applies the partner style of content to the given TextView {@code contentText}. The theme should
+ * set partner heavy theme first, and then the partner style of content would be applied. The user
+ * can also check if the {@code contentText} should apply partner heavy theme before calling this
+ * method.
*/
public final class ContentStyler {
- public static void applyPartnerCustomizationStyle(TextView contentText) {
+ public static void applyBodyPartnerCustomizationStyle(TextView contentText) {
+ if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(contentText)) {
+ return;
+ }
TextViewPartnerStyler.applyPartnerCustomizationStyle(
contentText,
@@ -38,10 +52,149 @@ public final class ContentStyler {
PartnerConfig.CONFIG_CONTENT_LINK_TEXT_COLOR,
PartnerConfig.CONFIG_CONTENT_TEXT_SIZE,
PartnerConfig.CONFIG_CONTENT_FONT_FAMILY,
+ null,
+ null,
ContentStyler.getPartnerContentTextGravity(contentText.getContext())));
}
- public static int getPartnerContentTextGravity(Context context) {
+ /**
+ * Applies the partner heavy style of content info to the given views including content info
+ * container, content info icon and content info text, the given views should be included in a
+ * layout which the same view hierarchy with {@link R.layout#sud_content_info}.
+ *
+ * @param infoContainer A view the container resource of content info
+ * @param infoIcon A image view the icon resource of content info
+ * @param infoText A text view content info resource
+ */
+ public static void applyInfoPartnerCustomizationStyle(
+ @Nullable View infoContainer, @Nullable ImageView infoIcon, TextView infoText) {
+ if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(infoText)) {
+ return;
+ }
+
+ Context context = infoText.getContext();
+
+ boolean textSizeConfigAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_TEXT_SIZE);
+ boolean fontFamilyConfigAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_FONT_FAMILY);
+
+ TextViewPartnerStyler.applyPartnerCustomizationStyle(
+ infoText,
+ new TextPartnerConfigs(
+ null,
+ null,
+ textSizeConfigAvailable ? PartnerConfig.CONFIG_CONTENT_INFO_TEXT_SIZE : null,
+ fontFamilyConfigAvailable ? PartnerConfig.CONFIG_CONTENT_INFO_FONT_FAMILY : null,
+ null,
+ null,
+ 0));
+
+ // TODO: Move CONFIG_CONTENT_INFO_LINE_SPACING_EXTRA to TextPartnerConfigs for
+ // customize
+ boolean isAtLeastP = VERSION.SDK_INT >= VERSION_CODES.P;
+ if (isAtLeastP
+ && PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_LINE_SPACING_EXTRA)) {
+ int textLineSpacingExtraInPx =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_LINE_SPACING_EXTRA);
+
+ float infoTextSizeInPx = infoText.getTextSize();
+ if (textSizeConfigAvailable) {
+ float textSizeInPx =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_TEXT_SIZE, 0);
+ if (textSizeInPx > 0) {
+ infoTextSizeInPx = textSizeInPx;
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ infoText.setLineHeight(Math.round(textLineSpacingExtraInPx + infoTextSizeInPx));
+ }
+ }
+
+ if (infoIcon != null) {
+ ViewGroup.LayoutParams lp = infoIcon.getLayoutParams();
+
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_ICON_SIZE)) {
+ int oldHeight = lp.height;
+ lp.height =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_ICON_SIZE);
+ lp.width = lp.width * (lp.height / oldHeight);
+ infoIcon.setScaleType(ScaleType.FIT_CENTER);
+ }
+
+ boolean partnerConfigAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_ICON_MARGIN_END);
+ if (partnerConfigAvailable && lp instanceof ViewGroup.MarginLayoutParams) {
+ final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+ int endMargin =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_ICON_MARGIN_END);
+ mlp.setMargins(mlp.leftMargin, mlp.topMargin, endMargin, mlp.bottomMargin);
+ }
+ }
+
+ if (infoContainer != null) {
+ float paddingTop;
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_PADDING_TOP)) {
+ paddingTop =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_PADDING_TOP);
+ } else {
+ paddingTop = infoContainer.getPaddingTop();
+ }
+
+ float paddingBottom;
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_PADDING_BOTTOM)) {
+ paddingBottom =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_PADDING_BOTTOM);
+ } else {
+ paddingBottom = infoContainer.getPaddingBottom();
+ }
+
+ if (paddingTop != infoContainer.getPaddingTop()
+ || paddingBottom != infoContainer.getPaddingBottom()) {
+ infoContainer.setPadding(0, (int) paddingTop, 0, (int) paddingBottom);
+ }
+ }
+ }
+
+ /**
+ * Returns the layout margin start from partner config. If the activity of given {@code context}
+ * does not enable the partner heavy theme, then returns the default value from GlifTheme.
+ *
+ * @param context The context of a GlifLayout activity.
+ */
+ public static float getPartnerContentMarginStart(Context context) {
+ // default value is GlifTheme layout margin start.
+ // That is the attr sudMarginStart, and the value is sud_layout_margin_sides.
+ float result = context.getResources().getDimension(R.dimen.sud_layout_margin_sides);
+ if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(context)) {
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+ result =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START, result);
+ }
+ }
+ return result;
+ }
+
+ private static int getPartnerContentTextGravity(Context context) {
String gravity =
PartnerConfigHelper.get(context)
.getString(context, PartnerConfig.CONFIG_CONTENT_LAYOUT_GRAVITY);
diff --git a/main/src/com/google/android/setupdesign/util/DescriptionStyler.java b/main/src/com/google/android/setupdesign/util/DescriptionStyler.java
index 0a05f9d..0a786c6 100644
--- a/main/src/com/google/android/setupdesign/util/DescriptionStyler.java
+++ b/main/src/com/google/android/setupdesign/util/DescriptionStyler.java
@@ -22,13 +22,18 @@ import com.google.android.setupdesign.util.TextViewPartnerStyler.TextPartnerConf
/**
* Applies the partner style of description to the given TextView {@code description}. The user
- * needs to check if the {@code description} should apply partner heavy theme before calling this
- * method.
+ * needs to check if the {@code description} should apply partner heavy theme or light theme before
+ * calling this method, only heavy theme can apply for all configs.
*/
public final class DescriptionStyler {
- public static void applyPartnerCustomizationStyle(TextView description) {
-
+ /**
+ * Applies the partner heavy style of description to the given text view. Must check the current
+ * text view applies partner customized configurations to heavy theme before applying.
+ *
+ * @param description A text view description resource
+ */
+ public static void applyPartnerCustomizationHeavyStyle(TextView description) {
TextViewPartnerStyler.applyPartnerCustomizationStyle(
description,
new TextPartnerConfigs(
@@ -36,6 +41,27 @@ public final class DescriptionStyler {
PartnerConfig.CONFIG_DESCRIPTION_LINK_TEXT_COLOR,
PartnerConfig.CONFIG_DESCRIPTION_TEXT_SIZE,
PartnerConfig.CONFIG_DESCRIPTION_FONT_FAMILY,
+ null,
+ null,
+ PartnerStyleHelper.getLayoutGravity(description.getContext())));
+ }
+
+ /**
+ * Applies the partner light style of description to the given text view. Must check the current
+ * text view applies partner customized configurations to light theme before applying.
+ *
+ * @param description A text view description resource
+ */
+ public static void applyPartnerCustomizationLightStyle(TextView description) {
+ TextViewPartnerStyler.applyPartnerCustomizationLightStyle(
+ description,
+ new TextPartnerConfigs(
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
PartnerStyleHelper.getLayoutGravity(description.getContext())));
}
diff --git a/main/src/com/google/android/setupdesign/util/DynamicColorPalette.java b/main/src/com/google/android/setupdesign/util/DynamicColorPalette.java
new file mode 100644
index 0000000..2db8c75
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/DynamicColorPalette.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 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.google.android.setupdesign.util;
+
+import android.content.Context;
+import androidx.annotation.ColorInt;
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.setupdesign.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** The class to get dynamic colors. */
+public final class DynamicColorPalette {
+
+ @VisibleForTesting static int colorRes = 0;
+
+ private DynamicColorPalette() {}
+
+ /** Dynamic color category. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ColorType.ACCENT,
+ ColorType.PRIMARY_TEXT,
+ ColorType.SECONDARY_TEXT,
+ ColorType.DISABLED_OPTION,
+ ColorType.ERROR_WARNING,
+ ColorType.SUCCESS_DONE,
+ ColorType.FALLBACK_ACCENT,
+ ColorType.BACKGROUND_SURFACE,
+ })
+ public @interface ColorType {
+ int ACCENT = 0;
+ int PRIMARY_TEXT = 1;
+ int SECONDARY_TEXT = 2;
+ int DISABLED_OPTION = 3;
+ int ERROR_WARNING = 4;
+ int SUCCESS_DONE = 5;
+ int FALLBACK_ACCENT = 6;
+ int BACKGROUND_SURFACE = 7;
+ }
+
+ @ColorInt
+ public static int getColor(Context context, @ColorType int dynamicColorCategory) {
+ switch (dynamicColorCategory) {
+ case ColorType.ACCENT:
+ colorRes = R.color.sud_dynamic_color_accent_glif_v3;
+ break;
+ case ColorType.PRIMARY_TEXT:
+ colorRes = R.color.sud_system_primary_text;
+ break;
+ case ColorType.SECONDARY_TEXT:
+ colorRes = R.color.sud_system_secondary_text;
+ break;
+ case ColorType.DISABLED_OPTION:
+ colorRes = R.color.sud_system_tertiary_text_inactive;
+ break;
+ case ColorType.ERROR_WARNING:
+ colorRes = R.color.sud_system_error_warning;
+ break;
+ case ColorType.SUCCESS_DONE:
+ colorRes = R.color.sud_system_success_done;
+ break;
+ case ColorType.FALLBACK_ACCENT:
+ colorRes = R.color.sud_system_fallback_accent;
+ break;
+ case ColorType.BACKGROUND_SURFACE:
+ colorRes = R.color.sud_system_background_surface;
+ break;
+ // fall out
+ }
+
+ return context.getResources().getColor(colorRes);
+ }
+}
diff --git a/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java b/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java
index cd5135d..1b3daac 100644
--- a/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java
+++ b/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java
@@ -16,24 +16,46 @@
package com.google.android.setupdesign.util;
+import static com.google.android.setupcompat.util.BuildCompatUtils.isAtLeastS;
+
import android.content.Context;
-import androidx.annotation.Nullable;
+import android.graphics.drawable.VectorDrawable;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.util.Log;
import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.ImageView.ScaleType;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import com.google.android.setupcompat.partnerconfig.PartnerConfig;
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupdesign.util.TextViewPartnerStyler.TextPartnerConfigs;
/**
- * Helper class to apply the partner customization for the header area widgets. The user needs to
- * check if the header area widgets should apply partner heavy theme before calling these methods.
+ * Applies the partner customization for the header area widgets. The user needs to check if the
+ * header area widgets should apply partner heavy theme or light theme before calling these methods.
*/
public final class HeaderAreaStyler {
- /** Applies the partner style of header text to the given textView {@code header}. */
- public static void applyPartnerCustomizationHeaderStyle(@Nullable TextView header) {
+ private static final String TAG = "HeaderAreaStyler";
+
+ @VisibleForTesting
+ static final String WARN_TO_USE_DRAWABLE =
+ "To achieve scaling icon in SetupDesign lib, should use vector drawable icon from ";
+
+ /**
+ * Applies the partner heavy style of header text to the given textView {@code header}.
+ *
+ * @param header A header text would apply partner heavy style
+ */
+ public static void applyPartnerCustomizationHeaderHeavyStyle(@Nullable TextView header) {
if (header == null) {
return;
@@ -45,38 +67,199 @@ public final class HeaderAreaStyler {
null,
PartnerConfig.CONFIG_HEADER_TEXT_SIZE,
PartnerConfig.CONFIG_HEADER_FONT_FAMILY,
+ PartnerConfig.CONFIG_HEADER_TEXT_MARGIN_TOP,
+ PartnerConfig.CONFIG_HEADER_TEXT_MARGIN_BOTTOM,
PartnerStyleHelper.getLayoutGravity(header.getContext())));
}
- /** Applies the partner style of header background to the given layout {@code headerArea}. */
- public static void applyPartnerCustomizationHeaderAreaStyle(ViewGroup headerArea) {
+ /**
+ * Applies the partner heavy style of description text to the given textView {@code description}.
+ *
+ * @param description A description text would apply partner heavy style
+ */
+ public static void applyPartnerCustomizationDescriptionHeavyStyle(
+ @Nullable TextView description) {
+
+ if (description == null) {
+ return;
+ }
+ TextViewPartnerStyler.applyPartnerCustomizationStyle(
+ description,
+ new TextPartnerConfigs(
+ PartnerConfig.CONFIG_DESCRIPTION_TEXT_COLOR,
+ PartnerConfig.CONFIG_DESCRIPTION_LINK_TEXT_COLOR,
+ PartnerConfig.CONFIG_DESCRIPTION_TEXT_SIZE,
+ PartnerConfig.CONFIG_DESCRIPTION_FONT_FAMILY,
+ PartnerConfig.CONFIG_DESCRIPTION_TEXT_MARGIN_TOP,
+ PartnerConfig.CONFIG_DESCRIPTION_TEXT_MARGIN_BOTTOM,
+ PartnerStyleHelper.getLayoutGravity(description.getContext())));
+ }
+
+ /**
+ * Applies the partner light style of header text to the given textView {@code header}.
+ *
+ * @param header A header text would apply partner light style
+ */
+ public static void applyPartnerCustomizationHeaderLightStyle(@Nullable TextView header) {
+
+ if (header == null) {
+ return;
+ }
+
+ TextViewPartnerStyler.applyPartnerCustomizationLightStyle(
+ header,
+ new TextPartnerConfigs(
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ PartnerStyleHelper.getLayoutGravity(header.getContext())));
+ }
+
+ /**
+ * Applies the partner light style of description text to the given textView {@code description}.
+ *
+ * @param description A description text would apply partner light style
+ */
+ public static void applyPartnerCustomizationDescriptionLightStyle(
+ @Nullable TextView description) {
+
+ if (description == null) {
+ return;
+ }
+ TextViewPartnerStyler.applyPartnerCustomizationLightStyle(
+ description,
+ new TextPartnerConfigs(
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ PartnerStyleHelper.getLayoutGravity(description.getContext())));
+ }
+
+ /**
+ * Applies the partner style of header area to the given layout {@code headerArea}. The theme
+ * should set partner heavy theme first, and then the partner style of header would be applied. As
+ * for the margin bottom of header, it would also be appied when heavy theme parter config is
+ * enabled.
+ *
+ * @param headerArea A ViewGroup would apply the partner style of header area
+ */
+ public static void applyPartnerCustomizationHeaderAreaStyle(ViewGroup headerArea) {
if (headerArea == null) {
return;
}
- Context context = headerArea.getContext();
- int color =
+ if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(headerArea)) {
+ Context context = headerArea.getContext();
+
+ int color =
+ PartnerConfigHelper.get(context)
+ .getColor(context, PartnerConfig.CONFIG_HEADER_AREA_BACKGROUND_COLOR);
+ headerArea.setBackgroundColor(color);
+
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_CONTAINER_MARGIN_BOTTOM)) {
+ final ViewGroup.LayoutParams lp = headerArea.getLayoutParams();
+ if (lp instanceof ViewGroup.MarginLayoutParams) {
+ final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+
+ int bottomMargin =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_HEADER_CONTAINER_MARGIN_BOTTOM);
+ mlp.setMargins(mlp.leftMargin, mlp.topMargin, mlp.rightMargin, bottomMargin);
+ headerArea.setLayoutParams(lp);
+ }
+ }
+ }
+ }
+
+ /**
+ * Applies the partner heavy style of header icon to the given {@code iconImage}. The theme should
+ * check partner heavy theme first, and then the partner icon size would be applied.
+ *
+ * @param iconImage A ImageView would apply the partner style of header icon
+ * @param iconContainer The container of the header icon
+ */
+ public static void applyPartnerCustomizationIconStyle(
+ @Nullable ImageView iconImage, FrameLayout iconContainer) {
+ if (iconImage == null || iconContainer == null) {
+ return;
+ }
+
+ Context context = iconImage.getContext();
+ int gravity = PartnerStyleHelper.getLayoutGravity(context);
+ if (gravity != 0) {
+ setGravity(iconImage, gravity);
+ }
+
+ final ViewGroup.LayoutParams lp = iconContainer.getLayoutParams();
+ boolean partnerConfigAvailable =
PartnerConfigHelper.get(context)
- .getColor(context, PartnerConfig.CONFIG_HEADER_AREA_BACKGROUND_COLOR);
- headerArea.setBackgroundColor(color);
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_ICON_MARGIN_TOP);
+ if (partnerConfigAvailable && lp instanceof ViewGroup.MarginLayoutParams) {
+ final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+ int topMargin =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_ICON_MARGIN_TOP);
+ mlp.setMargins(mlp.leftMargin, topMargin, mlp.rightMargin, mlp.bottomMargin);
+ }
+
+ if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(PartnerConfig.CONFIG_ICON_SIZE)) {
+
+ checkImageType(iconImage);
+
+ final ViewGroup.LayoutParams lpIcon = iconImage.getLayoutParams();
+ lpIcon.height =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_ICON_SIZE);
+ lpIcon.width = LayoutParams.WRAP_CONTENT;
+ iconImage.setScaleType(ScaleType.FIT_CENTER);
+ }
}
- /** Applies the partner style of header icon to the given {@code iconImage}. */
+ /** Applies the partner light style of header icon to the given {@code iconImage}. */
public static void applyPartnerCustomizationIconStyle(@Nullable ImageView iconImage) {
-
if (iconImage == null) {
return;
}
-
int gravity = PartnerStyleHelper.getLayoutGravity(iconImage.getContext());
if (gravity != 0) {
setGravity(iconImage, gravity);
}
}
+ private static void checkImageType(ImageView imageView) {
+ ViewTreeObserver vto = imageView.getViewTreeObserver();
+ vto.addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ imageView.getViewTreeObserver().removeOnPreDrawListener(this);
+ // TODO: Remove when Partners all used Drawable icon image and never use
+ if (isAtLeastS()
+ && !(imageView.getDrawable() == null
+ || (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
+ && imageView.getDrawable() instanceof VectorDrawable)
+ || imageView.getDrawable() instanceof VectorDrawableCompat)
+ && (Build.TYPE.equals("userdebug") || Build.TYPE.equals("eng"))) {
+ Log.w(TAG, WARN_TO_USE_DRAWABLE + imageView.getContext().getPackageName());
+ }
+ return true;
+ }
+ });
+ }
+
private static void setGravity(ImageView icon, int gravity) {
- if (icon.getLayoutParams() instanceof LinearLayout.LayoutParams) {
- LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) icon.getLayoutParams();
+ if (icon.getLayoutParams() instanceof FrameLayout.LayoutParams) {
+ FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) icon.getLayoutParams();
layoutParams.gravity = gravity;
icon.setLayoutParams(layoutParams);
}
diff --git a/main/src/com/google/android/setupdesign/util/ItemStyler.java b/main/src/com/google/android/setupdesign/util/ItemStyler.java
new file mode 100644
index 0000000..ecddfd4
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/ItemStyler.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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.google.android.setupdesign.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.TextViewPartnerStyler.TextPartnerConfigs;
+
+/**
+ * Applies the partner style of layout to the given View {@code view}. The user needs to check if
+ * the {@code view} should apply partner heavy theme before calling this method.
+ */
+public final class ItemStyler {
+
+ /**
+ * Applies the heavy theme partner configs to the given listItemView {@code listItemView}. The
+ * user needs to check before calling this method:
+ *
+ * <p>1) If the {@code listItemView} should apply heavy theme resource by calling {@link
+ * PartnerStyleHelper#shouldApplyPartnerHeavyThemeResource}.
+ *
+ * <p>2) If the layout of the {@code listItemView} contains fixed resource IDs which attempts to
+ * apply heavy theme resources (The resource ID of the title is "sud_items_title" and the resource
+ * ID of the summary is "sud_items_summary"), refer to {@link R.layout#sud_items_default}.
+ *
+ * @param listItemView A view would be applied heavy theme styles
+ */
+ @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+ public static void applyPartnerCustomizationItemStyle(@Nullable View listItemView) {
+ if (listItemView == null) {
+ return;
+ }
+ if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(listItemView)) {
+ return;
+ }
+
+ final TextView titleTextView = listItemView.findViewById(R.id.sud_items_title);
+ // apply title text style
+ applyPartnerCustomizationItemTitleStyle(titleTextView);
+
+ // adjust list item view gravity
+ TextView summaryTextView = listItemView.findViewById(R.id.sud_items_summary);
+ if (summaryTextView.getVisibility() == View.GONE && listItemView instanceof LinearLayout) {
+ // Set list items to vertical center when there is no summary.
+ ((LinearLayout) listItemView).setGravity(Gravity.CENTER_VERTICAL);
+ }
+
+ // apply summary text style
+ applyPartnerCustomizationItemSummaryStyle(summaryTextView);
+
+ // apply list item view style
+ applyPartnerCustomizationItemViewLayoutStyle(listItemView);
+ }
+
+ /**
+ * Applies the partner heavy style to the given list item title text view. Will check the current
+ * text view enabled the partner customized heavy theme configurations before applying.
+ *
+ * @param titleTextView A textView of a list item title text.
+ */
+ public static void applyPartnerCustomizationItemTitleStyle(TextView titleTextView) {
+ if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(titleTextView)) {
+ return;
+ }
+ TextViewPartnerStyler.applyPartnerCustomizationStyle(
+ titleTextView,
+ new TextPartnerConfigs(
+ null,
+ null,
+ PartnerConfig.CONFIG_ITEMS_TITLE_TEXT_SIZE,
+ PartnerConfig.CONFIG_ITEMS_TITLE_FONT_FAMILY,
+ null,
+ null,
+ PartnerStyleHelper.getLayoutGravity(titleTextView.getContext())));
+ }
+
+ /**
+ * Applies the partner heavy style to the given summary text view. Will check the current text
+ * view enabled the partner customized heavy theme configurations before applying.
+ *
+ * @param summaryTextView A textView of a list item summary text.
+ */
+ public static void applyPartnerCustomizationItemSummaryStyle(TextView summaryTextView) {
+ if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(summaryTextView)) {
+ return;
+ }
+
+ TextViewPartnerStyler.applyPartnerCustomizationStyle(
+ summaryTextView,
+ new TextPartnerConfigs(
+ null,
+ null,
+ PartnerConfig.CONFIG_ITEMS_SUMMARY_TEXT_SIZE,
+ PartnerConfig.CONFIG_ITEMS_SUMMARY_FONT_FAMILY,
+ PartnerConfig.CONFIG_ITEMS_SUMMARY_MARGIN_TOP,
+ null,
+ PartnerStyleHelper.getLayoutGravity(summaryTextView.getContext())));
+ }
+
+ private static void applyPartnerCustomizationItemViewLayoutStyle(@Nullable View listItemView) {
+ Context context = listItemView.getContext();
+ float paddingTop;
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_PADDING_TOP)) {
+ paddingTop =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_ITEMS_PADDING_TOP);
+ } else {
+ paddingTop = listItemView.getPaddingTop();
+ }
+
+ float paddingBottom;
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_PADDING_BOTTOM)) {
+ paddingBottom =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_ITEMS_PADDING_BOTTOM);
+ } else {
+ paddingBottom = listItemView.getPaddingBottom();
+ }
+
+ if (paddingTop != listItemView.getPaddingTop()
+ || paddingBottom != listItemView.getPaddingBottom()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ listItemView.setPadding(
+ listItemView.getPaddingStart(),
+ (int) paddingTop,
+ listItemView.getPaddingEnd(),
+ (int) paddingBottom);
+ } else {
+ listItemView.setPadding(
+ listItemView.getPaddingLeft(),
+ (int) paddingTop,
+ listItemView.getPaddingRight(),
+ (int) paddingBottom);
+ }
+ }
+
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_MIN_HEIGHT)) {
+ float minHeight =
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_ITEMS_MIN_HEIGHT);
+ listItemView.setMinimumHeight((int) minHeight);
+ }
+ }
+
+ private ItemStyler() {}
+}
diff --git a/main/src/com/google/android/setupdesign/util/LayoutStyler.java b/main/src/com/google/android/setupdesign/util/LayoutStyler.java
new file mode 100644
index 0000000..b707521
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/LayoutStyler.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 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.google.android.setupdesign.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupdesign.R;
+
+/**
+ * Applies the partner style of layout to the given View {@code view}. The user needs to check if
+ * the {@code view} should apply partner heavy theme before calling this method.
+ */
+public final class LayoutStyler {
+
+ /**
+ * Applies the partner layout padding style to the given view {@code view}. The theme should set
+ * partner heavy theme config first, and then the partner layout style would be applied.
+ *
+ * @param view A view would be applied partner layout padding style
+ */
+ @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+ public static void applyPartnerCustomizationLayoutPaddingStyle(@Nullable View view) {
+ if (view == null) {
+ return;
+ }
+
+ Context context = view.getContext();
+ boolean partnerMarginStartAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+ boolean partnerMarginEndAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+
+ if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(view)
+ && (partnerMarginStartAvailable || partnerMarginEndAvailable)) {
+ int paddingStart;
+ int paddingEnd;
+ if (partnerMarginStartAvailable) {
+ paddingStart =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+ } else {
+ paddingStart = view.getPaddingStart();
+ }
+ if (partnerMarginEndAvailable) {
+ paddingEnd =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+ } else {
+ paddingEnd = view.getPaddingEnd();
+ }
+
+ if (paddingStart != view.getPaddingStart() || paddingEnd != view.getPaddingEnd()) {
+ view.setPadding(paddingStart, view.getPaddingTop(), paddingEnd, view.getPaddingBottom());
+ }
+ }
+ }
+
+ /**
+ * Applies the extra padding style to the given view {@code view}. This method is used when {@code
+ * view} already sets its margin, and like to extra padding make view.margin + view.pendding =
+ * global page margin.
+ *
+ * @param view A view would be applied extra padding style based on the layout margin of partner
+ * config.
+ */
+ @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+ public static void applyPartnerCustomizationExtraPaddingStyle(@Nullable View view) {
+ if (view == null) {
+ return;
+ }
+
+ Context context = view.getContext();
+ boolean partnerMarginStartAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+ boolean partnerMarginEndAvailable =
+ PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+
+ if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(view)
+ && (partnerMarginStartAvailable || partnerMarginEndAvailable)) {
+ int extraPaddingStart;
+ int extraPaddingEnd;
+
+ TypedArray a =
+ context.obtainStyledAttributes(new int[] {R.attr.sudMarginStart, R.attr.sudMarginEnd});
+ int layoutMarginStart = a.getDimensionPixelSize(0, 0);
+ int layoutMarginEnd = a.getDimensionPixelSize(1, 0);
+ a.recycle();
+
+ if (partnerMarginStartAvailable) {
+ extraPaddingStart =
+ ((int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START))
+ - layoutMarginStart;
+ } else {
+ extraPaddingStart = view.getPaddingStart();
+ }
+
+ if (partnerMarginEndAvailable) {
+ extraPaddingEnd =
+ ((int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END))
+ - layoutMarginEnd;
+ } else {
+ extraPaddingEnd = view.getPaddingEnd();
+ }
+
+ if (extraPaddingStart != view.getPaddingStart() || extraPaddingEnd != view.getPaddingEnd()) {
+ // If the view is a content view, padding start and padding end will be the same.
+ view.setPadding(
+ extraPaddingStart,
+ view.getPaddingTop(),
+ view.getId() == R.id.sud_layout_content ? extraPaddingStart : extraPaddingEnd,
+ view.getPaddingBottom());
+ }
+ }
+ }
+
+ private LayoutStyler() {}
+}
diff --git a/main/src/com/google/android/setupdesign/util/Partner.java b/main/src/com/google/android/setupdesign/util/Partner.java
index aee5070..cf26f16 100644
--- a/main/src/com/google/android/setupdesign/util/Partner.java
+++ b/main/src/com/google/android/setupdesign/util/Partner.java
@@ -24,16 +24,20 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
+import android.util.Log;
+import android.util.TypedValue;
import androidx.annotation.AnyRes;
import androidx.annotation.ArrayRes;
+import androidx.annotation.BoolRes;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
-import android.util.Log;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -69,6 +73,17 @@ public class Partner {
}
/**
+ * Gets a boolean value from partner overlay, or if not available, gets the value from the
+ * original context instead.
+ *
+ * @see #getResourceEntry(Context, int)
+ */
+ public static boolean getBoolean(Context context, @BoolRes int id) {
+ final ResourceEntry entry = getResourceEntry(context, id);
+ return entry.resources.getBoolean(entry.id);
+ }
+
+ /**
* Gets a drawable from partner overlay, or if not available, the drawable from the original
* context.
*
@@ -107,6 +122,21 @@ public class Partner {
}
/**
+ * Gets an {@link Icon} from partner overlay, or if not available, the drawable from the original
+ * context. In some cases, icon can be set {@code null} to remove default icon.
+ *
+ * @see #getResourceEntry(Context, int)
+ */
+ @Nullable
+ @RequiresApi(VERSION_CODES.M)
+ public static Icon getIcon(Context context, @DrawableRes int id) {
+ Partner.ResourceEntry entry = Partner.getResourceEntry(context, id);
+ return (getTypedValue(entry).data == 0)
+ ? null
+ : Icon.createWithResource(entry.packageName, entry.id);
+ }
+
+ /**
* Finds an entry of resource in the overlay package provided by partners. It will first look for
* the resource in the overlay package, and if not available, will return the one in the original
* context.
@@ -214,4 +244,10 @@ public class Partner {
public int getIdentifier(String name, String defType) {
return resources.getIdentifier(name, defType, packageName);
}
+
+ private static TypedValue getTypedValue(ResourceEntry resourceEntry) {
+ TypedValue typedValue = new TypedValue();
+ resourceEntry.resources.getValue(resourceEntry.id, typedValue, true);
+ return typedValue;
+ }
}
diff --git a/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java b/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java
index d1d4a26..78900b1 100644
--- a/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java
+++ b/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java
@@ -16,19 +16,27 @@
package com.google.android.setupdesign.util;
+import android.app.Activity;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
import android.view.Gravity;
-import android.widget.FrameLayout;
+import android.view.View;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.partnerconfig.PartnerConfig;
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.R;
import java.util.Locale;
/** The helper reads styles from the partner configurations. */
public final class PartnerStyleHelper {
+ private static final String TAG = "PartnerStyleHelper";
/**
- * Returns the partner configuration of layout gravity, usually apply to wigets in header area.
+ * Returns the partner configuration of layout gravity, usually apply to widgets in header area.
*/
public static int getLayoutGravity(Context context) {
String gravity =
@@ -47,14 +55,152 @@ public final class PartnerStyleHelper {
}
/** Returns the given layout if apply partner heavy theme. */
- public static boolean isPartnerHeavyThemeLayout(FrameLayout layout) {
+ public static boolean isPartnerHeavyThemeLayout(TemplateLayout layout) {
if (!(layout instanceof GlifLayout)) {
return false;
}
- if (!((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource()) {
+ return ((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource();
+ }
+
+ /** Returns the given layout if apply partner light theme. */
+ public static boolean isPartnerLightThemeLayout(TemplateLayout layout) {
+ if (!(layout instanceof PartnerCustomizationLayout)) {
+ return false;
+ }
+ return ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
+ }
+
+ /**
+ * Returns if the current layout/activity of the given {@code view} applies partner customized
+ * configurations or not.
+ *
+ * @param view A PartnerCustomizationLayout view, would be used to get the activity and context.
+ */
+ public static boolean shouldApplyPartnerResource(View view) {
+ if (view == null) {
+ return false;
+ }
+ if (view instanceof PartnerCustomizationLayout) {
+ return isPartnerLightThemeLayout((PartnerCustomizationLayout) view);
+ }
+ return shouldApplyPartnerResource(view.getContext());
+ }
+
+ private static boolean shouldApplyPartnerResource(Context context) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ return false;
+ }
+
+ if (!PartnerConfigHelper.get(context).isAvailable()) {
return false;
}
- return true;
+
+ Activity activity = null;
+ try {
+ activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+ if (activity != null) {
+ TemplateLayout layout = findLayoutFromActivity(activity);
+ if (layout instanceof PartnerCustomizationLayout) {
+ return ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
+ }
+ }
+ } catch (IllegalArgumentException | ClassCastException ex) {
+ // fall through
+ }
+
+ // try best to get partner resource settings from attrs
+ boolean isSetupFlow = false;
+ if (activity != null) {
+ isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
+ }
+ TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.sucUsePartnerResource});
+ boolean usePartnerResource = a.getBoolean(0, true);
+ a.recycle();
+
+ return isSetupFlow || usePartnerResource;
+ }
+
+ /**
+ * Returns if the current layout/activity applies heavy partner customized configurations or not.
+ *
+ * @param view A view would be used to get the activity and context.
+ */
+ public static boolean shouldApplyPartnerHeavyThemeResource(View view) {
+ if (view == null) {
+ return false;
+ }
+ if (view instanceof GlifLayout) {
+ return isPartnerHeavyThemeLayout((GlifLayout) view);
+ }
+ return shouldApplyPartnerHeavyThemeResource(view.getContext());
+ }
+
+ static boolean shouldApplyPartnerHeavyThemeResource(Context context) {
+ try {
+ Activity activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+ TemplateLayout layout = findLayoutFromActivity(activity);
+ if (layout instanceof GlifLayout) {
+ return ((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource();
+ }
+ } catch (IllegalArgumentException | ClassCastException ex) {
+ // fall through
+ }
+
+ // try best to get partner resource settings from attr
+ TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.sudUsePartnerHeavyTheme});
+ boolean usePartnerHeavyTheme = a.getBoolean(0, false);
+ a.recycle();
+ usePartnerHeavyTheme =
+ usePartnerHeavyTheme || PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context);
+
+ return shouldApplyPartnerResource(context) && usePartnerHeavyTheme;
+ }
+
+ /**
+ * Returns if the current layout/activity applies dynamic color configurations or not.
+ *
+ * @param view A GlifLayout view would be used to get the activity and context.
+ */
+ public static boolean useDynamicColor(View view) {
+ if (view == null) {
+ return false;
+ }
+ return getDynamicColorAttributeFromTheme(view.getContext());
+ }
+
+ static boolean getDynamicColorAttributeFromTheme(Context context) {
+ try {
+ Activity activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+ TemplateLayout layout = findLayoutFromActivity(activity);
+ if (layout instanceof GlifLayout) {
+ return ((GlifLayout) layout).shouldApplyDynamicColor();
+ }
+ } catch (IllegalArgumentException | ClassCastException ex) {
+ // fall through
+ }
+
+ // try best to get dynamic color settings from attr
+ TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.sucFullDynamicColor});
+ boolean useDynamicColorTheme =
+ a.hasValue(
+ com.google
+ .android
+ .setupcompat
+ .R
+ .styleable
+ .SucPartnerCustomizationLayout_sucFullDynamicColor);
+ a.recycle();
+
+ return useDynamicColorTheme;
+ }
+
+ private static TemplateLayout findLayoutFromActivity(Activity activity) {
+ if (activity == null) {
+ return null;
+ }
+ // This only worked after activity setContentView, otherwise it will return null
+ View rootView = activity.findViewById(R.id.suc_layout_status);
+ return rootView != null ? (TemplateLayout) rootView.getParent() : null;
}
private PartnerStyleHelper() {}
diff --git a/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java b/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java
index 552102e..7b4acea 100644
--- a/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java
+++ b/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java
@@ -18,10 +18,12 @@ package com.google.android.setupdesign.util;
import android.content.Context;
import android.graphics.Typeface;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.TypedValue;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.android.setupcompat.partnerconfig.PartnerConfig;
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
@@ -37,7 +39,9 @@ final class TextViewPartnerStyler {
}
Context context = textView.getContext();
- if (textPartnerConfigs.getTextColorConfig() != null) {
+ if (textPartnerConfigs.getTextColorConfig() != null
+ && PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(textPartnerConfigs.getTextColorConfig())) {
int textColor =
PartnerConfigHelper.get(context)
.getColor(context, textPartnerConfigs.getTextColorConfig());
@@ -46,7 +50,10 @@ final class TextViewPartnerStyler {
}
}
- if (textPartnerConfigs.getTextLinkedColorConfig() != null) {
+ if (textPartnerConfigs.getTextLinkedColorConfig() != null
+ && PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(textPartnerConfigs.getTextLinkedColorConfig())
+ && !PartnerStyleHelper.useDynamicColor(textView)) {
int linkTextColor =
PartnerConfigHelper.get(context)
.getColor(context, textPartnerConfigs.getTextLinkedColorConfig());
@@ -55,7 +62,9 @@ final class TextViewPartnerStyler {
}
}
- if (textPartnerConfigs.getTextSizeConfig() != null) {
+ if (textPartnerConfigs.getTextSizeConfig() != null
+ && PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(textPartnerConfigs.getTextSizeConfig())) {
float textSize =
PartnerConfigHelper.get(context)
.getDimension(context, textPartnerConfigs.getTextSizeConfig(), 0);
@@ -64,7 +73,9 @@ final class TextViewPartnerStyler {
}
}
- if (textPartnerConfigs.getTextFontFamilyConfig() != null) {
+ if (textPartnerConfigs.getTextFontFamilyConfig() != null
+ && PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(textPartnerConfigs.getTextFontFamilyConfig())) {
String fontFamilyName =
PartnerConfigHelper.get(context)
.getString(context, textPartnerConfigs.getTextFontFamilyConfig());
@@ -74,6 +85,54 @@ final class TextViewPartnerStyler {
}
}
+ if (textPartnerConfigs.getTextMarginTop() != null
+ || textPartnerConfigs.getTextMarginBottom() != null) {
+ int topMargin;
+ int bottomMargin;
+ final ViewGroup.LayoutParams lp = textView.getLayoutParams();
+ if (lp instanceof LinearLayout.LayoutParams) {
+ final LinearLayout.LayoutParams mlp = (LinearLayout.LayoutParams) lp;
+ if (textPartnerConfigs.getTextMarginTop() != null
+ && PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(textPartnerConfigs.getTextMarginTop())) {
+ topMargin =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, textPartnerConfigs.getTextMarginTop());
+ } else {
+ topMargin = mlp.topMargin;
+ }
+
+ if (textPartnerConfigs.getTextMarginBottom() != null
+ && PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(textPartnerConfigs.getTextMarginBottom())) {
+ bottomMargin =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, textPartnerConfigs.getTextMarginBottom());
+ } else {
+ bottomMargin = mlp.bottomMargin;
+ }
+ mlp.setMargins(mlp.leftMargin, topMargin, mlp.rightMargin, bottomMargin);
+ textView.setLayoutParams(lp);
+ }
+ }
+ textView.setGravity(textPartnerConfigs.getTextGravity());
+ }
+
+ /**
+ * Applies given partner configurations {@code textPartnerConfigs} to the {@code textView}.
+ *
+ * @param textView A text view would apply the gravity
+ * @param textPartnerConfigs A partner conflagrations contains text gravity would be set
+ */
+ public static void applyPartnerCustomizationLightStyle(
+ @NonNull TextView textView, @NonNull TextPartnerConfigs textPartnerConfigs) {
+
+ if (textView == null || textPartnerConfigs == null) {
+ return;
+ }
+
textView.setGravity(textPartnerConfigs.getTextGravity());
}
@@ -83,6 +142,8 @@ final class TextViewPartnerStyler {
private final PartnerConfig textLinkedColorConfig;
private final PartnerConfig textSizeConfig;
private final PartnerConfig textFontFamilyConfig;
+ private final PartnerConfig textMarginTopConfig;
+ private final PartnerConfig textMarginBottomConfig;
private final int textGravity;
public TextPartnerConfigs(
@@ -90,11 +151,15 @@ final class TextViewPartnerStyler {
@Nullable PartnerConfig textLinkedColorConfig,
@Nullable PartnerConfig textSizeConfig,
@Nullable PartnerConfig textFontFamilyConfig,
+ @Nullable PartnerConfig textMarginTopConfig,
+ @Nullable PartnerConfig textMarginBottomConfig,
int textGravity) {
this.textColorConfig = textColorConfig;
this.textLinkedColorConfig = textLinkedColorConfig;
this.textSizeConfig = textSizeConfig;
this.textFontFamilyConfig = textFontFamilyConfig;
+ this.textMarginTopConfig = textMarginTopConfig;
+ this.textMarginBottomConfig = textMarginBottomConfig;
this.textGravity = textGravity;
}
@@ -114,6 +179,14 @@ final class TextViewPartnerStyler {
return textFontFamilyConfig;
}
+ public PartnerConfig getTextMarginTop() {
+ return textMarginTopConfig;
+ }
+
+ public PartnerConfig getTextMarginBottom() {
+ return textMarginBottomConfig;
+ }
+
public int getTextGravity() {
return textGravity;
}
diff --git a/main/src/com/google/android/setupdesign/util/ThemeHelper.java b/main/src/com/google/android/setupdesign/util/ThemeHelper.java
index 4247d99..0b750c9 100644
--- a/main/src/com/google/android/setupdesign/util/ThemeHelper.java
+++ b/main/src/com/google/android/setupdesign/util/ThemeHelper.java
@@ -17,11 +17,22 @@
package com.google.android.setupdesign.util;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
+import androidx.annotation.NonNull;
+import androidx.annotation.StyleRes;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.BuildCompatUtils;
+import com.google.android.setupcompat.util.Logger;
import com.google.android.setupcompat.util.WizardManagerHelper;
+import com.google.android.setupdesign.R;
+import java.util.Objects;
/** The helper class holds the constant names of themes and util functions */
-public class ThemeHelper {
+public final class ThemeHelper {
+
+ private static final Logger LOG = new Logger("ThemeHelper");
/**
* Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the dark
@@ -59,6 +70,16 @@ public class ThemeHelper {
*/
public static final String THEME_GLIF_V3_LIGHT = "glif_v3_light";
+ /**
+ * Placeholder, not avirailed yet.
+ */
+ public static final String THEME_GLIF_V4 = "glif_v4";
+
+ /**
+ * Placeholder, not avirailed yet.
+ */
+ public static final String THEME_GLIF_V4_LIGHT = "glif_v4_light";
+
public static final String THEME_HOLO = "holo";
public static final String THEME_HOLO_LIGHT = "holo_light";
public static final String THEME_MATERIAL = "material";
@@ -92,13 +113,15 @@ public class ThemeHelper {
|| THEME_MATERIAL_LIGHT.equals(theme)
|| THEME_GLIF_LIGHT.equals(theme)
|| THEME_GLIF_V2_LIGHT.equals(theme)
- || THEME_GLIF_V3_LIGHT.equals(theme)) {
+ || THEME_GLIF_V3_LIGHT.equals(theme)
+ || THEME_GLIF_V4_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_V3.equals(theme)) {
+ || THEME_GLIF_V3.equals(theme)
+ || THEME_GLIF_V4.equals(theme)) {
return false;
} else {
return def;
@@ -125,4 +148,127 @@ public class ThemeHelper {
public static void applyTheme(Activity activity) {
ThemeResolver.getDefault().applyTheme(activity);
}
+
+ /**
+ * Checks whether SetupWizard supports the DayNight theme during setup flow; if it returns false,
+ * setup flow is always light theme.
+ *
+ * @return true if the SetupWizard is listening to system DayNight theme setting.
+ */
+ public static boolean isSetupWizardDayNightEnabled(@NonNull Context context) {
+ return PartnerConfigHelper.isSetupWizardDayNightEnabled(context);
+ }
+
+ /**
+ * Returns true if the partner provider of SetupWizard is ready to support more partner configs.
+ */
+ public static boolean shouldApplyExtendedPartnerConfig(@NonNull Context context) {
+ return PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context);
+ }
+
+ /**
+ * Returns {@code true} if the partner provider of SetupWizard is ready to support dynamic color.
+ */
+ public static boolean isSetupWizardDynamicColorEnabled(@NonNull Context context) {
+ return PartnerConfigHelper.isSetupWizardDynamicColorEnabled(context);
+ }
+
+ /** Returns {@code true} if this {@code context} should apply dynamic color. */
+ public static boolean shouldApplyDynamicColor(@NonNull Context context) {
+ return shouldApplyExtendedPartnerConfig(context) && isSetupWizardDynamicColorEnabled(context);
+ }
+
+ /**
+ * Returns a theme resource id if the {@link com.google.android.setupdesign.GlifLayout} should
+ * apply dynamic color.
+ *
+ * <p>Otherwise returns {@code 0}.
+ */
+ @StyleRes
+ public static int getDynamicColorTheme(@NonNull Context context) {
+ @StyleRes int resId = 0;
+
+ Activity activity;
+ try {
+ activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+ } catch (IllegalArgumentException ex) {
+ LOG.e(Objects.requireNonNull(ex.getMessage()));
+ return resId;
+ }
+
+ boolean isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
+ boolean isDayNightEnabled = isSetupWizardDayNightEnabled(context);
+
+ if (isSetupFlow) {
+ // return theme for inside setup flow
+ resId =
+ isDayNightEnabled
+ ? R.style.SudDynamicColorThemeGlifV3_DayNight
+ : R.style.SudDynamicColorThemeGlifV3_Light;
+ } else {
+ // return theme for outside setup flow
+ resId =
+ isDayNightEnabled
+ ? R.style.SudFullDynamicColorThemeGlifV3_DayNight
+ : R.style.SudFullDynamicColorThemeGlifV3_Light;
+ LOG.atInfo(
+ "Return "
+ + (isDayNightEnabled
+ ? "SudFullDynamicColorThemeGlifV3_DayNight"
+ : "SudFullDynamicColorThemeGlifV3_Light"));
+ }
+
+ LOG.atDebug(
+ "Gets the dynamic accentColor: [Light] "
+ + colorIntToHex(context, R.color.sud_dynamic_color_accent_glif_v3_light)
+ + ", "
+ + (BuildCompatUtils.isAtLeastS()
+ ? colorIntToHex(context, android.R.color.system_accent1_600)
+ : "n/a")
+ + ", [Dark] "
+ + colorIntToHex(context, R.color.sud_dynamic_color_accent_glif_v3_dark)
+ + ", "
+ + (BuildCompatUtils.isAtLeastS()
+ ? colorIntToHex(context, android.R.color.system_accent1_100)
+ : "n/a"));
+
+ return resId;
+ }
+
+ /** Returns {@code true} if the dynamic color is set. */
+ public static boolean trySetDynamicColor(@NonNull Context context) {
+ if (!shouldApplyExtendedPartnerConfig(context)) {
+ LOG.w("SetupWizard does not supports the extended partner configs.");
+ return false;
+ }
+
+ if (!isSetupWizardDynamicColorEnabled(context)) {
+ LOG.w("SetupWizard does not support the dynamic color or supporting status unknown.");
+ return false;
+ }
+
+ Activity activity;
+ try {
+ activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+ } catch (IllegalArgumentException ex) {
+ LOG.e(Objects.requireNonNull(ex.getMessage()));
+ return false;
+ }
+
+ @StyleRes int resId = getDynamicColorTheme(context);
+ if (resId != 0) {
+ activity.setTheme(resId);
+ } else {
+ LOG.w("Error occurred on getting dynamic color theme.");
+ return false;
+ }
+
+ return true;
+ }
+
+ private static String colorIntToHex(Context context, int colorInt) {
+ return String.format("#%06X", (0xFFFFFF & context.getResources().getColor(colorInt)));
+ }
+
+ private ThemeHelper() {}
}
diff --git a/main/src/com/google/android/setupdesign/util/ThemeResolver.java b/main/src/com/google/android/setupdesign/util/ThemeResolver.java
index 98b8883..c7a28b1 100644
--- a/main/src/com/google/android/setupdesign/util/ThemeResolver.java
+++ b/main/src/com/google/android/setupdesign/util/ThemeResolver.java
@@ -86,6 +86,17 @@ public class ThemeResolver {
}
/**
+ * Returns the style for the given SetupWizard intent. If the specified intent does not include
+ * the intent extra {@link WizardManagerHelper#EXTRA_THEME}, the default theme will be returned
+ * instead. Note that the default theme is returned without processing -- it may not be a DayNight
+ * theme even if {@link #useDayNight} is true.
+ */
+ @StyleRes
+ public int resolve(Intent intent, boolean suppressDayNight) {
+ return resolve(intent.getStringExtra(WizardManagerHelper.EXTRA_THEME), suppressDayNight);
+ }
+
+ /**
* Returns the style for the given string theme. If the specified string theme is older than the
* oldest supported theme, the default will be returned instead. Note that the default theme is
* returned without processing -- it may not be a DayNight theme even if {@link #useDayNight} is
@@ -128,7 +139,11 @@ public class ThemeResolver {
/** Reads the theme from the intent, and applies the resolved theme to the activity. */
public void applyTheme(Activity activity) {
- activity.setTheme(resolve(activity.getIntent()));
+ activity.setTheme(
+ resolve(
+ activity.getIntent(),
+ /* suppressDayNight= */ WizardManagerHelper.isAnySetupWizard(activity.getIntent())
+ && !ThemeHelper.isSetupWizardDayNightEnabled(activity)));
}
/**
diff --git a/main/src/com/google/android/setupdesign/view/BottomScrollView.java b/main/src/com/google/android/setupdesign/view/BottomScrollView.java
index 83527b0..a3b250d 100644
--- a/main/src/com/google/android/setupdesign/view/BottomScrollView.java
+++ b/main/src/com/google/android/setupdesign/view/BottomScrollView.java
@@ -17,10 +17,10 @@
package com.google.android.setupdesign.view;
import android.content.Context;
-import androidx.annotation.VisibleForTesting;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView;
+import androidx.annotation.VisibleForTesting;
/**
* An extension of ScrollView that will invoke a listener callback when the ScrollView needs
@@ -68,7 +68,6 @@ public class BottomScrollView extends ScrollView {
return listener;
}
- @VisibleForTesting
public int getScrollThreshold() {
return scrollThreshold;
}
diff --git a/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java b/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java
index b12a20f..34d6ae3 100644
--- a/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java
+++ b/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java
@@ -19,10 +19,10 @@ package com.google.android.setupdesign.view;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.Checkable;
import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
/**
* A LinearLayout which is checkable. This will set the checked state when {@link
diff --git a/main/src/com/google/android/setupdesign/view/FillContentLayout.java b/main/src/com/google/android/setupdesign/view/FillContentLayout.java
index 49e195f..af49fbb 100644
--- a/main/src/com/google/android/setupdesign/view/FillContentLayout.java
+++ b/main/src/com/google/android/setupdesign/view/FillContentLayout.java
@@ -58,6 +58,10 @@ public class FillContentLayout extends FrameLayout {
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
diff --git a/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java b/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java
index b3161fd..57fc35d 100644
--- a/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java
+++ b/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java
@@ -218,6 +218,10 @@ public class HeaderRecyclerView extends RecyclerView {
}
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
final TypedArray a =
getContext()
.obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
diff --git a/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java
new file mode 100644
index 0000000..9f0e21d
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 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.google.android.setupdesign.view;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.Outline;
+import android.graphics.RectF;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
+import androidx.annotation.ColorRes;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.ImageView;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.widget.CardBackgroundDrawable;
+
+/** An ImageView that displays an app icon according to the icon uniformity spec. */
+public class IconUniformityAppImageView extends ImageView
+ implements IconUniformityAppImageViewBindable {
+ // Scaling factor for inset on each side of legacy icon.
+ private static final Float LEGACY_SIZE_SCALE_FACTOR = 0.75f;
+
+ private static final Float LEGACY_SIZE_SCALE_MARGIN_FACTOR = (1f - LEGACY_SIZE_SCALE_FACTOR) / 2f;
+
+ // Apps & games radius is 20% of icon height.
+ private static final Float APPS_ICON_RADIUS_MULTIPLIER = 0.20f;
+
+ @ColorRes private int backdropColorResId = 0;
+
+ private static final boolean ON_L_PLUS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+
+ private CardBackgroundDrawable cardBackgroundDrawable;
+ /** Drawable used as background after the actual image data is visible. */
+ private final GradientDrawable backdropDrawable = new GradientDrawable();
+
+ public IconUniformityAppImageView(Context context) {
+ super(context);
+ }
+
+ public IconUniformityAppImageView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public IconUniformityAppImageView(
+ Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @TargetApi(23)
+ public IconUniformityAppImageView(
+ Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ backdropColorResId = R.color.sud_uniformity_backdrop_color;
+ backdropDrawable.setColor(ContextCompat.getColor(getContext(), backdropColorResId));
+ }
+
+ @Override
+ public void bindView(IconUniformityAppImageViewData viewData) {
+ if (Build.VERSION.SDK_INT <= 17) {
+ // clipPath is not supported on hardware accelerated canvas so won't take effect unless we
+ // manually set to software.
+ setLayerType(LAYER_TYPE_SOFTWARE, /* paint= */ null);
+ }
+
+ setLegacyTransformationMatrix(
+ viewData.icon.getMinimumWidth(),
+ viewData.icon.getMinimumHeight(),
+ getLayoutParams().width,
+ getLayoutParams().height);
+
+ float radius = getLayoutParams().height * APPS_ICON_RADIUS_MULTIPLIER;
+
+ if (ON_L_PLUS) {
+ setBackgroundColor(ContextCompat.getColor(getContext(), backdropColorResId));
+ backdropDrawable.setCornerRadius(radius);
+ setElevation(getContext().getResources().getDimension(R.dimen.sud_icon_uniformity_elevation));
+ setClipToOutline(true);
+ setOutlineProvider(
+ new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(
+ /* left= */ 0,
+ /* top= */ 0,
+ /* right= */ getLayoutParams().width,
+ /* bottom= */ getLayoutParams().height,
+ /* radius= */ radius);
+ }
+ });
+ } else {
+ cardBackgroundDrawable =
+ new CardBackgroundDrawable(
+ ContextCompat.getColor(getContext(), backdropColorResId),
+ /* radius= */ radius,
+ /* inset= */ 0f);
+ cardBackgroundDrawable.setBounds(
+ /* left= */ 0,
+ /* top= */ 0,
+ /* right= */ getLayoutParams().width,
+ /* bottom= */ getLayoutParams().height);
+ }
+
+ setImageDrawable(viewData.icon);
+ }
+
+ @Override
+ public void onRecycle() {
+ setImageDrawable(null);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (!ON_L_PLUS && cardBackgroundDrawable != null) {
+ cardBackgroundDrawable.draw(canvas);
+ }
+ super.onDraw(canvas);
+ }
+
+ private void setLegacyTransformationMatrix(
+ float drawableWidth, float drawableHeight, float imageViewWidth, float imageViewHeight) {
+ Matrix scaleMatrix = new Matrix();
+ float verticalMargin = imageViewHeight * LEGACY_SIZE_SCALE_MARGIN_FACTOR;
+ float horizontalMargin = imageViewWidth * LEGACY_SIZE_SCALE_MARGIN_FACTOR;
+ RectF scrRectF = new RectF(0f, 0f, drawableWidth, drawableHeight);
+ RectF destRectF =
+ new RectF(
+ horizontalMargin,
+ verticalMargin,
+ imageViewWidth - horizontalMargin,
+ imageViewHeight - verticalMargin);
+
+ scaleMatrix.setRectToRect(scrRectF, destRectF, ScaleToFit.FILL);
+
+ setScaleType(ScaleType.MATRIX);
+ setImageMatrix(scaleMatrix);
+ }
+}
diff --git a/main/src/com/google/android/setupdesign/view/IconUniformityAppImageViewBindable.java b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageViewBindable.java
new file mode 100644
index 0000000..1eab81b
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageViewBindable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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.google.android.setupdesign.view;
+
+import android.graphics.drawable.Drawable;
+
+/** ViewBindable for [IconUniformityAppImageView] */
+public interface IconUniformityAppImageViewBindable {
+
+ /** Data for [IconUniformityAppImageView] */
+ class IconUniformityAppImageViewData {
+ public Drawable icon;
+
+ public IconUniformityAppImageViewData(Drawable icon) {
+ this.icon = icon;
+ }
+ }
+
+ void bindView(IconUniformityAppImageViewData viewData);
+
+ void onRecycle();
+}
diff --git a/main/src/com/google/android/setupdesign/view/Illustration.java b/main/src/com/google/android/setupdesign/view/Illustration.java
index 14072db..e890307 100644
--- a/main/src/com/google/android/setupdesign/view/Illustration.java
+++ b/main/src/com/google/android/setupdesign/view/Illustration.java
@@ -70,6 +70,10 @@ public class Illustration extends FrameLayout {
// All the constructors delegate to this init method. The 3-argument constructor is not
// available in FrameLayout before v11, so call super with the exact same arguments.
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
if (attrs != null) {
TypedArray a =
getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
diff --git a/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java b/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java
index 2e4fd71..de71f7c 100644
--- a/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java
+++ b/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java
@@ -28,15 +28,16 @@ import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.net.Uri;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.Nullable;
-import androidx.annotation.RawRes;
-import androidx.annotation.VisibleForTesting;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
+import androidx.annotation.Nullable;
+import androidx.annotation.RawRes;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.setupcompat.util.BuildCompatUtils;
import com.google.android.setupdesign.R;
import java.io.IOException;
@@ -76,6 +77,8 @@ public class IllustrationVideoView extends TextureView
private boolean prepared;
+ private boolean shouldPauseVideoWhenFinished = true;
+
/**
* The visibility of this view as set by the user. This view combines this with {@link
* #isMediaPlayerLoading} to determine the final visibility.
@@ -92,9 +95,24 @@ public class IllustrationVideoView extends TextureView
public IllustrationVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
+ if (!isInEditMode()) {
+ init(context, attrs);
+ }
+ }
+
+ private void init(Context context, AttributeSet attrs) {
final TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
final int videoResId = a.getResourceId(R.styleable.SudIllustrationVideoView_sudVideo, 0);
+
+ // TODO: remove the usage of BuildCompatUtils#isAtLeatestS if VERSION_CODE.S is
+ // support by system.
+ if (BuildCompatUtils.isAtLeastS()) {
+ boolean shouldPauseVideo =
+ a.getBoolean(R.styleable.SudIllustrationVideoView_sudPauseVideoWhenFinished, true);
+ setPauseVideoWhenFinished(shouldPauseVideo);
+ }
+
a.recycle();
setVideoResource(videoResId);
@@ -143,12 +161,43 @@ public class IllustrationVideoView extends TextureView
/**
* Set the video to be played by this view.
*
+ * @param resourceEntry the {@link com.google.android.setupdesign.util.Partner.ResourceEntry} of
+ * the video, typically an MP4 under res/raw.
+ */
+ public void setVideoResourceEntry(
+ com.google.android.setupdesign.util.Partner.ResourceEntry resourceEntry) {
+ setVideoResource(resourceEntry.id, resourceEntry.packageName);
+ }
+
+ /**
+ * Set the video to be played by this view.
+ *
+ * @param resourceEntry the {@link com.google.android.setupcompat.partnerconfig.ResourceEntry} of
+ * the video, typically an MP4 under res/raw.
+ */
+ public void setVideoResourceEntry(
+ com.google.android.setupcompat.partnerconfig.ResourceEntry resourceEntry) {
+ setVideoResource(resourceEntry.getResourceId(), resourceEntry.getPackageName());
+ }
+
+ /**
+ * Set the video to be played by this view.
+ *
* @param resId Resource ID of the video, typically an MP4 under res/raw.
*/
public void setVideoResource(@RawRes int resId) {
setVideoResource(resId, getContext().getPackageName());
}
+ /**
+ * Sets whether the video pauses during the screen transition.
+ *
+ * @param paused Whether the video pauses.
+ */
+ public void setPauseVideoWhenFinished(boolean paused) {
+ shouldPauseVideoWhenFinished = paused;
+ }
+
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
@@ -304,8 +353,12 @@ public class IllustrationVideoView extends TextureView
@Override
public void stop() {
- if (prepared && mediaPlayer != null) {
- mediaPlayer.pause();
+ if (shouldPauseVideoWhenFinished) {
+ if (prepared && mediaPlayer != null) {
+ mediaPlayer.pause();
+ }
+ } else {
+ // do not pause the media player.
}
}
diff --git a/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java b/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java
index f714205..d02839b 100644
--- a/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java
+++ b/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java
@@ -21,7 +21,11 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.BuildCompatUtils;
import com.google.android.setupdesign.R;
/**
@@ -54,6 +58,10 @@ public class IntrinsicSizeFrameLayout extends FrameLayout {
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
final TypedArray a =
context.obtainStyledAttributes(
attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
@@ -62,6 +70,37 @@ public class IntrinsicSizeFrameLayout extends FrameLayout {
intrinsicWidth =
a.getDimensionPixelSize(R.styleable.SudIntrinsicSizeFrameLayout_android_width, 0);
a.recycle();
+
+ if (BuildCompatUtils.isAtLeastS()) {
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_HEIGHT)) {
+ intrinsicHeight =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_HEIGHT);
+ }
+ if (PartnerConfigHelper.get(context)
+ .isPartnerConfigAvailable(PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_WIDTH)) {
+ intrinsicWidth =
+ (int)
+ PartnerConfigHelper.get(context)
+ .getDimension(context, PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_WIDTH);
+ }
+ }
+ }
+
+ @Override
+ public void setLayoutParams(ViewGroup.LayoutParams params) {
+ if (BuildCompatUtils.isAtLeastS()) {
+ // When both intrinsic height and width are 0, the card view style would be removed from
+ // foldable/tablet layout. It must set the layout width and height to MATCH_PARENT and then it
+ // can ignore the IntrinsicSizeFrameLayout from the foldable/tablet layout.
+ if (intrinsicHeight == 0 && intrinsicWidth == 0) {
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+ }
+ super.setLayoutParams(params);
}
@Override
diff --git a/main/src/com/google/android/setupdesign/view/NavigationBar.java b/main/src/com/google/android/setupdesign/view/NavigationBar.java
index 9d978f0..df3bee4 100644
--- a/main/src/com/google/android/setupdesign/view/NavigationBar.java
+++ b/main/src/com/google/android/setupdesign/view/NavigationBar.java
@@ -21,12 +21,12 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build.VERSION_CODES;
-import androidx.annotation.StyleableRes;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
+import androidx.annotation.StyleableRes;
import com.google.android.setupdesign.R;
/**
@@ -103,6 +103,10 @@ public class NavigationBar extends LinearLayout implements View.OnClickListener
// All the constructors delegate to this init method. The 3-argument constructor is not
// available in LinearLayout before v11, so call super with the exact same arguments.
private void init() {
+ if (isInEditMode()) {
+ return;
+ }
+
View.inflate(getContext(), R.layout.sud_navbar_view, this);
nextButton = (Button) findViewById(R.id.sud_navbar_next);
backButton = (Button) findViewById(R.id.sud_navbar_back);
diff --git a/main/src/com/google/android/setupdesign/view/NavigationBarButton.java b/main/src/com/google/android/setupdesign/view/NavigationBarButton.java
index 44a5b85..bb1e506 100644
--- a/main/src/com/google/android/setupdesign/view/NavigationBarButton.java
+++ b/main/src/com/google/android/setupdesign/view/NavigationBarButton.java
@@ -23,9 +23,9 @@ import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
-import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.widget.Button;
+import androidx.annotation.NonNull;
/**
* Button for navigation bar, which includes tinting of its compound drawables to be used for dark
@@ -45,6 +45,10 @@ public class NavigationBarButton extends Button {
}
private void init() {
+ if (isInEditMode()) {
+ return;
+ }
+
// Unfortunately, drawableStart and drawableEnd set through XML does not call the setter,
// so manually getting it and wrapping it in the compat drawable.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
diff --git a/main/src/com/google/android/setupdesign/view/RichTextView.java b/main/src/com/google/android/setupdesign/view/RichTextView.java
index 338b856..f3348b4 100644
--- a/main/src/com/google/android/setupdesign/view/RichTextView.java
+++ b/main/src/com/google/android/setupdesign/view/RichTextView.java
@@ -106,6 +106,10 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen
}
private void init() {
+ if (isInEditMode()) {
+ return;
+ }
+
accessibilityHelper = new LinkAccessibilityHelper(this);
ViewCompat.setAccessibilityDelegate(this, accessibilityHelper);
}
diff --git a/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java b/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java
index 3efb85c..07d1781 100644
--- a/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java
+++ b/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java
@@ -73,6 +73,10 @@ public class StickyHeaderListView extends ListView {
}
private void init(AttributeSet attrs, int defStyleAttr) {
+ if (isInEditMode()) {
+ return;
+ }
+
final TypedArray a =
getContext()
.obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
diff --git a/main/src/com/google/android/setupdesign/widget/CardBackgroundDrawable.java b/main/src/com/google/android/setupdesign/widget/CardBackgroundDrawable.java
new file mode 100644
index 0000000..b354921
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/widget/CardBackgroundDrawable.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 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.google.android.setupdesign.widget;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Path.Direction;
+import android.graphics.Path.FillType;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
+
+/** A rounded rectangle drawable. */
+public class CardBackgroundDrawable extends Drawable {
+ private final float inset;
+
+ private final Paint paint;
+ private final RectF cardBounds = new RectF();
+ private final Path clipPath = new Path();
+
+ private float cornerRadius;
+ private boolean dirty = false;
+
+ /**
+ * @param color Background color of the card to be rendered
+ * @param radius Corner rounding radius
+ * @param inset Inset from the edge of the canvas to the card
+ */
+ public CardBackgroundDrawable(@ColorInt int color, float radius, float inset) {
+ cornerRadius = radius;
+ paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+ paint.setColor(color);
+ this.inset = inset;
+ }
+
+ @Override
+ public void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ dirty = true;
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter cf) {
+ paint.setColorFilter(cf);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ public void setCornerRadius(float radius) {
+ if (cornerRadius == radius) {
+ return;
+ }
+
+ cornerRadius = radius;
+ dirty = true;
+ invalidateSelf();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (dirty) {
+ buildComponents(getBounds());
+ dirty = false;
+ }
+
+ if (cornerRadius > 0) {
+ canvas.clipPath(clipPath);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {}
+
+ private void buildComponents(Rect bounds) {
+ cardBounds.set(bounds);
+ cardBounds.inset(inset, inset);
+
+ clipPath.reset();
+ clipPath.setFillType(FillType.EVEN_ODD);
+ clipPath.addRoundRect(cardBounds, cornerRadius, cornerRadius, Direction.CW);
+ }
+}