Merging from ub-launcher3-master @ build 6519151 am: 225204bc25
Change-Id: Iae04db5d9c56f8e2c8c4cfa6bf409e311fb13eac
diff --git a/res/drawable/option_border_custom.xml b/res/drawable/option_border_custom.xml
index 0fe8d12..648bc4f 100644
--- a/res/drawable/option_border_custom.xml
+++ b/res/drawable/option_border_custom.xml
@@ -18,7 +18,7 @@
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@android:color/white"/>
- <corners android:radius="4dp" />
+ <corners android:radius="?android:dialogCornerRadius" />
</shape>
</item>
<item android:drawable="@drawable/option_border_edge_custom" />
diff --git a/res/drawable/option_border_edge_custom.xml b/res/drawable/option_border_edge_custom.xml
index 1fba26b..1863c5d 100644
--- a/res/drawable/option_border_edge_custom.xml
+++ b/res/drawable/option_border_edge_custom.xml
@@ -20,7 +20,7 @@
<stroke
android:color="@color/option_border_color"
android:width="@dimen/option_selected_border_width" />
- <corners android:radius="4dp" />
+ <corners android:radius="?android:dialogCornerRadius" />
</shape>
</item>
<item android:state_activated="false">
@@ -29,7 +29,7 @@
android:color="@color/black_24_alpha"
android:alpha="0.24"
android:width="@dimen/option_border_width" />
- <corners android:radius="4dp" />
+ <corners android:radius="?android:dialogCornerRadius" />
</shape>
</item>
</selector>
diff --git a/res/layout-land/fragment_grid_picker.xml b/res/layout-land/fragment_grid_picker.xml
index 6c2d8c5..dab9308 100644
--- a/res/layout-land/fragment_grid_picker.xml
+++ b/res/layout-land/fragment_grid_picker.xml
@@ -31,13 +31,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
- <com.android.wallpaper.widget.PreviewPager
- android:id="@+id/grid_preview_pager"
+ <FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:background="@color/secondary_color"
- app:card_style="screen_aspect_ratio"/>
+ android:clipToPadding="false"
+ android:paddingTop="@dimen/preview_content_padding_top"
+ android:paddingBottom="@dimen/preview_content_padding_bottom"
+ android:background="@color/fullscreen_preview_background">
+ <include layout="@layout/grid_preview_card"/>
+ </FrameLayout>
<FrameLayout
android:id="@+id/options_section"
diff --git a/res/layout-land/fragment_theme_picker.xml b/res/layout-land/fragment_theme_picker.xml
index dc30232..915fb47 100644
--- a/res/layout-land/fragment_theme_picker.xml
+++ b/res/layout-land/fragment_theme_picker.xml
@@ -31,39 +31,28 @@
android:layout_height="match_parent"
android:orientation="horizontal">
- <com.android.wallpaper.widget.PreviewPager
- android:id="@+id/theme_preview_pager"
+ <FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="@color/secondary_color"/>
- <View
- android:layout_width="1dp"
- android:layout_height="match_parent"
- android:background="@color/divider_color"/>
- <LinearLayout
- android:id="@+id/options_section"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:paddingVertical="10dp"
- android:orientation="vertical">
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/options_container"
+ android:layout_weight="1">
+ <FrameLayout
+ android:id="@+id/preview_card_container"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"/>
+ android:layout_height="@dimen/preview_content_height"
+ android:paddingTop="@dimen/preview_content_padding_top"
+ android:paddingBottom="@dimen/preview_content_padding_bottom"
+ android:clipToPadding="false"
+ android:background="@color/fullscreen_preview_background">
+ <include layout="@layout/theme_preview_card_v2"/>
+ </FrameLayout>
+ </FrameLayout>
- <Button
- android:id="@+id/apply_button"
- style="@style/ActionPrimaryButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:paddingHorizontal="10dp"
- android:text="@string/apply_theme_btn"/>
- </LinearLayout>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/options_container"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingVertical="10dp" />
</LinearLayout>
<androidx.core.widget.ContentLoadingProgressBar
diff --git a/res/layout/activity_custom_theme.xml b/res/layout/activity_custom_theme.xml
index b2aaa73..24d58b7 100644
--- a/res/layout/activity_custom_theme.xml
+++ b/res/layout/activity_custom_theme.xml
@@ -33,7 +33,8 @@
android:id="@+id/custom_theme_nav"
android:layout_width="match_parent"
android:layout_height="@dimen/custom_theme_nav_height"
- android:layout_marginHorizontal="12dp">
+ android:paddingHorizontal="12dp"
+ android:background="?android:colorPrimary">
<Button
android:id="@+id/previous_button"
style="@style/ActionSecondaryButton"
diff --git a/res/layout/custom_theme_option.xml b/res/layout/custom_theme_option.xml
index c91cbf1..2805185 100644
--- a/res/layout/custom_theme_option.xml
+++ b/res/layout/custom_theme_option.xml
@@ -16,15 +16,22 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal="@dimen/option_padding_horizontal"
+ android:paddingBottom="@dimen/option_bottom_margin"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:orientation="vertical">
<TextView
android:id="@+id/option_label"
- android:layout_width="wrap_content"
+ android:layout_width="@dimen/option_tile_width"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/theme_option_label_margin"
+ android:ellipsize="end"
+ android:gravity="center_horizontal"
+ android:maxLines="1"
android:textAppearance="@style/OptionTitleTextAppearance"/>
<FrameLayout
android:id="@+id/option_tile"
@@ -35,8 +42,8 @@
android:paddingVertical="@dimen/option_tile_padding_vertical"
android:background="@drawable/option_border_custom">
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/option_icon_size"
+ android:layout_height="@dimen/option_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_add_24px"
android:tint="?android:attr/colorAccent" />
diff --git a/res/layout/fragment_grid_full_preview.xml b/res/layout/fragment_grid_full_preview.xml
index 1c01743..d39be36 100644
--- a/res/layout/fragment_grid_full_preview.xml
+++ b/res/layout/fragment_grid_full_preview.xml
@@ -26,33 +26,11 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
+ android:paddingTop="@dimen/full_preview_page_default_padding_top"
+ android:paddingBottom="@dimen/full_preview_page_default_padding_bottom"
+ android:clipToPadding="false"
android:background="@color/fullscreen_preview_background">
- <androidx.cardview.widget.CardView
- style="@style/FullContentPreviewCard"
- android:id="@+id/grid_full_preview_card"
- android:layout_marginTop="24dp"
- android:layout_marginBottom="32dp"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center">
-
- <ImageView
- android:id="@+id/grid_full_preview_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/primary_color"/>
-
- <SurfaceView
- android:id="@+id/grid_full_preview_option_surface"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- <SurfaceView
- android:id="@+id/grid_full_preview_wallpaper_surface"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- </androidx.cardview.widget.CardView>
+ <include layout="@layout/grid_preview_card"/>
</FrameLayout>
</LinearLayout>
diff --git a/res/layout/fragment_grid_picker.xml b/res/layout/fragment_grid_picker.xml
index 5bf96e1..af70e0c 100644
--- a/res/layout/fragment_grid_picker.xml
+++ b/res/layout/fragment_grid_picker.xml
@@ -16,7 +16,6 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@@ -27,26 +26,21 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <androidx.constraintlayout.widget.ConstraintLayout
+ <LinearLayout
android:id="@+id/content_section"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <com.android.wallpaper.widget.PreviewPager
- android:id="@+id/grid_preview_pager"
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/preview_pager_background"
- app:card_style="screen_aspect_ratio"
- app:layout_constrainedHeight="true"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/options_title"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
- app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
- app:layout_constraintVertical_bias="0.0"
- app:layout_constraintVertical_chainStyle="spread_inside"/>
+ android:layout_height="@dimen/preview_content_height"
+ android:paddingTop="@dimen/preview_content_padding_top"
+ android:paddingBottom="@dimen/preview_content_padding_bottom"
+ android:clipToPadding="false"
+ android:background="@color/fullscreen_preview_background">
+ <include layout="@layout/grid_preview_card"/>
+ </FrameLayout>
<TextView
android:id="@+id/options_title"
@@ -57,22 +51,14 @@
android:lineHeight="24dp"
android:singleLine="true"
android:text="@string/grid_options_title"
- android:textAppearance="@style/TitleTextAppearance"
- app:layout_constraintTop_toBottomOf="@id/grid_preview_pager"
- app:layout_constraintBottom_toTopOf="@id/options_container"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"/>
+ android:textAppearance="@style/TitleTextAppearance"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/options_container"
android:layout_width="match_parent"
- android:layout_height="@dimen/options_container_height"
- android:layout_gravity="center_horizontal"
- app:layout_constraintTop_toBottomOf="@+id/options_title"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintVertical_bias="1.0"/>
- </androidx.constraintlayout.widget.ConstraintLayout>
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"/>
+ </LinearLayout>
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/loading_indicator"
diff --git a/res/layout/fragment_theme_full_preview.xml b/res/layout/fragment_theme_full_preview.xml
new file mode 100644
index 0000000..7573c5b
--- /dev/null
+++ b/res/layout/fragment_theme_full_preview.xml
@@ -0,0 +1,36 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/section_header"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:paddingTop="@dimen/full_preview_page_default_padding_top"
+ android:paddingBottom="@dimen/full_preview_page_default_padding_bottom"
+ android:clipToPadding="false"
+ android:background="@color/fullscreen_preview_background">
+
+ <include layout="@layout/theme_preview_card_v2"/>
+ </FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_theme_picker.xml b/res/layout/fragment_theme_picker.xml
index d15d93f..704114f 100644
--- a/res/layout/fragment_theme_picker.xml
+++ b/res/layout/fragment_theme_picker.xml
@@ -16,7 +16,6 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@@ -27,49 +26,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <androidx.constraintlayout.widget.ConstraintLayout
+ <LinearLayout
android:id="@+id/content_section"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <com.android.wallpaper.widget.PreviewPager
- android:id="@+id/theme_preview_pager"
+ <FrameLayout
+ android:id="@+id/preview_card_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/preview_pager_background"
- app:layout_constrainedHeight="true"
- app:layout_constraintBottom_toTopOf="@id/options_container"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- app:layout_constraintVertical_chainStyle="spread_inside"/>
+ android:layout_height="@dimen/preview_content_height"
+ android:paddingTop="@dimen/preview_content_padding_top"
+ android:paddingBottom="@dimen/preview_content_padding_bottom"
+ android:clipToPadding="false"
+ android:background="@color/fullscreen_preview_background">
+ <include layout="@layout/theme_preview_card_v2"/>
+ </FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/options_container"
android:layout_width="match_parent"
- android:layout_height="@dimen/options_container_height"
+ android:layout_height="0dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginTop="10dp"
- android:layout_weight="1"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/theme_preview_pager"
- app:layout_constraintVertical_bias="1.0"/>
-
- <Button
- android:id="@+id/apply_button"
- style="@style/ActionPrimaryButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginEnd="10dp"
- android:layout_marginBottom="10dp"
- android:text="@string/apply_theme_btn"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"/>
- </androidx.constraintlayout.widget.ConstraintLayout>
+ android:layout_weight="1"/>
+ </LinearLayout>
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/loading_indicator"
diff --git a/res/layout/grid_preview_card.xml b/res/layout/grid_preview_card.xml
index 90b5578..2939f80 100644
--- a/res/layout/grid_preview_card.xml
+++ b/res/layout/grid_preview_card.xml
@@ -18,18 +18,22 @@
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/FullContentPreviewCard"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:layout_gravity="center">
<ImageView
- android:id="@+id/grid_preview_image"
+ android:id="@+id/wallpaper_preview_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/primary_color" />
<SurfaceView
- android:id="@+id/grid_preview_surface"
+ android:id="@+id/wallpaper_preview_surface"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/primary_color" />
+ android:layout_height="match_parent" />
+ <FrameLayout
+ android:id="@+id/grid_preview_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/res/layout/theme_info_view.xml b/res/layout/theme_info_view.xml
new file mode 100644
index 0000000..2bb8701
--- /dev/null
+++ b/res/layout/theme_info_view.xml
@@ -0,0 +1,77 @@
+<?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.
+-->
+<com.android.customization.widget.ThemeInfoView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:paddingHorizontal="@dimen/wallpaper_info_pane_horizontal_padding"
+ android:paddingTop="@dimen/wallpaper_info_pane_top_padding"
+ android:paddingBottom="@dimen/wallpaper_info_pane_bottom_padding"
+ android:theme="@style/WallpaperPicker.BottomPaneStyle">
+
+ <TextView
+ android:id="@+id/style_info_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/theme_info_margin"
+ android:gravity="center"
+ android:lineHeight="24dp"
+ android:textAppearance="@style/SubtitleTextAppearance"
+ android:textColor="@color/action_bar_bottom_sheet_text_color"
+ android:textSize="16sp"
+ android:text="@string/style_info_description"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center">
+
+ <TextView
+ android:id="@+id/font_preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginHorizontal="@dimen/theme_info_margin"
+ android:textSize="@dimen/theme_info_text_size"
+ android:textColor="?android:attr/colorForeground"
+ android:text="@string/font_component_option_thumbnail"/>
+
+ <ImageView
+ android:id="@+id/qs_preview_icon"
+ android:layout_width="@dimen/theme_info_icon_size"
+ android:layout_height="@dimen/theme_info_icon_size"
+ android:layout_marginHorizontal="@dimen/theme_info_margin"
+ android:tint="@color/theme_preview_icon_color"/>
+
+ <ImageView
+ android:id="@+id/app_preview_icon"
+ android:layout_width="@dimen/theme_info_icon_size"
+ android:layout_height="@dimen/theme_info_icon_size"
+ android:layout_marginHorizontal="@dimen/theme_info_margin"
+ android:layout_marginVertical="@dimen/theme_info_app_preview_icon_margin"
+ android:elevation="@dimen/theme_info_app_preview_icon_elevation"/>
+
+ <ImageView
+ android:id="@+id/shape_preview_icon"
+ android:layout_width="@dimen/theme_info_icon_size"
+ android:layout_height="@dimen/theme_info_icon_size"
+ android:layout_marginHorizontal="@dimen/theme_info_margin"/>
+ </LinearLayout>
+</com.android.customization.widget.ThemeInfoView>
\ No newline at end of file
diff --git a/res/layout/theme_option.xml b/res/layout/theme_option.xml
index 98fae71..557d532 100644
--- a/res/layout/theme_option.xml
+++ b/res/layout/theme_option.xml
@@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingRight="2dp"
+ android:paddingHorizontal="@dimen/option_padding_horizontal"
android:paddingBottom="@dimen/option_bottom_margin"
android:clipChildren="false"
android:clipToPadding="false"
@@ -47,21 +47,21 @@
android:layout_height="@dimen/theme_option_icon_sample_height"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
- android:layout_margin="@dimen/theme_option_sample_padding"
android:tint="?android:colorForeground"/>
<ImageView
android:id="@+id/theme_option_shape"
android:layout_width="@dimen/theme_option_shape_sample_width"
android:layout_height="@dimen/theme_option_shape_sample_height"
- android:layout_alignParentRight="true"
android:layout_alignBottom="@+id/theme_option_icon"
- android:layout_marginHorizontal="@dimen/theme_option_sample_padding"/>
+ android:layout_toEndOf="@id/theme_option_icon"
+ android:layout_marginLeft="@dimen/theme_option_sample_margin"/>
<TextView
android:id="@+id/theme_option_font"
android:layout_width="@dimen/theme_option_font_sample_width"
android:layout_height="@dimen/theme_option_font_sample_height"
- android:layout_alignParentBottom="true"
- android:layout_gravity="center_horizontal"
+ android:layout_gravity="center"
+ android:layout_below="@id/theme_option_icon"
+ android:layout_marginTop="@dimen/option_bottom_margin"
android:autoSizeMaxTextSize="@dimen/theme_option_font_text_size"
android:autoSizeMinTextSize="@dimen/theme_option_font_min_text_size"
android:autoSizeTextType="uniform"
diff --git a/res/layout/theme_preview_app_icon_shape.xml b/res/layout/theme_preview_app_icon_shape.xml
new file mode 100644
index 0000000..c6f4cd9
--- /dev/null
+++ b/res/layout/theme_preview_app_icon_shape.xml
@@ -0,0 +1,122 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal="@dimen/preview_theme_app_icon_shape_padding_horizontal"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/preview_theme_app_icon_shape_padding_top"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/shape_preview_icon_0"
+ android:layout_width="@dimen/preview_theme_shape_size"
+ android:layout_height="@dimen/preview_theme_shape_size"
+ android:elevation="4dp"/>
+ <TextView
+ android:id="@+id/shape_preview_icon_app_name_0"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+ android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+ android:lineHeight="20dp"/>
+ </LinearLayout>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/shape_preview_icon_1"
+ android:layout_width="@dimen/preview_theme_shape_size"
+ android:layout_height="@dimen/preview_theme_shape_size"
+ android:elevation="4dp"/>
+ <TextView
+ android:id="@+id/shape_preview_icon_app_name_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+ android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+ android:lineHeight="20dp"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/preview_theme_app_icon_shape_padding_top"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/shape_preview_icon_2"
+ android:layout_width="@dimen/preview_theme_shape_size"
+ android:layout_height="@dimen/preview_theme_shape_size"
+ android:elevation="4dp"/>
+ <TextView
+ android:id="@+id/shape_preview_icon_app_name_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+ android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+ android:lineHeight="20dp"/>
+ </LinearLayout>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/shape_preview_icon_3"
+ android:layout_width="@dimen/preview_theme_shape_size"
+ android:layout_height="@dimen/preview_theme_shape_size"
+ android:elevation="4dp"/>
+ <TextView
+ android:id="@+id/shape_preview_icon_app_name_3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+ android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+ android:lineHeight="20dp"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/theme_preview_card_v2.xml b/res/layout/theme_preview_card_v2.xml
new file mode 100644
index 0000000..74d1e68
--- /dev/null
+++ b/res/layout/theme_preview_card_v2.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+<androidx.cardview.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/FullContentPreviewCard"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center">
+
+ <ImageView
+ android:id="@+id/wallpaper_preview_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/primary_color" />
+
+ <SurfaceView
+ android:id="@+id/wallpaper_preview_surface"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <FrameLayout
+ android:id="@+id/theme_preview_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/res/layout/theme_preview_color_icons.xml b/res/layout/theme_preview_color_icons.xml
new file mode 100644
index 0000000..7a068ee
--- /dev/null
+++ b/res/layout/theme_preview_color_icons.xml
@@ -0,0 +1,169 @@
+<?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.
+-->
+<androidx.cardview.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/color_icons_section"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/primary_color">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal="@dimen/preview_theme_color_icons_padding_horizontal"
+ android:orientation="vertical">
+
+ <!-- Title -->
+ <TextView
+ android:id="@+id/color_icons_section_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/preview_theme_color_icons_title_margin_top"
+ android:layout_marginBottom="@dimen/preview_theme_color_icons_title_margin_bottom"
+ android:text="@string/theme_preview_icons_section_title"
+ android:textSize="@dimen/preview_theme_color_icons_title_text_size"
+ android:lineHeight="16dp"
+ android:gravity="center"/>
+
+ <!-- QS icons -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <FrameLayout
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size">
+ <ImageView
+ android:id="@+id/preview_color_qs_0_bg"
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+ <ImageView
+ android:id="@+id/preview_color_qs_0_icon"
+ android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+ android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+ android:tint="@color/tile_enabled_icon_color"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <FrameLayout
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_gravity="center_horizontal">
+ <ImageView
+ android:id="@+id/preview_color_qs_1_bg"
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+ <ImageView
+ android:id="@+id/preview_color_qs_1_icon"
+ android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+ android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+ android:tint="@color/tile_enabled_icon_color"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <FrameLayout
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size">
+ <ImageView
+ android:id="@+id/preview_color_qs_2_bg"
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+ <ImageView
+ android:id="@+id/preview_color_qs_2_icon"
+ android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+ android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+ android:tint="@color/tile_enabled_icon_color"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <FrameLayout
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size">
+ <ImageView
+ android:id="@+id/preview_color_qs_3_bg"
+ android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+ android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+ <ImageView
+ android:id="@+id/preview_color_qs_3_icon"
+ android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+ android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+ android:tint="@color/tile_enabled_icon_color"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+ </LinearLayout>
+
+ <!-- Icons of CheckBox/RadioButton/Switch. -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/preview_theme_color_icons_buttons_margin_top"
+ android:layout_marginBottom="@dimen/preview_theme_color_icons_buttons_margin_bottom"
+ android:orientation="horizontal">
+ <FrameLayout
+ android:layout_width="@dimen/preview_theme_icon_size"
+ android:layout_height="@dimen/preview_theme_icon_size">
+ <CheckBox
+ android:id="@+id/preview_check_selected"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:checked="true"
+ android:enabled="false"/>
+ </FrameLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ <FrameLayout
+ android:layout_width="@dimen/preview_theme_icon_size"
+ android:layout_height="@dimen/preview_theme_icon_size">
+ <RadioButton
+ android:id="@+id/preview_radio_selected"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:checked="true"
+ android:enabled="false"/>
+ </FrameLayout>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ <FrameLayout
+ android:layout_width="@dimen/preview_theme_icon_size"
+ android:layout_height="@dimen/preview_theme_icon_size">
+ <Switch
+ android:id="@+id/preview_toggle_selected"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:checked="true"
+ android:enabled="false"/>
+ </FrameLayout>
+ </LinearLayout>
+
+ </LinearLayout>
+</androidx.cardview.widget.CardView>
diff --git a/res/layout/theme_preview_content_v2.xml b/res/layout/theme_preview_content_v2.xml
new file mode 100644
index 0000000..ea5db19
--- /dev/null
+++ b/res/layout/theme_preview_content_v2.xml
@@ -0,0 +1,50 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/preview_theme_content_padding_top"
+ android:paddingHorizontal="@dimen/preview_theme_content_padding"
+ android:paddingBottom="@dimen/preview_theme_content_padding"
+ android:orientation="vertical">
+
+ <include layout="@layout/theme_preview_topbar" />
+
+ <TextView
+ android:id="@+id/smart_space_date"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/preview_theme_smart_space_date_size"
+ android:lineHeight="24dp"
+ android:gravity="center"
+ android:layout_marginTop="@dimen/preview_theme_smart_space_margin_top"/>
+
+ <include layout="@layout/theme_preview_app_icon_shape" />
+
+ <include layout="@layout/theme_preview_color_icons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/preview_theme_color_icons_margin_top"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <include layout="@layout/theme_cover_qsb" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/bools.xml b/res/values/bools.xml
index 078b50c..409538d 100644
--- a/res/values/bools.xml
+++ b/res/values/bools.xml
@@ -16,5 +16,5 @@
limitations under the License.
-->
<resources>
- <bool name="use_grid_for_options">false</bool>
+ <bool name="use_grid_for_options">true</bool>
</resources>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3c78967..ec21329 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -30,20 +30,22 @@
<dimen name="options_container_height">120dp</dimen>
<dimen name="options_container_width">0dp</dimen>
<dimen name="option_tile_width">88dp</dimen>
- <dimen name="theme_option_icon_sample_height">18dp</dimen>
- <dimen name="theme_option_icon_sample_width">18dp</dimen>
- <dimen name="theme_option_shape_sample_height">16dp</dimen>
- <dimen name="theme_option_shape_sample_width">16dp</dimen>
- <dimen name="theme_option_font_sample_height">16dp</dimen>
+ <dimen name="option_icon_size">16dp</dimen>
+ <dimen name="theme_option_icon_sample_height">22dp</dimen>
+ <dimen name="theme_option_icon_sample_width">22dp</dimen>
+ <dimen name="theme_option_shape_sample_height">20dp</dimen>
+ <dimen name="theme_option_shape_sample_width">20dp</dimen>
+ <dimen name="theme_option_font_sample_height">20dp</dimen>
<dimen name="theme_option_font_sample_width">52dp</dimen>
- <dimen name="theme_option_sample_padding">5dp</dimen>
+ <dimen name="theme_option_sample_margin">10dp</dimen>
<!-- Note, using dp instead of sp as this is just the "+" symbol, not text -->
- <dimen name="option_tile_padding_vertical">16dp</dimen>
- <dimen name="option_tile_padding_horizontal">16dp</dimen>
+ <dimen name="option_tile_padding_vertical">18dp</dimen>
+ <dimen name="option_tile_padding_horizontal">18dp</dimen>
<dimen name="option_bottom_margin">8dp</dimen>
+ <dimen name="option_padding_horizontal">2dp</dimen>
<!-- Note, using dp instead of sp as this text is more like a "snapshot" of the font -->
- <dimen name="theme_option_font_text_size">16dp</dimen>
+ <dimen name="theme_option_font_text_size">20dp</dimen>
<dimen name="theme_option_font_min_text_size">15dp</dimen>
<dimen name="option_tile_margin_horizontal">6dp</dimen>
@@ -64,12 +66,36 @@
<dimen name="preview_theme_icon_size">30dp</dimen>
<dimen name="preview_theme_tile_size">16dp</dimen>
<dimen name="preview_theme_shape_size">36dp</dimen>
- <dimen name="preview_theme_cover_topbar_clock_size">14sp</dimen>
+ <dimen name="preview_theme_cover_topbar_clock_size">12sp</dimen>
<dimen name="preview_theme_cover_topbar_icon_size">16dp</dimen>
<dimen name="preview_theme_cover_content_extra_margin">16dp</dimen>
<dimen name="preview_theme_content_bottom">@dimen/min_taptarget_height</dimen>
<dimen name="preview_theme_cover_content_bottom">@dimen/preview_theme_content_bottom</dimen>
+ <!-- For the new preview of theme picker. -->
+ <dimen name="preview_theme_content_padding_top">12dp</dimen>
+ <dimen name="preview_theme_content_padding">@dimen/preview_card_padding</dimen>
+ <dimen name="preview_theme_smart_space_margin_top">48dp</dimen>
+ <dimen name="preview_theme_smart_space_date_size">16sp</dimen>
+ <dimen name="preview_theme_app_icon_shape_padding_horizontal">36dp</dimen>
+ <dimen name="preview_theme_app_icon_shape_padding_top">28dp</dimen>
+ <dimen name="preview_theme_app_icon_shape_text_margin_top">8dp</dimen>
+ <dimen name="preview_theme_app_icon_shape_text_size">16sp</dimen>
+ <dimen name="preview_theme_color_icons_margin_top">40dp</dimen>
+ <dimen name="preview_theme_color_icons_padding_horizontal">18dp</dimen>
+ <dimen name="preview_theme_color_icons_title_text_size">12sp</dimen>
+ <dimen name="preview_theme_color_icons_title_margin_top">12dp</dimen>
+ <dimen name="preview_theme_color_icons_title_margin_bottom">20dp</dimen>
+ <dimen name="preview_theme_color_icons_icon_size">@dimen/preview_theme_icon_size</dimen>
+ <dimen name="preview_theme_color_icons_tile_size">@dimen/preview_theme_tile_size</dimen>
+ <dimen name="preview_theme_color_icons_buttons_margin_top">32dp</dimen>
+ <dimen name="preview_theme_color_icons_buttons_margin_bottom">28dp</dimen>
+
+ <!-- For the customization previews on the picker. -->
+ <dimen name="preview_content_height">@dimen/preview_pager_height</dimen>
+ <dimen name="preview_content_padding_top">@dimen/preview_page_top_margin</dimen>
+ <dimen name="preview_content_padding_bottom">@dimen/indicator_container_height</dimen>
+
<dimen name="font_preview_body_width">200dp</dimen>
<dimen name="font_preview_divider_gap">24dp</dimen>
@@ -91,4 +117,11 @@
<dimen name="min_taptarget_height">48dp</dimen>
+ <!-- For the style info preview sheet. -->
+ <dimen name="theme_info_margin">12dp</dimen>
+ <dimen name="theme_info_icon_size">24dp</dimen>
+ <dimen name="theme_info_app_preview_icon_margin">2dp</dimen>
+ <dimen name="theme_info_app_preview_icon_elevation">2dp</dimen>
+ <dimen name="theme_info_text_size">28sp</dimen>
+
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c0aef7a..f1cd169 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -119,6 +119,9 @@
[CHAR LIMIT=20] -->
<string name="custom_theme_previous">Previous</string>
+ <!-- Title for "Custom theme" option. [CHAR LIMIT=15] -->
+ <string name="custom_theme">Custom</string>
+
<!-- Generic label for one system Style/Theme (combination of fonts/colors/icons) that is
defined and customized by the user (note there could be more than one so the label includes
a number, eg "Custom 1, Custom 2, etc") [CHAR LIMIT=15] -->
@@ -189,4 +192,11 @@
<!-- Generic error message [CHAR_LIMIT=NONE] -->
<string name="something_went_wrong">Oops! Something went wrong.</string>
+
+ <!-- Title for a section of a style preview screen that shows a preview of the style color and icons. [CHAR_LIMIT=30]-->
+ <string name="theme_preview_icons_section_title">Color / Icons</string>
+
+ <!-- Bottom sheet dialog which displaying different theme's info. [CHAR_LIMIT=30]
+ (eg, Fonts, icons, shape, color will change to the "Default" Style) -->
+ <string name="style_info_description">Fonts, icons, shape, color will change to the <xliff:g name="style_name">%1$s</xliff:g> Style</string>
</resources>
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
index 7c99f9c..60eb66d 100644
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ b/src/com/android/customization/model/theme/DefaultThemeProvider.java
@@ -28,6 +28,7 @@
import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources.NotFoundException;
import android.text.TextUtils;
@@ -78,9 +79,6 @@
private static final String THEME_TITLE_FIELD = "_theme_title";
private static final String THEME_ID_FIELD = "_theme_id";
- // Maximum number of themes allowed (including default, pre-bundled and custom)
- private static final int MAX_TOTAL_THEMES = 10;
-
private final OverlayThemeExtractor mOverlayProvider;
private List<ThemeBundle> mThemes;
private final CustomizationPreferences mCustomizationPreferences;
@@ -109,6 +107,10 @@
}
private void loadAll() {
+ // Add "Custom" option at the first.
+ mThemes.add(new CustomTheme(CustomTheme.newId(), mContext.getString(
+ R.string.custom_theme), new HashMap<>(), null));
+
addDefaultTheme();
String[] themeNames = getItemsFromStub(THEMES_ARRAY);
@@ -211,6 +213,11 @@
try {
builder.addShapePreviewIcon(
mContext.getPackageManager().getApplicationIcon(packageName));
+ // Add the shape icon app name.
+ ApplicationInfo appInfo = mContext.getPackageManager()
+ .getApplicationInfo(packageName, /* flag= */ 0);
+ builder.addShapePreviewIconName(
+ String.valueOf(mContext.getPackageManager().getApplicationLabel(appInfo)));
} catch (NameNotFoundException e) {
Log.d(TAG, "Couldn't find app " + packageName + ", won't use it for icon shape"
+ "preview");
@@ -321,15 +328,15 @@
new HashMap<>(), null));
}
}
-
- if (mThemes.size() < MAX_TOTAL_THEMES) {
- // Add an empty one at the end.
- mThemes.add(new CustomTheme(CustomTheme.newId(), mContext.getString(
- R.string.custom_theme_title, customThemesCount + 1), new HashMap<>(), null));
- }
-
}
+ @Nullable
+ @Override
+ public ThemeBundle.Builder parseThemeBundle(String serializedTheme) throws JSONException {
+ return parseCustomTheme(serializedTheme);
+ }
+
+ @Nullable
@Override
public CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException {
JSONObject theme = new JSONObject(serializedTheme);
diff --git a/src/com/android/customization/model/theme/OverlayThemeExtractor.java b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
index 9c85fa4..f1698f6 100644
--- a/src/com/android/customization/model/theme/OverlayThemeExtractor.java
+++ b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
@@ -8,6 +8,7 @@
import android.content.Context;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -102,6 +103,11 @@
builder.addShapePreviewIcon(
mContext.getPackageManager().getApplicationIcon(
packageName));
+ // Add the shape icon app name.
+ ApplicationInfo appInfo = mContext.getPackageManager()
+ .getApplicationInfo(packageName, /* flag= */ 0);
+ builder.addShapePreviewIconName(
+ String.valueOf(mContext.getPackageManager().getApplicationLabel(appInfo)));
} catch (NameNotFoundException e) {
Log.d(TAG, "Couldn't find app " + packageName
+ ", won't use it for icon shape preview");
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
index 14b2dcd..9928c76 100644
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ b/src/com/android/customization/model/theme/ThemeBundle.java
@@ -251,12 +251,13 @@
public final List<Drawable> icons;
public final Drawable shapeDrawable;
public final List<Drawable> shapeAppIcons;
+ public final List<String> shapeAppIconNames;
@Dimension public final int bottomSheeetCornerRadius;
private PreviewInfo(Context context, Typeface bodyFontFamily, Typeface headlineFontFamily,
int colorAccentLight, int colorAccentDark, List<Drawable> icons,
Drawable shapeDrawable, @Dimension int cornerRadius,
- List<Drawable> shapeAppIcons) {
+ List<Drawable> shapeAppIcons, List<String> shapeAppIconNames) {
this.bodyFontFamily = bodyFontFamily;
this.headlineFontFamily = headlineFontFamily;
this.colorAccentLight = colorAccentLight;
@@ -265,6 +266,7 @@
this.shapeDrawable = shapeDrawable;
this.bottomSheeetCornerRadius = cornerRadius;
this.shapeAppIcons = shapeAppIcons;
+ this.shapeAppIconNames = shapeAppIconNames;
}
/**
@@ -292,6 +294,7 @@
@Dimension private int mCornerRadius;
protected Map<String, String> mPackages = new HashMap<>();
private List<Drawable> mAppIcons = new ArrayList<>();
+ private List<String> mAppIconNames = new ArrayList<>();
public ThemeBundle build(Context context) {
return new ThemeBundle(mTitle, mPackages, mIsDefault, createPreviewInfo(context));
@@ -322,7 +325,8 @@
}
}
return new PreviewInfo(context, mBodyFontFamily, mHeadlineFontFamily, mColorAccentLight,
- mColorAccentDark, mIcons, shapeDrawable, mCornerRadius, shapeIcons);
+ mColorAccentDark, mIcons, shapeDrawable, mCornerRadius, shapeIcons,
+ mAppIconNames);
}
public Map<String, String> getPackages() {
@@ -388,6 +392,11 @@
return this;
}
+ public Builder addShapePreviewIconName(String appIconName) {
+ mAppIconNames.add(appIconName);
+ return this;
+ }
+
public Builder setBottomSheetCornerRadius(@Dimension int radius) {
mCornerRadius = radius;
return this;
diff --git a/src/com/android/customization/model/theme/ThemeBundleProvider.java b/src/com/android/customization/model/theme/ThemeBundleProvider.java
index fac11cd..34342b3 100644
--- a/src/com/android/customization/model/theme/ThemeBundleProvider.java
+++ b/src/com/android/customization/model/theme/ThemeBundleProvider.java
@@ -43,6 +43,8 @@
void removeCustomTheme(CustomTheme theme);
+ @Nullable ThemeBundle.Builder parseThemeBundle(String serializedTheme) throws JSONException;
+
@Nullable CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException;
ThemeBundle findEquivalent(ThemeBundle other);
diff --git a/src/com/android/customization/picker/TimeTicker.java b/src/com/android/customization/picker/TimeTicker.java
deleted file mode 100644
index 2cf5dbf..0000000
--- a/src/com/android/customization/picker/TimeTicker.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.android.customization.picker;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-
-import androidx.annotation.Nullable;
-
-/**
- * BroadcastReceiver that can notify a listener when the system time (minutes) changes.
- * Use {@link #registerNewReceiver(Context, TimeListener)} to create a new instance that will be
- * automatically registered using the given Context.
- */
-public class TimeTicker extends BroadcastReceiver {
-
- public interface TimeListener {
- void onCurrentTimeChanged();
- }
-
- public static TimeTicker registerNewReceiver(Context context, TimeListener listener) {
- TimeTicker receiver = new TimeTicker(listener);
- // Register broadcast receiver for time tick
- final IntentFilter filter = new IntentFilter(Intent.ACTION_TIME_TICK);
- context.registerReceiver(receiver, filter);
- return receiver;
- }
-
- @Nullable private TimeListener mListener;
-
- private TimeTicker(TimeListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mListener != null) {
- mListener.onCurrentTimeChanged();
- }
- }
-}
diff --git a/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java b/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
index 19324da..2afbc16 100644
--- a/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
+++ b/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
@@ -15,9 +15,12 @@
*/
package com.android.customization.picker;
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_THEME_OPTION_TITLE;
+
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.text.TextUtils;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -25,6 +28,7 @@
import androidx.fragment.app.FragmentActivity;
import com.android.customization.picker.grid.GridFullPreviewFragment;
+import com.android.customization.picker.theme.ThemeFullPreviewFragment;
import com.android.wallpaper.R;
import com.android.wallpaper.widget.BottomActionBar;
import com.android.wallpaper.widget.BottomActionBar.BottomActionBarHost;
@@ -63,8 +67,15 @@
@Section final int section = intent.getIntExtra(EXTRA_PREVIEW_SECTION, 0);
final Bundle bundle = intent.getBundleExtra(EXTRA_PREVIEW_BUNDLE);
if (section == SECTION_GRID) {
- showFragment(
- GridFullPreviewFragment.newInstance(getString(R.string.grid_title), bundle));
+ showFragment(GridFullPreviewFragment.newInstance(
+ getString(R.string.grid_title), bundle));
+ } else if (section == SECTION_STYLE) {
+ final String themeTitle = bundle.getString(EXTRA_THEME_OPTION_TITLE);
+ showFragment(ThemeFullPreviewFragment.newInstance(
+ TextUtils.isEmpty(themeTitle)
+ ? getString(R.string.theme_title)
+ : themeTitle,
+ bundle));
}
}
diff --git a/src/com/android/customization/picker/WallpaperPreviewer.java b/src/com/android/customization/picker/WallpaperPreviewer.java
new file mode 100644
index 0000000..0185925
--- /dev/null
+++ b/src/com/android/customization/picker/WallpaperPreviewer.java
@@ -0,0 +1,219 @@
+/*
+ * 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.android.customization.picker;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.service.wallpaper.WallpaperService;
+import android.view.Surface;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.MainThread;
+import androidx.cardview.widget.CardView;
+import androidx.core.content.ContextCompat;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.model.LiveWallpaperInfo;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.SizeCalculator;
+import com.android.wallpaper.util.WallpaperConnection;
+import com.android.wallpaper.widget.LiveTileOverlay;
+
+/** A class to load the wallpaper to the view. */
+public class WallpaperPreviewer implements LifecycleObserver {
+
+ private final Rect mPreviewLocalRect = new Rect();
+ private final Rect mPreviewGlobalRect = new Rect();
+ private final int[] mLivePreviewLocation = new int[2];
+
+ private final Activity mActivity;
+ private final ImageView mHomePreview;
+ private final SurfaceView mWallpaperSurface;
+
+ private WallpaperInfo mWallpaper;
+ private WallpaperConnection mWallpaperConnection;
+ // Home workspace surface is behind the app window, and so must the home image wallpaper like
+ // the live wallpaper. This view is rendered on mWallpaperSurface for home image wallpaper.
+ private ImageView mHomeImageWallpaper;
+
+ public WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview,
+ SurfaceView wallpaperSurface) {
+ lifecycle.addObserver(this);
+
+ mActivity = activity;
+ mHomePreview = homePreview;
+ mWallpaperSurface = wallpaperSurface;
+ mWallpaperSurface.setZOrderMediaOverlay(false);
+ mWallpaperSurface.getHolder().addCallback(mSurfaceCallback);
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ @MainThread
+ public void onResume() {
+ if (mWallpaperConnection != null) {
+ mWallpaperConnection.setVisibility(true);
+ }
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ @MainThread
+ public void onPause() {
+ if (mWallpaperConnection != null) {
+ mWallpaperConnection.setVisibility(false);
+ }
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ @MainThread
+ public void onDestroy() {
+ LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
+ if (mWallpaperConnection != null) {
+ mWallpaperConnection.disconnect();
+ mWallpaperConnection = null;
+ }
+
+ mWallpaperSurface.getHolder().removeCallback(mSurfaceCallback);
+ Surface surface = mWallpaperSurface.getHolder().getSurface();
+ if (surface != null) {
+ surface.release();
+ }
+ }
+
+ /** Updates the preview card view radius to match the device radius. */
+ public void updatePreviewCardRadius() {
+ final float screenAspectRatio =
+ ScreenSizeCalculator.getInstance().getScreenAspectRatio(mActivity);
+ CardView cardView = (CardView) mHomePreview.getParent();
+ final int cardWidth = (int) (cardView.getMeasuredHeight() / screenAspectRatio);
+ ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
+ layoutParams.width = cardWidth;
+ cardView.setLayoutParams(layoutParams);
+ cardView.setRadius(SizeCalculator.getPreviewCornerRadius(mActivity, cardWidth));
+ }
+
+ /** Loads the wallpaper. */
+ public void setWallpaper(WallpaperInfo wallpaperInfo) {
+ mWallpaper = wallpaperInfo;
+ setUpWallpaperPreview();
+ }
+
+ private void setUpWallpaperPreview() {
+ if (mWallpaper != null && mHomeImageWallpaper != null) {
+ boolean renderInImageWallpaperSurface = !(mWallpaper instanceof LiveWallpaperInfo);
+ mWallpaper.getThumbAsset(mActivity.getApplicationContext())
+ .loadPreviewImage(mActivity,
+ renderInImageWallpaperSurface ? mHomeImageWallpaper : mHomePreview,
+ mActivity.getResources().getColor(R.color.secondary_color));
+ LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
+ if (mWallpaper instanceof LiveWallpaperInfo) {
+ mWallpaper.getThumbAsset(mActivity.getApplicationContext())
+ .loadPreviewImage(
+ mActivity,
+ mHomeImageWallpaper,
+ mActivity.getColor(R.color.secondary_color));
+ setUpLiveWallpaperPreview(mWallpaper);
+ } else {
+ if (mWallpaperConnection != null) {
+ mWallpaperConnection.disconnect();
+ mWallpaperConnection = null;
+ }
+ }
+ }
+ }
+
+ private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) {
+ if (mActivity == null || mActivity.isFinishing()) {
+ return;
+ }
+
+ if (mWallpaperConnection != null) {
+ mWallpaperConnection.disconnect();
+ }
+
+ mHomePreview.getLocationOnScreen(mLivePreviewLocation);
+ mPreviewGlobalRect.set(0, 0, mHomePreview.getMeasuredWidth(),
+ mHomePreview.getMeasuredHeight());
+ mPreviewLocalRect.set(mPreviewGlobalRect);
+ mPreviewGlobalRect.offset(mLivePreviewLocation[0], mLivePreviewLocation[1]);
+
+ mWallpaperConnection = new WallpaperConnection(
+ getWallpaperIntent(homeWallpaper.getWallpaperComponent()), mActivity,
+ new WallpaperConnection.WallpaperConnectionListener() {
+ @Override
+ public void onEngineShown() {}
+ }, mPreviewGlobalRect);
+
+ LiveTileOverlay.INSTANCE.update(new RectF(mPreviewLocalRect),
+ ((CardView) mHomePreview.getParent()).getRadius());
+
+ mWallpaperConnection.setVisibility(true);
+ mHomePreview.post(() -> {
+ if (!mWallpaperConnection.connect()) {
+ mWallpaperConnection = null;
+ LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
+ }
+ });
+ }
+
+ private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+
+ private Surface mLastSurface;
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (mLastSurface != holder.getSurface()) {
+ mLastSurface = holder.getSurface();
+ mHomeImageWallpaper = new ImageView(mActivity);
+ mHomeImageWallpaper.setBackgroundColor(
+ ContextCompat.getColor(mActivity, R.color.primary_color));
+ mHomeImageWallpaper.measure(makeMeasureSpec(mHomePreview.getWidth(), EXACTLY),
+ makeMeasureSpec(mHomePreview.getHeight(), EXACTLY));
+ mHomeImageWallpaper.layout(0, 0, mHomePreview.getWidth(), mHomePreview.getHeight());
+
+ SurfaceControlViewHost host = new SurfaceControlViewHost(mActivity,
+ mActivity.getDisplay(), mWallpaperSurface.getHostToken());
+ host.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(),
+ mHomeImageWallpaper.getHeight());
+ mWallpaperSurface.setChildSurfacePackage(host.getSurfacePackage());
+ }
+ setUpWallpaperPreview();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {}
+ };
+
+ private static Intent getWallpaperIntent(android.app.WallpaperInfo info) {
+ return new Intent(WallpaperService.SERVICE_INTERFACE)
+ .setClassName(info.getPackageName(), info.getServiceName());
+ }
+}
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
index c9af437..a8f6eda 100644
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ b/src/com/android/customization/picker/grid/GridFragment.java
@@ -23,30 +23,19 @@
import static com.android.customization.picker.grid.GridFullPreviewFragment.EXTRA_WALLPAPER_INFO;
import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
-import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.cardview.widget.CardView;
import androidx.core.widget.ContentLoadingProgressBar;
import androidx.recyclerview.widget.RecyclerView;
@@ -55,23 +44,17 @@
import com.android.customization.model.grid.GridOption;
import com.android.customization.model.grid.GridOptionsManager;
import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.BasePreviewAdapter;
-import com.android.customization.picker.BasePreviewAdapter.PreviewPage;
import com.android.customization.picker.ViewOnlyFullPreviewActivity;
+import com.android.customization.picker.WallpaperPreviewer;
import com.android.customization.widget.OptionSelectorController;
import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.ContentUriAsset;
import com.android.wallpaper.model.WallpaperInfo;
import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.util.SurfaceViewUtils;
import com.android.wallpaper.widget.BottomActionBar;
-import com.android.wallpaper.widget.PreviewPager;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.RequestOptions;
import java.util.List;
@@ -80,8 +63,6 @@
*/
public class GridFragment extends AppbarFragment {
- static final int PREVIEW_FADE_DURATION_MS = 100;
-
private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
private static final String TAG = "GridFragment";
@@ -100,23 +81,20 @@
}
private WallpaperInfo mHomeWallpaper;
- private float mScreenAspectRatio;
- private int mCardHeight;
- private int mCardWidth;
- private BitmapDrawable mCardBackground;
- private GridPreviewAdapter mAdapter;
private RecyclerView mOptionsContainer;
private OptionSelectorController<GridOption> mOptionsController;
private GridOptionsManager mGridManager;
private GridOption mSelectedOption;
- private PreviewPager mPreviewPager;
private ContentLoadingProgressBar mLoading;
+ private ViewGroup mGridPreviewContainer;
private View mContent;
private View mError;
private BottomActionBar mBottomActionBar;
private ThemesUserEventLogger mEventLogger;
private boolean mReloadOptionsAfterApplying;
+ private GridOptionPreviewer mGridOptionPreviewer;
+
private final Callback mApplyGridCallback = new Callback() {
@Override
public void onSuccess() {
@@ -166,42 +144,50 @@
View view = inflater.inflate(
R.layout.fragment_grid_picker, container, /* attachToRoot */ false);
setUpToolbar(view);
+ mGridPreviewContainer = view.findViewById(R.id.grid_preview_container);
mContent = view.findViewById(R.id.content_section);
- mPreviewPager = view.findViewById(R.id.grid_preview_pager);
mOptionsContainer = view.findViewById(R.id.options_container);
mLoading = view.findViewById(R.id.loading_indicator);
mError = view.findViewById(R.id.error_section);
- final Resources res = getResources();
- DisplayMetrics dm = res.getDisplayMetrics();
- mScreenAspectRatio = (float) dm.heightPixels / dm.widthPixels;
// Clear memory cache whenever grid fragment view is being loaded.
Glide.get(getContext()).clearMemory();
setUpOptions();
+ ImageView wallpaperPreviewImage = view.findViewById(R.id.wallpaper_preview_image);
+ wallpaperPreviewImage.setOnClickListener(v -> showFullPreview());
+ SurfaceView wallpaperSurface = view.findViewById(R.id.wallpaper_preview_surface);
+ WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(
+ getLifecycle(), getActivity(), wallpaperPreviewImage, wallpaperSurface);
+
+ // Loads current Wallpaper.
CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
.getCurrentWallpaperFactory(getContext().getApplicationContext());
-
factory.createCurrentWallpaperInfos((homeWallpaper, lockWallpaper, presentationMode) -> {
mHomeWallpaper = homeWallpaper;
- loadWallpaperBackground();
-
+ wallpaperPreviewer.setWallpaper(mHomeWallpaper);
}, false);
- view.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+
+ view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- mCardHeight = mPreviewPager.getHeight() - mPreviewPager.getPaddingTop() -
- res.getDimensionPixelSize(R.dimen.indicator_container_height);
- mCardWidth = (int) (mCardHeight / mScreenAspectRatio);
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ wallpaperPreviewer.updatePreviewCardRadius();
view.removeOnLayoutChangeListener(this);
- loadWallpaperBackground();
}
});
return view;
}
@Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mGridOptionPreviewer != null) {
+ mGridOptionPreviewer.release();
+ }
+ }
+
+ @Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FULL_PREVIEW_REQUEST_CODE && resultCode == RESULT_OK) {
@@ -222,23 +208,13 @@
mGridManager.apply(gridOption, mApplyGridCallback);
}
- private void loadWallpaperBackground() {
- if (mHomeWallpaper != null && mCardHeight > 0 && mCardWidth > 0) {
- mHomeWallpaper.getThumbAsset(getContext()).decodeBitmap(mCardWidth,
- mCardHeight,
- bitmap -> {
- mCardBackground =
- new BitmapDrawable(getResources(), bitmap);
- if (mAdapter != null) {
- mAdapter.onWallpaperInfoLoaded();
- }
- });
+ private void updatePreview() {
+ if (mGridOptionPreviewer != null) {
+ mGridOptionPreviewer.release();
}
- }
-
- private void createAdapter() {
- mAdapter = new GridPreviewAdapter(mSelectedOption);
- mPreviewPager.setAdapter(mAdapter);
+ mGridOptionPreviewer = new GridOptionPreviewer(
+ getContext(), mGridManager, mGridPreviewContainer);
+ mGridOptionPreviewer.setGridOption(mSelectedOption, mGridManager.usesSurfaceView());
}
private void setUpOptions() {
@@ -258,11 +234,11 @@
}
mBottomActionBar.show();
mEventLogger.logGridSelected(mSelectedOption);
- createAdapter();
+ updatePreview();
});
mOptionsController.initOptions(mGridManager);
mSelectedOption = getSelectedOption(options);
- createAdapter();
+ updatePreview();
}
@Override
@@ -302,123 +278,4 @@
Intent intent = ViewOnlyFullPreviewActivity.newIntent(getContext(), SECTION_GRID, bundle);
startActivityForResult(intent, FULL_PREVIEW_REQUEST_CODE);
}
-
- private class GridPreviewPage extends PreviewPage {
- private final int mPageId;
- private final Asset mPreviewAsset;
- private final int mCols;
- private final int mRows;
- private final Activity mActivity;
-
- private final String mName;
-
- private ImageView mPreview;
- private SurfaceView mPreviewSurface;
-
- private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
-
- private Surface mLastSurface;
- private Message mCallback;
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- if (mLastSurface != holder.getSurface()) {
- mLastSurface = holder.getSurface();
- Bundle result = mGridManager.renderPreview(
- SurfaceViewUtils.createSurfaceViewRequest(mPreviewSurface), mName);
- if (result != null) {
- mPreviewSurface.setChildSurfacePackage(
- SurfaceViewUtils.getSurfacePackage(result));
- mCallback = SurfaceViewUtils.getCallback(result);
- }
- }
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {}
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- if (mCallback != null) {
- try {
- mCallback.replyTo.send(mCallback);
- } catch (RemoteException e) {
- e.printStackTrace();
- } finally {
- mCallback = null;
- }
- }
- }
- };
-
- private GridPreviewPage(Activity activity, int id, Uri previewUri, String name, int rows,
- int cols) {
- super(null, activity);
- mPageId = id;
- mPreviewAsset = new ContentUriAsset(activity, previewUri,
- RequestOptions.fitCenterTransform());
- mName = name;
- mRows = rows;
- mCols = cols;
- mActivity = activity;
- }
-
- @Override
- public void setCard(CardView card) {
- super.setCard(card);
- mPreview = card.findViewById(R.id.grid_preview_image);
- mPreviewSurface = card.findViewById(R.id.grid_preview_surface);
- // PreviewSurface is the top of its window(card view), due to #setZOrderOnTop(true).
- mPreviewSurface.setOnClickListener(view -> showFullPreview());
- }
-
- public void bindPreviewContent() {
- Resources resources = card.getResources();
- bindWallpaperIfAvailable();
- final boolean usesSurfaceViewForPreview = mGridManager.usesSurfaceView();
- mPreview.setVisibility(usesSurfaceViewForPreview ? View.GONE : View.VISIBLE);
- mPreviewSurface.setVisibility(usesSurfaceViewForPreview ? View.VISIBLE : View.GONE);
- if (usesSurfaceViewForPreview) {
- mPreviewSurface.setZOrderOnTop(true);
- mPreviewSurface.getHolder().addCallback(mSurfaceCallback);
- } else {
- mPreviewAsset.loadDrawableWithTransition(mActivity,
- mPreview /* imageView */,
- PREVIEW_FADE_DURATION_MS /* duration */,
- null /* drawableLoadedListener */,
- resources.getColor(android.R.color.transparent,
- null) /* placeHolderColorJ */);
- }
- }
-
- void bindWallpaperIfAvailable() {
- if (card != null && mCardBackground != null) {
- mPreview.setBackground(mCardBackground);
- mPreviewSurface.setBackground(mCardBackground);
- }
- }
- }
- /**
- * Adapter class for mPreviewPager.
- * This is a ViewPager as it allows for a nice pagination effect (ie, pages snap on swipe,
- * we don't want to just scroll)
- */
- class GridPreviewAdapter extends BasePreviewAdapter<GridPreviewPage> {
-
- GridPreviewAdapter(GridOption gridOption) {
- super(getContext(), R.layout.grid_preview_card);
- for (int i = 0; i < gridOption.previewPagesCount; i++) {
- addPage(new GridPreviewPage(getActivity(), i,
- gridOption.previewImageUri.buildUpon().appendPath("" + i).build(),
- gridOption.name, gridOption.rows, gridOption.cols));
- }
- }
-
- void onWallpaperInfoLoaded() {
- for (GridPreviewPage page : mPages) {
- page.bindWallpaperIfAvailable();
- }
- }
- }
}
diff --git a/src/com/android/customization/picker/grid/GridFullPreviewFragment.java b/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
index 29d74de..2be77d6 100644
--- a/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
+++ b/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
@@ -16,25 +16,13 @@
package com.android.customization.picker.grid;
import static android.app.Activity.RESULT_OK;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-import static com.android.customization.picker.grid.GridFragment.PREVIEW_FADE_DURATION_MS;
import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
import android.app.Activity;
import android.content.Intent;
-import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Bundle;
-import android.os.Message;
-import android.os.RemoteException;
-import android.service.wallpaper.WallpaperService;
-import android.util.DisplayMetrics;
import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
@@ -42,28 +30,21 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.cardview.widget.CardView;
-import androidx.core.content.ContextCompat;
import com.android.customization.model.grid.GridOption;
import com.android.customization.model.grid.GridOptionsManager;
import com.android.customization.model.grid.LauncherGridOptionsProvider;
import com.android.customization.module.CustomizationInjector;
import com.android.customization.module.ThemesUserEventLogger;
+import com.android.customization.picker.WallpaperPreviewer;
import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.ContentUriAsset;
-import com.android.wallpaper.model.LiveWallpaperInfo;
import com.android.wallpaper.model.WallpaperInfo;
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.util.SizeCalculator;
-import com.android.wallpaper.util.SurfaceViewUtils;
-import com.android.wallpaper.util.WallpaperConnection;
import com.android.wallpaper.widget.BottomActionBar;
-import com.android.wallpaper.widget.LiveTileOverlay;
-import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.Glide;
+
/** A Fragment for grid full preview page. */
public class GridFullPreviewFragment extends AppbarFragment {
@@ -72,24 +53,12 @@
static final String EXTRA_GRID_OPTION = "grid_option";
static final String EXTRA_GRID_USES_SURFACE_VIEW = "uses_surface_view";
- private final Rect mPreviewLocalRect = new Rect();
- private final Rect mPreviewGlobalRect = new Rect();
- private final int[] mLivePreviewLocation = new int[2];
-
- private GridOptionsManager mGridManager;
private WallpaperInfo mWallpaper;
private GridOption mGridOption;
private boolean mUsesSurfaceView;
- private CardView mCardView;
- private ImageView mHomePreview;
- private SurfaceView mGridOptionSurface;
- private SurfaceView mWallpaperSurface;
- private WallpaperConnection mWallpaperConnection;
-
- // Home workspace surface is behind the app window, and so must the home image wallpaper like
- // the live wallpaper. This view is rendered on mWallpaperSurface for home image wallpaper.
- private ImageView mHomeImageWallpaper;
+ private WallpaperPreviewer mWallpaperPreviewer;
+ private GridOptionPreviewer mGridOptionPreviewer;
/**
* Returns a new {@link GridFullPreviewFragment} with the provided title and bundle arguments
@@ -110,14 +79,6 @@
mWallpaper = getArguments().getParcelable(EXTRA_WALLPAPER_INFO);
mGridOption = getArguments().getParcelable(EXTRA_GRID_OPTION);
mUsesSurfaceView = getArguments().getBoolean(EXTRA_GRID_USES_SURFACE_VIEW);
-
- CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
- ThemesUserEventLogger eventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(
- getContext());
-
- mGridManager = new GridOptionsManager(new LauncherGridOptionsProvider(getContext(),
- getString(R.string.grid_control_metadata_name)),
- eventLogger);
}
@Nullable
@@ -128,25 +89,31 @@
R.layout.fragment_grid_full_preview, container, /* attachToRoot */ false);
setUpToolbar(view);
- mCardView = view.findViewById(R.id.grid_full_preview_card);
- mHomePreview = view.findViewById(R.id.grid_full_preview_image);
- mGridOptionSurface = view.findViewById(R.id.grid_full_preview_option_surface);
- mWallpaperSurface = view.findViewById(R.id.grid_full_preview_wallpaper_surface);
- mGridOptionSurface.setVisibility(View.GONE);
+ // Clear memory cache whenever grid fragment view is being loaded.
+ Glide.get(getContext()).clearMemory();
- final DisplayMetrics dm = getResources().getDisplayMetrics();
- float screenAspectRatio = (float) dm.heightPixels / dm.widthPixels;
+ ImageView wallpaperPreviewImage = view.findViewById(R.id.wallpaper_preview_image);
+ SurfaceView wallpaperSurface = view.findViewById(R.id.wallpaper_preview_surface);
+ mWallpaperPreviewer = new WallpaperPreviewer(
+ getLifecycle(), getActivity(), wallpaperPreviewImage, wallpaperSurface);
+
+ CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
+ ThemesUserEventLogger eventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(
+ getContext());
+ final GridOptionsManager gridManager = new GridOptionsManager(
+ new LauncherGridOptionsProvider(getContext(),
+ getString(R.string.grid_control_metadata_name)),
+ eventLogger);
+
+ ViewGroup gridPreviewContainer = view.findViewById(R.id.grid_preview_container);
+ mGridOptionPreviewer = new GridOptionPreviewer(
+ getContext(), gridManager, gridPreviewContainer);
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
- int cardWidth = (int) (mCardView.getMeasuredHeight() / screenAspectRatio);
- ViewGroup.LayoutParams layoutParams = mCardView.getLayoutParams();
- layoutParams.width = cardWidth;
- mCardView.setLayoutParams(layoutParams);
- mCardView.setRadius(SizeCalculator.getPreviewCornerRadius(
- getActivity(), mCardView.getMeasuredWidth()));
+ mWallpaperPreviewer.updatePreviewCardRadius();
view.removeOnLayoutChangeListener(this);
}
});
@@ -156,44 +123,25 @@
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- updateWallpaperSurface();
- updateWorkspaceSurface();
+ mWallpaperPreviewer.setWallpaper(mWallpaper);
+ mGridOptionPreviewer.setGridOption(mGridOption, mUsesSurfaceView);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mGridOptionPreviewer != null) {
+ mGridOptionPreviewer.release();
+ }
}
@Override
protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
- bottomActionBar.bindBackButtonToSystemBackKey(getActivity());
bottomActionBar.showActionsOnly(APPLY);
bottomActionBar.setActionClickListener(APPLY, v -> finishActivityWithResultOk());
bottomActionBar.show();
}
- @Override
- public void onResume() {
- super.onResume();
- if (mWallpaperConnection != null) {
- mWallpaperConnection.setVisibility(true);
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- if (mWallpaperConnection != null) {
- mWallpaperConnection.setVisibility(false);
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
- if (mWallpaperConnection != null) {
- mWallpaperConnection.disconnect();
- mWallpaperConnection = null;
- }
- }
-
private void finishActivityWithResultOk() {
Activity activity = requireActivity();
activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
@@ -202,159 +150,4 @@
activity.setResult(RESULT_OK, intent);
activity.finish();
}
-
- private void updateWallpaperSurface() {
- mWallpaperSurface.setZOrderMediaOverlay(false);
- mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
- }
-
- private void updateWorkspaceSurface() {
- if (mUsesSurfaceView) {
- mGridOptionSurface.setZOrderOnTop(true);
- mGridOptionSurface.getHolder().addCallback(mGridOptionSurfaceCallback);
- mGridOptionSurface.setVisibility(View.VISIBLE);
- } else {
- final Asset previewAsset = new ContentUriAsset(
- getContext(),
- mGridOption.previewImageUri,
- RequestOptions.fitCenterTransform());
- previewAsset.loadDrawableWithTransition(getContext(),
- mHomePreview /* imageView */,
- PREVIEW_FADE_DURATION_MS /* duration */,
- null /* drawableLoadedListener */,
- getResources().getColor(android.R.color.transparent,
- null) /* placeHolderColorJ */);
- }
- }
-
- private void setUpWallpaperPreview() {
- if (mWallpaper != null && mHomeImageWallpaper != null) {
- boolean renderInImageWallpaperSurface = !(mWallpaper instanceof LiveWallpaperInfo);
- mWallpaper.getThumbAsset(getContext())
- .loadPreviewImage(getActivity(),
- renderInImageWallpaperSurface ? mHomeImageWallpaper : mHomePreview,
- getResources().getColor(R.color.secondary_color));
- LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
- if (mWallpaper instanceof LiveWallpaperInfo) {
- mWallpaper.getThumbAsset(getContext().getApplicationContext())
- .loadPreviewImage(
- getActivity(),
- mHomeImageWallpaper,
- getContext().getColor(R.color.secondary_color));
- setUpLiveWallpaperPreview(mWallpaper);
- } else {
- if (mWallpaperConnection != null) {
- mWallpaperConnection.disconnect();
- mWallpaperConnection = null;
- }
- }
- }
- }
-
- private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) {
- Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- if (mWallpaperConnection != null) {
- mWallpaperConnection.disconnect();
- }
-
- mHomePreview.getLocationOnScreen(mLivePreviewLocation);
- mPreviewGlobalRect.set(0, 0, mHomePreview.getMeasuredWidth(),
- mHomePreview.getMeasuredHeight());
- mPreviewLocalRect.set(mPreviewGlobalRect);
- mPreviewGlobalRect.offset(mLivePreviewLocation[0], mLivePreviewLocation[1]);
-
- mWallpaperConnection = new WallpaperConnection(
- getWallpaperIntent(homeWallpaper.getWallpaperComponent()), activity,
- new WallpaperConnection.WallpaperConnectionListener() {
- @Override
- public void onEngineShown() {}
- }, mPreviewGlobalRect);
-
- LiveTileOverlay.INSTANCE.update(new RectF(mPreviewLocalRect), mCardView.getRadius());
-
- mWallpaperConnection.setVisibility(true);
- mHomePreview.post(() -> {
- if (!mWallpaperConnection.connect()) {
- mWallpaperConnection = null;
- LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
- }
- });
- }
-
- private Intent getWallpaperIntent(android.app.WallpaperInfo info) {
- return new Intent(WallpaperService.SERVICE_INTERFACE)
- .setClassName(info.getPackageName(), info.getServiceName());
- }
-
- private final SurfaceHolder.Callback mGridOptionSurfaceCallback = new SurfaceHolder.Callback() {
-
- private Surface mLastSurface;
- private Message mCallback;
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- if (mLastSurface != holder.getSurface()) {
- mLastSurface = holder.getSurface();
- Bundle result = mGridManager.renderPreview(
- SurfaceViewUtils.createSurfaceViewRequest(mGridOptionSurface),
- mGridOption.name);
- if (result != null) {
- mGridOptionSurface.setChildSurfacePackage(
- SurfaceViewUtils.getSurfacePackage(result));
- mCallback = SurfaceViewUtils.getCallback(result);
- }
- }
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- if (mCallback != null) {
- try {
- mCallback.replyTo.send(mCallback);
- } catch (RemoteException e) {
- e.printStackTrace();
- } finally {
- mCallback = null;
- }
- }
- }
- };
-
- private final SurfaceHolder.Callback mWallpaperSurfaceCallback = new SurfaceHolder.Callback() {
-
- private Surface mLastSurface;
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- if (mLastSurface != holder.getSurface()) {
- mLastSurface = holder.getSurface();
- mHomeImageWallpaper = new ImageView(getContext());
- mHomeImageWallpaper.setBackgroundColor(
- ContextCompat.getColor(getContext(), R.color.primary_color));
- mHomeImageWallpaper.measure(makeMeasureSpec(mHomePreview.getWidth(), EXACTLY),
- makeMeasureSpec(mHomePreview.getHeight(), EXACTLY));
- mHomeImageWallpaper.layout(0, 0, mHomePreview.getWidth(), mHomePreview.getHeight());
-
- SurfaceControlViewHost host = new SurfaceControlViewHost(getContext(),
- getContext().getDisplay(), mWallpaperSurface.getHostToken());
- host.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(),
- mHomeImageWallpaper.getHeight());
- mWallpaperSurface.setChildSurfacePackage(host.getSurfacePackage());
- }
- setUpWallpaperPreview();
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {}
- };
}
diff --git a/src/com/android/customization/picker/grid/GridOptionPreviewer.java b/src/com/android/customization/picker/grid/GridOptionPreviewer.java
new file mode 100644
index 0000000..6d31689
--- /dev/null
+++ b/src/com/android/customization/picker/grid/GridOptionPreviewer.java
@@ -0,0 +1,144 @@
+/*
+ * 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.android.customization.picker.grid;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.customization.model.grid.GridOption;
+import com.android.customization.model.grid.GridOptionsManager;
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.asset.ContentUriAsset;
+import com.android.wallpaper.util.SurfaceViewUtils;
+
+import com.bumptech.glide.request.RequestOptions;
+
+/** A class to load the {@link GridOption} preview to the view. */
+class GridOptionPreviewer {
+
+ private static final int PREVIEW_FADE_DURATION_MS = 100;
+
+ private final Context mContext;
+ private final GridOptionsManager mGridManager;
+ private final ViewGroup mPreviewContainer;
+
+ private SurfaceView mGridOptionSurface;
+ private GridOption mGridOption;
+
+ GridOptionPreviewer(Context context, GridOptionsManager gridManager,
+ ViewGroup previewContainer) {
+ mContext = context;
+ mGridManager = gridManager;
+ mPreviewContainer = previewContainer;
+ }
+
+ /** Loads the Grid option into the container view. */
+ public void setGridOption(GridOption gridOption, boolean usesSurfaceView) {
+ mGridOption = gridOption;
+ updateWorkspacePreview(usesSurfaceView);
+ }
+
+ /** Releases the view resource. */
+ public void release() {
+ if (mGridOptionSurface != null) {
+ mGridOptionSurface.getHolder().removeCallback(mSurfaceCallback);
+ Surface surface = mGridOptionSurface.getHolder().getSurface();
+ if (surface != null) {
+ surface.release();
+ }
+ }
+ mPreviewContainer.removeAllViews();
+ }
+
+ private void updateWorkspacePreview(boolean usesSurfaceView) {
+ if (mGridOption == null) {
+ return;
+ }
+ mPreviewContainer.removeAllViews();
+
+ if (usesSurfaceView) {
+ mGridOptionSurface = new SurfaceView(mContext);
+ setUpView(mGridOptionSurface);
+ mGridOptionSurface.setZOrderMediaOverlay(true);
+ mGridOptionSurface.getHolder().addCallback(mSurfaceCallback);
+ } else {
+ final ImageView previewImage = new ImageView(mContext);
+ setUpView(previewImage);
+ final Asset previewAsset = new ContentUriAsset(
+ mContext,
+ mGridOption.previewImageUri,
+ RequestOptions.fitCenterTransform());
+ previewAsset.loadDrawableWithTransition(mContext,
+ previewImage /* imageView */,
+ PREVIEW_FADE_DURATION_MS /* duration */,
+ null /* drawableLoadedListener */,
+ mContext.getResources().getColor(android.R.color.transparent,
+ null) /* placeHolderColorJ */);
+ }
+ }
+
+ private void setUpView(View view) {
+ view.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mPreviewContainer.addView(view);
+ }
+
+ private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+ private Surface mLastSurface;
+ private Message mCallback;
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (mLastSurface != holder.getSurface() && mGridOption != null) {
+ mLastSurface = holder.getSurface();
+ Bundle result = mGridManager.renderPreview(
+ SurfaceViewUtils.createSurfaceViewRequest(mGridOptionSurface),
+ mGridOption.name);
+ if (result != null) {
+ mGridOptionSurface.setChildSurfacePackage(
+ SurfaceViewUtils.getSurfacePackage(result));
+ mCallback = SurfaceViewUtils.getCallback(result);
+ }
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (mCallback != null) {
+ try {
+ mCallback.replyTo.send(mCallback);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } finally {
+ mCallback.recycle();
+ mCallback = null;
+ }
+ }
+ }
+ };
+}
diff --git a/src/com/android/customization/picker/theme/CustomThemeNameFragment.java b/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
index 45e4324..8075ffa 100644
--- a/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
+++ b/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
@@ -35,7 +35,6 @@
import androidx.cardview.widget.CardView;
import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
-import com.android.customization.picker.TimeTicker;
import com.android.customization.picker.theme.ThemePreviewPage.ThemeCoverPage;
import com.android.wallpaper.R;
import com.android.wallpaper.asset.Asset;
@@ -43,6 +42,7 @@
import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.picker.AppbarFragment;
+import com.android.wallpaper.util.TimeTicker;
public class CustomThemeNameFragment extends CustomThemeStepFragment {
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
index e04d8d2..3da41c6 100644
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ b/src/com/android/customization/picker/theme/ThemeFragment.java
@@ -15,29 +15,24 @@
*/
package com.android.customization.picker.theme;
-import android.app.Activity;
-import android.app.WallpaperColors;
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.customization.picker.ViewOnlyFullPreviewActivity.SECTION_STYLE;
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_THEME_OPTION;
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_THEME_OPTION_TITLE;
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_WALLPAPER_INFO;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.CUSTOMIZE;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
+
import android.content.Context;
import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
-import android.widget.CompoundButton;
import android.widget.ImageView;
-import android.widget.SeekBar;
-import android.widget.Switch;
-import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -48,24 +43,20 @@
import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
import com.android.customization.model.theme.ThemeBundle;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
import com.android.customization.model.theme.ThemeManager;
import com.android.customization.model.theme.custom.CustomTheme;
import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.BasePreviewAdapter;
-import com.android.customization.picker.TimeTicker;
-import com.android.customization.picker.theme.ThemePreviewPage.ThemeCoverPage;
-import com.android.customization.picker.theme.ThemePreviewPage.TimeContainer;
+import com.android.customization.picker.ViewOnlyFullPreviewActivity;
+import com.android.customization.picker.WallpaperPreviewer;
import com.android.customization.widget.OptionSelectorController;
+import com.android.customization.widget.ThemeInfoView;
import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.Asset.CenterCropBitmapTask;
-import com.android.wallpaper.asset.BitmapCachingAsset;
import com.android.wallpaper.model.WallpaperInfo;
import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.widget.PreviewPager;
+import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.WallpaperColorsLoader;
import java.util.List;
@@ -76,6 +67,7 @@
private static final String TAG = "ThemeFragment";
private static final String KEY_SELECTED_THEME = "ThemeFragment.SelectedThemeBundle";
+ private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
/**
* Interface to be implemented by an Activity hosting a {@link ThemeFragment}
@@ -94,16 +86,16 @@
private ThemeManager mThemeManager;
private ThemesUserEventLogger mEventLogger;
private ThemeBundle mSelectedTheme;
- private ThemePreviewAdapter mAdapter;
- private PreviewPager mPreviewPager;
private ContentLoadingProgressBar mLoading;
private View mContent;
private View mError;
- private boolean mUseMyWallpaper = true;
private WallpaperInfo mCurrentHomeWallpaper;
- private Asset mCurrentWallpaperThumbAsset;
private CurrentWallpaperInfoFactory mCurrentWallpaperFactory;
- private TimeTicker mTicker;
+ private BottomActionBar mBottomActionBar;
+ private WallpaperPreviewer mWallpaperPreviewer;
+ private ImageView mWallpaperImage;
+ private ThemeOptionPreviewer mThemeOptionPreviewer;
+ private ThemeInfoView mThemeInfoView;
@Override
public void onAttach(Context context) {
@@ -126,29 +118,91 @@
mError = view.findViewById(R.id.error_section);
mCurrentWallpaperFactory = InjectorProvider.getInjector()
.getCurrentWallpaperFactory(getActivity().getApplicationContext());
- mPreviewPager = view.findViewById(R.id.theme_preview_pager);
mOptionsContainer = view.findViewById(R.id.options_container);
- view.findViewById(R.id.apply_button).setOnClickListener(v -> {
+
+ // Set Wallpaper background.
+ mWallpaperImage = view.findViewById(R.id.wallpaper_preview_image);
+ mWallpaperPreviewer = new WallpaperPreviewer(
+ getLifecycle(),
+ getActivity(),
+ mWallpaperImage,
+ view.findViewById(R.id.wallpaper_preview_surface));
+ mCurrentWallpaperFactory.createCurrentWallpaperInfos(
+ (homeWallpaper, lockWallpaper, presentationMode) -> {
+ mCurrentHomeWallpaper = homeWallpaper;
+ mWallpaperPreviewer.setWallpaper(mCurrentHomeWallpaper);
+ updateThemePreviewColorPerWallpaper();
+ }, false);
+
+ ViewGroup previewContainer = view.findViewById(R.id.theme_preview_container);
+ previewContainer.setOnClickListener(v -> showFullPreview());
+ mThemeOptionPreviewer = new ThemeOptionPreviewer(
+ getLifecycle(),
+ getContext(),
+ previewContainer);
+
+ view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ mWallpaperPreviewer.updatePreviewCardRadius();
+ updateThemePreviewColorPerWallpaper();
+ view.removeOnLayoutChangeListener(this);
+ }
+ });
+ return view;
+ }
+
+ @Override
+ protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
+ mBottomActionBar = bottomActionBar;
+ mBottomActionBar.showActionsOnly(INFORMATION, APPLY);
+ mBottomActionBar.setActionClickListener(APPLY, v -> {
+ mBottomActionBar.disableActions();
applyTheme();
});
- setUpOptions(savedInstanceState);
+ mThemeInfoView = (ThemeInfoView) LayoutInflater.from(getContext()).inflate(
+ R.layout.theme_info_view, /* root= */ null);
+ mBottomActionBar.attachViewToBottomSheetAndBindAction(mThemeInfoView, INFORMATION);
+ mBottomActionBar.setActionClickListener(CUSTOMIZE, this::onCustomizeClicked);
+ }
- return view;
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ // Setup options here when all views are ready(including BottomActionBar), since we need to
+ // update views after options are loaded.
+ setUpOptions(savedInstanceState);
+ }
+
+ private void updateThemePreviewColorPerWallpaper() {
+ if (mCurrentHomeWallpaper != null && mWallpaperImage.getMeasuredWidth() > 0
+ && mWallpaperImage.getMeasuredHeight() > 0) {
+ WallpaperColorsLoader.getWallpaperColors(
+ mCurrentHomeWallpaper.getThumbAsset(getContext()),
+ mWallpaperImage.getMeasuredWidth(),
+ mWallpaperImage.getMeasuredHeight(),
+ mThemeOptionPreviewer::updateColorForLauncherWidgets);
+ }
}
private void applyTheme() {
mThemeManager.apply(mSelectedTheme, new Callback() {
@Override
public void onSuccess() {
+ // Since we disabled it when clicked apply button.
+ mBottomActionBar.enableActions();
+ mBottomActionBar.hide();
Toast.makeText(getContext(), R.string.applied_theme_msg,
Toast.LENGTH_LONG).show();
- getActivity().finish();
- getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
@Override
public void onError(@Nullable Throwable throwable) {
Log.w(TAG, "Error applying theme", throwable);
+ // Since we disabled it when clicked apply button.
+ mBottomActionBar.enableActions();
+ mBottomActionBar.hide();
Toast.makeText(getContext(), R.string.apply_theme_error_msg,
Toast.LENGTH_LONG).show();
}
@@ -156,28 +210,6 @@
}
@Override
- public void onResume() {
- super.onResume();
- mTicker = TimeTicker.registerNewReceiver(getContext(), this::updateTime);
- reloadWallpaper();
- updateTime();
- }
-
- private void updateTime() {
- if (mAdapter != null) {
- mAdapter.updateTime();
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- if (getContext() != null) {
- getContext().unregisterReceiver(mTicker);
- }
- }
-
- @Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mSelectedTheme != null && !mSelectedTheme.isActive(mThemeManager)) {
@@ -196,35 +228,20 @@
} else {
if (mSelectedTheme != null) {
mOptionsController.setSelectedOption(mSelectedTheme);
+ // Set selected option above will show BottomActionBar,
+ // hide BottomActionBar for the mis-trigger.
+ mBottomActionBar.hide();
} else {
reloadOptions();
}
}
+ } else if (requestCode == FULL_PREVIEW_REQUEST_CODE && resultCode == RESULT_OK) {
+ applyTheme();
}
super.onActivityResult(requestCode, resultCode, data);
}
- private void reloadWallpaper() {
- mCurrentWallpaperFactory.createCurrentWallpaperInfos(
- (homeWallpaper, lockWallpaper, presentationMode) -> {
- mCurrentHomeWallpaper = homeWallpaper;
- mCurrentWallpaperThumbAsset = new BitmapCachingAsset(getContext(),
- mCurrentHomeWallpaper.getThumbAsset(getContext()));
- if (mSelectedTheme != null && mAdapter != null) {
- mAdapter.setWallpaperAsset(mCurrentWallpaperThumbAsset);
- mAdapter.rebindWallpaperIfAvailable();
- }
- }, false);
- }
-
- private void createAdapter(List<ThemeBundle> options) {
- mAdapter = new ThemePreviewAdapter(getActivity(), mSelectedTheme,
- mCurrentWallpaperThumbAsset,
- mSelectedTheme instanceof CustomTheme ? this::onEditClicked : null);
- mPreviewPager.setAdapter(mAdapter);
- }
-
- private void onEditClicked(View view) {
+ private void onCustomizeClicked(View view) {
if (mSelectedTheme instanceof CustomTheme) {
navigateToCustomTheme((CustomTheme) mSelectedTheme);
}
@@ -254,14 +271,20 @@
navigateToCustomTheme((CustomTheme) selected);
} else {
mSelectedTheme = (ThemeBundle) selected;
- if (mUseMyWallpaper || mSelectedTheme instanceof CustomTheme) {
- mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
- } else {
- mSelectedTheme.setOverrideThemeWallpaper(null);
- }
+ mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
mEventLogger.logThemeSelected(mSelectedTheme,
selected instanceof CustomTheme);
- createAdapter(options);
+ mThemeOptionPreviewer.setPreviewInfo(mSelectedTheme.getPreviewInfo());
+ if (mThemeInfoView != null && mSelectedTheme != null) {
+ mThemeInfoView.populateThemeInfo(mSelectedTheme);
+ }
+
+ if (selected instanceof CustomTheme) {
+ mBottomActionBar.showActionsOnly(INFORMATION, CUSTOMIZE, APPLY);
+ } else {
+ mBottomActionBar.showActionsOnly(INFORMATION, APPLY);
+ }
+ mBottomActionBar.show();
}
});
mOptionsController.initOptions(mThemeManager);
@@ -278,13 +301,15 @@
}
if (mSelectedTheme == null) {
// Select the default theme if there is no matching custom enabled theme
- // TODO(b/124796742): default to custom if there is no matching theme bundle
- mSelectedTheme = options.get(0);
+ mSelectedTheme = findDefaultThemeBundle(options);
} else {
// Only show show checkmark if we found a matching theme
mOptionsController.setAppliedOption(mSelectedTheme);
}
mOptionsController.setSelectedOption(mSelectedTheme);
+ // Set selected option above will show BottomActionBar when entering the tab. But
+ // it should not show when entering the tab.
+ mBottomActionBar.hide();
}
@Override
public void onError(@Nullable Throwable throwable) {
@@ -307,16 +332,29 @@
}
if (mSelectedTheme == null) {
// Select the default theme if there is no matching custom enabled theme
- // TODO(b/124796742): default to custom if there is no matching theme bundle
- mSelectedTheme = options.get(0);
+ mSelectedTheme = findDefaultThemeBundle(options);
} else {
// Only show show checkmark if we found a matching theme
mOptionsController.setAppliedOption(mSelectedTheme);
}
mOptionsController.setSelectedOption(mSelectedTheme);
+ // Set selected option above will show BottomActionBar,
+ // hide BottomActionBar for the mis-trigger.
+ mBottomActionBar.hide();
}, true);
}
+ private ThemeBundle findDefaultThemeBundle(List<ThemeBundle> options) {
+ String defaultThemeTitle =
+ getActivity().getResources().getString(R.string.default_theme_title);
+ for (ThemeBundle bundle : options) {
+ if (bundle.getTitle().equals(defaultThemeTitle)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
private void navigateToCustomTheme(CustomTheme themeToEdit) {
Intent intent = new Intent(getActivity(), CustomThemeActivity.class);
intent.putExtra(CustomThemeActivity.EXTRA_THEME_TITLE, themeToEdit.getTitle());
@@ -326,241 +364,12 @@
startActivityForResult(intent, CustomThemeActivity.REQUEST_CODE_CUSTOM_THEME);
}
- /**
- * Adapter class for mPreviewPager.
- * This is a ViewPager as it allows for a nice pagination effect (ie, pages snap on swipe,
- * we don't want to just scroll)
- */
- private static class ThemePreviewAdapter extends BasePreviewAdapter<ThemePreviewPage> {
-
- private int[] mIconIds = {
- R.id.preview_icon_0, R.id.preview_icon_1, R.id.preview_icon_2, R.id.preview_icon_3,
- R.id.preview_icon_4, R.id.preview_icon_5
- };
- private int[] mColorButtonIds = {
- R.id.preview_check_selected, R.id.preview_radio_selected, R.id.preview_toggle_selected
- };
- private int[] mColorTileIds = {
- R.id.preview_color_qs_0_bg, R.id.preview_color_qs_1_bg, R.id.preview_color_qs_2_bg
- };
- private int[][] mColorTileIconIds = {
- new int[]{ R.id.preview_color_qs_0_icon, 0},
- new int[]{ R.id.preview_color_qs_1_icon, 1},
- new int[] { R.id.preview_color_qs_2_icon, 3}
- };
-
- private int[] mShapeIconIds = {
- R.id.shape_preview_icon_0, R.id.shape_preview_icon_1, R.id.shape_preview_icon_2,
- R.id.shape_preview_icon_3, R.id.shape_preview_icon_4, R.id.shape_preview_icon_5
- };
- private Asset mWallpaperAsset;
-
- ThemePreviewAdapter(Activity activity, ThemeBundle theme, @Nullable Asset wallpaperAsset,
- @Nullable OnClickListener editClickListener) {
- super(activity, R.layout.theme_preview_card);
- mWallpaperAsset = wallpaperAsset;
- final Resources res = activity.getResources();
- final PreviewInfo previewInfo = theme.getPreviewInfo();
-
- Drawable coverScrim = theme instanceof CustomTheme
- ? res.getDrawable(R.drawable.theme_cover_scrim, activity.getTheme())
- : null;
-
- WallpaperPreviewLayoutListener wallpaperListener = new WallpaperPreviewLayoutListener(
- () -> mWallpaperAsset, previewInfo, coverScrim, true);
-
- addPage(new ThemeCoverPage(activity, theme.getTitle(),
- previewInfo.resolveAccentColor(res), previewInfo.icons,
- previewInfo.headlineFontFamily, previewInfo.bottomSheeetCornerRadius,
- previewInfo.shapeDrawable, previewInfo.shapeAppIcons, editClickListener,
- mColorButtonIds, mColorTileIds, mColorTileIconIds, mShapeIconIds,
- wallpaperListener));
- addPage(new ThemePreviewPage(activity, R.string.preview_name_font, R.drawable.ic_font,
- R.layout.preview_card_font_content,
- previewInfo.resolveAccentColor(res)) {
- @Override
- protected void bindBody(boolean forceRebind) {
- TextView title = card.findViewById(R.id.font_card_title);
- title.setTypeface(previewInfo.headlineFontFamily);
- TextView body = card.findViewById(R.id.font_card_body);
- body.setTypeface(previewInfo.bodyFontFamily);
- card.findViewById(R.id.font_card_divider).setBackgroundColor(accentColor);
- }
- });
- if (previewInfo.icons.size() >= mIconIds.length) {
- addPage(new ThemePreviewPage(activity, R.string.preview_name_icon,
- R.drawable.ic_wifi_24px, R.layout.preview_card_icon_content,
- previewInfo.resolveAccentColor(res)) {
- @Override
- protected void bindBody(boolean forceRebind) {
- for (int i = 0; i < mIconIds.length && i < previewInfo.icons.size(); i++) {
- ((ImageView) card.findViewById(mIconIds[i]))
- .setImageDrawable(previewInfo.icons.get(i)
- .getConstantState().newDrawable().mutate());
- }
- }
- });
- }
- if (previewInfo.colorAccentDark != -1 && previewInfo.colorAccentLight != -1) {
- addPage(new ThemePreviewPage(activity, R.string.preview_name_color,
- R.drawable.ic_colorize_24px, R.layout.preview_card_color_content,
- previewInfo.resolveAccentColor(res)) {
- @Override
- protected void bindBody(boolean forceRebind) {
- int controlGreyColor = res.getColor(R.color.control_grey);
- ColorStateList tintList = new ColorStateList(
- new int[][]{
- new int[]{android.R.attr.state_selected},
- new int[]{android.R.attr.state_checked},
- new int[]{-android.R.attr.state_enabled},
- },
- new int[] {
- accentColor,
- accentColor,
- controlGreyColor
- }
- );
-
- for (int i = 0; i < mColorButtonIds.length; i++) {
- CompoundButton button = card.findViewById(mColorButtonIds[i]);
- button.setButtonTintList(tintList);
- }
-
- Switch enabledSwitch = card.findViewById(R.id.preview_toggle_selected);
- enabledSwitch.setThumbTintList(tintList);
- enabledSwitch.setTrackTintList(tintList);
-
- ColorStateList seekbarTintList = ColorStateList.valueOf(accentColor);
- SeekBar seekbar = card.findViewById(R.id.preview_seekbar);
- seekbar.setThumbTintList(seekbarTintList);
- seekbar.setProgressTintList(seekbarTintList);
- seekbar.setProgressBackgroundTintList(seekbarTintList);
- // Disable seekbar
- seekbar.setOnTouchListener((view, motionEvent) -> true);
-
- int iconFgColor = res.getColor(R.color.tile_enabled_icon_color, null);
- for (int i = 0; i < mColorTileIds.length && i < previewInfo.icons.size();
- i++) {
- Drawable icon = previewInfo.icons.get(mColorTileIconIds[i][1])
- .getConstantState().newDrawable().mutate();
- icon.setTint(iconFgColor);
- Drawable bgShape =
- previewInfo.shapeDrawable.getConstantState().newDrawable();
- bgShape.setTint(accentColor);
-
- ImageView bg = card.findViewById(mColorTileIds[i]);
- bg.setImageDrawable(bgShape);
- ImageView fg = card.findViewById(mColorTileIconIds[i][0]);
- fg.setImageDrawable(icon);
- }
- }
- });
- }
- if (!previewInfo.shapeAppIcons.isEmpty()) {
- addPage(new ThemePreviewPage(activity, R.string.preview_name_shape,
- R.drawable.ic_shapes_24px, R.layout.preview_card_shape_content,
- previewInfo.resolveAccentColor(res)) {
- @Override
- protected void bindBody(boolean forceRebind) {
- for (int i = 0; i < mShapeIconIds.length
- && i < previewInfo.shapeAppIcons.size(); i++) {
- ImageView iconView = card.findViewById(mShapeIconIds[i]);
- iconView.setBackground(
- previewInfo.shapeAppIcons.get(i));
- }
- }
- });
- }
- }
-
- public void rebindWallpaperIfAvailable() {
- for (ThemePreviewPage page : mPages) {
- if (page.containsWallpaper()) {
- page.bindBody(true);
- }
- }
- }
-
- public void updateTime() {
- for (ThemePreviewPage page : mPages) {
- if (page instanceof TimeContainer) {
- ((TimeContainer)page).updateTime();
- }
- }
- }
-
- public void setWallpaperAsset(Asset wallpaperAsset) {
- mWallpaperAsset = wallpaperAsset;
- }
-
- private static class WallpaperPreviewLayoutListener implements OnLayoutChangeListener {
- interface WallpaperPreviewAssetProvider {
- Asset getAsset();
- }
- private final WallpaperPreviewAssetProvider mWallpaperPreviewAssetProvider;
- private final PreviewInfo mPreviewInfo;
- private final Drawable mScrim;
- private final boolean mIsTranslucent;
-
- WallpaperPreviewLayoutListener(
- WallpaperPreviewAssetProvider wallpaperPreviewAssetProvider,
- PreviewInfo previewInfo, Drawable scrim, boolean translucent) {
- mWallpaperPreviewAssetProvider = wallpaperPreviewAssetProvider;
- mPreviewInfo = previewInfo;
- mScrim = scrim;
- mIsTranslucent = translucent;
- }
-
- @Override
- public void onLayoutChange(View view, int left, int top, int right,
- int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
- int targetWidth = right - left;
- int targetHeight = bottom - top;
- if (targetWidth > 0 && targetHeight > 0) {
- Asset wallpaperPreviewAsset = mWallpaperPreviewAssetProvider.getAsset();
- if (wallpaperPreviewAsset != null) {
- wallpaperPreviewAsset.decodeBitmap(
- targetWidth, targetHeight,
- bitmap -> new CenterCropBitmapTask(bitmap, view,
- croppedBitmap -> setWallpaperBitmap(view, croppedBitmap))
- .execute());
- }
- view.removeOnLayoutChangeListener(this);
- }
- }
-
- private void setWallpaperBitmap(View view, Bitmap bitmap) {
- Resources res = view.getContext().getResources();
- Drawable background = new BitmapDrawable(res, bitmap);
- if (mIsTranslucent) {
- background.setAlpha(ThemeCoverPage.COVER_PAGE_WALLPAPER_ALPHA);
- }
- if (mScrim != null) {
- background = new LayerDrawable(new Drawable[]{background, mScrim});
- }
- view.findViewById(R.id.theme_preview_card_background).setBackground(background);
- if (mScrim == null && !mIsTranslucent) {
- boolean shouldRecycle = false;
- if (bitmap.getConfig() == Config.HARDWARE) {
- bitmap = bitmap.copy(Config.ARGB_8888, false);
- shouldRecycle = true;
- }
- int colorsHint = WallpaperColors.fromBitmap(bitmap).getColorHints();
- if (shouldRecycle) {
- bitmap.recycle();
- }
- TextView header = view.findViewById(R.id.theme_preview_card_header);
- if ((colorsHint & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0) {
- int colorLight = res.getColor(R.color.text_color_light, null);
- header.setTextColor(colorLight);
- header.setCompoundDrawableTintList(ColorStateList.valueOf(colorLight));
- } else {
- header.setTextColor(res.getColor(R.color.text_color_dark, null));
- header.setCompoundDrawableTintList(ColorStateList.valueOf(
- mPreviewInfo.colorAccentLight));
- }
- }
- }
- }
+ private void showFullPreview() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA_WALLPAPER_INFO, mCurrentHomeWallpaper);
+ bundle.putString(EXTRA_THEME_OPTION, mSelectedTheme.getSerializedPackages());
+ bundle.putString(EXTRA_THEME_OPTION_TITLE, mSelectedTheme.getTitle());
+ Intent intent = ViewOnlyFullPreviewActivity.newIntent(getContext(), SECTION_STYLE, bundle);
+ startActivityForResult(intent, FULL_PREVIEW_REQUEST_CODE);
}
}
diff --git a/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java b/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java
new file mode 100644
index 0000000..3b6923c
--- /dev/null
+++ b/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java
@@ -0,0 +1,154 @@
+/*
+ * 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.android.customization.picker.theme;
+
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.theme.DefaultThemeProvider;
+import com.android.customization.model.theme.ThemeBundle;
+import com.android.customization.model.theme.ThemeBundleProvider;
+import com.android.customization.module.CustomizationInjector;
+import com.android.customization.picker.WallpaperPreviewer;
+import com.android.customization.widget.ThemeInfoView;
+import com.android.wallpaper.R;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.picker.AppbarFragment;
+import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.WallpaperColorsLoader;
+
+import com.bumptech.glide.Glide;
+
+import org.json.JSONException;
+
+/** A Fragment for theme full preview page. */
+public class ThemeFullPreviewFragment extends AppbarFragment {
+ private static final String TAG = "ThemeFullPreviewFragment";
+
+ public static final String EXTRA_THEME_OPTION_TITLE = "theme_option_title";
+ protected static final String EXTRA_THEME_OPTION = "theme_option";
+ protected static final String EXTRA_WALLPAPER_INFO = "wallpaper_info";
+
+ private WallpaperInfo mWallpaper;
+ private ThemeBundle mThemeBundle;
+
+ /**
+ * Returns a new {@link ThemeFullPreviewFragment} with the provided title and bundle arguments
+ * set.
+ */
+ public static ThemeFullPreviewFragment newInstance(CharSequence title, Bundle intentBundle) {
+ ThemeFullPreviewFragment fragment = new ThemeFullPreviewFragment();
+ Bundle bundle = new Bundle();
+ bundle.putAll(AppbarFragment.createArguments(title));
+ bundle.putAll(intentBundle);
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mWallpaper = getArguments().getParcelable(EXTRA_WALLPAPER_INFO);
+ CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
+ ThemeBundleProvider themeProvider = new DefaultThemeProvider(
+ getContext(), injector.getCustomizationPreferences(getContext()));
+ try {
+ ThemeBundle.Builder builder = themeProvider.parseThemeBundle(
+ getArguments().getString(EXTRA_THEME_OPTION));
+ if (builder != null) {
+ builder.setTitle(getArguments().getString(EXTRA_THEME_OPTION_TITLE));
+ mThemeBundle = builder.build(getContext());
+ }
+ } catch (JSONException e) {
+ Log.w(TAG, "Couldn't parse provided custom theme, will override it");
+ // TODO(chihhangchuang): Handle the error case.
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(
+ R.layout.fragment_theme_full_preview, container, /* attachToRoot */ false);
+ setUpToolbar(view);
+ Glide.get(getContext()).clearMemory();
+
+ // Set wallpaper background.
+ ImageView wallpaperImageView = view.findViewById(R.id.wallpaper_preview_image);
+ final WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(
+ getLifecycle(),
+ getActivity(),
+ wallpaperImageView,
+ view.findViewById(R.id.wallpaper_preview_surface));
+ wallpaperPreviewer.setWallpaper(mWallpaper);
+
+ // Set theme option.
+ final ThemeOptionPreviewer themeOptionPreviewer = new ThemeOptionPreviewer(
+ getLifecycle(),
+ getContext(),
+ view.findViewById(R.id.theme_preview_container));
+ themeOptionPreviewer.setPreviewInfo(mThemeBundle.getPreviewInfo());
+ view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ wallpaperPreviewer.updatePreviewCardRadius();
+ WallpaperColorsLoader.getWallpaperColors(
+ mWallpaper.getThumbAsset(getContext()),
+ wallpaperImageView.getMeasuredWidth(),
+ wallpaperImageView.getMeasuredHeight(),
+ themeOptionPreviewer::updateColorForLauncherWidgets);
+ view.removeOnLayoutChangeListener(this);
+ }
+ });
+ return view;
+ }
+
+ @Override
+ protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
+ bottomActionBar.showActionsOnly(INFORMATION, APPLY);
+ bottomActionBar.setActionClickListener(APPLY, v -> finishActivityWithResultOk());
+ ThemeInfoView themeInfoView = (ThemeInfoView) LayoutInflater.from(getContext()).inflate(
+ R.layout.theme_info_view, /* root= */ null);
+ themeInfoView.populateThemeInfo(mThemeBundle);
+ bottomActionBar.attachViewToBottomSheetAndBindAction(themeInfoView, INFORMATION);
+ bottomActionBar.show();
+ }
+
+ private void finishActivityWithResultOk() {
+ Activity activity = requireActivity();
+ activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+ Intent intent = new Intent();
+ activity.setResult(RESULT_OK, intent);
+ activity.finish();
+ }
+}
diff --git a/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java b/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
new file mode 100644
index 0000000..6096762
--- /dev/null
+++ b/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
@@ -0,0 +1,359 @@
+/*
+ * 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.android.customization.picker.theme;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.app.WallpaperColors;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.MainThread;
+import androidx.cardview.widget.CardView;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.android.customization.model.theme.ThemeBundle;
+import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
+import com.android.wallpaper.R;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.TimeTicker;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
+
+/** A class to load the {@link ThemeBundle} preview to the view. */
+class ThemeOptionPreviewer implements LifecycleObserver {
+ // Maps which icon from ResourceConstants#ICONS_FOR_PREVIEW.
+ private static final int ICON_WIFI = 0;
+ private static final int ICON_BLUETOOTH = 1;
+ private static final int ICON_FLASHLIGHT = 3;
+ private static final int ICON_AUTO_ROTATE = 4;
+ private static final int ICON_CELLULAR_SIGNAL = 6;
+ private static final int ICON_BATTERY = 7;
+
+ // Icons in the top bar (fake "status bar") with the particular order.
+ private static final int [] sTopBarIconToPreviewIcon = new int [] {
+ ICON_WIFI, ICON_CELLULAR_SIGNAL, ICON_BATTERY };
+
+ // Ids of app icon shape preview.
+ private int[] mShapeAppIconIds = {
+ R.id.shape_preview_icon_0, R.id.shape_preview_icon_1,
+ R.id.shape_preview_icon_2, R.id.shape_preview_icon_3
+ };
+ private int[] mShapeIconAppNameIds = {
+ R.id.shape_preview_icon_app_name_0, R.id.shape_preview_icon_app_name_1,
+ R.id.shape_preview_icon_app_name_2, R.id.shape_preview_icon_app_name_3
+ };
+
+ // Ids of color/icons section.
+ private int[][] mColorTileIconIds = {
+ new int[] { R.id.preview_color_qs_0_icon, ICON_WIFI},
+ new int[] { R.id.preview_color_qs_1_icon, ICON_BLUETOOTH},
+ new int[] { R.id.preview_color_qs_2_icon, ICON_FLASHLIGHT},
+ new int[] { R.id.preview_color_qs_3_icon, ICON_AUTO_ROTATE},
+ };
+ private int[] mColorTileIds = {
+ R.id.preview_color_qs_0_bg, R.id.preview_color_qs_1_bg,
+ R.id.preview_color_qs_2_bg, R.id.preview_color_qs_3_bg
+ };
+ private int[] mColorButtonIds = {
+ R.id.preview_check_selected, R.id.preview_radio_selected, R.id.preview_toggle_selected
+ };
+
+ private final Context mContext;
+
+ private View mContentView;
+ private TextView mClock;
+ private TimeTicker mTicker;
+
+ ThemeOptionPreviewer(Lifecycle lifecycle, Context context, ViewGroup previewContainer) {
+ lifecycle.addObserver(this);
+
+ mContext = context;
+ mContentView = LayoutInflater.from(context).inflate(
+ R.layout.theme_preview_content_v2, /* root= */ null);
+ mClock = mContentView.findViewById(R.id.theme_preview_clock);
+ updateTime();
+ final float screenAspectRatio =
+ ScreenSizeCalculator.getInstance().getScreenAspectRatio(mContext);
+ previewContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View view, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ // Calculate the full preview card height and width.
+ int fullPreviewCardHeight = getFullPreviewCardHeight();
+ int fullPreviewCardWidth = (int) (getFullPreviewCardHeight() / screenAspectRatio);
+
+ // Relayout the content view to match full preview card size.
+ mContentView.measure(
+ makeMeasureSpec(fullPreviewCardWidth, EXACTLY),
+ makeMeasureSpec(fullPreviewCardHeight, EXACTLY));
+ mContentView.layout(0, 0, fullPreviewCardWidth, fullPreviewCardHeight);
+
+ // Scale the content view from full preview size to the container size. For full
+ // preview, the scale value is 1.
+ float scale =
+ (float) previewContainer.getMeasuredHeight() / getFullPreviewCardHeight();
+ mContentView.setScaleX(scale);
+ mContentView.setScaleY(scale);
+ // The pivot point is centered by default, set to (0, 0).
+ mContentView.setPivotX(0f);
+ mContentView.setPivotY(0f);
+
+ // Ensure there will be only one content view in the container.
+ previewContainer.removeAllViews();
+ // Finally, add the content view to the container.
+ previewContainer.addView(
+ mContentView,
+ mContentView.getMeasuredWidth(),
+ mContentView.getMeasuredHeight());
+
+ previewContainer.removeOnLayoutChangeListener(this);
+ }
+ });
+ }
+
+ /** Loads the Theme option preview into the container view. */
+ public void setPreviewInfo(PreviewInfo previewInfo) {
+ setHeadlineFont(previewInfo.headlineFontFamily);
+ setTopBarIcons(previewInfo.icons);
+ setAppIconShape(previewInfo.shapeAppIcons);
+ setAppIconName(previewInfo.shapeAppIconNames);
+ setColorAndIconsSection(previewInfo.icons, previewInfo.shapeDrawable,
+ previewInfo.resolveAccentColor(mContext.getResources()));
+ setColorAndIconsBoxRadius(previewInfo.bottomSheeetCornerRadius);
+ setQsbRadius(previewInfo.bottomSheeetCornerRadius);
+ }
+
+ /**
+ * Updates the color of widgets in launcher (like top status bar, smart space, and app name
+ * text) which will change its content color according to different wallpapers.
+ */
+ public void updateColorForLauncherWidgets(WallpaperColors colors) {
+ int color = mContext.getColor(
+ (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
+ ? R.color.text_color_light
+ : R.color.text_color_dark);
+ // Update the top status bar clock text color.
+ mClock.setTextColor(color);
+ // Update the top status bar icon color.
+ ViewGroup iconsContainer = mContentView.findViewById(R.id.theme_preview_top_bar_icons);
+ for (int i = 0; i < iconsContainer.getChildCount(); i++) {
+ ((ImageView) iconsContainer.getChildAt(i))
+ .setImageTintList(ColorStateList.valueOf(color));
+ }
+ // Update smart space date color.
+ ((TextView) mContentView.findViewById(R.id.smart_space_date)).setTextColor(color);
+ // Update shape app icon name text color.
+ for (int id : mShapeIconAppNameIds) {
+ ((TextView) mContentView.findViewById(id)).setTextColor(color);
+ }
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ @MainThread
+ public void onResume() {
+ mTicker = TimeTicker.registerNewReceiver(mContext, this::updateTime);
+ updateTime();
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ @MainThread
+ public void onPause() {
+ if (mContext != null) {
+ mContext.unregisterReceiver(mTicker);
+ }
+ }
+
+ private void setHeadlineFont(Typeface headlineFont) {
+ // Update font of status bar clock.
+ mClock.setTypeface(headlineFont);
+
+ // Update font of the smart space date.
+ TextView date = mContentView.findViewById(R.id.smart_space_date);
+ date.setTypeface(headlineFont);
+ // TODO(chihhangchuang): Use real date.
+ date.setText("Friday, Nov 12");
+
+ // Update font of app names.
+ for (int id : mShapeIconAppNameIds) {
+ TextView appName = mContentView.findViewById(id);
+ appName.setTypeface(headlineFont);
+ }
+
+ // Update font of color/icons section title.
+ TextView colorIconsSectionTitle = mContentView.findViewById(R.id.color_icons_section_title);
+ colorIconsSectionTitle.setTypeface(headlineFont);
+ }
+
+ private void setTopBarIcons(List<Drawable> icons) {
+ ViewGroup iconsContainer = mContentView.findViewById(R.id.theme_preview_top_bar_icons);
+ for (int i = 0; i < iconsContainer.getChildCount(); i++) {
+ int iconIndex = sTopBarIconToPreviewIcon[i];
+ if (iconIndex < icons.size()) {
+ ((ImageView) iconsContainer.getChildAt(i))
+ .setImageDrawable(icons.get(iconIndex).getConstantState()
+ .newDrawable().mutate());
+ } else {
+ iconsContainer.getChildAt(i).setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private void setAppIconShape(List<Drawable> appIcons) {
+ for (int i = 0; i < mShapeAppIconIds.length && i < appIcons.size(); i++) {
+ ImageView iconView = mContentView.findViewById(mShapeAppIconIds[i]);
+ iconView.setBackground(appIcons.get(i));
+ }
+ }
+
+ private void setAppIconName(List<String> appIconNames) {
+ for (int i = 0; i < mShapeIconAppNameIds.length && i < appIconNames.size(); i++) {
+ TextView appName = mContentView.findViewById(mShapeIconAppNameIds[i]);
+ appName.setText(appIconNames.get(i));
+ }
+ }
+
+ private void setColorAndIconsSection(List<Drawable> icons, Drawable shapeDrawable,
+ int accentColor) {
+ // Set QS icons and background.
+ for (int i = 0; i < mColorTileIconIds.length && i < icons.size(); i++) {
+ Drawable icon = icons.get(mColorTileIconIds[i][1]).getConstantState()
+ .newDrawable().mutate();
+ Drawable bgShape = shapeDrawable.getConstantState().newDrawable();
+ bgShape.setTint(accentColor);
+
+ ImageView bg = mContentView.findViewById(mColorTileIds[i]);
+ bg.setImageDrawable(bgShape);
+ ImageView fg = mContentView.findViewById(mColorTileIconIds[i][0]);
+ fg.setImageDrawable(icon);
+ }
+
+ // Set color for Buttons (CheckBox, RadioButton, and Switch).
+ ColorStateList tintList = getColorStateList(accentColor);
+ for (int mColorButtonId : mColorButtonIds) {
+ CompoundButton button = mContentView.findViewById(mColorButtonId);
+ button.setButtonTintList(tintList);
+ if (button instanceof Switch) {
+ ((Switch) button).setThumbTintList(tintList);
+ ((Switch) button).setTrackTintList(tintList);
+ }
+ }
+ }
+
+ private void setColorAndIconsBoxRadius(int cornerRadius) {
+ ((CardView) mContentView.findViewById(R.id.color_icons_section)).setRadius(cornerRadius);
+ }
+
+ private void setQsbRadius(int cornerRadius) {
+ View qsb = mContentView.findViewById(R.id.theme_qsb);
+ if (qsb != null && qsb.getVisibility() == View.VISIBLE) {
+ if (qsb.getBackground() instanceof GradientDrawable) {
+ GradientDrawable bg = (GradientDrawable) qsb.getBackground();
+ float radius = useRoundedQSB(cornerRadius)
+ ? (float) qsb.getLayoutParams().height / 2 : cornerRadius;
+ bg.setCornerRadii(new float[]{
+ radius, radius, radius, radius,
+ radius, radius, radius, radius});
+ }
+ }
+ }
+
+ private void updateTime() {
+ if (mClock != null) {
+ mClock.setText(getFormattedTime());
+ }
+ }
+
+ private String getFormattedTime() {
+ DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+ StringBuffer time = new StringBuffer();
+ FieldPosition amPmPosition = new FieldPosition(DateFormat.Field.AM_PM);
+ df.format(Calendar.getInstance(TimeZone.getDefault()).getTime(), time, amPmPosition);
+ if (amPmPosition.getBeginIndex() > 0) {
+ time.delete(amPmPosition.getBeginIndex(), amPmPosition.getEndIndex());
+ }
+ return time.toString();
+ }
+
+ private boolean useRoundedQSB(int cornerRadius) {
+ return cornerRadius >= mContext.getResources().getDimensionPixelSize(
+ R.dimen.roundCornerThreshold);
+ }
+
+ private ColorStateList getColorStateList(int accentColor) {
+ int controlGreyColor = mContext.getColor(R.color.control_grey);
+ return new ColorStateList(
+ new int[][]{
+ new int[]{android.R.attr.state_selected},
+ new int[]{android.R.attr.state_checked},
+ new int[]{-android.R.attr.state_enabled},
+ },
+ new int[] {
+ accentColor,
+ accentColor,
+ controlGreyColor
+ }
+ );
+ }
+
+ /**
+ * Gets the screen height which does not include the system status bar and bottom navigation
+ * bar.
+ */
+ private int getDisplayHeight() {
+ final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+ return dm.heightPixels;
+ }
+
+ // The height of top tool bar (R.layout.section_header).
+ private int getTopToolBarHeight() {
+ final TypedValue typedValue = new TypedValue();
+ return mContext.getTheme().resolveAttribute(
+ android.R.attr.actionBarSize, typedValue, true)
+ ? TypedValue.complexToDimensionPixelSize(
+ typedValue.data, mContext.getResources().getDisplayMetrics())
+ : 0;
+ }
+
+ private int getFullPreviewCardHeight() {
+ final Resources res = mContext.getResources();
+ return getDisplayHeight()
+ - getTopToolBarHeight()
+ - res.getDimensionPixelSize(R.dimen.bottom_navbar_height)
+ - res.getDimensionPixelSize(R.dimen.full_preview_page_default_padding_top)
+ - res.getDimensionPixelSize(R.dimen.full_preview_page_default_padding_bottom);
+ }
+}
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
index 9c92079..19dc34e 100644
--- a/src/com/android/customization/widget/OptionSelectorController.java
+++ b/src/com/android/customization/widget/OptionSelectorController.java
@@ -245,6 +245,10 @@
int numColumns = res.getInteger(R.integer.options_grid_num_columns);
int widthPerItem = totalWidth / mAdapter.getItemCount();
int extraSpace = availableWidth - widthPerItem * numColumns;
+ while (extraSpace < 0) {
+ numColumns -= 1;
+ extraSpace = availableWidth - widthPerItem * numColumns;
+ }
int containerSidePadding = extraSpace / (numColumns + 1);
mContainer.setLayoutManager(new GridLayoutManager(mContainer.getContext(), numColumns));
mContainer.setPaddingRelative(containerSidePadding, 0, containerSidePadding, 0);
diff --git a/src/com/android/customization/widget/ThemeInfoView.java b/src/com/android/customization/widget/ThemeInfoView.java
new file mode 100644
index 0000000..5b05484
--- /dev/null
+++ b/src/com/android/customization/widget/ThemeInfoView.java
@@ -0,0 +1,85 @@
+/*
+ * 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.android.customization.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.theme.ThemeBundle;
+import com.android.wallpaper.R;
+
+/** A view for displaying style info. */
+public class ThemeInfoView extends LinearLayout {
+ private static final int WIFI_ICON_PREVIEW_INDEX = 0;
+ private static final int SHAPE_PREVIEW_INDEX = 0;
+
+ private TextView mTitle;
+ private TextView mFontPreviewTextView;
+ private ImageView mIconPreviewImageView;
+ private ImageView mAppPreviewImageView;
+ private ImageView mShapePreviewImageView;
+
+ public ThemeInfoView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTitle = findViewById(R.id.style_info_title);
+ mFontPreviewTextView = findViewById(R.id.font_preview);
+ mIconPreviewImageView = findViewById(R.id.qs_preview_icon);
+ mAppPreviewImageView = findViewById(R.id.app_preview_icon);
+ mShapePreviewImageView = findViewById(R.id.shape_preview_icon);
+ }
+
+ /** Populates theme info. */
+ public void populateThemeInfo(@NonNull ThemeBundle selectedTheme) {
+ ThemeBundle.PreviewInfo previewInfo = selectedTheme.getPreviewInfo();
+
+ if (previewInfo != null) {
+ mTitle.setText(getContext().getString(
+ R.string.style_info_description, selectedTheme.getTitle()));
+ if (previewInfo.headlineFontFamily != null) {
+ mTitle.setTypeface(previewInfo.headlineFontFamily);
+ mFontPreviewTextView.setTypeface(previewInfo.headlineFontFamily);
+ }
+
+ if (previewInfo.icons.get(WIFI_ICON_PREVIEW_INDEX) != null) {
+ mIconPreviewImageView.setImageDrawable(
+ previewInfo.icons.get(WIFI_ICON_PREVIEW_INDEX));
+ }
+
+ if (previewInfo.shapeAppIcons.get(SHAPE_PREVIEW_INDEX) != null) {
+ mAppPreviewImageView.setBackground(
+ previewInfo.shapeAppIcons.get(SHAPE_PREVIEW_INDEX));
+ }
+
+ if (previewInfo.shapeDrawable != null) {
+ mShapePreviewImageView.setImageDrawable(previewInfo.shapeDrawable);
+ mShapePreviewImageView.setImageTintList(
+ ColorStateList.valueOf(previewInfo.resolveAccentColor(getResources())));
+ }
+ }
+ }
+}