Custom Theme 3/n: font step screen

Implement the generic step fragment and add hook up the
font step.

Bug: 124796742

Change-Id: I9ff490669829bfaa9c8d4e63e259fe421a7217ea
diff --git a/res/layout/activity_custom_theme.xml b/res/layout/activity_custom_theme.xml
index d69d9d7..82b7887 100644
--- a/res/layout/activity_custom_theme.xml
+++ b/res/layout/activity_custom_theme.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -26,13 +26,14 @@
     <FrameLayout
         android:id="@+id/fragment_container"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginBottom="@dimen/custom_theme_nav_height"/>
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
 
     <FrameLayout
         android:id="@+id/custom_theme_nav"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/custom_theme_nav_height">
+        android:layout_height="@dimen/custom_theme_nav_height"
+        android:layout_marginHorizontal="12dp">
         <Button
             android:id="@+id/next_button"
             style="@style/ActionPrimaryButton"
@@ -41,4 +42,4 @@
             android:layout_gravity="end|center_vertical"
             android:text="@string/custom_theme_next"/>
     </FrameLayout>
-</FrameLayout>
+</LinearLayout>
diff --git a/res/layout/fragment_custom_theme_component.xml b/res/layout/fragment_custom_theme_component.xml
new file mode 100644
index 0000000..fb26432
--- /dev/null
+++ b/res/layout/fragment_custom_theme_component.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="?android:colorPrimary">
+    <include layout="@layout/section_header"/>
+
+    <FrameLayout
+        android:id="@+id/component_preview_container"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:background="@color/secondary_color">
+        <include
+            android:id="@+id/component_preview_card"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginHorizontal="@dimen/preview_page_horizontal_margin"
+            android:layout_marginTop="@dimen/preview_page_top_margin"
+            android:layout_marginBottom="@dimen/component_preview_page_bottom_margin"
+            layout="@layout/theme_preview_card"/>
+        <TextView
+            android:id="@+id/component_step_counter"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center_horizontal"
+            android:layout_marginVertical="10dp"
+            android:textAlignment="center"
+            android:textAppearance="@style/ComponentCounterTextAppearance"/>
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/options_section"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="10dp"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/component_options_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_margin="10dp"
+            android:textAlignment="center"
+            android:textAppearance="@style/HeaderTextAppearance"/>
+
+        <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"/>
+
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/theme_font_option.xml b/res/layout/theme_font_option.xml
new file mode 100644
index 0000000..8445921
--- /dev/null
+++ b/res/layout/theme_font_option.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="wrap_content"
+              android:layout_height="match_parent"
+              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"
+        android:layout_height="@dimen/option_tile_width"
+        android:layout_gravity="center_horizontal"
+        android:paddingHorizontal="@dimen/option_tile_padding_horizontal"
+        android:paddingVertical="@dimen/option_tile_padding_vertical"
+        android:background="@drawable/option_border">
+        <TextView
+            android:id="@+id/thumbnail_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:textSize="@dimen/font_comonent_option_thumbnail_size"
+            android:textAlignment="center"
+            android:textColor="?android:attr/colorForeground"
+            android:text="@string/font_component_option_thumbnail"/>
+    </FrameLayout>
+</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8cf3a1f..93a24d8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -31,6 +31,7 @@
     <dimen name="preview_page_horizontal_margin">56dp</dimen>
     <dimen name="preview_page_top_margin">16dp</dimen>
     <dimen name="preview_page_bottom_margin">2dp</dimen>
+    <dimen name="component_preview_page_bottom_margin">36dp</dimen>
 
     <!-- Dimensions for the customization option tiles -->
     <dimen name="options_container_height">100dp</dimen>
@@ -62,4 +63,7 @@
     <dimen name="shape_preview_image_height">153dp</dimen>
 
     <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>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index dc5f7f5..0a981f9 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -97,7 +97,20 @@
         [CHAR LIMIT=20] -->
     <string name="custom_theme_next">Next</string>
 
-    <!-- Title for a system Style/Theme (combination of fonts/colors/icons) that is defined and
+    <!-- Label for a system Style/Theme (combination of fonts/colors/icons) that is defined and
         customized by the user [CHAR LIMIT=15] -->
     <string name="custom_theme_title">Custom</string>
