OmniControl: playing with overlays

Change-Id: Ie6f760ca463bc1622c65dcbe69948d1fadb1df30
diff --git a/.idea/misc.xml b/.idea/misc.xml
index b1a190f..fbd59d8 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,27 +3,42 @@
   <component name="DesignSurface">
     <option name="filePathToZoomLevelMap">
       <map>
+        <entry key="../../../../../../../layout/custom_preview.xml" value="0.5130208333333334" />
         <entry key="../../../../layout/custom_preview.xml" value="0.2526041666666667" />
         <entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.3067708333333333" />
         <entry key="app/src/main/res/drawable/applist_icon.xml" value="0.30885416666666665" />
         <entry key="app/src/main/res/drawable/bottom_navigation_selector.xml" value="0.3067708333333333" />
+        <entry key="app/src/main/res/drawable/check_circle.xml" value="0.5130208333333334" />
+        <entry key="app/src/main/res/drawable/check_circle_outline.xml" value="0.5130208333333334" />
+        <entry key="app/src/main/res/drawable/check_circle_shape.xml" value="0.30885416666666665" />
+        <entry key="app/src/main/res/drawable/enabled_selector.xml" value="0.5130208333333334" />
         <entry key="app/src/main/res/drawable/grid_item_background.xml" value="0.30833333333333335" />
         <entry key="app/src/main/res/drawable/grid_item_background_shape.xml" value="0.3104166666666667" />
         <entry key="app/src/main/res/drawable/ic_bars_tile.xml" value="0.30885416666666665" />
+        <entry key="app/src/main/res/drawable/ic_baseline_circle_24.xml" value="0.30885416666666665" />
         <entry key="app/src/main/res/drawable/ic_google.xml" value="0.5051282051282051" />
         <entry key="app/src/main/res/drawable/ic_homepage_bars.xml" value="0.5109375" />
+        <entry key="app/src/main/res/drawable/ic_info_outline.xml" value="0.5130208333333334" />
         <entry key="app/src/main/res/drawable/ic_launcher_background.xml" value="0.3067708333333333" />
         <entry key="app/src/main/res/drawable/ic_launcher_foreground.xml" value="0.2869791666666667" />
         <entry key="app/src/main/res/drawable/ic_lockscreen_tile.xml" value="0.5130208333333334" />
         <entry key="app/src/main/res/drawable/ic_settings_buttons.xml" value="0.3078125" />
         <entry key="app/src/main/res/drawable/ic_settings_more.xml" value="0.30885416666666665" />
         <entry key="app/src/main/res/drawable/ic_settings_omnigears.xml" value="0.3098958333333333" />
+        <entry key="app/src/main/res/drawable/ic_settings_overlays.xml" value="0.3078125" />
         <entry key="app/src/main/res/drawable/ic_system_update.xml" value="0.2786458333333333" />
         <entry key="app/src/main/res/drawable/ic_wallpaper.xml" value="0.3098958333333333" />
         <entry key="app/src/main/res/drawable/ic_weather.xml" value="0.5051282051282051" />
+        <entry key="app/src/main/res/drawable/overlay_item_background.xml" value="0.5109375" />
+        <entry key="app/src/main/res/drawable/overlay_item_background_shape.xml" value="0.5130208333333334" />
         <entry key="app/src/main/res/drawable/settings_icon.xml" value="0.30885416666666665" />
+        <entry key="app/src/main/res/layout/color_item.xml" value="0.3677536231884058" />
+        <entry key="app/src/main/res/layout/colors_item.xml" value="0.75" />
         <entry key="app/src/main/res/layout/grid_fragment.xml" value="0.3020833333333333" />
         <entry key="app/src/main/res/layout/grid_item.xml" value="0.29270833333333335" />
+        <entry key="app/src/main/res/layout/icon_shape_item.xml" value="0.9" />
+        <entry key="app/src/main/res/layout/overlays_fragment.xml" value="0.29270833333333335" />
+        <entry key="app/src/main/res/layout/overlays_item.xml" value="0.3423913043478261" />
         <entry key="app/src/main/res/layout/settings_activity.xml" value="0.259375" />
         <entry key="app/src/main/res/menu/bottom_navbar.xml" value="0.23541666666666666" />
         <entry key="app/src/main/res/menu/bottom_navigation.xml" value="0.2375" />
diff --git a/Android.bp b/Android.bp
index df8a12a..1bb22cd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7,6 +7,7 @@
 
   srcs: [
     "app/src/main/**/*.kt",
+    "app/src/main/java/org/omnirom/control/**/*.java",
   ],
 
   resource_dirs: [
@@ -18,6 +19,7 @@
     "androidx.appcompat_appcompat",
     "androidx.preference_preference",
     "com.google.android.material_material",
+    "androidx.recyclerview_recyclerview",
     "OmniLib",
   ],
 
diff --git a/app/build.gradle b/app/build.gradle
index cb09029..72e14f7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -41,4 +41,5 @@
     implementation 'androidx.appcompat:appcompat:1.3.1'
     implementation 'com.google.android.material:material:1.4.0'
     implementation 'androidx.preference:preference-ktx:1.1.1'
+    implementation 'androidx.recyclerview:recyclerview:1.1.0'
 }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index df1b0f0..f149f41 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -11,6 +11,8 @@
         tools:ignore="ProtectedPermissions" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
         tools:ignore="ProtectedPermissions" />
+    <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES"
+        tools:ignore="ProtectedPermissions" />
 
     <application
         android:allowBackup="true"
