Custom Theme 8/n: add color step screen

Bug: 124796742

Change-Id: I4944a74f49e672783d373c0197b6f296916963b6
diff --git a/res/drawable/color_chip.xml b/res/drawable/color_chip.xml
new file mode 100644
index 0000000..bc09992
--- /dev/null
+++ b/res/drawable/color_chip.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<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"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/color_chip_filled.xml b/res/drawable/color_chip_filled.xml
new file mode 100644
index 0000000..34ef0ef
--- /dev/null
+++ b/res/drawable/color_chip_filled.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <size
+        android:width="@dimen/component_color_chip_size"
+        android:height="@dimen/component_color_chip_size" />
+    <solid android:color="@android:color/black" />
+</shape>
diff --git a/res/drawable/color_chip_hollow.xml b/res/drawable/color_chip_hollow.xml
new file mode 100644
index 0000000..76ab6b8
--- /dev/null
+++ b/res/drawable/color_chip_hollow.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shape="ring"
+        android:innerRadius="13dp"
+        android:thickness="8dp"
+        android:useLevel="false">
+    <solid android:color="@android:color/black"/>
+</shape>
diff --git a/res/layout/theme_color_option.xml b/res/layout/theme_color_option.xml
new file mode 100644
index 0000000..fbede9f
--- /dev/null
+++ b/res/layout/theme_color_option.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:layout_marginHorizontal="10dp">
+
+    <ImageView
+        android:id="@+id/option_tile"
+        android:layout_width="@dimen/component_color_chip_size"
+        android:layout_height="@dimen/component_color_chip_size"
+        android:layout_gravity="center"
+        android:src="@drawable/color_chip"/>
+</FrameLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d940e7c..d76236d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -74,4 +74,6 @@
     <dimen name="font_comonent_option_thumbnail_size">32dp</dimen>
 
     <dimen name="component_icon_thumb_size">40dp</dimen>
+
+    <dimen name="component_color_chip_size">42dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ca56f83..e04fd0f 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -119,6 +119,9 @@
     <!-- Title of a page allowing the user to choose an icon set for a custom theme -->
     <string name="icon_component_title">Choose icons</string>
 
+    <!-- Title of a page allowing the user to choose a color for a custom theme -->
+    <string name="color_component_title">Choose color</string>
+
     <!-- Title of a set of icons that the user can chose for their custom style (eg, "Icons 2") -->
     <string name="icon_component_label">Icons <xliff:g name="component_number" example="1">%1$d</xliff:g></string>
 
diff --git a/src/com/android/customization/model/ResourceConstants.java b/src/com/android/customization/model/ResourceConstants.java
index 3b831b3..8a3e436 100644
--- a/src/com/android/customization/model/ResourceConstants.java
+++ b/src/com/android/customization/model/ResourceConstants.java
@@ -16,6 +16,7 @@
 package com.android.customization.model;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.provider.Settings.Secure;
 
 import com.android.wallpaper.R;
@@ -60,6 +61,11 @@
     String OVERLAY_CATEGORY_ICON_LAUNCHER = "android.theme.customization.icon_pack.launcher";
 
     /**
+     * Global Android theme category (default theme prebundled with the OS)
+     */
+    String OVERLAY_CATEGORY_ANDROID_THEME = "android.theme";
+
+    /**
      * Secure Setting used to store the currently set theme.
      */
     String THEME_SETTING = Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES;
@@ -75,6 +81,10 @@
     };
 
     ArrayList<String> sTargetPackages = new ArrayList<>();