+
+    <!-- Title for a screen that lets the user define their custom system Style/Theme
+        [CHAR LIMIT=30] -->
+    <string name="custom_theme_fragment_title">Custom Style</string>
+
+    <!-- Title of a page allowing the user to choose a font for a custom theme -->
+    <string name="font_component_title">Choose font</string>
+
+    <string name="font_component_option_thumbnail" translatable="false">A</string>
+
+    <!-- Text explaining what the current step is in setting a custom theme, eg: "1 of 4".
+    [CHAR_LIMIT=40] -->
+    <string name="component_step_counter"><xliff:g name="current_step" example="1">%1$d</xliff:g> of <xliff:g name="total_steps" example="4">%2$d</xliff:g></string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a4e79ba..18482ba 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -28,6 +28,8 @@
     <style name="CustomizationTheme.NoActionBar">
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
+        <item name="android:fitsSystemWindows">false</item>
+        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
     </style>
 
     <style name="BottomNavStyle">
@@ -84,4 +86,8 @@
         <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault</item>
     </style>
 
+    <style name="ComponentCounterTextAppearance" parent="@android:style/TextAppearance.DeviceDefault.Small">
+
+
+    </style>
 </resources>
diff --git a/src/com/android/customization/model/theme/custom/CustomThemeManager.java b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
new file mode 100644
index 0000000..0a46ecf
--- /dev/null
+++ b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.customization.model.CustomizationManager;
+
+public class CustomThemeManager implements CustomizationManager<ThemeComponentOption> {
+    @Override
+    public boolean isAvailable() {
+        return false;
+    }
+
+    @Override
+    public void apply(ThemeComponentOption option, Callback callback) {
+
+    }
+
+    @Override
+    public void fetchOptions(OptionsFetchedListener<ThemeComponentOption> callback) {
+
+    }
+}
diff --git a/src/com/android/customization/model/theme/custom/FontOptionsProvider.java b/src/com/android/customization/model/theme/custom/FontOptionsProvider.java
index ef423e7..53568c9 100644
--- a/src/com/android/customization/model/theme/custom/FontOptionsProvider.java
+++ b/src/com/android/customization/model/theme/custom/FontOptionsProvider.java
@@ -15,6 +15,7 @@
  */
 package com.android.customization.model.theme.custom;
 
+import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
 import static com.android.customization.model.ResourceConstants.CONFIG_BODY_FONT_FAMILY;
 import static com.android.customization.model.ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY;
 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
@@ -27,8 +28,10 @@
 import android.graphics.Typeface;
 import android.util.Log;
 
+import com.android.customization.model.ResourceConstants;
 import com.android.customization.model.theme.OverlayManagerCompat;
 import com.android.customization.model.theme.custom.ThemeComponentOption.FontOption;
