Merging from ub-launcher3-master @ build 6552470 am: 1598484f96

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/ThemePicker/+/11688450

Change-Id: I1fd63c02091b062aac7d4978bbd3635d12e4db30
diff --git a/res/drawable/color_chip.xml b/res/drawable/color_chip.xml
index bc09992..11301b1 100644
--- a/res/drawable/color_chip.xml
+++ b/res/drawable/color_chip.xml
@@ -18,10 +18,10 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:state_activated="true"
-        android:drawable="@drawable/color_chip_filled" />
-    <item
-        android:state_activated="false"
         android:drawable="@drawable/color_chip_hollow" />
     <item
-        android:drawable="@drawable/color_chip_hollow"/>
+        android:state_activated="false"
+        android:drawable="@drawable/color_chip_filled" />
+    <item
+        android:drawable="@drawable/color_chip_filled"/>
 </selector>
\ No newline at end of file
diff --git a/res/drawable/color_chip_hollow.xml b/res/drawable/color_chip_hollow.xml
index c0eab74..699dc60 100644
--- a/res/drawable/color_chip_hollow.xml
+++ b/res/drawable/color_chip_hollow.xml
@@ -15,10 +15,27 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shape="ring"
-        android:innerRadius="10dp"
-        android:thickness="6dp"
-        android:useLevel="false">
-    <solid android:color="@android:color/black"/>
-</shape>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:height="@dimen/component_color_chip_size"
+        android:width="@dimen/component_color_chip_size"
+        android:gravity="center">
+        <shape
+            android:shape="oval">
+            <solid android:color="@android:color/black" />
+        </shape>
+    </item>
+    <item
+        android:height="@dimen/component_color_chip_container_size"
+        android:width="@dimen/component_color_chip_container_size"
+        android:gravity="center">
+        <shape
+            android:shape="ring"
+            android:innerRadius="24dp"
+            android:thickness="@dimen/option_border_width"
+            android:useLevel="false">
+        <solid android:color="@android:color/black"/>
+        </shape>
+    </item>
+</layer-list>
+
diff --git a/res/drawable/option_border_edge_custom.xml b/res/drawable/option_border_edge_custom.xml
index 1863c5d..6d633c3 100644
--- a/res/drawable/option_border_edge_custom.xml
+++ b/res/drawable/option_border_edge_custom.xml
@@ -26,8 +26,7 @@
     <item android:state_activated="false">
         <shape android:shape="rectangle">
             <stroke
-                android:color="@color/black_24_alpha"
-                android:alpha="0.24"
+                android:color="@color/option_border_color"
                 android:width="@dimen/option_border_width" />
             <corners android:radius="?android:dialogCornerRadius" />
         </shape>
diff --git a/res/layout/fragment_custom_theme_component.xml b/res/layout/fragment_custom_theme_component.xml
index cf80192..fb3e5b3 100644
--- a/res/layout/fragment_custom_theme_component.xml
+++ b/res/layout/fragment_custom_theme_component.xml
@@ -33,13 +33,13 @@
             android:layout_height="0dp"
             android:background="?android:colorPrimary"
             app:layout_constrainedHeight="true"
-            app:layout_constraintBottom_toTopOf="@+id/guideline"
+            app:layout_constraintBottom_toTopOf="@+id/divider"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
-            app:layout_constraintHorizontal_bias="0.5"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintVertical_bias="0.0">
+            app:layout_constraintVertical_bias="0.0"
+            app:layout_constraintHeight_percent="@dimen/preview_pager_maximum_height_ratio">
 
             <include
                 android:id="@+id/component_preview_content"
@@ -50,6 +50,7 @@
                 android:layout_marginHorizontal="@dimen/preview_page_horizontal_margin"
                 android:layout_marginTop="@dimen/preview_page_top_margin"/>
         </FrameLayout>
+
         <View
             android:id="@+id/divider"
             android:layout_width="match_parent"
@@ -58,43 +59,31 @@
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@+id/component_preview_container"/>
-
-        <androidx.constraintlayout.widget.Guideline
-            android:id="@+id/guideline"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            app:layout_constraintGuide_percent=".7"/>
-
-
+        
         <TextView
             android:id="@+id/component_options_title"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="10dp"