+    String ACCENT_COLOR_LIGHT_NAME = "accent_device_default_light";
+    String ACCENT_COLOR_DARK_NAME = "accent_device_default_dark";
+
+    float PATH_SIZE = 100f;
 
     static String[] getPackagesToOverlay(Context context) {
         if (sTargetPackages.isEmpty()) {
@@ -88,4 +98,11 @@
     static String getLauncherPackage(Context context) {
         return context.getString(R.string.launcher_overlayable_package);
     }
+
+    /**
+     * @return the value CONFIG_ICON_MASK for the given package name using the given Resources
+     */
+    static String getIconMask(Resources res, String packageName) {
+        return res.getString(res.getIdentifier(CONFIG_ICON_MASK, "string", packageName));
+    }
 }
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
index 28af9ea..510b5e7 100644
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ b/src/com/android/customization/model/theme/DefaultThemeProvider.java
@@ -15,6 +15,8 @@
  */
 package com.android.customization.model.theme;
 
+import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_DARK_NAME;
+import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_LIGHT_NAME;
 import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
 import static com.android.customization.model.ResourceConstants.CONFIG_ICON_MASK;
 import static com.android.customization.model.ResourceConstants.ICON_PREVIEW_DRAWABLE_NAME;
@@ -87,8 +89,6 @@
 
     private static final String DEFAULT_THEME_NAME= "default";
 
-    private static final String ACCENT_COLOR_LIGHT_NAME = "accent_device_default_light";
-    private static final String ACCENT_COLOR_DARK_NAME = "accent_device_default_dark";
     // List of packages
     private final String[] mShapePreviewIconPackages;
     private List<ThemeBundle> mThemes;
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
index 8c78239..8e5061d 100644
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ b/src/com/android/customization/model/theme/ThemeBundle.java
@@ -15,6 +15,8 @@
  */
 package com.android.customization.model.theme;
 
+import static com.android.customization.model.ResourceConstants.PATH_SIZE;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -38,6 +40,7 @@
 import com.android.customization.model.CustomizationManager;
 import com.android.customization.model.CustomizationOption;
 import com.android.customization.widget.DynamicAdaptiveIconDrawable;
+import com.android.customization.model.ResourceConstants;
 import com.android.wallpaper.R;
 import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.asset.ResourceAsset;
@@ -195,7 +198,6 @@
     }
 
     public static class Builder {
-        private static final float PATH_SIZE = 100f;
         protected String mTitle;
         private Typeface mBodyFontFamily;
         private Typeface mHeadlineFontFamily;
diff --git a/src/com/android/customization/model/theme/custom/ColorOptionsProvider.java b/src/com/android/customization/model/theme/custom/ColorOptionsProvider.java
new file mode 100644
index 0000000..3603533
--- /dev/null
+++ b/src/com/android/customization/model/theme/custom/ColorOptionsProvider.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+package com.android.customization.model.theme.custom;
+
+import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_DARK_NAME;
+import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_LIGHT_NAME;
+import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
+import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ANDROID_THEME;
+import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
+import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SYSUI;
+import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
+import static com.android.customization.model.ResourceConstants.PATH_SIZE;
+import static com.android.customization.model.ResourceConstants.SYSUI_ICONS_FOR_PREVIEW;
+import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
+import static com.android.customization.model.theme.custom.ThemeComponentOption.ColorOption.COLOR_TILES_ICON_IDS;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.core.graphics.PathParser;
+
+import com.android.customization.model.ResourceConstants;
+import com.android.customization.model.theme.OverlayManagerCompat;
+import com.android.customization.model.theme.custom.ThemeComponentOption.ColorOption;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link ThemeComponentOptionProvider} that reads {@link ColorOption}s from
+ * icon overlays.
+ */
+public class ColorOptionsProvider extends ThemeComponentOptionProvider<ColorOption> {
+
+    private static final String TAG = "ColorOptionsProvider";
+    private final CustomThemeManager mCustomThemeManager;
+    private final String mDefaultThemePackage;
+
+    public ColorOptionsProvider(Context context, OverlayManagerCompat manager,
+            CustomThemeManager customThemeManager) {
+        super(context, manager, OVERLAY_CATEGORY_COLOR);
+        mCustomThemeManager = customThemeManager;
+        // System color is set with a static overlay for android.theme category, so let's try to
+        // find that first, and if that's not present, we'll default to System resources.
+        // (see #addDefault())
+        List<String> themePackages = manager.getOverlayPackagesForCategory(
+                OVERLAY_CATEGORY_ANDROID_THEME, UserHandle.myUserId(), ANDROID_PACKAGE);
+        mDefaultThemePackage = themePackages.isEmpty() ? null : themePackages.get(0);
+    }
+
+    @Override
+    protected void loadOptions() {
+        List<Drawable> previewIcons = new ArrayList<>();
+        String iconPackage =
+                mCustomThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_ICON_SYSUI);
+        if (TextUtils.isEmpty(iconPackage)) {
+            iconPackage = SYSUI_PACKAGE;
+        }
+        for (String iconName : SYSUI_ICONS_FOR_PREVIEW) {
+            try {
+                if (previewIcons.size() == COLOR_TILES_ICON_IDS.length) {
+                    break;
+                }
+                previewIcons.add(loadIconPreviewDrawable(iconName, iconPackage));
+            } catch (NameNotFoundException | NotFoundException e) {
+                Log.w(TAG, String.format("Couldn't load icon in %s for color preview, will skip it",
+                        iconPackage), e);
+            }
+        }
+        String shapePackage = mCustomThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_SHAPE);
+        if (TextUtils.isEmpty(shapePackage)) {
+            shapePackage = ANDROID_PACKAGE;
+        }
+        Drawable shape = loadShape(shapePackage);
+        addDefault(previewIcons, shape);
+        for (String overlayPackage : mOverlayPackages) {
+            try {
+                Resources overlayRes = getOverlayResources(overlayPackage);
+                int lightColor = overlayRes.getColor(
+                        overlayRes.getIdentifier(ACCENT_COLOR_LIGHT_NAME, "color", overlayPackage),
+                        null);
+                int darkColor = overlayRes.getColor(
+                        overlayRes.getIdentifier(ACCENT_COLOR_DARK_NAME, "color", overlayPackage),
+                        null);
+
+                ColorOption option = new ColorOption(overlayPackage, lightColor, darkColor);
+                option.setPreviewIcons(previewIcons);
+                option.setShapeDrawable(shape);
+                mOptions.add(option);
+            } catch (NameNotFoundException | NotFoundException e) {
+                Log.w(TAG, String.format("Couldn't load color overlay %s, will skip it",
+                        overlayPackage), e);
+            }
+        }
+    }
+
+    private void addDefault(List<Drawable> previewIcons, Drawable shape) {
+        int lightColor, darkColor;
+        Resources system = Resources.getSystem();
+        try {
+            Resources r = getOverlayResources(mDefaultThemePackage);
+            lightColor = r.getColor(
+                    r.getIdentifier(ACCENT_COLOR_LIGHT_NAME, "color", mDefaultThemePackage),
+                    null);
+            darkColor = r.getColor(
+                    r.getIdentifier(ACCENT_COLOR_DARK_NAME, "color", mDefaultThemePackage),
+                    null);
+        } catch (NotFoundException | NameNotFoundException e) {
+            Log.d(TAG, "Didn't find default color, will use system option", e);
+
+            lightColor = system.getColor(
+                    system.getIdentifier(ACCENT_COLOR_LIGHT_NAME, "color", ANDROID_PACKAGE), null);
+
+            darkColor = system.getColor(
+                    system.getIdentifier(ACCENT_COLOR_DARK_NAME, "color", ANDROID_PACKAGE), null);
+        }
+        ColorOption option = new ColorOption(null, lightColor, darkColor);
+        option.setPreviewIcons(previewIcons);
+        option.setShapeDrawable(shape);
+        mOptions.add(option);
+    }
+
+    private Drawable loadIconPreviewDrawable(String drawableName, String packageName)
+            throws NameNotFoundException, NotFoundException {
+
+        Resources overlayRes = getOverlayResources(packageName);
+        return overlayRes.getDrawable(
+                overlayRes.getIdentifier(drawableName, "drawable", packageName), null);
+    }
+
+    private Drawable loadShape(String packageName) {
+        String path = null;
+        try {
+            Resources r = getOverlayResources(packageName);
+
+            path = ResourceConstants.getIconMask(r, packageName);
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, String.format("Couldn't load shape icon for %s, skipping.", packageName), e);
+        }
+        ShapeDrawable shapeDrawable = null;
+        if (!TextUtils.isEmpty(path)) {
+            PathShape shape = new PathShape(PathParser.createPathFromPathData(path),
+                    PATH_SIZE, PATH_SIZE);
+            shapeDrawable = new ShapeDrawable(shape);
+            shapeDrawable.setIntrinsicHeight((int) PATH_SIZE);
+            shapeDrawable.setIntrinsicWidth((int) PATH_SIZE);
+        }
+        return shapeDrawable;
+    }
+
+}
diff --git a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
index 352b0e7..f3cf7cc 100644
--- a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
+++ b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
@@ -21,15 +21,23 @@
 import static com.android.customization.model.ResourceConstants.SYSUI_ICONS_FOR_PREVIEW;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
 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 androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