+import com.android.wallpaper.R;
 
 /**
  * Implementation of {@link ThemeComponentOptionProvider} that reads {@link FontOption}s from
@@ -44,6 +47,7 @@
 
     @Override
     protected void loadOptions() {
+        addDefault();
         for (String overlayPackage : mOverlayPackages) {
             try {
                 Resources overlayRes = getOverlayResources(overlayPackage);
@@ -63,6 +67,19 @@
         }
     }
 
+    private void addDefault() {
+        Resources system = Resources.getSystem();
+        Typeface headlineFont = Typeface.create(system.getString(system.getIdentifier(
+                ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY,"string", ANDROID_PACKAGE)),
+                Typeface.NORMAL);
+        Typeface bodyFont = Typeface.create(system.getString(system.getIdentifier(
+                ResourceConstants.CONFIG_BODY_FONT_FAMILY,
+                "string", ANDROID_PACKAGE)),
+                Typeface.NORMAL);
+        mOptions.add(new FontOption(null, mContext.getString(R.string.default_theme_title),
+                headlineFont, bodyFont));
+    }
+
     private String getFontFamily(String overlayPackage, Resources overlayRes, String configName) {
         return overlayRes.getString(overlayRes.getIdentifier(configName, "string", overlayPackage));
     }
diff --git a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
index 71cb31a..53c9d37 100644
--- a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
+++ b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
@@ -16,11 +16,14 @@
 package com.android.customization.model.theme.custom;
 
 import android.graphics.Typeface;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.TextView;
 
 import com.android.customization.model.CustomizationManager;
 import com.android.customization.model.CustomizationOption;
+import com.android.wallpaper.R;
 
 /**
  * Represents an option of a component of a custom Theme (for example, a possible color, or font,
@@ -63,12 +66,13 @@
 
         @Override
         public String getTitle() {
-            return mLabel;
+            return null;
         }
 
         @Override
         public void bindThumbnailTile(View view) {
-
+            ((TextView) view.findViewById(R.id.thumbnail_text)).setTypeface(
+                    mHeadlineFont);
         }
 
         @Override
@@ -78,12 +82,25 @@
 
         @Override
         public int getLayoutResId() {
-            return 0;
+            return R.layout.theme_font_option;
         }
 
         @Override
         public void bindPreview(ViewGroup container) {
+            TextView header = container.findViewById(R.id.theme_preview_card_header);
+            header.setText(mLabel);
+            header.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_font, 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_font_content,
+                        cardBody, true);
+            }
+            TextView title = container.findViewById(R.id.font_card_title);
+            title.setTypeface(mHeadlineFont);
+            TextView bodyText = container.findViewById(R.id.font_card_body);
+            bodyText.setTypeface(mBodyFont);
         }
     }
 
diff --git a/src/com/android/customization/picker/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
index 9ed9f66..0d97715 100644
--- a/src/com/android/customization/picker/theme/CustomThemeActivity.java
+++ b/src/com/android/customization/picker/theme/CustomThemeActivity.java
@@ -17,18 +17,32 @@
 
 import android.os.Bundle;
 
+import androidx.annotation.StringRes;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
 
+import com.android.customization.model.theme.OverlayManagerCompat;
+import com.android.customization.model.theme.custom.CustomThemeManager;
+import com.android.customization.model.theme.custom.FontOptionsProvider;
+import com.android.customization.model.theme.custom.ThemeComponentOption;
+import com.android.customization.model.theme.custom.ThemeComponentOption.FontOption;
+import com.android.customization.model.theme.custom.ThemeComponentOptionProvider;
+import com.android.customization.picker.theme.CustomThemeComponentFragment.CustomThemeComponentFragmentHost;
 import com.android.wallpaper.R;
 import com.android.wallpaper.module.Injector;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.module.UserEventLogger;
 
-public class CustomThemeActivity extends FragmentActivity {
+import java.util.ArrayList;
+import java.util.List;
+
+public class CustomThemeActivity extends FragmentActivity implements
+        CustomThemeComponentFragmentHost {
 
     private UserEventLogger mUserEventLogger;
+    private List<ComponentStep<?>> mSteps;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -36,6 +50,7 @@
         Injector injector = InjectorProvider.getInjector();
         mUserEventLogger = injector.getUserEventLogger(this);
         setContentView(R.layout.activity_custom_theme);
+        initSteps();
 
         FragmentManager fm = getSupportFragmentManager();
         Fragment fragment = fm.findFragmentById(R.id.fragment_container);
@@ -47,6 +62,90 @@
     }
 
     private void navigateToStep(int i) {
-        //TODO(santie): implement
+        final FragmentManager fragmentManager = getSupportFragmentManager();
+        ComponentStep step = mSteps.get(i);
+        Fragment fragment = step.getFragment();
+
+        final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+        fragmentTransaction.replace(R.id.fragment_container, fragment);
+        fragmentTransaction.commitNow();
+    }
+
+    private void initSteps() {
+        mSteps = new ArrayList<>();
+        OverlayManagerCompat manager = new OverlayManagerCompat(this);
+        mSteps.add(new FontStep(new FontOptionsProvider(this, manager), 0, mSteps.size() + 1));
+    }
+
+
+    @Override
+    public void delete() {
+
+    }
+
+    @Override
+    public void cancel() {
+
+    }
+
+    @Override
+    public ThemeComponentOptionProvider<? extends ThemeComponentOption> getComponentOptionProvider(
+            int position) {
+        return mSteps.get(position).provider;
+    }
+
+    @Override
+    public CustomThemeManager getCustomThemeManager() {
+        return null;
+    }
+
+    /**
+     * Represents a step in selecting a custom theme, picking a particular component (eg font,
+     * color, shape, etc).
+     * Each step has a Fragment instance associated that instances of this class will provide.
+     */
+    private static abstract class ComponentStep<T extends ThemeComponentOption> {
+        @StringRes final int titleResId;
+        final ThemeComponentOptionProvider<T> provider;
+        final int totalSteps;
+        final int position;
+        private CustomThemeComponentFragment mFragment;
+
+        protected ComponentStep(@StringRes int titleResId, ThemeComponentOptionProvider<T> provider,
+                int position, int totalSteps) {
+            this.titleResId = titleResId;
+            this.provider = provider;
+            this.position = position;
+            this.totalSteps = totalSteps;
+        }
+
+        CustomThemeComponentFragment getFragment() {
+            if (mFragment == null) {
+                mFragment = createFragment();
+            }
+            return mFragment;
+        }
+
+        /**
+         * @return a newly created fragment that will handle this step's UI.
+         */
+        abstract CustomThemeComponentFragment createFragment();
+    }
+
+    private class FontStep extends ComponentStep<FontOption> {
+
+        protected FontStep(ThemeComponentOptionProvider<FontOption> provider,
+                int position, int totalSteps) {
+            super(R.string.font_component_title, provider, position, totalSteps);
+        }
+
+        @Override
+        CustomThemeComponentFragment createFragment() {
+            return CustomThemeComponentFragment.newInstance(
+                    CustomThemeActivity.this.getString(R.string.custom_theme_fragment_title),
+                    position,
+                    totalSteps,
+                    titleResId);
+        }
     }
 }