-            android:layout_marginStart="10dp"
-            android:layout_marginEnd="10dp"
-            android:layout_marginBottom="10dp"
+            android:layout_marginVertical="18dp"
+            android:layout_marginHorizontal="16dp"
             android:textAlignment="center"
             android:textAppearance="@style/TitleTextAppearance"
             android:textSize="@dimen/component_options_title_size"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@+id/component_preview_container"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintVertical_bias=".15"/>
+            app:layout_constraintBottom_toTopOf="@+id/options_container"/>
 
         <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="center_horizontal"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintHorizontal_bias="0.5"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@+id/component_options_title"
-            app:layout_constraintVertical_bias=".25"/>
+            app:layout_constraintVertical_bias="1"/>
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 </LinearLayout>
diff --git a/res/layout/fragment_custom_theme_name.xml b/res/layout/fragment_custom_theme_name.xml
index 27a8f19..ab3c459 100644
--- a/res/layout/fragment_custom_theme_name.xml
+++ b/res/layout/fragment_custom_theme_name.xml
@@ -36,55 +36,51 @@
             android:clipToPadding="false"
             android:background="@color/fullscreen_preview_background"
             app:layout_constrainedHeight="true"
-            app:layout_constraintBottom_toTopOf="@+id/guideline"
+            app:layout_constraintBottom_toTopOf="@+id/component_options_title"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
-            app:layout_constraintHorizontal_bias="0.5"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintVertical_bias="0.0">
+            app:layout_constraintVertical_bias="0.0"
+            app:layout_constraintHeight_percent="@dimen/preview_pager_maximum_height_ratio">
+
             <include layout="@layout/theme_preview_card"/>
         </FrameLayout>
 
-        <androidx.constraintlayout.widget.Guideline
-            android:id="@+id/guideline"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            app:layout_constraintGuide_percent=".7"/>
-
         <TextView
             android:id="@+id/component_options_title"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="10dp"
-            android:layout_marginStart="10dp"
-            android:layout_marginEnd="10dp"
-            android:layout_marginBottom="10dp"
+            android:layout_marginVertical="18dp"
+            android:layout_marginHorizontal="16dp"
             android:textAlignment="center"
             android:textAppearance="@style/TitleTextAppearance"
             android:textSize="@dimen/component_options_title_size"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@+id/component_preview_container"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintVertical_bias=".15"/>
+            app:layout_constraintBottom_toTopOf="@+id/edit_text_container"/>
 
-        <EditText
-            android:id="@+id/custom_theme_name"
-            style="@style/CustomThemeNameEditText"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:importantForAutofill="no"
-            android:minWidth="300dp"
-            app:layout_constraintBottom_toBottomOf="parent"
+        <FrameLayout
+            android:id="@+id/edit_text_container"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintHorizontal_bias="0.5"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@+id/component_options_title"
-            app:layout_constraintVertical_bias=".25"/>
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintVertical_bias="1">
 
-
+            <EditText
+                android:id="@+id/custom_theme_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginVertical="16dp"
+                android:layout_marginHorizontal="16dp"
+                android:layout_gravity="center|top"
+                android:importantForAutofill="no"
+                android:minWidth="300dp"
+                style="@style/CustomThemeNameEditText"/>
+        </FrameLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
-
 </LinearLayout>
diff --git a/res/layout/theme_color_option.xml b/res/layout/theme_color_option.xml
index 33e4668..9a3c4c4 100644
--- a/res/layout/theme_color_option.xml
+++ b/res/layout/theme_color_option.xml
@@ -15,15 +15,17 @@
      limitations under the License.
 -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="center"
-    android:padding="8dp">
+    android:layout_marginTop="28dp"
+    android:layout_marginHorizontal="@dimen/component_options_margin_horizontal">
 
     <ImageView
         android:id="@+id/option_tile"
         android:layout_width="@dimen/component_color_chip_container_size"
         android:layout_height="@dimen/component_color_chip_container_size"
         android:layout_gravity="center"
+        android:scaleType="center"
         android:src="@drawable/color_chip"/>
 </FrameLayout>
diff --git a/res/layout/theme_font_option.xml b/res/layout/theme_font_option.xml
index 48dcaa2..dea4f78 100644
--- a/res/layout/theme_font_option.xml
+++ b/res/layout/theme_font_option.xml
@@ -19,13 +19,6 @@
               android:layout_height="wrap_content"
               android:orientation="vertical">
 