+
 import com.android.customization.model.CustomizationManager;
 import com.android.customization.model.CustomizationOption;
 import com.android.customization.model.ResourceConstants;
@@ -46,6 +54,8 @@
  * shape, etc).
  * Extending classes correspond to each component's options and provide the structure to bind
  * preview and thumbnails.
+ * // TODO (santie): refactor the logic to bind preview cards to reuse between ThemeFragment and
+ * // here
  */
 public abstract class ThemeComponentOption implements CustomizationOption<ThemeComponentOption> {
 
@@ -199,28 +209,129 @@
 
     public static class ColorOption extends ThemeComponentOption {
 
-        ColorOption(String packageName) {
+        /**
+         * Ids of views used to represent quick setting tiles in the color preview screen
+         */
+        private static int[] COLOR_TILE_IDS = {
+                R.id.preview_color_qs_0_bg, R.id.preview_color_qs_1_bg, R.id.preview_color_qs_2_bg
+        };
+        static int[] COLOR_TILES_ICON_IDS = {
+                R.id.preview_color_qs_0_icon, R.id.preview_color_qs_1_icon,
+                R.id.preview_color_qs_2_icon
+        };
+
+        /**
+         * Ids of views used to represent control buttons in the color preview screen
+         */
+        private static int[] COLOR_BUTTON_IDS = {
+                R.id.preview_check_selected, R.id.preview_radio_selected,
+                R.id.preview_toggle_selected,
+        };
+
+        @ColorInt private int mColorAccentLight;
+        @ColorInt private int mColorAccentDark;
+        /**
+         * Icons shown as example of QuickSettings tiles in the color preview screen.
+         */
+        private List<Drawable> mIcons = new ArrayList<>();
+
+        /**
+         * Drawable with the currently selected shape to be used as background of the sample
+         * QuickSetting icons in the color preview screen.
+         */
+        private Drawable mShapeDrawable;
+
+        ColorOption(String packageName, @ColorInt int lightColor,
+                @ColorInt int darkColor) {
             addOverlayPackage(OVERLAY_CATEGORY_COLOR, packageName);
+            mColorAccentLight = lightColor;
+            mColorAccentDark = darkColor;
         }
 
         @Override
         public void bindThumbnailTile(View view) {
+            int color = resolveColor(view.getResources());
+            ((ImageView) view.findViewById(R.id.option_tile)).getDrawable().setTint(color);
+        }
 
+        private int resolveColor(Resources res) {
+            Configuration configuration = res.getConfiguration();
+            return (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                    == Configuration.UI_MODE_NIGHT_YES ? mColorAccentDark : mColorAccentLight;
         }
 
         @Override
         public boolean isActive(CustomizationManager<ThemeComponentOption> manager) {
-            return false;
+            CustomThemeManager customThemeManager = (CustomThemeManager) manager;
+            return Objects.equals(getOverlayPackages().get(OVERLAY_CATEGORY_COLOR),
+                    customThemeManager.getOverlayPackages().get(OVERLAY_CATEGORY_COLOR));
         }
 
         @Override
         public int getLayoutResId() {
-            return 0;
+            return R.layout.theme_color_option;
         }
 
         @Override
         public void bindPreview(ViewGroup container) {
+            TextView header = container.findViewById(R.id.theme_preview_card_header);
+            header.setText(R.string.preview_name_color);
+            header.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_colorize_24px, 0, 0);
 
+            ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
+            if (cardBody.getChildCount() == 0) {
+                LayoutInflater.from(container.getContext()).inflate(
+                        R.layout.preview_card_color_content, cardBody, true);
+            }
+            @ColorInt int accentColor = resolveColor(container.getResources());
+            ColorStateList tintList = new ColorStateList(
+                    new int[][]{
+                            new int[]{android.R.attr.state_selected},
+                            new int[]{android.R.attr.state_checked}
+                    },
+                    new int[] {
+                            accentColor,
+                            accentColor
+                    }
+            );
+
+            for (int i = 0; i < COLOR_BUTTON_IDS.length; i++) {
+                CompoundButton button = container.findViewById(COLOR_BUTTON_IDS[i]);
+                button.setButtonTintList(tintList);
+            }
+
+            Switch toggle = container.findViewById(R.id.preview_toggle_selected);
+            toggle.setThumbTintList(tintList);
+            toggle.setTrackTintList(tintList);
+
+            ColorStateList seekbarTintList = ColorStateList.valueOf(accentColor);
+            SeekBar seekbar = container.findViewById(R.id.preview_seekbar);
+            seekbar.setThumbTintList(seekbarTintList);
+            seekbar.setProgressTintList(seekbarTintList);
+            seekbar.setProgressBackgroundTintList(seekbarTintList);
+            // Disable seekbar
+            seekbar.setOnTouchListener((view, motionEvent) -> true);
+            if (!mIcons.isEmpty() && mShapeDrawable != null) {
+                for (int i = 0; i < COLOR_TILE_IDS.length; i++) {
+                    Drawable icon = mIcons.get(i).getConstantState().newDrawable();
+                    //TODO: load and set the shape.
+                    Drawable bgShape = mShapeDrawable.getConstantState().newDrawable();
+                    bgShape.setTint(accentColor);
+
+                    ImageView bg = container.findViewById(COLOR_TILE_IDS[i]);
+                    bg.setImageDrawable(bgShape);
+                    ImageView fg = container.findViewById(COLOR_TILES_ICON_IDS[i]);
+                    fg.setImageDrawable(icon);
+                }
+            }
+        }
+
+        public void setPreviewIcons(List<Drawable> icons) {
+            mIcons.addAll(icons);
+        }
+
+        public void setShapeDrawable(@Nullable Drawable shapeDrawable) {
+            mShapeDrawable = shapeDrawable;
         }
     }
 
diff --git a/src/com/android/customization/picker/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
index 0fd2b6b..50a3534 100644
--- a/src/com/android/customization/picker/theme/CustomThemeActivity.java
+++ b/src/com/android/customization/picker/theme/CustomThemeActivity.java
@@ -34,11 +34,13 @@
 import com.android.customization.model.theme.ThemeBundle.Builder;
 import com.android.customization.model.theme.ThemeBundleProvider;
 import com.android.customization.model.theme.ThemeManager;
+import com.android.customization.model.theme.custom.ColorOptionsProvider;
 import com.android.customization.model.theme.custom.CustomTheme;
 import com.android.customization.model.theme.custom.CustomThemeManager;
 import com.android.customization.model.theme.custom.FontOptionsProvider;
 import com.android.customization.model.theme.custom.IconOptionsProvider;
 import com.android.customization.model.theme.custom.ThemeComponentOption;
+import com.android.customization.model.theme.custom.ThemeComponentOption.ColorOption;
 import com.android.customization.model.theme.custom.ThemeComponentOption.FontOption;
 import com.android.customization.model.theme.custom.ThemeComponentOption.IconOption;
 import com.android.customization.model.theme.custom.ThemeComponentOptionProvider;
@@ -128,8 +130,10 @@
     private void initSteps() {
         mSteps = new ArrayList<>();
         OverlayManagerCompat manager = new OverlayManagerCompat(this);
-        mSteps.add(new FontStep(new FontOptionsProvider(this, manager), 0, 2));
-        mSteps.add(new IconStep(new IconOptionsProvider(this, manager), 1, 2));
+        mSteps.add(new FontStep(new FontOptionsProvider(this, manager), 0, 3));
+        mSteps.add(new IconStep(new IconOptionsProvider(this, manager), 1, 3));
+        mSteps.add(new ColorStep(new ColorOptionsProvider(this, manager, mCustomThemeManager),
+                2, 3));
         mCurrentStep = 0;
     }
 
@@ -278,4 +282,21 @@
                     titleResId);
         }
     }
+
+    private class ColorStep extends ComponentStep<ColorOption> {
+
+        protected ColorStep(ThemeComponentOptionProvider<ColorOption> provider,
+                int position, int totalSteps) {
+            super(R.string.color_component_title, provider, position, totalSteps);
+        }
+
+        @Override
+        CustomThemeComponentFragment createFragment() {
+            return CustomThemeComponentFragment.newInstance(
+                    CustomThemeActivity.this.getString(R.string.custom_theme_fragment_title),
+                    position,
+                    totalSteps,
+                    titleResId);
+        }
+    }
 }