[1/n] Move new picker logic to AOSP

- Dark/light Theme.
- Themed Icon.
- App Grid.

Bug: 190354625
Test: Build and run the app.
Change-Id: I54c7c3897568019326b827091270091bc114611c
diff --git a/Android.bp b/Android.bp
index c8bf759..69b0ab2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,8 +54,9 @@
 
     static_libs: [
         "wallpaper-common-deps",
-        "styleprotoslite",
+        "SettingsLibSettingsTheme",
         "SystemUI-statsd",
+        "styleprotoslite",
     ],
 
     srcs: [
diff --git a/res/drawable/ic_grid_24px.xml b/res/drawable/ic_grid_24px.xml
new file mode 100644
index 0000000..872ba87
--- /dev/null
+++ b/res/drawable/ic_grid_24px.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="?android:textColorPrimary"
+      android:pathData="M22,9L22,7h-5L17,2h-2v5L9,7L9,2L7,2v5L2,7v2h5v6L2,15v2h5v5h2v-5h6v5h2v-5h5v-2h-5L17,9h5zM15,15L9,15L9,9h6v6z"/>
+</vector>
diff --git a/res/drawable/option_border_color.xml b/res/drawable/option_border_color.xml
new file mode 100644
index 0000000..ba5097b
--- /dev/null
+++ b/res/drawable/option_border_color.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@+id/systempalettecolor">
+        <shape android:shape="rectangle">
+            <solid android:color="?androidprv:attr/colorSurface"/>
+            <corners android:radius="@dimen/option_tile_radius" />
+        </shape>
+    </item>
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="?androidprv:attr/colorSurface"/>
+            <corners android:radius="@dimen/option_tile_radius" />
+        </shape>
+    </item>
+</ripple>
diff --git a/res/layout/grid_section_view.xml b/res/layout/grid_section_view.xml
new file mode 100644
index 0000000..b10b894
--- /dev/null
+++ b/res/layout/grid_section_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.customization.picker.grid.GridSectionView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
+    android:clickable="true"
+    android:paddingTop="@dimen/section_top_padding"
+    android:paddingHorizontal="@dimen/section_horizontal_padding"
+    android:orientation="horizontal">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/grid_title"
+            style="@style/SectionTitleTextStyle" />
+
+        <TextView
+            android:id="@+id/grid_section_description"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="@style/SectionSubtitleTextStyle"/>
+    </LinearLayout>
+
+    <ImageView
+        android:id="@+id/grid_section_tile"
+        android:layout_width="@dimen/option_tile_width"
+        android:layout_height="@dimen/option_tile_width"
+        android:scaleType="center"
+        android:src="@drawable/ic_grid_24px"
+        android:background="@drawable/option_border_color"
+        android:contentDescription="@string/gird_picker_entry_content_description" />
+
+</com.android.customization.picker.grid.GridSectionView>
\ No newline at end of file
diff --git a/res/layout/mode_section_view.xml b/res/layout/mode_section_view.xml
new file mode 100644
index 0000000..12e0c04
--- /dev/null
+++ b/res/layout/mode_section_view.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.customization.picker.mode.ModeSectionView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
+    android:clickable="true"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingVertical="@dimen/section_vertical_padding"
+    android:paddingHorizontal="@dimen/section_horizontal_padding">
+
+    <TextView
+        android:id="@+id/dark_mode_toggle_title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:text="@string/mode_title"
+        style="@style/SectionTitleTextStyle" />
+
+    <Switch
+        android:id="@+id/dark_mode_toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:clickable="false"
+        android:focusable="false"
+        style="@style/Switch.SettingsLib" />
+
+</com.android.customization.picker.mode.ModeSectionView>
diff --git a/res/layout/themed_icon_section_view.xml b/res/layout/themed_icon_section_view.xml
new file mode 100644
index 0000000..cd4268c
--- /dev/null
+++ b/res/layout/themed_icon_section_view.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.customization.picker.themedicon.ThemedIconSectionView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
+    android:clickable="true"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingHorizontal="@dimen/section_horizontal_padding"
+    android:paddingVertical="@dimen/section_vertical_padding">
+
+    <TextView
+        android:id="@+id/themed_icon_title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:text="@string/themed_icon_title"
+        style="@style/SectionTitleTextStyle" />
+
+    <Switch
+        android:id="@+id/themed_icon_toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:clickable="false"
+        android:focusable="false"
+        style="@style/Switch.SettingsLib"/>
+
+</com.android.customization.picker.themedicon.ThemedIconSectionView>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b5e231d..51ff6f1 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -121,4 +121,11 @@
     <dimen name="theme_info_app_preview_icon_elevation">2dp</dimen>
     <dimen name="theme_info_text_size">28sp</dimen>
 
+    <!-- Common dimensions for option sections -->
+    <dimen name="section_container_vertical_margin">24dp</dimen>
+    <dimen name="section_horizontal_padding">24dp</dimen>
+    <dimen name="section_vertical_padding">24dp</dimen>
+    <dimen name="section_top_padding">16dp</dimen>
+    <dimen name="section_bottom_padding">16dp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eb36162..1c71e6c 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -231,4 +231,16 @@
     <!--Name of metadata in the main launcher Activity which values contains the authority
     corresponding to a ContentProvider in launcher to query or change themed icon option  -->
     <string name="themed_icon_metadata_key" translatable="false">com.android.launcher3.themedicon.option</string>
+
+    <!--  Strings for new customization UI.  -->
+
+    <!-- The title of mode section view. [CHAR_LIMIT=20] -->
+    <string name="mode_title" msgid="1000319159005403986">Dark theme</string>
+    <string name="mode_disabled_msg" msgid="1926077857799715086">Temporarily disabled due to Battery Saver</string>
+
+    <!-- Title of a section of the customization where the user can select color override for icon. [CHAR LIMIT=30] -->
+    <string name="themed_icon_title" msgid="4305612684940220645">Themed icons</string>
+
+    <!-- The content description of grid picker entry. [CHAR LIMIT=NONE] -->
+    <string name="gird_picker_entry_content_description" msgid="7538418512525897691">Change app grid</string>
 </resources>
diff --git a/src/com/android/customization/model/HubSectionController.java b/src/com/android/customization/model/HubSectionController.java
new file mode 100644
index 0000000..765e520
--- /dev/null
+++ b/src/com/android/customization/model/HubSectionController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.android.customization.picker.SectionView;
+
+/**
+ * The interface for the behavior of section in the Customization hub.
+ *
+ * @param <T> the {@link SectionView} to create for the section
+ */
+public interface HubSectionController<T extends SectionView> {
+
+    /** Interface for Customization hub section navigation. */
+    interface HubSectionNavigationController {
+        /** Navigates to the given {@code fragment}. */
+        void navigateTo(Fragment fragment);
+    }
+
+    /** Interface for Customization hub section's dark mode responding to battery saver. */
+    interface HubSectionBatterySaverListener {
+        /** Callback when battery saver's state changed with given {@code isEnabled}. */
+        void onBatterySaverStateChanged(boolean isEnabled);
+    }
+
+    /** Returns {@code true} if the Customization hub section is available. */
+    boolean isAvailable(@Nullable Context context);
+
+    /**
+     * Returns a newly created {@link SectionView} for the section.
+     *
+     * @param context the {@link Context} to inflate view
+     */
+    T createView(Context context);
+
+    /** Saves the view state for configuration changes. */
+    default void onSaveInstanceState(Bundle savedInstanceState) {}
+
+    /** Releases the controller. */
+    default void release() {}
+}
diff --git a/src/com/android/customization/model/grid/GridSectionController.java b/src/com/android/customization/model/grid/GridSectionController.java
new file mode 100644
index 0000000..a84e878
--- /dev/null
+++ b/src/com/android/customization/model/grid/GridSectionController.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model.grid;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
+import com.android.customization.model.HubSectionController;
+import com.android.customization.picker.grid.GridFragment;
+import com.android.customization.picker.grid.GridSectionView;
+import com.android.wallpaper.R;
+
+import java.util.List;
+
+/** A {@link HubSectionController} for app grid. */
+public class GridSectionController implements HubSectionController<GridSectionView> {
+
+    private static final String TAG = "GridSectionController";
+
+    private final GridOptionsManager mGridOptionsManager;
+    private final HubSectionNavigationController mHubSectionNavigationController;
+
+    public GridSectionController(GridOptionsManager gridOptionsManager,
+            HubSectionNavigationController hubSectionNavigationController) {
+        mGridOptionsManager = gridOptionsManager;
+        mHubSectionNavigationController = hubSectionNavigationController;
+    }
+
+    @Override
+    public boolean isAvailable(Context context) {
+        return mGridOptionsManager.isAvailable();
+    }
+
+    @Override
+    public GridSectionView createView(Context context) {
+        GridSectionView gridSectionView = (GridSectionView) LayoutInflater.from(context)
+                .inflate(R.layout.grid_section_view, /* root= */ null);
+        TextView sectionDescription = gridSectionView.findViewById(R.id.grid_section_description);
+        View sectionTile = gridSectionView.findViewById(R.id.grid_section_tile);
+
+        // Fetch grid options to show currently set grid.
+        mGridOptionsManager.fetchOptions(new OptionsFetchedListener<GridOption>() {
+            @Override
+            public void onOptionsLoaded(List<GridOption> options) {
+                sectionDescription.setText(getActiveOption(options).getTitle());
+            }
+
+            @Override
+            public void onError(@Nullable Throwable throwable) {
+                if (throwable != null) {
+                    Log.e(TAG, "Error loading grid options", throwable);
+                }
+                sectionDescription.setText(R.string.something_went_wrong);
+                sectionTile.setVisibility(View.GONE);
+            }
+        }, /* reload= */ true);
+
+        gridSectionView.setOnClickListener(v -> mHubSectionNavigationController.navigateTo(
+                GridFragment.newInstance(context.getString(R.string.grid_title))));
+
+        return gridSectionView;
+    }
+
+    private GridOption getActiveOption(List<GridOption> options) {
+        return options.stream()
+                .filter(option -> option.isActive(mGridOptionsManager))
+                .findAny()
+                // For development only, as there should always be a grid set.
+                .orElse(options.get(0));
+    }
+}
diff --git a/src/com/android/customization/model/mode/BatterySaverStateReceiver.java b/src/com/android/customization/model/mode/BatterySaverStateReceiver.java
new file mode 100644
index 0000000..f3a9c05
--- /dev/null
+++ b/src/com/android/customization/model/mode/BatterySaverStateReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model.mode;
+
+import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGED;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.text.TextUtils;
+
+import com.android.customization.model.HubSectionController.HubSectionBatterySaverListener;
+
+/**
+ * Broadcast receiver for getting battery saver state and callback to
+ * {@link HubSectionBatterySaverListener}
+ */
+public class BatterySaverStateReceiver extends BroadcastReceiver {
+
+    private final HubSectionBatterySaverListener mHubSectionBatterySaverListener;
+
+    public BatterySaverStateReceiver(HubSectionBatterySaverListener batterySaverController) {
+        mHubSectionBatterySaverListener = batterySaverController;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (TextUtils.equals(intent.getAction(), ACTION_POWER_SAVE_MODE_CHANGED)) {
+            PowerManager pm = context.getSystemService(PowerManager.class);
+            mHubSectionBatterySaverListener.onBatterySaverStateChanged(pm.isPowerSaveMode());
+        }
+    }
+}
diff --git a/src/com/android/customization/model/mode/ModeSection.java b/src/com/android/customization/model/mode/ModeSection.java
new file mode 100644
index 0000000..9dd4b65
--- /dev/null
+++ b/src/com/android/customization/model/mode/ModeSection.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model.mode;
+
+
+import static android.Manifest.permission.MODIFY_DAY_NIGHT_MODE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGED;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+import android.view.LayoutInflater;
+import android.widget.Switch;
+import android.widget.Toast;
+
+import androidx.annotation.MainThread;
+import androidx.core.content.ContextCompat;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.android.customization.model.HubSectionController;
+import com.android.customization.model.HubSectionController.HubSectionBatterySaverListener;
+import com.android.customization.picker.mode.ModeSectionView;
+import com.android.wallpaper.R;
+
+/**
+ * Section for dark theme toggle that controls if this section will be shown visually
+ */
+public class ModeSection implements HubSectionController<ModeSectionView>, LifecycleObserver,
+        HubSectionBatterySaverListener {
+
+    private Context mContext;
+    private Lifecycle mLifecycle;
+    private BatterySaverStateReceiver mBatterySaverStateReceiver;
+    private ModeSectionView mModeSectionView;
+
+    public ModeSection(Context context, Lifecycle lifecycle) {
+        mContext = context;
+        mLifecycle = lifecycle;
+        mBatterySaverStateReceiver = new BatterySaverStateReceiver(this);
+        mLifecycle.addObserver(this);
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_START)
+    @MainThread
+    public void onStart() {
+        if (mContext != null) {
+            mContext.registerReceiver(mBatterySaverStateReceiver,
+                    new IntentFilter(ACTION_POWER_SAVE_MODE_CHANGED));
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+    @MainThread
+    public void onStop() {
+        if (mContext != null && mBatterySaverStateReceiver != null) {
+            mContext.unregisterReceiver(mBatterySaverStateReceiver);
+        }
+    }
+
+    @Override
+    public void release() {
+        mLifecycle.removeObserver(this);
+        mContext = null;
+    }
+
+    @Override
+    public boolean isAvailable(Context context) {
+        if (context == null) {
+            return false;
+        }
+        return ContextCompat.checkSelfPermission(context, MODIFY_DAY_NIGHT_MODE)
+                == PERMISSION_GRANTED;
+    }
+
+    @Override
+    public ModeSectionView createView(Context context) {
+        mModeSectionView = (ModeSectionView) LayoutInflater.from(
+                context).inflate(R.layout.mode_section_view, /* root= */ null);
+        mModeSectionView.setViewListener(this::onViewActivated);
+        PowerManager pm = context.getSystemService(PowerManager.class);
+        mModeSectionView.setEnabled(!pm.isPowerSaveMode());
+        return mModeSectionView;
+    }
+
+    private void onViewActivated(Context context, boolean viewActivated) {
+        if (context == null) {
+            return;
+        }
+        Switch switchView = mModeSectionView.findViewById(R.id.dark_mode_toggle);
+        if (!switchView.isEnabled()) {
+            Toast disableToast = Toast.makeText(mContext,
+                    mContext.getString(R.string.mode_disabled_msg), Toast.LENGTH_SHORT);
+            disableToast.show();
+            return;
+        }
+        UiModeManager uiModeManager = context.getSystemService(UiModeManager.class);
+        uiModeManager.setNightModeActivated(viewActivated);
+    }
+
+    @Override
+    public void onBatterySaverStateChanged(boolean isEnabled) {
+        mModeSectionView.setEnabled(!isEnabled);
+    }
+}
diff --git a/src/com/android/customization/model/themedicon/ThemedIconSectionController.java b/src/com/android/customization/model/themedicon/ThemedIconSectionController.java
new file mode 100644
index 0000000..7b2d06a
--- /dev/null
+++ b/src/com/android/customization/model/themedicon/ThemedIconSectionController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model.themedicon;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.HubSectionController;
+import com.android.customization.picker.themedicon.ThemedIconSectionView;
+import com.android.wallpaper.R;
+
+/** The {@link HubSectionController} for themed icon section. */
+public class ThemedIconSectionController implements HubSectionController<ThemedIconSectionView> {
+
+    private final ThemedIconSwitchProvider mThemedIconOptionsProvider;
+    private final ThemedIconViewModel mThemedIconViewModel;
+
+    public ThemedIconSectionController(ThemedIconSwitchProvider themedIconOptionsProvider,
+            ThemedIconViewModel themedIconViewModel) {
+        mThemedIconOptionsProvider = themedIconOptionsProvider;
+        mThemedIconViewModel = themedIconViewModel;
+    }
+
+    @Override
+    public boolean isAvailable(@Nullable Context context) {
+        return context != null && mThemedIconOptionsProvider.isThemedIconAvailable();
+    }
+
+    @Override
+    public ThemedIconSectionView createView(Context context) {
+        ThemedIconSectionView themedIconColorSectionView =
+                (ThemedIconSectionView) LayoutInflater.from(context).inflate(
+                        R.layout.themed_icon_section_view, /* root= */ null);
+        themedIconColorSectionView.setViewListener(this::onViewActivated);
+        themedIconColorSectionView.getSwitch()
+                .setChecked(mThemedIconOptionsProvider.fetchThemedIconEnabled());
+        return themedIconColorSectionView;
+    }
+
+    private void onViewActivated(Context context, boolean viewActivated) {
+        if (context == null) {
+            return;
+        }
+        mThemedIconOptionsProvider.setThemedIconEnabled(viewActivated);
+        mThemedIconViewModel.getThemedIconEnabled().setValue(viewActivated);
+    }
+}
diff --git a/src/com/android/customization/picker/SectionView.java b/src/com/android/customization/picker/SectionView.java
new file mode 100644
index 0000000..c1fd21a
--- /dev/null
+++ b/src/com/android/customization/picker/SectionView.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * The SectionView base for views hosting in the {@link
+ * com.android.customization.picker.hub.HubFragment}.
+ */
+public abstract class SectionView extends LinearLayout {
+
+    /** The callback for the section view updates. */
+    public interface SectionViewListener {
+        void onViewActivated(@Nullable Context context, boolean viewActivated);
+    }
+
+    protected SectionViewListener mSectionViewListener;
+    private String mTitle;
+
+    public SectionView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setTitle(String title) {
+        mTitle = title;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /** Sets the listener to the {@code SectionView} instance for reacting the view changes. */
+    public void setViewListener(SectionViewListener sectionViewListener) {
+        mSectionViewListener = sectionViewListener;
+    }
+}
diff --git a/src/com/android/customization/picker/grid/GridSectionView.java b/src/com/android/customization/picker/grid/GridSectionView.java
new file mode 100644
index 0000000..12f7274
--- /dev/null
+++ b/src/com/android/customization/picker/grid/GridSectionView.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker.grid;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+
+import com.android.customization.picker.SectionView;
+
+/** The {@link SectionView} for app grid. */
+public final class GridSectionView extends SectionView {
+
+    public GridSectionView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+}
diff --git a/src/com/android/customization/picker/mode/ModeSectionView.java b/src/com/android/customization/picker/mode/ModeSectionView.java
new file mode 100644
index 0000000..353579a
--- /dev/null
+++ b/src/com/android/customization/picker/mode/ModeSectionView.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker.mode;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.customization.picker.SectionView;
+import com.android.wallpaper.R;
+
+/**
+ * The view of section in the Customization Hub fragment.
+ */
+public final class ModeSectionView extends SectionView {
+
+    private boolean mIsDarkModeActivated;
+
+    public ModeSectionView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        setTitle(context.getString(R.string.mode_title));
+        mIsDarkModeActivated = (context.getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_NIGHT_YES) != 0;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        Switch switchView = findViewById(R.id.dark_mode_toggle);
+        switchView.setChecked(mIsDarkModeActivated);
+        switchView.setOnCheckedChangeListener((buttonView, isChecked) ->
+                switchView.setChecked(mIsDarkModeActivated)
+        );
+        setOnClickListener(view -> modeToggleClicked());
+    }
+
+    private void modeToggleClicked() {
+        mIsDarkModeActivated = !mIsDarkModeActivated;
+        viewActivated(mIsDarkModeActivated);
+    }
+
+    private void viewActivated(boolean isChecked) {
+        if (mSectionViewListener != null) {
+            mSectionViewListener.onViewActivated(getContext(), isChecked);
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        final int numOfChildViews = getChildCount();
+        for (int i = 0; i < numOfChildViews; i++) {
+            getChildAt(i).setEnabled(enabled);
+        }
+    }
+}
diff --git a/src/com/android/customization/picker/themedicon/ThemedIconSectionView.java b/src/com/android/customization/picker/themedicon/ThemedIconSectionView.java
new file mode 100644
index 0000000..1280106
--- /dev/null
+++ b/src/com/android/customization/picker/themedicon/ThemedIconSectionView.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker.themedicon;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.customization.picker.SectionView;
+import com.android.wallpaper.R;
+
+/**
+ * The {@link SectionView} for themed icon section view
+ */
+public class ThemedIconSectionView extends SectionView {
+
+    private Switch mSwitchView;
+
+    public ThemedIconSectionView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        setTitle(context.getString(R.string.themed_icon_title));
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mSwitchView = findViewById(R.id.themed_icon_toggle);
+        setOnClickListener(v -> mSwitchView.toggle());
+        mSwitchView.setOnCheckedChangeListener((buttonView, isChecked) -> viewActivated(isChecked));
+    }
+
+    /** Gets the switch view. */
+    public Switch getSwitch() {
+        return mSwitchView;
+    }
+
+    private void viewActivated(boolean isChecked) {
+        if (mSectionViewListener != null) {
+            mSectionViewListener.onViewActivated(getContext(), isChecked);
+        }
+    }
+}