-    <TextView
-        android:id="@+id/option_label"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:layout_marginBottom="@dimen/theme_option_label_margin"
-        android:textAppearance="@style/OptionTitleTextAppearance"/>
     <FrameLayout
         android:id="@+id/option_tile"
         android:layout_width="@dimen/option_tile_width"
@@ -33,6 +26,7 @@
         android:layout_gravity="center_horizontal"
         android:paddingHorizontal="@dimen/option_tile_padding_horizontal"
         android:paddingVertical="@dimen/option_tile_padding_vertical"
+        android:layout_marginHorizontal="@dimen/component_options_margin_horizontal"
         android:background="@drawable/option_border">
         <TextView
             android:id="@+id/thumbnail_text"
diff --git a/res/layout/theme_icon_option.xml b/res/layout/theme_icon_option.xml
index 62925ad..292b8cd 100644
--- a/res/layout/theme_icon_option.xml
+++ b/res/layout/theme_icon_option.xml
@@ -16,16 +16,9 @@
 -->
 <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:orientation="vertical">
 
-    <TextView
-        android:id="@+id/option_label"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:layout_marginBottom="@dimen/theme_option_label_margin"
-        android:textAppearance="@style/OptionTitleTextAppearance"/>
     <FrameLayout
         android:id="@+id/option_tile"
         android:layout_width="@dimen/option_tile_width"
@@ -33,6 +26,7 @@
         android:layout_gravity="center_horizontal"
         android:paddingHorizontal="@dimen/option_tile_padding_horizontal"
         android:paddingVertical="@dimen/option_tile_padding_vertical"
+        android:layout_marginHorizontal="@dimen/component_options_margin_horizontal"
         android:background="@drawable/option_border">
         <ImageView
             android:id="@+id/option_icon"
diff --git a/res/layout/theme_shape_option.xml b/res/layout/theme_shape_option.xml
index 94e31ea..c5682c0 100644
--- a/res/layout/theme_shape_option.xml
+++ b/res/layout/theme_shape_option.xml
@@ -16,15 +16,14 @@
 -->
 <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:orientation="vertical">
     <FrameLayout
         android:id="@+id/option_tile"
         android:layout_width="@dimen/option_tile_width"
         android:layout_height="@dimen/option_tile_width"
         android:layout_gravity="center"
-        android:layout_marginTop="16dp"
-        android:padding="2dp">
+        android:layout_marginHorizontal="@dimen/component_options_margin_horizontal">
         <ImageView
             android:id="@+id/shape_thumbnail"
             android:layout_width="match_parent"
diff --git a/res/values-land/bools.xml b/res/values-land/bools.xml
deleted file mode 100644
index 409538d..0000000
--- a/res/values-land/bools.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
-     Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <bool name="use_grid_for_options">true</bool>
-</resources>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index feaf26f..c6b2a3f 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -24,8 +24,6 @@
     <color name="switch_track_tint">#171717</color>
     <color name="switch_thumb_tint">#bdbdbd</color>
 
-    <color name="theme_preview_workspace_shadow_color">@android:color/transparent</color>
-
     <color name="tile_enabled_icon_color">#2d2d2d</color>
     <color name="tile_disabled_icon_color">@color/light_grey</color>
     <color name="tile_disabled_background_color">#353535</color>
diff --git a/res/values/bools.xml b/res/values/bools.xml
deleted file mode 100644
index 409538d..0000000
--- a/res/values/bools.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
-     Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <bool name="use_grid_for_options">true</bool>
-</resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d4ad0e1..82d02b0 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -31,7 +31,8 @@
     <color name="clockface_preview_background">@android:color/black</color>
 
     <color name="theme_preview_icon_color">@color/google_grey700</color>
-    <color name="theme_preview_workspace_shadow_color">#B0000000</color>
+    <color name="theme_preview_workspace_shadow_color_dark">#B0000000</color>
+    <color name="theme_preview_workspace_shadow_color_transparent">@android:color/transparent</color>
 
     <color name="text_color_dark">#2d2d2d</color>
     <color name="text_color_light">@color/material_white_text</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 461b39e..4f78920 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -99,15 +99,16 @@
     <dimen name="custom_theme_nav_height">56dp</dimen>
 
     <!-- Note, using dp instead of sp as this is just the font thumbnail, not text -->
-    <dimen name="font_comonent_option_thumbnail_size">32dp</dimen>
+    <dimen name="font_comonent_option_thumbnail_size">36dp</dimen>
 
     <dimen name="component_icon_thumb_size">40dp</dimen>
 