diff --git a/app/src/main/java/org/omnirom/control/GridViewFragment.kt b/app/src/main/java/org/omnirom/control/GridViewFragment.kt
index 1254d4b..c7e9764 100644
--- a/app/src/main/java/org/omnirom/control/GridViewFragment.kt
+++ b/app/src/main/java/org/omnirom/control/GridViewFragment.kt
@@ -185,6 +185,14 @@
         }
         gridItems.add(
             FragmentGridItem(
+                R.string.overlays_settings_title,
+                R.string.overlays_settings_summary,
+                R.drawable.ic_settings_overlays,
+                OverlaysFragment()
+            )
+        )
+        gridItems.add(
+            FragmentGridItem(
                 R.string.more_settings_title,
                 R.string.more_settings_summary,
                 R.drawable.ic_settings_more,
diff --git a/app/src/main/java/org/omnirom/control/OverlaysFragment.kt b/app/src/main/java/org/omnirom/control/OverlaysFragment.kt
new file mode 100644
index 0000000..bf91a74
--- /dev/null
+++ b/app/src/main/java/org/omnirom/control/OverlaysFragment.kt
@@ -0,0 +1,450 @@
+/*
+ *  Copyright (C) 2021 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package org.omnirom.control
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.res.Resources.NotFoundException
+import android.graphics.Path
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.PathShape
+import android.os.Bundle
+import android.os.PatternMatcher
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.graphics.PathParser
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import org.omnirom.control.widget.DynamicAdaptiveIconDrawable
+
+
+class OverlaysFragment : Fragment() {
+    private lateinit var overlayProvider: OverlaysProvider
+    private lateinit var iconShapeListView: RecyclerView
+    private lateinit var primaryColorsListView: RecyclerView
+    private lateinit var accentColorsListView: RecyclerView
+    private lateinit var overlayItemListView: RecyclerView
+
+    private val iconShapeGridItems = ArrayList<IconShapeGridItem>()
+    private val primaryColorsGridItems = ArrayList<ColorsGridItem>()
+    private val accentColorsGridItems = ArrayList<ColorsGridItem>()
+    private val overlayItemList = ArrayList<View>()
+
+    private val TAG = "OverlaysFragment"
+
+    val COLORS_NEUTRAL1 = arrayOf(
+        "system_neutral1_50",
+        "system_neutral1_100",
+        "system_neutral1_200",
+        "system_neutral1_300",
+        "system_neutral1_400",
+        "system_neutral1_500",
+        "system_neutral1_600",
+        "system_neutral1_700",
+        "system_neutral1_800",
+        "system_neutral1_900"
+    )
+
+    val COLORS_ACCENT1 = arrayOf(
+        "system_accent1_100",
+        "system_accent1_600"
+    )
+
+    abstract class GridItem(
+        packageName: String,
+        isEnabled: Boolean,
+        isSystem: Boolean,
+        category: String
+    ) {
+        val packageName: String = packageName
+        val isEnabled: Boolean = isEnabled
+        val isSystem: Boolean = isSystem
+        val category: String = category
+    }
+
+    class IconShapeGridItem(
+        packageName: String,
+        iconShape: String,
+        isEnabled: Boolean,
+        isSystem: Boolean,
+        category: String
+    ) : GridItem(packageName, isEnabled, isSystem, category) {
+        val iconShape: String = iconShape
+    }
+
+    class ColorsGridItem(
+        packageName: String,
+        colorList: ArrayList<Int>,
+        isEnabled: Boolean,
+        isSystem: Boolean,
+        category: String
+    ) : GridItem(packageName, isEnabled, isSystem, category) {
+        val colorList: ArrayList<Int> = colorList
+    }
+
+    inner class IconShapeListViewAdapter(
+        val context: Context,
+        val gridItems: ArrayList<IconShapeGridItem>
+    ) :
+        RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+        private val iconShapeSize = context.resources.getDimensionPixelSize(R.dimen.icon_shape_size)
+        private val PATH_SIZE = 100f
+
+        inner class IconShapeListItem(view: View) : RecyclerView.ViewHolder(view) {
+            val shapeImage: ImageView
+            val enabledImage: ImageView
+
+            init {
+                shapeImage = view.findViewById(R.id.icon_shape_icon)
+                enabledImage = view.findViewById(R.id.icon_shape_icon_enabled)
+            }
+        }
+
+        override fun getItemCount(): Int {
+            return gridItems.size
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+            return IconShapeListItem(
+                LayoutInflater.from(context).inflate(R.layout.icon_shape_item, parent, false)
+            )
+        }
+
+        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+            if (holder is IconShapeListItem) {
+                val gridItem: IconShapeGridItem = gridItems[position]
+                holder.shapeImage.setImageDrawable(
+                    createShapeIconDrawable(
+                        PathParser.createPathFromPathData(
+                            gridItem.iconShape
+                        ), context.resources.getDrawable(R.mipmap.ic_launcher, null)
+                    )
+                )
+                if (gridItem.isEnabled || (gridItem.isSystem && !isOverlayEnabled())) {
+                    holder.enabledImage.visibility =
+                        View.VISIBLE
+                } else {
+                    holder.enabledImage.visibility =
+                        View.GONE
+                }
+                holder.itemView.setOnClickListener {
+                    if (gridItem.isSystem) {
+                        overlayProvider.disableAllInCategory(gridItem.category)
+                    } else if (!gridItem.isEnabled) {
+                        overlayProvider.enableOverlayExclusive(gridItem.packageName)
+                    }
+                    loadIconShapeOverlays()
+                    notifyDataSetChanged()
+                }
+            }
+        }
+
+        private fun createShapeDrawable(path: Path): ShapeDrawable {
+            val shape = PathShape(path, PATH_SIZE, PATH_SIZE)
+            val shapeDrawable = ShapeDrawable(shape)
+            shapeDrawable.intrinsicHeight = iconShapeSize
+            shapeDrawable.intrinsicWidth = iconShapeSize
+            return shapeDrawable
+        }
+
+        private fun createShapeIconDrawable(path: Path, appIcon: Drawable): Drawable {
+            if (appIcon is AdaptiveIconDrawable) {
+                return DynamicAdaptiveIconDrawable(
+                    appIcon.background,
+                    appIcon.foreground, path
+                )
+            }
+            return createShapeDrawable(path)
+        }
+
+        private fun isOverlayEnabled(): Boolean {
+            return gridItems.any { it.isEnabled }
+        }
+    }
+
+    inner class ColorsListViewAdapter(
+        val context: Context,
+        val gridItems: ArrayList<ColorsGridItem>,
+        val isPrimary: Boolean
+    ) :
+        RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+
+        inner class ColorsListItem(view: View) : RecyclerView.ViewHolder(view) {
+            val colorList: LinearLayout = view.findViewById(R.id.colors_item_list)
+            val enabledImage: ImageView = view.findViewById(R.id.colors_enabled)
+        }
+
+        override fun getItemCount(): Int {
+            return gridItems.size
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+            return ColorsListItem(
+                LayoutInflater.from(context).inflate(R.layout.colors_item, parent, false)
+            )
+        }
+
+        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+            if (holder is ColorsListItem) {
+                val gridItem: ColorsGridItem = gridItems[position]
+                holder.colorList.removeAllViews()
+
+                for (color in gridItem.colorList) {
+                    val colorItem: ImageView =
+                        LayoutInflater.from(context).inflate(R.layout.color_item, null) as ImageView
+                    colorItem.setImageDrawable(createColorDrawable(color))
+                    holder.colorList.addView(
+                        colorItem,
+                        context.resources.getDimensionPixelSize(R.dimen.color_item_width),
+                        context.resources.getDimensionPixelSize(R.dimen.color_item_height)
+                    )
+                }
+
+                if (gridItem.isEnabled || (gridItem.isSystem && !isOverlayEnabled())) {
+                    holder.enabledImage.visibility =
+                        View.VISIBLE
+                } else {
+                    holder.enabledImage.visibility =
+                        View.GONE
+                }
+                holder.itemView.setOnClickListener {
+                    val gridItem: ColorsGridItem = gridItems.get(position)
+                    if (gridItem.isSystem) {
+                        overlayProvider.disableAllInCategory(gridItem.category)
+                    } else if (!gridItem.isEnabled) {
+                        overlayProvider.enableOverlayExclusive(gridItem.packageName)
+                    }
+                    if (isPrimary) {
+                        loadPrimaryColorsOverlays()
+                    } else {
+                        loadAccentColorsOverlays()
+                    }
+                    notifyDataSetChanged()
+                }
+            }
+        }
+
+        private fun createColorDrawable(color: Int): ColorDrawable {
+            return ColorDrawable(color)
+        }
+
+        private fun isOverlayEnabled(): Boolean {
+            return gridItems.any { it.isEnabled }
+        }
+    }
+
+    inner class OverlayItemListViewAdapter(
+        val context: Context,
+        val overlayItems: ArrayList<View>
+    ) :
+        RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+
+        inner class OverlayListItem(view: View) : RecyclerView.ViewHolder(view) {
+        }
+
+        override fun getItemCount(): Int {
+            return overlayItems.size
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+            return OverlayListItem(overlayItems[0])
+        }
+
+        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+        }
+    }
+
+    fun getFragmentTitle(): String {
+        return resources.getString(R.string.overlays_settings_title)
+    }
+
+    fun getFragmentSummary(): String {
+        return resources.getString(R.string.overlays_settings_summary)
+    }
+
+    fun getFragmentIcon(): Int {
+        return R.drawable.ic_settings_overlays
+    }
+
+    override fun onResume() {
+        super.onResume()
+        (activity as? AppCompatActivity)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
+        (activity as? SettingsActivity)?.updateFragmentTitle(
+            getFragmentTitle(),
+            getFragmentSummary(),
+            getFragmentIcon()
+        )
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        overlayProvider = OverlaysProvider(requireContext())
+        overlayProvider.init()
+        return inflater.inflate(R.layout.overlays_fragment, container, false)
+    }
+
+    private fun loadIconShapeOverlays() {
+        iconShapeGridItems.clear();
+        iconShapeGridItems.add(
+            IconShapeGridItem(
+                "android",
+                overlayProvider.getSystemIconMask(),
+                false,
+                true,
+                overlayProvider.getIconShapeCategory()
+            )
+        )
+        for (packageName in overlayProvider.getIconShapeOverlays()) {
+            try {
+                val iconMask = overlayProvider.loadString("config_icon_mask", packageName)
+                iconShapeGridItems.add(
+                    IconShapeGridItem(
+                        packageName,
+                        iconMask,
+                        overlayProvider.isOverlayEnabled(packageName),
+                        false,
+                        overlayProvider.getIconShapeCategory()
+
+                    )
+                )
+            } catch (e: NotFoundException) {
+            }
+        }
+    }
+
+    private fun loadPrimaryColorsOverlays() {
+        primaryColorsGridItems.clear();
+        val colorList = ArrayList<Int>()
+        for (color in COLORS_NEUTRAL1) {
+            colorList.add(overlayProvider.getSystemColor(color))
+        }
+        primaryColorsGridItems.add(
+            ColorsGridItem(
+                "android",
+                colorList,
+                false,
+                true,
+                overlayProvider.getPrimaryColorCategory()
+            )
+        )
+        for (packageName in overlayProvider.getPrimaryColorOverlays()) {
+            try {
+                val colorList = ArrayList<Int>()
+                for (color in COLORS_NEUTRAL1) {
+                    colorList.add(overlayProvider.loadColor(color, packageName))
+                }
+
+                primaryColorsGridItems.add(
+                    ColorsGridItem(
+                        packageName,
+                        colorList,
+                        overlayProvider.isOverlayEnabled(packageName),
+                        false,
+                        overlayProvider.getPrimaryColorCategory()
+                    )
+                )
+            } catch (e: NotFoundException) {
+            }
+        }
+    }
+
+    private fun loadAccentColorsOverlays() {
+        accentColorsGridItems.clear();
+        val colorList = ArrayList<Int>()
+        for (color in COLORS_ACCENT1) {
+            colorList.add(overlayProvider.getSystemColor(color))
+        }
+        accentColorsGridItems.add(
+            ColorsGridItem(
+                "android",
+                colorList,
+                false,
+                true,
+                overlayProvider.getAccentColorCategory()
+            )
+        )
+        for (packageName in overlayProvider.getAccentColorOverlays()) {
+            try {
+                val colorList = ArrayList<Int>()
+                for (color in COLORS_ACCENT1) {
+                    colorList.add(overlayProvider.loadColor(color, packageName))
+                }
+
+                accentColorsGridItems.add(
+                    ColorsGridItem(
+                        packageName,
+                        colorList,
+                        overlayProvider.isOverlayEnabled(packageName),
+                        false,
+                        overlayProvider.getAccentColorCategory()
+                    )
+                )
+            } catch (e: NotFoundException) {
+            }
+        }
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        loadIconShapeOverlays()
+        loadPrimaryColorsOverlays()
+        loadAccentColorsOverlays()
+
+        overlayItemList.clear()
+        val overlayItemView =
+            LayoutInflater.from(context).inflate(R.layout.overlays_item, null, false)
+        overlayItemList.add(overlayItemView)
+
+        iconShapeListView = overlayItemView.findViewById(R.id.icon_shape_list_view)
+        iconShapeListView.layoutManager =
+            LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
+        iconShapeListView.adapter = IconShapeListViewAdapter(requireContext(), iconShapeGridItems)
+
+        primaryColorsListView = overlayItemView.findViewById(R.id.primary_color_list_view)
+        primaryColorsListView.layoutManager =
+            LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
+        primaryColorsListView.adapter =
+            ColorsListViewAdapter(requireContext(), primaryColorsGridItems, true)
+
+        accentColorsListView = overlayItemView.findViewById(R.id.accent_color_list_view)
+        accentColorsListView.layoutManager =
+            LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
+        accentColorsListView.adapter =
+            ColorsListViewAdapter(requireContext(), accentColorsGridItems, false)
+
+        overlayItemListView = view.findViewById(R.id.overlay_item_list)
+        overlayItemListView.adapter = OverlayItemListViewAdapter(requireContext(), overlayItemList)
+        overlayItemListView.layoutManager =
+            LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
+    }
+}
diff --git a/app/src/main/java/org/omnirom/control/OverlaysProvider.kt b/app/src/main/java/org/omnirom/control/OverlaysProvider.kt
new file mode 100644
index 0000000..2e46d03
--- /dev/null
+++ b/app/src/main/java/org/omnirom/control/OverlaysProvider.kt
@@ -0,0 +1,151 @@
+/*
+ *  Copyright (C) 2021 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package org.omnirom.control
+
+import android.content.Context
+import android.content.om.OverlayInfo
+import android.content.om.OverlayManager
+import android.content.res.Resources
+import android.os.UserHandle
+import android.util.Log
+
+class OverlaysProvider(context: Context) {
+    private val overlaysMap = HashMap<String, OverlayInfo>()
+    private val TAG = "OverlayProvider"
+    private val context = context
+    private var om: OverlayManager? = null
+    private lateinit var user: UserHandle
+
+    fun init() {
+        user = UserHandle.of(UserHandle.myUserId())
+        om = context.getSystemService(OverlayManager::class.java)
+        loadOverlayInfo()
+    }
+
+    private fun loadOverlayInfo() {
+        if (om != null) {
+            overlaysMap.clear()
+            val user: UserHandle = UserHandle.of(UserHandle.myUserId())
+            om!!.getOverlayInfosForTarget("android", user).forEach { overlayInfo ->
+                overlaysMap.put(overlayInfo.getPackageName(), overlayInfo)
+            }
+        }
+    }
+
+    fun getIconShapeOverlays(): ArrayList<String> {
+        return getCategoryOverlays(getIconShapeCategory())
+    }
+
+    fun getAccentColorOverlays(): ArrayList<String> {
+        return getCategoryOverlays(getAccentColorCategory())
+    }
+
+    fun getPrimaryColorOverlays(): ArrayList<String> {
+        return getCategoryOverlays(getPrimaryColorCategory())
+    }
+
+    fun getIconShapeCategory(): String {
+        return "android.theme.customization.adaptive_icon_shape"
+    }
+
+    fun getAccentColorCategory(): String {
+        return "android.theme.customization.accent_color"
+    }
+
+    fun getPrimaryColorCategory(): String {
+        return "android.theme.customization.system_palette"
+    }
+
+    fun getCategoryOverlays(category: String): ArrayList<String> {
+        val packageList = ArrayList<String>()
+        overlaysMap.values.filter { it.getCategory() == category }
+            .forEach { packageList.add(it.getPackageName()) }
+        return packageList
+    }
+
+    @Throws(Resources.NotFoundException::class)
+    fun loadString(stringName: String, packageName: String): String {
+        val overlayRes: Resources = context.packageManager.getResourcesForApplication(
+            packageName
+        )
+        return overlayRes.getString(overlayRes.getIdentifier(stringName, "string", packageName))
+    }
+
+    @Throws(Resources.NotFoundException::class)
+    fun loadColor(colorName: String, packageName: String): Int {
+        val overlayRes: Resources = context.packageManager
+            .getResourcesForApplication(packageName)
+        return overlayRes.getColor(
+            overlayRes.getIdentifier(colorName, "color", packageName),
+            null
+        )
+    }
+
+    fun getSystemIconMask(): String {
+        val system: Resources = Resources.getSystem()
+        return system.getString(
+            system.getIdentifier(
+                "config_icon_mask",
+                "string", "android"
+            )
+        )
+    }
+
+    fun getSystemColor(colorName: String): Int {
+        val system: Resources = Resources.getSystem()
+        return system.getColor(
+            system.getIdentifier(
+                colorName,
+                "color", "android"
+            ), null
+        )
+    }
+
+    fun isOverlayEnabled(packageName: String): Boolean {
+        val overlayInfo = overlaysMap.get(packageName)
+        if (overlayInfo != null) {
+            return overlayInfo.isEnabled()
+        }
+        return false
+    }
+
+    fun enableOverlayExclusive(packageName: String) {
+        if (om != null) {
+            om!!.setEnabledExclusiveInCategory(packageName, user)
+        }
+    }
+
+    fun enableOverlay(packageName: String, reload: Boolean) {
+        if (om != null) {
+            om!!.setEnabled(packageName, true, user)
+            if (reload) loadOverlayInfo()
+        }
+    }
+
+    fun disableOverlay(packageName: String, reload: Boolean) {
+        if (om != null) {
+            om!!.setEnabled(packageName, false, user)
+            if (reload) loadOverlayInfo()
+        }
+    }
+
+    fun disableAllInCategory(category: String) {
+        getCategoryOverlays(category).forEach { disableOverlay(it, false) }
+        loadOverlayInfo()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/omnirom/control/SettingsActivity.kt b/app/src/main/java/org/omnirom/control/SettingsActivity.kt
index a9f7b58..55965e7 100644
--- a/app/src/main/java/org/omnirom/control/SettingsActivity.kt
+++ b/app/src/main/java/org/omnirom/control/SettingsActivity.kt
@@ -33,7 +33,6 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        //setTheme(android.R.style.Theme_DeviceDefault_Settings)
         setContentView(R.layout.settings_activity)
 
         var fragment: Fragment = GridViewFragment()
diff --git a/app/src/main/java/org/omnirom/control/widget/DynamicAdaptiveIconDrawable.java b/app/src/main/java/org/omnirom/control/widget/DynamicAdaptiveIconDrawable.java
new file mode 100644
index 0000000..2905d24
--- /dev/null
+++ b/app/src/main/java/org/omnirom/control/widget/DynamicAdaptiveIconDrawable.java
@@ -0,0 +1,934 @@
+package org.omnirom.control.widget;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * This is basically a copy of {@link AdaptiveIconDrawable} but which allows a custom path for
+ * the icon mask instead of using the one defined in the system.
+ * Note: Unlike AdaptiveIconDrawable we don't need to deal with densityOverride here so that
+ * logic is omitted.
+ */
+public class DynamicAdaptiveIconDrawable extends Drawable implements Drawable.Callback {
+
+    /**
+     * Mask path is defined inside device configuration in following dimension: [100 x 100]
+     */
+    private static final float MASK_SIZE = 100f;
+
+    /**
+     * All four sides of the layers are padded with extra inset so as to provide
+     * extra content to reveal within the clip path when performing affine transformations on the
+     * layers.
+     *
+     * Each layers will reserve 25% of it's width and height.
+     *
+     * As a result, the view port of the layers is smaller than their intrinsic width and height.
+     */
+    private static final float EXTRA_INSET_PERCENTAGE = 1 / 4f;
+    private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
+
+    private final Path mOriginalMask;
+
+    /**
+     * Scaled mask based on the view bounds.
+     */
+    private final Path mMask;
+    private final Path mMaskScaleOnly;
+    private final Matrix mMaskMatrix;
+    private final Region mTransparentRegion;
+
+    /**
+     * Indices used to access {@link #mLayerState.mChildren} array for foreground and
+     * background layer.
+     */
+    private static final int BACKGROUND_ID = 0;
+    private static final int FOREGROUND_ID = 1;
+
+    /**
+     * State variable that maintains the {@link ChildDrawable} array.
+     */
+    private LayerState mLayerState;
+
+    private Shader mLayersShader;
+    private Bitmap mLayersBitmap;
+
+    private final Rect mTmpOutRect = new Rect();
+    private Rect mHotspotBounds;
+    private boolean mMutated;
+
+    private boolean mSuspendChildInvalidation;
+    private boolean mChildRequestedInvalidation;
+    private final Canvas mCanvas;
+    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
+            Paint.FILTER_BITMAP_FLAG);
+
+    /**
+     * Constructor used for xml inflation.
+     */
+    DynamicAdaptiveIconDrawable() {
+        this((LayerState) null, null, null);
+    }
+
+    /**
+     * The one constructor to rule them all. This is called by all public
+     * constructors to set the state and initialize local properties.
+     */
+    private DynamicAdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res,
+            Path iconMask) {
+        mLayerState = createConstantState(state, res);
+
+        mOriginalMask = iconMask;
+        mMask = new Path(iconMask);
+        mMaskScaleOnly = new Path(mMask);
+        mMaskMatrix = new Matrix();
+        mCanvas = new Canvas();
+        mTransparentRegion = new Region();
+    }
+
+    private ChildDrawable createChildDrawable(Drawable drawable) {
+        final ChildDrawable layer = new ChildDrawable(mLayerState.mDensity);
+        layer.mDrawable = drawable;
+        layer.mDrawable.setCallback(this);
+        mLayerState.mChildrenChangingConfigurations |=
+                layer.mDrawable.getChangingConfigurations();
+        return layer;
+    }
+
+    private LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) {
+        return new LayerState(state, this, res);
+    }
+
+    /**
+     * Constructor used to dynamically create this drawable.
+     *
+     * @param backgroundDrawable drawable that should be rendered in the background
+     * @param foregroundDrawable drawable that should be rendered in the foreground
+     * @param iconMask path to use to mask the icon
+     */
+    public DynamicAdaptiveIconDrawable(Drawable backgroundDrawable,
+            Drawable foregroundDrawable, Path iconMask) {
+        this((LayerState)null, null, iconMask);
+        if (backgroundDrawable != null) {
+            addLayer(BACKGROUND_ID, createChildDrawable(backgroundDrawable));
+        }
+        if (foregroundDrawable != null) {
+            addLayer(FOREGROUND_ID, createChildDrawable(foregroundDrawable));
+        }
+    }
+
+    /**
+     * Sets the layer to the {@param index} and invalidates cache.
+     *
+     * @param index The index of the layer.
+     * @param layer The layer to add.
+     */
+    private void addLayer(int index, @NonNull ChildDrawable layer) {
+        mLayerState.mChildren[index] = layer;
+        mLayerState.invalidateCache();
+    }
+
+    @Override
+    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        super.inflate(r, parser, attrs, theme);
+
+        final LayerState state = mLayerState;
+        if (state == null) {
+            return;
+        }
+
+        inflateLayers(r, parser, attrs, theme);
+    }
+
+    /**
+     * When called before the bound is set, the returned path is identical to
+     * R.string.config_icon_mask. After the bound is set, the
+     * returned path's computed bound is same as the #getBounds().
+     *
+     * @return the mask path object used to clip the drawable
+     */
+    public Path getIconMask() {
+        return mMask;
+    }
+
+    /**
+     * Returns the foreground drawable managed by this class.
+     *
+     * @return the foreground drawable managed by this drawable
+     */
+    public Drawable getForeground() {
+        return mLayerState.mChildren[FOREGROUND_ID].mDrawable;
+    }
+
+    /**
+     * Returns the foreground drawable managed by this class. The bound of this drawable is
+     * extended by {@link #getExtraInsetFraction()} * getBounds().width on left/right sides and by
+     * {@link #getExtraInsetFraction()} * getBounds().height on top/bottom sides.
+     *
+     * @return the background drawable managed by this drawable
+     */
+    public Drawable getBackground() {
+        return mLayerState.mChildren[BACKGROUND_ID].mDrawable;
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (bounds.isEmpty()) {
+            return;
+        }
+        updateLayerBounds(bounds);
+    }
+
+    private void updateLayerBounds(Rect bounds) {
+        if (bounds.isEmpty()) {
+            return;
+        }
+        try {
+            suspendChildInvalidation();
+            updateLayerBoundsInternal(bounds);
+            updateMaskBoundsInternal(bounds);
+        } finally {
+            resumeChildInvalidation();
+        }
+    }
+
+    /**
+     * Set the child layer bounds bigger than the view port size by {@link #DEFAULT_VIEW_PORT_SCALE}
+     */
+    private void updateLayerBoundsInternal(Rect bounds) {
+        int cX = bounds.width() / 2;
+        int cY = bounds.height() / 2;
+
+        for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
+            final ChildDrawable r = mLayerState.mChildren[i];
+            if (r == null) {
+                continue;
+            }
+            final Drawable d = r.mDrawable;
+            if (d == null) {
+                continue;
+            }
+
+            int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            final Rect outRect = mTmpOutRect;
+            outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
+
+            d.setBounds(outRect);
+        }
+    }
+
+    private void updateMaskBoundsInternal(Rect b) {
+        // reset everything that depends on the view bounds
+        mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+        mOriginalMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+        mMaskMatrix.postTranslate(b.left, b.top);
+        mOriginalMask.transform(mMaskMatrix, mMask);
+
+        if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+                || mLayersBitmap.getHeight() != b.height()) {
+            mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
+        }
+
+        mPaint.setShader(null);
+        mTransparentRegion.setEmpty();
+        mLayersShader = null;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mLayersBitmap == null) {
+            return;
+        }
+        if (mLayersShader == null) {
+            mCanvas.setBitmap(mLayersBitmap);
+            mCanvas.drawColor(Color.BLACK);
+            for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+                if (mLayerState.mChildren[i] == null) {
+                    continue;
+                }
+                final Drawable dr = mLayerState.mChildren[i].mDrawable;
+                if (dr != null) {
+                    dr.draw(mCanvas);
+                }
+            }
+            mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
+            mPaint.setShader(mLayersShader);
+        }
+        if (mMaskScaleOnly != null) {
+            Rect bounds = getBounds();
+            canvas.translate(bounds.left, bounds.top);
+            canvas.drawPath(mMaskScaleOnly, mPaint);
+            canvas.translate(-bounds.left, -bounds.top);
+        }
+    }
+
+    @Override
+    public void invalidateSelf() {
+        mLayersShader = null;
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void getOutline(@NonNull Outline outline) {
+        outline.setConvexPath(mMask);
+    }
+
+    @Override
+    public @Nullable Region getTransparentRegion() {
+        if (mTransparentRegion.isEmpty()) {
+            mMask.toggleInverseFillType();
+            mTransparentRegion.set(getBounds());
+            mTransparentRegion.setPath(mMask, mTransparentRegion);
+            mMask.toggleInverseFillType();
+        }
+        return mTransparentRegion;
+    }
+
+    @Override
+    public void applyTheme(@NonNull Theme t) {
+        super.applyTheme(t);
+
+        final LayerState state = mLayerState;
+        if (state == null) {
+            return;
+        }
+
+        final ChildDrawable[] array = state.mChildren;
+        for (int i = 0; i < state.N_CHILDREN; i++) {
+            final ChildDrawable layer = array[i];
+
+            final Drawable d = layer.mDrawable;
+            if (d != null && d.canApplyTheme()) {
+                d.applyTheme(t);
+
+                // Update cached mask of child changing configurations.
+                state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
+            }
+        }
+    }
+
+    /**
+     * Inflates child layers using the specified parser.
+     */
+    private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final LayerState state = mLayerState;
+
+        final int innerDepth = parser.getDepth() + 1;
+        int type;
+        int depth;
+        int childIndex = 0;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            if (depth > innerDepth) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals("background")) {
+                childIndex = BACKGROUND_ID;
+            } else if (tagName.equals("foreground")) {
+                childIndex = FOREGROUND_ID;
+            } else {
+                continue;
+            }
+
+            final ChildDrawable layer = new ChildDrawable(state.mDensity);
+
+            // If the layer doesn't have a drawable or unresolved theme
+            // attribute for a drawable, attempt to parse one from the child
+            // element. If multiple child elements exist, we'll only use the
+            // first one.
+            if (layer.mDrawable == null && (layer.mThemeAttrs == null)) {
+                while ((type = parser.next()) == XmlPullParser.TEXT) {
+                }
+                if (type != XmlPullParser.START_TAG) {
+                    throw new XmlPullParserException(parser.getPositionDescription()
+                            + ": <foreground> or <background> tag requires a 'drawable'"
+                            + "attribute or child tag defining a drawable");
+                }
+
+                // We found a child drawable. Take ownership.
+                layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
+                layer.mDrawable.setCallback(this);
+                state.mChildrenChangingConfigurations |=
+                        layer.mDrawable.getChangingConfigurations();
+            }
+            addLayer(childIndex, layer);
+        }
+    }
+
+    @Override
+    public boolean canApplyTheme() {
+        return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
+    }
+
+    @Override
+    public boolean isProjected() {
+        if (super.isProjected()) {
+            return true;
+        }
+
+        final ChildDrawable[] layers = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            if (layers[i].mDrawable != null && layers[i].mDrawable.isProjected()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Temporarily suspends child invalidation.
+     *
+     * @see #resumeChildInvalidation()
+     */
+    private void suspendChildInvalidation() {
+        mSuspendChildInvalidation = true;
+    }
+
+    /**
+     * Resumes child invalidation after suspension, immediately performing an
+     * invalidation if one was requested by a child during suspension.
+     *
+     * @see #suspendChildInvalidation()
+     */
+    private void resumeChildInvalidation() {
+        mSuspendChildInvalidation = false;
+
+        if (mChildRequestedInvalidation) {
+            mChildRequestedInvalidation = false;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void invalidateDrawable(@NonNull Drawable who) {
+        if (mSuspendChildInvalidation) {
+            mChildRequestedInvalidation = true;
+        } else {
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    @Override
+    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+        unscheduleSelf(what);
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
+    }
+
+    @Override
+    public void setHotspot(float x, float y) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setHotspot(x, y);
+            }
+        }
+    }
+
+    @Override
+    public void setHotspotBounds(int left, int top, int right, int bottom) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setHotspotBounds(left, top, right, bottom);
+            }
+        }
+
+        if (mHotspotBounds == null) {
+            mHotspotBounds = new Rect(left, top, right, bottom);
+        } else {
+            mHotspotBounds.set(left, top, right, bottom);
+        }
+    }
+
+    @Override
+    public void getHotspotBounds(Rect outRect) {
+        if (mHotspotBounds != null) {
+            outRect.set(mHotspotBounds);
+        } else {
+            super.getHotspotBounds(outRect);
+        }
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        final boolean changed = super.setVisible(visible, restart);
+        final ChildDrawable[] array = mLayerState.mChildren;
+
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setVisible(visible, restart);
+            }
+        }
+
+        return changed;
+    }
+
+    @Override
+    public void setDither(boolean dither) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setDither(dither);
+            }
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+    }
+
+    @Override
+    public int getAlpha() {
+        return mPaint.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setColorFilter(colorFilter);
+            }
+        }
+    }
+
+    @Override
+    public void setTintList(ColorStateList tint) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        final int N = mLayerState.N_CHILDREN;
+        for (int i = 0; i < N; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setTintList(tint);
+            }
+        }
+    }
+
+    @Override
+    public void setTintMode(Mode tintMode) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        final int N = mLayerState.N_CHILDREN;
+        for (int i = 0; i < N; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setTintMode(tintMode);
+            }
+        }
+    }
+
+    public void setOpacity(int opacity) {
+        mLayerState.mOpacityOverride = opacity;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        mLayerState.mAutoMirrored = mirrored;
+
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setAutoMirrored(mirrored);
+            }
+        }
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mLayerState.mAutoMirrored;
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.jumpToCurrentState();
+            }
+        }
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mLayerState.isStateful();
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        boolean changed = false;
+
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null && dr.isStateful() && dr.setState(state)) {
+                changed = true;
+            }
+        }
+
+        if (changed) {
+            updateLayerBounds(getBounds());
+        }
+
+        return changed;
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        boolean changed = false;
+
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null && dr.setLevel(level)) {
+                changed = true;
+            }
+        }
+
+        if (changed) {
+            updateLayerBounds(getBounds());
+        }
+
+        return changed;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return (int)(getMaxIntrinsicWidth() * DEFAULT_VIEW_PORT_SCALE);
+    }
+
+    private int getMaxIntrinsicWidth() {
+        int width = -1;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final ChildDrawable r = mLayerState.mChildren[i];
+            if (r.mDrawable == null) {
+                continue;
+            }
+            final int w = r.mDrawable.getIntrinsicWidth();
+            if (w > width) {
+                width = w;
+            }
+        }
+        return width;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return (int)(getMaxIntrinsicHeight() * DEFAULT_VIEW_PORT_SCALE);
+    }
+
+    private int getMaxIntrinsicHeight() {
+        int height = -1;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final ChildDrawable r = mLayerState.mChildren[i];
+            if (r.mDrawable == null) {
+                continue;
+            }
+            final int h = r.mDrawable.getIntrinsicHeight();
+            if (h > height) {
+                height = h;
+            }
+        }
+        return height;
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        if (mLayerState.canConstantState()) {
+            mLayerState.mChangingConfigurations = getChangingConfigurations();
+            return mLayerState;
+        }
+        return null;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            mLayerState = createConstantState(mLayerState, null);
+            for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+                final Drawable dr = mLayerState.mChildren[i].mDrawable;
+                if (dr != null) {
+                    dr.mutate();
+                }
+            }
+            mMutated = true;
+        }
+        return this;
+    }
+
+    static class ChildDrawable {
+        public Drawable mDrawable;
+        public int[] mThemeAttrs;
+        public int mDensity = DisplayMetrics.DENSITY_DEFAULT;
+
+        ChildDrawable(int density) {
+            mDensity = density;
+        }
+
+        ChildDrawable(@NonNull ChildDrawable orig, @NonNull DynamicAdaptiveIconDrawable owner,
+                @Nullable Resources res) {
+
+            final Drawable dr = orig.mDrawable;
+            final Drawable clone;
+            if (dr != null) {
+                final ConstantState cs = dr.getConstantState();
+                if (cs == null) {
+                    clone = dr;
+                } else if (res != null) {
+                    clone = cs.newDrawable(res);
+                } else {
+                    clone = cs.newDrawable();
+                }
+                clone.setCallback(owner);
+                clone.setBounds(dr.getBounds());
+                clone.setLevel(dr.getLevel());
+            } else {
+                clone = null;
+            }
+
+            mDrawable = clone;
+            mThemeAttrs = orig.mThemeAttrs;
+        }
+
+        public boolean canApplyTheme() {
+            return mThemeAttrs != null
+                    || (mDrawable != null && mDrawable.canApplyTheme());
+        }
+
+        public final void setDensity(int targetDensity) {
+            if (mDensity != targetDensity) {
+                mDensity = targetDensity;
+            }
+        }
+    }
+
+    static class LayerState extends ConstantState {
+        private final DynamicAdaptiveIconDrawable mOwner;
+
+        private int[] mThemeAttrs;
+
+        final static int N_CHILDREN = 2;
+        ChildDrawable[] mChildren;
+
+        // The density at which to render the drawable and its children.
+        int mDensity;
+
+        // The density to use when inflating/looking up the children drawables. A value of 0 means
+        // use the system's density.
+        int mSrcDensityOverride = 0;
+
+        int mOpacityOverride = PixelFormat.UNKNOWN;
+
+        int mChangingConfigurations;
+        int mChildrenChangingConfigurations;
+
+        private boolean mCheckedOpacity;
+        private int mOpacity;
+
+        private boolean mCheckedStateful;
+        private boolean mIsStateful;
+        private boolean mAutoMirrored = false;
+
+        LayerState(@Nullable LayerState orig, @NonNull DynamicAdaptiveIconDrawable owner,
+                @Nullable Resources res) {
+            mOwner = owner;
+            mChildren = new ChildDrawable[N_CHILDREN];
+            if (orig != null) {
+                final ChildDrawable[] origChildDrawable = orig.mChildren;
+
+                mChangingConfigurations = orig.mChangingConfigurations;
+                mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
+
+                for (int i = 0; i < N_CHILDREN; i++) {
+                    final ChildDrawable or = origChildDrawable[i];
+                    mChildren[i] = new ChildDrawable(or, mOwner, res);
+                }
+
+                mCheckedOpacity = orig.mCheckedOpacity;
+                mOpacity = orig.mOpacity;
+                mCheckedStateful = orig.mCheckedStateful;
+                mIsStateful = orig.mIsStateful;
+                mAutoMirrored = orig.mAutoMirrored;
+                mThemeAttrs = orig.mThemeAttrs;
+                mOpacityOverride = orig.mOpacityOverride;
+                mSrcDensityOverride = orig.mSrcDensityOverride;
+            } else {
+                for (int i = 0; i < N_CHILDREN; i++) {
+                    mChildren[i] = new ChildDrawable(mDensity);
+                }
+            }
+        }
+
+        public final void setDensity(int targetDensity) {
+            if (mDensity != targetDensity) {
+                mDensity = targetDensity;
+            }
+        }
+
+        @Override
+        public boolean canApplyTheme() {
+            if (mThemeAttrs != null || super.canApplyTheme()) {
+                return true;
+            }
+
+            final ChildDrawable[] array = mChildren;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                final ChildDrawable layer = array[i];
+                if (layer.canApplyTheme()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new DynamicAdaptiveIconDrawable(mOwner.getBackground(), mOwner.getForeground(),
+                    mOwner.mOriginalMask);
+        }
+
+        @Override
+        public Drawable newDrawable(@Nullable Resources res) {
+            return newDrawable();
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return mChangingConfigurations
+                    | mChildrenChangingConfigurations;
+        }
+
+        public final int getOpacity() {
+            if (mCheckedOpacity) {
+                return mOpacity;
+            }
+
+            final ChildDrawable[] array = mChildren;
+
+            // Seek to the first non-null drawable.
+            int firstIndex = -1;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                if (array[i].mDrawable != null) {
+                    firstIndex = i;
+                    break;
+                }
+            }
+
+            int op;
+            if (firstIndex >= 0) {
+                op = array[firstIndex].mDrawable.getOpacity();
+            } else {
+                op = PixelFormat.TRANSPARENT;
+            }
+
+            // Merge all remaining non-null drawables.
+            for (int i = firstIndex + 1; i < N_CHILDREN; i++) {
+                final Drawable dr = array[i].mDrawable;
+                if (dr != null) {
+                    op = Drawable.resolveOpacity(op, dr.getOpacity());
+                }
+            }
+
+            mOpacity = op;
+            mCheckedOpacity = true;
+            return op;
+        }
+
+        public final boolean isStateful() {
+            if (mCheckedStateful) {
+                return mIsStateful;
+            }
+
+            final ChildDrawable[] array = mChildren;
+            boolean isStateful = false;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                final Drawable dr = array[i].mDrawable;
+                if (dr != null && dr.isStateful()) {
+                    isStateful = true;
+                    break;
+                }
+            }
+
+            mIsStateful = isStateful;
+            mCheckedStateful = true;
+            return isStateful;
+        }
+
+        public final boolean canConstantState() {
+            final ChildDrawable[] array = mChildren;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                final Drawable dr = array[i].mDrawable;
+                if (dr != null && dr.getConstantState() == null) {
+                    return false;
+                }
+            }
+
+            // Don't cache the result, this method is not called very often.
+            return true;
+        }
+
+        public void invalidateCache() {
+            mCheckedOpacity = false;
+            mCheckedStateful = false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/check_circle.xml b/app/src/main/res/drawable/check_circle.xml
new file mode 100644
index 0000000..e1e3f25
--- /dev/null
+++ b/app/src/main/res/drawable/check_circle.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:textColorPrimary"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM16.59,7.58L10,14.17l-2.59,-2.58L6,13l4,4 8,-8z" />
+</vector>
diff --git a/app/src/main/res/drawable/check_circle_shape.xml b/app/src/main/res/drawable/check_circle_shape.xml
new file mode 100644
index 0000000..2e6add0
--- /dev/null
+++ b/app/src/main/res/drawable/check_circle_shape.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="@color/colorAccent"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2z" />
+</vector>
diff --git a/app/src/main/res/drawable/ic_settings_overlays.xml b/app/src/main/res/drawable/ic_settings_overlays.xml
new file mode 100644
index 0000000..ff59963
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings_overlays.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:textColorPrimary"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10c1.38,0 2.5,-1.12 2.5,-2.5c0,-0.61 -0.23,-1.2 -0.64,-1.67c-0.08,-0.1 -0.13,-0.21 -0.13,-0.33c0,-0.28 0.22,-0.5 0.5,-0.5H16c3.31,0 6,-2.69 6,-6C22,6.04 17.51,2 12,2zM17.5,13c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5C19,12.33 18.33,13 17.5,13zM14.5,9C13.67,9 13,8.33 13,7.5C13,6.67 13.67,6 14.5,6S16,6.67 16,7.5C16,8.33 15.33,9 14.5,9zM5,11.5C5,10.67 5.67,10 6.5,10S8,10.67 8,11.5C8,12.33 7.33,13 6.5,13S5,12.33 5,11.5zM11,7.5C11,8.33 10.33,9 9.5,9S8,8.33 8,7.5C8,6.67 8.67,6 9.5,6S11,6.67 11,7.5z" />
+</vector>
diff --git a/app/src/main/res/drawable/overlay_item_background.xml b/app/src/main/res/drawable/overlay_item_background.xml
new file mode 100644
index 0000000..161d450
--- /dev/null
+++ b/app/src/main/res/drawable/overlay_item_background.xml
@@ -0,0 +1,23 @@
+<?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"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask"
+        android:drawable="@drawable/overlay_item_background_shape" />
+    <item android:id="@+id/background"
+        android:drawable="@drawable/overlay_item_background_shape"/>
+</ripple>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/overlay_item_background_shape.xml b/app/src/main/res/drawable/overlay_item_background_shape.xml
new file mode 100644
index 0000000..57a6481
--- /dev/null
+++ b/app/src/main/res/drawable/overlay_item_background_shape.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="@dimen/grid_item_corner_radius" />
+    <solid android:color="@color/overlay_item_background" />
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/layout/color_item.xml b/app/src/main/res/layout/color_item.xml
new file mode 100644
index 0000000..3dd586e
--- /dev/null
+++ b/app/src/main/res/layout/color_item.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content" />
diff --git a/app/src/main/res/layout/colors_item.xml b/app/src/main/res/layout/colors_item.xml
new file mode 100644
index 0000000..a449869
--- /dev/null
+++ b/app/src/main/res/layout/colors_item.xml
@@ -0,0 +1,29 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/color_item_column_width"
+    android:layout_height="@dimen/color_item_column_height"
+    android:layout_margin="5dp"
+    android:background="@drawable/overlay_item_background">
+
+    <FrameLayout
+        android:layout_width="@dimen/color_item_container_width"
+        android:layout_height="@dimen/color_item_container_height"
+        android:layout_gravity="center">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:id="@+id/colors_item_list"
+            android:orientation="vertical"
+            android:layout_gravity="center_vertical">
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/colors_enabled"
+            android:layout_width="@dimen/check_circle_size"
+            android:layout_height="@dimen/check_circle_size"
+            android:layout_gravity="bottom|end"
+            android:src="@drawable/check_circle"
+            android:background="@drawable/check_circle_shape"
+            android:visibility="gone" />
+    </FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/grid_fragment.xml b/app/src/main/res/layout/grid_fragment.xml
index ee2303f..26b0003 100644
--- a/app/src/main/res/layout/grid_fragment.xml
+++ b/app/src/main/res/layout/grid_fragment.xml
@@ -1,15 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<GridView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/grid_view"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <GridView
-        android:id="@+id/grid_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:numColumns="@integer/grid_view_columns"
-        android:verticalSpacing="@dimen/grip_view_spacing"
-        android:horizontalSpacing="@dimen/grip_view_spacing"
-        android:stretchMode="columnWidth"
-        android:gravity="center" />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:numColumns="@integer/grid_view_columns"
+    android:stretchMode="columnWidth" />
diff --git a/app/src/main/res/layout/icon_shape_item.xml b/app/src/main/res/layout/icon_shape_item.xml
new file mode 100644
index 0000000..9b2f9c6
--- /dev/null
+++ b/app/src/main/res/layout/icon_shape_item.xml
@@ -0,0 +1,27 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/icon_shape_column_size"
+    android:layout_height="@dimen/icon_shape_column_size"
+    android:layout_margin="5dp"
+    android:background="@drawable/overlay_item_background">
+
+    <FrameLayout
+        android:layout_width="@dimen/icon_shape_container_size"
+        android:layout_height="@dimen/icon_shape_container_size"
+        android:layout_gravity="center" >
+
+        <ImageView
+            android:id="@+id/icon_shape_icon"
+            android:layout_width="@dimen/icon_shape_size"
+            android:layout_height="@dimen/icon_shape_size"
+            android:layout_gravity="center" />
+
+        <ImageView
+            android:id="@+id/icon_shape_icon_enabled"
+            android:layout_width="@dimen/check_circle_size"
+            android:layout_height="@dimen/check_circle_size"
+            android:layout_gravity="bottom|end"
+            android:src="@drawable/check_circle"
+            android:background="@drawable/check_circle_shape"
+            android:visibility="gone" />
+    </FrameLayout>
+</FrameLayout>
diff --git a/app/src/main/res/layout/overlays_fragment.xml b/app/src/main/res/layout/overlays_fragment.xml
new file mode 100644
index 0000000..179a970
--- /dev/null
+++ b/app/src/main/res/layout/overlays_fragment.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/overlay_item_list"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginStart="@dimen/overlay_fragment_side_margin"
+    android:layout_marginEnd="@dimen/overlay_fragment_side_margin"/>
diff --git a/app/src/main/res/layout/overlays_item.xml b/app/src/main/res/layout/overlays_item.xml
new file mode 100644
index 0000000..8b33cac
--- /dev/null
+++ b/app/src/main/res/layout/overlays_item.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:gravity="center_vertical"
+        android:text="@string/icon_shape_title"
+        android:textAppearance="@style/Theme.OmniControl.GridItem.TitleTextStyle" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/icon_shape_list_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:gravity="center_vertical"
+        android:text="@string/primary_color_title"
+        android:textAppearance="@style/Theme.OmniControl.GridItem.TitleTextStyle" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/primary_color_list_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:gravity="center_vertical"
+        android:text="@string/accent_color_title"
+        android:textAppearance="@style/Theme.OmniControl.GridItem.TitleTextStyle" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/accent_color_list_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="100dp" />
+</LinearLayout>
diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml
index b8ad2de..62ebde0 100644
--- a/app/src/main/res/values-land/dimens.xml
+++ b/app/src/main/res/values-land/dimens.xml
@@ -3,5 +3,7 @@
     <dimen name="fragment_side_margin">30dp</dimen>
     <dimen name="fragment_icon_margin_start">20dp</dimen>
     <dimen name="grid_icon_margin_start">10dp</dimen>
-
+    <dimen name="color_item_column_width">@dimen/color_item_column_height</dimen>
+    <dimen name="color_item_container_width">@dimen/color_item_container_height</dimen>
+    <dimen name="overlay_fragment_side_margin">20dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index 70a2802..927d9b7 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -3,4 +3,5 @@
     <color name="colorPrimary">@android:color/system_neutral1_900</color>
     <color name="colorPrimaryDark">@android:color/system_neutral1_900</color>
     <color name="colorAccent">@android:color/system_accent1_100</color>
+    <color name="overlay_item_background">@android:color/system_neutral1_800</color>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3676baa..e7dea2f 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -4,4 +4,5 @@
     <color name="colorPrimaryDark">@android:color/system_neutral1_50</color>
     <color name="colorAccent">@android:color/system_accent1_600</color>
     <color name="grid_shape_background">@color/colorPrimary</color>
+    <color name="overlay_item_background">@android:color/system_neutral1_0</color>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/config.xml b/app/src/main/res/values/config.xml
index 69ac701..6cddf27 100644
--- a/app/src/main/res/values/config.xml
+++ b/app/src/main/res/values/config.xml
@@ -2,4 +2,5 @@
 <resources>
     <integer name="grid_view_columns">1</integer>
     <bool name="config_show_battery_options">true</bool>
+    <integer name="icon_shape_view_columns">20</integer>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 2b40579..4c7126d 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,11 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <dimen name="applist_icon_size">108dp</dimen>
-    <dimen name="grip_view_spacing">0dp</dimen>
     <dimen name="grid_item_corner_radius">16dp</dimen>
     <dimen name="fragment_side_margin">10dp</dimen>
     <dimen name="grid_item_border">10dp</dimen>
     <dimen name="fragment_icon_margin_start">10dp</dimen>
     <dimen name="grid_item_icon_size">32dp</dimen>
     <dimen name="grid_icon_margin_start">0dp</dimen>
-</resources>
\ No newline at end of file
+    <dimen name="icon_shape_size">64dp</dimen>
+    <dimen name="check_circle_size">24dp</dimen>
+    <dimen name="icon_shape_container_size">72dp</dimen>
+    <dimen name="icon_shape_column_size">80dp</dimen>
+    <dimen name="color_item_height">16dp</dimen>
+    <dimen name="color_item_width">@dimen/color_item_container_width</dimen>
+    <dimen name="color_item_container_width">120dp</dimen>
+    <dimen name="color_item_container_height">160dp</dimen>
+    <dimen name="color_item_column_width">140dp</dimen>
+    <dimen name="color_item_column_height">180dp</dimen>
+    <dimen name="overlay_fragment_side_margin">10dp</dimen>
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index af44f95..d754c64 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -45,6 +45,12 @@
     <string name="device_settings_title">Device settings</string>
     <string name="device_settings_summary">Advanced device specific settings</string>
 
+    <string name="overlays_settings_title">Styles</string>
+    <string name="overlays_settings_summary">Change system default styles and colors</string>
+    <string name="icon_shape_title">Icon shape</string>
+    <string name="primary_color_title">Primary color</string>
+    <string name="accent_color_title">Accent color</string>
+
     <!-- doze on charge -->
     <string name="doze_on_charge_title">Show ambient display when charging</string>
     <string name="doze_on_charge_summary">Wake screen when charging</string>