diff --git a/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java b/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java
new file mode 100644
index 0000000..5c74ca3
--- /dev/null
+++ b/src/com/android/customization/picker/theme/CustomThemeComponentFragment.java
@@ -0,0 +1,136 @@
+/*
+ * 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.picker.theme;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.cardview.widget.CardView;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.customization.model.theme.custom.CustomThemeManager;
+import com.android.customization.model.theme.custom.ThemeComponentOption;
+import com.android.customization.model.theme.custom.ThemeComponentOptionProvider;
+import com.android.customization.widget.OptionSelectorController;
+import com.android.wallpaper.R;
+import com.android.wallpaper.picker.ToolbarFragment;
+
+public class CustomThemeComponentFragment extends ToolbarFragment {
+    private static final String ARG_KEY_POSITION = "CustomThemeComponentFragment.position";
+    private static final String ARG_KEY_TOTAL_STEPS = "CustomThemeComponentFragment.steps";
+    private static final String ARG_KEY_TITLE_RES_ID = "CustomThemeComponentFragment.title_res";
+
+    public interface CustomThemeComponentFragmentHost {
+        void delete();
+        void cancel();
+        ThemeComponentOptionProvider<? extends ThemeComponentOption> getComponentOptionProvider(
+                int position);
+
+        CustomThemeManager getCustomThemeManager();
+    }
+
+    public static CustomThemeComponentFragment newInstance(CharSequence toolbarTitle, int position,
+            int totalSteps, int titleResId) {
+        CustomThemeComponentFragment fragment = new CustomThemeComponentFragment();
+        Bundle arguments = ToolbarFragment.createArguments(toolbarTitle);
+        arguments.putInt(ARG_KEY_POSITION, position);
+        arguments.putInt(ARG_KEY_TOTAL_STEPS, totalSteps);
+        arguments.putInt(ARG_KEY_TITLE_RES_ID, titleResId);
+        fragment.setArguments(arguments);
+        return fragment;
+    }
+
+    private ThemeComponentOptionProvider<? extends ThemeComponentOption> mProvider;
+    private CustomThemeManager mCustomThemeManager;
+    private int mPosition;
+    private int mTotalSteps;
+    @StringRes private int mTitleResId;
+
+    private RecyclerView mOptionsContainer;
+    private OptionSelectorController mOptionsController;
+    private CardView mPreviewCard;
+    private TextView mTitle;
+    private ThemeComponentOption mSelectedOption;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mPosition = getArguments().getInt(ARG_KEY_POSITION);
+        mTotalSteps = getArguments().getInt(ARG_KEY_TOTAL_STEPS);
+        mTitleResId = getArguments().getInt(ARG_KEY_TITLE_RES_ID);
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        CustomThemeComponentFragmentHost host = (CustomThemeComponentFragmentHost) context;
+        mProvider = host.getComponentOptionProvider(mPosition);
+        mCustomThemeManager = host.getCustomThemeManager();
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(
+                R.layout.fragment_custom_theme_component, container, /* attachToRoot */ false);
+        setUpToolbar(view);
+        mOptionsContainer = view.findViewById(R.id.options_container);
+        mPreviewCard = view.findViewById(R.id.component_preview_card);
+        mTitle = view.findViewById(R.id.component_options_title);
+        mTitle.setText(mTitleResId);
+        TextView stepCounter = view.findViewById(R.id.component_step_counter);
+        stepCounter.setText(getContext().getString(R.string.component_step_counter,
+                mPosition + 1, mTotalSteps));
+        setUpOptions();
+
+        return view;
+    }
+
+    private void bindPreview() {
+        mSelectedOption.bindPreview(mPreviewCard);
+    }
+
+    private void setUpOptions() {
+        mProvider.fetch(options -> {
+            mOptionsController = new OptionSelectorController(mOptionsContainer, options);
+
+            mOptionsController.addListener(selected -> {
+                mSelectedOption = (ThemeComponentOption) selected;
+                bindPreview();
+            });
+            mOptionsController.initOptions(mCustomThemeManager);
+
+            for (ThemeComponentOption option : options) {
+                if (option.isActive(mCustomThemeManager)) {
+                    mSelectedOption = option;
+                    break;
+                }
+            }
+            if (mSelectedOption == null) {
+                mSelectedOption = options.get(0);
+            }
+            mOptionsController.setSelectedOption(mSelectedOption);
+        }, false);
+    }
+}