-    <dimen name="component_color_chip_size">32dp</dimen>
-    <dimen name="component_color_chip_container_size">33dp</dimen>
+    <dimen name="component_color_chip_size">40dp</dimen>
+    <dimen name="component_color_chip_container_size">52dp</dimen>
 
     <dimen name="component_shape_thumb_size">72dp</dimen>
-    <dimen name="component_options_title_size">18sp</dimen>
+    <dimen name="component_options_title_size">20sp</dimen>
+    <dimen name="component_options_margin_horizontal">4dp</dimen>
 
     <!-- For a corner radius of this size or larger, we'll preview a rounded qsb widget. -->
     <dimen name="roundCornerThreshold">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f1cd169..d80f74d 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -30,10 +30,6 @@
         the home screen. [CHAR LIMIT=15] -->
     <string name="grid_title">Grid</string>
 
-    <!-- Title of a section of the customization picker where the user can select a Wallpaper.
-        [CHAR LIMIT=19] -->
-    <string name="wallpaper_title">Wallpaper</string>
-
     <!-- Label for a button that allows the user to apply the currently selected Theme.
         [CHAR LIMIT=20] -->
     <string name="apply_theme_btn">Apply</string>
diff --git a/src/com/android/customization/model/grid/GridOption.java b/src/com/android/customization/model/grid/GridOption.java
index 2f32ce1..43afee4 100644
--- a/src/com/android/customization/model/grid/GridOption.java
+++ b/src/com/android/customization/model/grid/GridOption.java
@@ -20,9 +20,12 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.view.View;
 import android.widget.ImageView;
 
+import androidx.annotation.Nullable;
+
 import com.android.customization.model.CustomizationManager;
 import com.android.customization.model.CustomizationOption;
 import com.android.customization.widget.GridTileDrawable;
@@ -31,7 +34,6 @@
 /**
  * Represents a grid layout option available in the current launcher.
  */
-// TODO(chihhangchuang): Consider moving Parcelable into CustomizationOption.
 public class GridOption implements CustomizationOption<GridOption>, Parcelable {
     public static final Creator<GridOption> CREATOR = new Creator<GridOption>() {
         @Override
@@ -101,6 +103,21 @@
     }
 
     @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof GridOption) {
+            GridOption other = (GridOption) obj;
+            return TextUtils.equals(this.name, other.name)
+                    && this.cols == other.cols
+                    && this.rows == other.rows;
+        }
+        return false;
+    }
+
+    @Override
     public int getLayoutResId() {
         return R.layout.grid_option;
     }
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
index 8cb2865..43b07ff 100644
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ b/src/com/android/customization/model/theme/ThemeBundle.java
@@ -62,6 +62,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Represents a Theme component available in the system as a "persona" bundle.
@@ -87,7 +88,7 @@
         mTitle = title;
         mIsDefault = isDefault;
         mPreviewInfo = previewInfo;
-        mPackagesByCategory = Collections.unmodifiableMap(overlayPackages);
+        mPackagesByCategory = Collections.unmodifiableMap(removeNullValues(overlayPackages));
     }
 
     @Override
@@ -214,6 +215,13 @@
         }
     }
 
+    private Map<String, String> removeNullValues(Map<String, String> map) {
+        return map.entrySet()
+                .stream()
+                .filter(entry -> entry.getValue() != null)
+                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+    }
+
     protected CharSequence getContentDescription(Context context) {
         if (mContentDescription == null) {
             CharSequence defaultName = context.getString(R.string.default_theme_title);
diff --git a/src/com/android/customization/model/theme/custom/CustomThemeManager.java b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
index 7b9b67c..42d73e6 100644
--- a/src/com/android/customization/model/theme/custom/CustomThemeManager.java
+++ b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
@@ -16,20 +16,29 @@
 package com.android.customization.model.theme.custom;
 
 import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.Nullable;
 
 import com.android.customization.model.CustomizationManager;
 import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
+import com.android.customization.model.theme.ThemeBundleProvider;
 import com.android.customization.model.theme.ThemeManager;
 import com.android.customization.model.theme.custom.CustomTheme.Builder;
 
+import org.json.JSONException;
+
 import java.util.Map;
 
 public class CustomThemeManager implements CustomizationManager<ThemeComponentOption> {
 
+    private static final String TAG = "CustomThemeManager";
+    private static final String KEY_STATE_CURRENT_SELECTION = "CustomThemeManager.currentSelection";
+
     private final CustomTheme mOriginalTheme;
-    private final CustomTheme.Builder mBuilder;
+    private CustomTheme.Builder mBuilder;
 
     private CustomThemeManager(Map<String, String> overlayPackages,
             @Nullable CustomTheme originalTheme) {
@@ -72,6 +81,29 @@
         return mBuilder.createPreviewInfo(context);
     }
 
+    /** Saves the custom theme selections while system config changes. */
+    public void saveCustomTheme(Context context, Bundle savedInstanceState) {
+        CustomTheme customTheme =
+                buildPartialCustomTheme(context, /* id= */ null, /* title= */ null);
+        savedInstanceState.putString(KEY_STATE_CURRENT_SELECTION,
+                customTheme.getSerializedPackages());
+    }
+
+    /** Reads the saved custom theme after system config changed. */
+    public void readCustomTheme(ThemeBundleProvider themeBundleProvider,
+                                Bundle savedInstanceState) {
+        String packages = savedInstanceState.getString(KEY_STATE_CURRENT_SELECTION);
+        if (!TextUtils.isEmpty(packages)) {
+            try {
+                mBuilder = themeBundleProvider.parseCustomTheme(packages);
+            } catch (JSONException e) {
+                Log.w(TAG, "Couldn't parse provided custom theme.");
+            }
+        } else {
+            Log.w(TAG, "No custom theme being restored.");
+        }
+    }
+
     public static CustomThemeManager create(
             @Nullable CustomTheme customTheme, ThemeManager themeManager) {
         if (customTheme != null && customTheme.isDefined()) {
diff --git a/src/com/android/customization/picker/WallpaperPreviewer.java b/src/com/android/customization/picker/WallpaperPreviewer.java
index 0185925..8f69d59 100644
--- a/src/com/android/customization/picker/WallpaperPreviewer.java
+++ b/src/com/android/customization/picker/WallpaperPreviewer.java
@@ -174,7 +174,7 @@
 
         mWallpaperConnection.setVisibility(true);
         mHomePreview.post(() -> {
-            if (!mWallpaperConnection.connect()) {
+            if (mWallpaperConnection != null && !mWallpaperConnection.connect()) {
                 mWallpaperConnection = null;
                 LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
             }
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
index a8f6eda..05f535c 100644
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ b/src/com/android/customization/picker/grid/GridFragment.java
@@ -64,6 +64,9 @@
 public class GridFragment extends AppbarFragment {
 
     private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
+    private static final String KEY_STATE_SELECTED_OPTION = "GridFragment.selectedOption";
+    private static final String KEY_STATE_BOTTOM_ACTION_BAR_VISIBILITY =
+            "GridFragment.bottomActionBarVisibility";
 
     private static final String TAG = "GridFragment";
 
@@ -98,33 +101,16 @@
     private final Callback mApplyGridCallback = new Callback() {
         @Override
         public void onSuccess() {
-            mGridManager.fetchOptions(new OptionsFetchedListener<GridOption>() {
-                @Override
-                public void onOptionsLoaded(List<GridOption> options) {
-                    mOptionsController.resetOptions(options);
-                    mSelectedOption = getSelectedOption(options);
-                    mReloadOptionsAfterApplying = true;
-                    // It will trigger OptionSelectedListener#onOptionSelected.
-                    mOptionsController.setSelectedOption(mSelectedOption);
-                    Toast.makeText(getContext(), R.string.applied_grid_msg, Toast.LENGTH_SHORT)
-                            .show();
-                    // Since we disabled it when clicked apply button.
-                    mBottomActionBar.enableActions();
-                    mBottomActionBar.hide();
-                }
-
-                @Override
-                public void onError(@Nullable Throwable throwable) {
-                    if (throwable != null) {
-                        Log.e(TAG, "Error loading grid options", throwable);
-                    }
-                    showError();
-                }
-            }, true);
+            Toast.makeText(getContext(), R.string.applied_grid_msg, Toast.LENGTH_SHORT).show();
+            getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+            getActivity().finish();
         }
 
         @Override
         public void onError(@Nullable Throwable throwable) {
+            // Since we disabled it when clicked apply button.
+            mBottomActionBar.enableActions();
+            mBottomActionBar.hide();
             //TODO(chihhangchuang): handle
         }
     };
@@ -152,7 +138,7 @@
 
         // Clear memory cache whenever grid fragment view is being loaded.
         Glide.get(getContext()).clearMemory();
-        setUpOptions();
+        setUpOptions(savedInstanceState);
 
         ImageView wallpaperPreviewImage = view.findViewById(R.id.wallpaper_preview_image);
         wallpaperPreviewImage.setOnClickListener(v -> showFullPreview());
@@ -188,6 +174,18 @@
     }
 
     @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mSelectedOption != null) {
+            outState.putParcelable(KEY_STATE_SELECTED_OPTION, mSelectedOption);
+        }
+        if (mBottomActionBar != null) {
+            outState.putBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBILITY,
+                    mBottomActionBar.isVisible());
+        }
+    }
+
+    @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) {
@@ -212,12 +210,15 @@
         if (mGridOptionPreviewer != null) {
             mGridOptionPreviewer.release();
         }
+        if (getContext() == null) {
+            return;
+        }
         mGridOptionPreviewer = new GridOptionPreviewer(
                 getContext(), mGridManager, mGridPreviewContainer);
         mGridOptionPreviewer.setGridOption(mSelectedOption, mGridManager.usesSurfaceView());
     }
 
-    private void setUpOptions() {
+    private void setUpOptions(@Nullable Bundle savedInstanceState) {
         hideError();
         mLoading.show();
         mGridManager.fetchOptions(new OptionsFetchedListener<GridOption>() {
@@ -225,7 +226,6 @@
             public void onOptionsLoaded(List<GridOption> options) {
                 mLoading.hide();
                 mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
-
                 mOptionsController.addListener(selected -> {
                     mSelectedOption = (GridOption) selected;
                     if (mReloadOptionsAfterApplying) {
@@ -237,7 +237,23 @@
                     updatePreview();
                 });
                 mOptionsController.initOptions(mGridManager);
-                mSelectedOption = getSelectedOption(options);
+
+                GridOption previouslySelectedOption = null;
+                if (savedInstanceState != null) {
+                    previouslySelectedOption = findEquivalent(
+                            options, savedInstanceState.getParcelable(KEY_STATE_SELECTED_OPTION));
+                }
+                mSelectedOption = previouslySelectedOption != null
+                        ? previouslySelectedOption
+                        : getActiveOption(options);
+                mOptionsController.setSelectedOption(mSelectedOption);
+                boolean bottomActionBarVisibility = savedInstanceState != null
+                        && savedInstanceState.getBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBILITY);
+                if (bottomActionBarVisibility) {
+                    mBottomActionBar.show();
+                } else {
+                    mBottomActionBar.hide();
+                }
                 updatePreview();
             }
 
@@ -251,7 +267,7 @@
         }, false);
     }
 
-    private GridOption getSelectedOption(List<GridOption> options) {
+    private GridOption getActiveOption(List<GridOption> options) {
         return options.stream()
                 .filter(option -> option.isActive(mGridManager))
                 .findAny()
@@ -259,6 +275,14 @@
                 .orElse(options.get(0));
     }
 
+    @Nullable
+    private GridOption findEquivalent(List<GridOption> options, GridOption target) {
+        return options.stream()
+                .filter(option -> option.equals(target))
+                .findAny()
+                .orElse(null);
+    }
+
     private void hideError() {
         mContent.setVisibility(View.VISIBLE);
         mError.setVisibility(View.GONE);
diff --git a/src/com/android/customization/picker/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
index a5ce222..fc56337 100644
--- a/src/com/android/customization/picker/theme/CustomThemeActivity.java
+++ b/src/com/android/customization/picker/theme/CustomThemeActivity.java
@@ -85,12 +85,12 @@
     protected void onCreate(Bundle savedInstanceState) {
         CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
         mUserEventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(this);
+        ThemeBundleProvider themeProvider =
+                new DefaultThemeProvider(this, injector.getCustomizationPreferences(this));
         Intent intent = getIntent();
         CustomTheme customTheme = null;
         if (intent != null && intent.hasExtra(EXTRA_THEME_PACKAGES)
                 && intent.hasExtra(EXTRA_THEME_TITLE) && intent.hasExtra(EXTRA_THEME_ID)) {
-            ThemeBundleProvider themeProvider =
-                    new DefaultThemeProvider(this, injector.getCustomizationPreferences(this));
             mIsDefinedTheme = intent.getBooleanExtra(CREATE_NEW_THEME, true);
             try {
                 CustomTheme.Builder themeBuilder = themeProvider.parseCustomTheme(
@@ -112,6 +112,9 @@
                 mUserEventLogger);
         mThemeManager.fetchOptions(null, false);
         mCustomThemeManager = CustomThemeManager.create(customTheme, mThemeManager);
+        if (savedInstanceState != null) {
+            mCustomThemeManager.readCustomTheme(themeProvider, savedInstanceState);
+        }
 
         int currentStep = 0;
         if (savedInstanceState != null) {
@@ -138,6 +141,9 @@
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putInt(KEY_STATE_CURRENT_STEP, mCurrentStep);
+        if (mCustomThemeManager != null) {
+            mCustomThemeManager.saveCustomTheme(this, outState);
+        }
     }
 
     private void navigateToStep(int i) {
@@ -355,8 +361,7 @@
             return CustomThemeComponentFragment.newInstance(
                     title,
                     position,
-                    titleResId,
-                    true);
+                    titleResId);
         }
     }
 
diff --git a/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java b/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java
index b2217aa..a6fdb1c 100644
--- a/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java
+++ b/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java
@@ -98,6 +98,9 @@
             mOptionsController.addListener(selected -> {
                 mSelectedOption = (ThemeComponentOption) selected;
                 bindPreview();
+                // Preview and apply. The selection will be kept whatever user goes to previous page
+                // or encounter system config changes, the current selection can be recovered.
+                mCustomThemeManager.apply(mSelectedOption, /* callback= */ null);
             });
             mOptionsController.initOptions(mCustomThemeManager);
 
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
index 1f4917e..5c5dcdd 100644
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ b/src/com/android/customization/picker/theme/ThemeFragment.java
@@ -68,6 +68,8 @@
 
     private static final String TAG = "ThemeFragment";
     private static final String KEY_SELECTED_THEME = "ThemeFragment.SelectedThemeBundle";
+    private static final String KEY_STATE_BOTTOM_ACTION_BAR_VISIBILITY =
+            "ThemeFragment.bottomActionBarVisibility";
     private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
 
     /**
@@ -177,6 +179,9 @@
     }
 
     private void updateThemePreviewColorPerWallpaper() {
+        if (getContext() == null) {
+            return;
+        }
         if (mCurrentHomeWallpaper != null && mWallpaperImage.getMeasuredWidth() > 0
                 && mWallpaperImage.getMeasuredHeight() > 0) {
             WallpaperColorsLoader.getWallpaperColors(
@@ -192,11 +197,9 @@
         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();
+                Toast.makeText(getContext(), R.string.applied_theme_msg, Toast.LENGTH_LONG).show();
+                getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+                getActivity().finish();
             }
 
             @Override
@@ -217,6 +220,10 @@
         if (mSelectedTheme != null && !mSelectedTheme.isActive(mThemeManager)) {
             outState.putString(KEY_SELECTED_THEME, mSelectedTheme.getSerializedPackages());
         }
+        if (mBottomActionBar != null) {
+            outState.putBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBILITY,
+                    mBottomActionBar.isVisible());
+        }
     }
 
     @Override
@@ -226,7 +233,8 @@
                 mSelectedTheme = null;
                 reloadOptions();
             } else if (resultCode == CustomThemeActivity.RESULT_THEME_APPLIED) {
-                reloadOptions();
+                getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+                getActivity().finish();
             } else {
                 if (mSelectedTheme != null) {
                     mOptionsController.setSelectedOption(mSelectedTheme);
@@ -290,28 +298,36 @@
                     }
                 });
                 mOptionsController.initOptions(mThemeManager);
+
                 String previouslySelected = savedInstanceState != null
                         ? savedInstanceState.getString(KEY_SELECTED_THEME) : null;
+                ThemeBundle previouslySelectedTheme = null;
+                ThemeBundle activeTheme = null;
                 for (ThemeBundle theme : options) {
                     if (previouslySelected != null
                             && previouslySelected.equals(theme.getSerializedPackages())) {
-                        mSelectedTheme = theme;
-                    } else if (theme.isActive(mThemeManager)) {
-                        mSelectedTheme = theme;
-                        break;
+                        previouslySelectedTheme = theme;
+                    }
+                    if (theme.isActive(mThemeManager)) {
+                        activeTheme = theme;
                     }
                 }
+                mSelectedTheme = previouslySelectedTheme != null
+                        ? previouslySelectedTheme
+                        : activeTheme;
+
                 if (mSelectedTheme == null) {
                     // Select the default theme if there is no matching custom enabled theme
                     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();
+                boolean bottomActionBarVisibility = savedInstanceState != null
+                        && savedInstanceState.getBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBILITY);
+                if (bottomActionBarVisibility) {
+                    mBottomActionBar.show();
+                } else {
+                    mBottomActionBar.hide();
+                }
             }
             @Override
             public void onError(@Nullable Throwable throwable) {
@@ -335,9 +351,6 @@
             if (mSelectedTheme == null) {
                 // Select the default theme if there is no matching custom enabled theme
                 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,
diff --git a/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java b/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
index afeb6ff..f24cf64 100644
--- a/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
+++ b/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
@@ -174,23 +174,41 @@
      * 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);
+        boolean useLightTextColor =
+                (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0;
+        int textColor = mContext.getColor(useLightTextColor
+                ? R.color.text_color_light
+                : R.color.text_color_dark);
+        int textShadowColor = mContext.getColor(useLightTextColor
+                ? R.color.theme_preview_workspace_shadow_color_dark
+                : R.color.theme_preview_workspace_shadow_color_transparent);
         // Update the top status bar clock text color.
-        mStatusBarClock.setTextColor(color);
+        mStatusBarClock.setTextColor(textColor);
         // 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));
+                    .setImageTintList(ColorStateList.valueOf(textColor));
         }
         // Update smart space date color.
-        ((TextView) mContentView.findViewById(R.id.smart_space_date)).setTextColor(color);
+        mSmartSpaceDate.setTextColor(textColor);
+        mSmartSpaceDate.setShadowLayer(
+                mContext.getResources().getDimension(
+                        R.dimen.preview_theme_smartspace_key_ambient_shadow_blur),
+                /* dx = */ 0,
+                /* dy = */ 0,
+                textShadowColor);
+
         // Update shape app icon name text color.
         for (int id : mShapeIconAppNameIds) {
-            ((TextView) mContentView.findViewById(id)).setTextColor(color);
+            TextView appName = mContentView.findViewById(id);
+            appName.setTextColor(textColor);
+            appName.setShadowLayer(
+                    mContext.getResources().getDimension(
+                            R.dimen.preview_theme_app_name_key_ambient_shadow_blur),
+                    /* dx = */ 0,
+                    /* dy = */ 0,
+                    textShadowColor);
         }
 
         mHasWallpaperColorSet = true;
@@ -229,23 +247,11 @@
     private void setHeadlineFont(Typeface headlineFont) {
         mStatusBarClock.setTypeface(headlineFont);
         mSmartSpaceDate.setTypeface(headlineFont);
-        mSmartSpaceDate.setShadowLayer(
-                mContext.getResources().getDimension(
-                        R.dimen.preview_theme_smartspace_key_ambient_shadow_blur),
-                /* dx = */ 0,
-                /* dy = */ 0,
-                mContext.getColor(R.color.theme_preview_workspace_shadow_color));
 
         // Update font of app names.
         for (int id : mShapeIconAppNameIds) {
             TextView appName = mContentView.findViewById(id);
             appName.setTypeface(headlineFont);
-            appName.setShadowLayer(
-                    mContext.getResources().getDimension(
-                            R.dimen.preview_theme_app_name_key_ambient_shadow_blur),
-                    /* dx = */ 0,
-                    /* dy = */ 0,
-                    mContext.getColor(R.color.theme_preview_workspace_shadow_color));
         }
 
         // Update font of color/icons section title.
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
index 19dc34e..5b76b36 100644
--- a/src/com/android/customization/widget/OptionSelectorController.java
+++ b/src/com/android/customization/widget/OptionSelectorController.java
@@ -72,14 +72,14 @@
     private CustomizationOption mAppliedOption;
 
     public OptionSelectorController(RecyclerView container, List<T> options) {
-        this(container, options, false, true);
+        this(container, options, true, true);
     }
 
     public OptionSelectorController(RecyclerView container, List<T> options,
             boolean useGrid, boolean showCheckmark) {
         mContainer = container;
         mOptions = options;
-        mUseGrid = container.getResources().getBoolean(R.bool.use_grid_for_options) || useGrid;
+        mUseGrid = useGrid;
         mShowCheckmark = showCheckmark;
     }