parts: introduce berry styles

Change-Id: Ifaacc78eed5234cdbe2fd089a06524bdf1193c8f
Signed-off-by: Joey <joey@lineageos.org>
diff --git a/Android.mk b/Android.mk
index d73478c..12763b9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -9,6 +9,7 @@
     android-support-v13 \
     android-support-dynamic-animation \
     android-support-v7-recyclerview \
+    android-support-v7-palette \
     android-support-v7-preference \
     android-support-v7-appcompat \
     android-support-v14-preference \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 152ddd5..6e2cd9b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -189,6 +189,24 @@
                 android:resource="@string/summary_empty" />
         </activity-alias>
 
+        <!-- Style settings (Display category) -->
+        <activity-alias
+            android:name=".style.StylePreferences"
+            android:label="@string/style_title"
+            android:targetActivity="PartsActivity">
+            <intent-filter android:priority="-3">
+                <action android:name="org.lineageos.lineageparts.action.SETTINGS" />
+                <action android:name="org.lineageos.lineageparts.STYLE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data
+                android:name="com.android.settings.category"
+                android:value="com.android.settings.category.ia.display" />
+            <meta-data
+                android:name="com.android.settings.summary"
+                android:resource="@string/style_summary" />
+        </activity-alias>
+
         <!-- Anonymous Statistics -->
         <receiver android:name=".lineagestats.ReportingServiceManager"
             android:enabled="true"
diff --git a/res/drawable/ic_automagic.xml b/res/drawable/ic_automagic.xml
new file mode 100644
index 0000000..03f420f
--- /dev/null
+++ b/res/drawable/ic_automagic.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2018 The LineageOS 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M7.5,5.6L5,7L6.4,4.5L5,2L7.5,3.4L10,2L8.6,4.5L10,7L7.5,5.6M19.5,15.4L22,14L20.6,16.5L22,19L19.5,17.6L17,19L18.4,16.5L17,14L19.5,15.4M22,2L20.6,4.5L22,7L19.5,5.6L17,7L18.4,4.5L17,2L19.5,3.4L22,2M13.34,12.78L15.78,10.34L13.66,8.22L11.22,10.66L13.34,12.78M14.37,7.29L16.71,9.63C17.1,10 17.1,10.65 16.71,11.04L5.04,22.71C4.65,23.1 4,23.1 3.63,22.71L1.29,20.37C0.9,20 0.9,19.35 1.29,18.96L12.96,7.29C13.35,6.9 14,6.9 14.37,7.29Z" />
+</vector>
diff --git a/res/drawable/ic_style_auto.xml b/res/drawable/ic_style_auto.xml
new file mode 100644
index 0000000..80b6832
--- /dev/null
+++ b/res/drawable/ic_style_auto.xml
@@ -0,0 +1,27 @@
+<!--
+     Copyright (C) 2018 The LineageOS 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M19,5v14H5V5H19
+M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3L19,3z
+M10.7,13.3h2.6L12,9.2L10.7,13.3z
+M14.6,17l-0.8-2.2h-3.6L9.4,17H7.3l3.6-10h2.2l3.6,10H14.6z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_style_dark.xml b/res/drawable/ic_style_dark.xml
new file mode 100644
index 0000000..5def313
--- /dev/null
+++ b/res/drawable/ic_style_dark.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2018 The LineageOS 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="32.0"
+    android:viewportWidth="32.0">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M26.86,17.31C25.97,22.93 21.2,27 15.51,27c-6.34,0 -11.5,-5.16 -11.5,-11.5S9.16,4 15.51,4c0.64,0 1.3,0.06 1.95,0.17c0.46,0.08 1.27,0.11 1.28,0.57c0,0.46 -0.23,0.31 -0.7,0.4c-3.5,0.69 -6.04,4.43 -6.04,7.8c0,3.9 3.16,7.07 7.04,7.07c2.51,0 5.58,-1.44 6.92,-3.77c0.18,-0.32 0.14,-0.52 0.48,-0.52c0.11,0 0.22,0.02 0.32,0.05C27.18,15.94 26.93,16.86 26.86,17.31z" />
+</vector>
diff --git a/res/drawable/ic_style_light.xml b/res/drawable/ic_style_light.xml
new file mode 100644
index 0000000..5ed6ff3
--- /dev/null
+++ b/res/drawable/ic_style_light.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2018 The LineageOS 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5L1,10.5v2h3v-2zM13,0.55h-2L11,3.5h2L13,0.55zM20.45,4.46l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zM17.24,18.16l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zM12,5.5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM11,22.45h2L13,19.5h-2v2.95zM3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z" />
+</vector>
diff --git a/res/drawable/ic_style_time.xml b/res/drawable/ic_style_time.xml
new file mode 100644
index 0000000..c01b7aa
--- /dev/null
+++ b/res/drawable/ic_style_time.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2018 The LineageOS 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
+</vector>
diff --git a/res/layout/style_item_accent_preview.xml b/res/layout/style_item_accent_preview.xml
new file mode 100644
index 0000000..8dacdac
--- /dev/null
+++ b/res/layout/style_item_accent_preview.xml
@@ -0,0 +1,28 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/item_accent_layout"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:focusable="false"
+    android:paddingHorizontal="16dp"
+    android:paddingVertical="8dp"
+    android:focusableInTouchMode="false"
+    android:gravity="center_vertical"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/item_accent_preview"
+        android:layout_width="@dimen/style_accent_preview"
+        android:layout_height="@dimen/style_accent_preview"
+        android:layout_marginEnd="16dp"/>
+
+    <TextView
+        android:id="@+id/item_accent_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:textColor="?android:textColorPrimary"
+        android:textSize="18sp"
+        tools:text="Default" />
+</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 31fb124..9ef1f3b 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -365,4 +365,33 @@
         <item>3</item>
     </string-array>
 
+    <string-array name="style_entries" translatable="false">
+        <item>@string/style_global_entry_automatic</item>
+        <item>@string/style_global_entry_time</item>
+        <item>@string/style_global_entry_light</item>
+        <item>@string/style_global_entry_dark</item>
+    </string-array>
+
+    <!-- Global style values -->
+    <string-array name="style_values" translatable="false">
+        <item>0</item> <!-- Based on wallpaper -->
+        <item>1</item> <!-- Based on LiveDisplay -->
+        <item>2</item> <!-- Force light -->
+        <item>3</item> <!-- Force dark -->
+    </string-array>
+
+    <!-- Supported accent packages -->
+    <string-array name="accent_packages" translatable="false">
+        <item>org.lineageos.overlay.accent.red</item>
+        <item>org.lineageos.overlay.accent.pink</item>
+        <item>org.lineageos.overlay.accent.purple</item>
+        <item>org.lineageos.overlay.accent.blue</item>
+        <item>org.lineageos.overlay.accent.cyan</item>
+        <item>org.lineageos.overlay.accent.green</item>
+        <item>org.lineageos.overlay.accent.orange</item>
+        <item>org.lineageos.overlay.accent.yellow</item>
+        <item>org.lineageos.overlay.accent.brown</item>
+        <item>org.lineageos.overlay.accent.black</item>
+    </string-array>
+
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 9e31a1f..52ddb2d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -55,5 +55,9 @@
     <dimen name="fab_press_translation_z">9dp</dimen>
 
     <dimen name="profile_instruction_padding">8dp</dimen>
+
+    <!-- Berry lite -->
+    <dimen name="style_accent_preview">32dp</dimen>
+    <dimen name="style_accent_icon">24dp</dimen>
 </resources>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1ce916d..4f5b47b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -672,4 +672,22 @@
 
     <!-- Egg: Title for the easter egg activity -->
     <string name="egg_title" translatable="false">LineageOS Easter Egg</string>
+
+    <!-- Berry Lite -->
+    <string name="style_title">Style</string>
+    <string name="style_summary">Choose system colors</string>
+    <string name="style_global_title">Style</string>
+    <string name="style_global_entry_automatic">Automatic (wallpaper)</string>
+    <string name="style_global_entry_light">Light</string>
+    <string name="style_global_entry_dark">Dark</string>
+    <string name="style_global_entry_time">Automatic (time of the day)</string>
+    <string name="style_accent_title">Accent color</string>
+    <string name="style_accent_configuration_not_supported">The current accent isn\'t compatible with the selected style. In order to proceed another accent has to be selected, do you want to continue?</string>
+    <string name="style_accent_configuration_positive">Proceed</string>
+    <string name="style_automagic_title">Automagic</string>
+    <string name="style_automagic_description">Tune the style to match your wallpaper</string>
+    <string name="style_automagic_dialog_content">Suggested style: <b>%1$s</b> with <b>%2$s</b> accent.\nDo you want to apply it?</string>
+    <string name="style_automagic_dialog_positive">Apply</string>
+    <string name="style_accent_default_name">Default</string>
+    <string name="style_permission_error">Storage access permission is needed to analyze your wallpaper, please grant it to use this feature</string>
 </resources>
diff --git a/res/xml/parts_catalog.xml b/res/xml/parts_catalog.xml
index 6318f03..db18231 100644
--- a/res/xml/parts_catalog.xml
+++ b/res/xml/parts_catalog.xml
@@ -116,4 +116,10 @@
           android:summary="@string/touchscreen_gesture_settings_summary"
           android:fragment="org.lineageos.lineageparts.gestures.TouchscreenGestureSettings"
           lineage:xmlRes="@xml/touchscreen_gesture_settings" />
+
+    <part android:key="style"
+          android:title="@string/style_title"
+          android:summary="@string/style_summary"
+          android:fragment="org.lineageos.lineageparts.style.StylePreferences"
+          lineage:xmlRes="@xml/style_preferences" />
 </parts-catalog>
diff --git a/res/xml/style_preferences.xml b/res/xml/style_preferences.xml
new file mode 100644
index 0000000..d623a9e
--- /dev/null
+++ b/res/xml/style_preferences.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 The LineageOS 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.
+-->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <lineageos.preference.LineageSystemSettingListPreference
+        android:defaultValue="0"
+        android:entries="@array/style_entries"
+        android:entryValues="@array/style_values"
+        android:key="berry_global_style"
+        android:summary="%s"
+        android:icon="@drawable/ic_style_auto"
+        android:title="@string/style_global_title" />
+
+    <Preference
+        android:key="style_accent"
+        android:title="@string/style_accent_title" />
+
+    <Preference
+        android:key="style_automagic"
+        android:icon="@drawable/ic_automagic"
+        android:summary="@string/style_automagic_description"
+        android:title="@string/style_automagic_title" />
+
+</PreferenceScreen>
diff --git a/src/org/lineageos/lineageparts/style/StylePreferences.java b/src/org/lineageos/lineageparts/style/StylePreferences.java
new file mode 100644
index 0000000..8d9361c
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/StylePreferences.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.WallpaperManager;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.preference.Preference;
+import android.support.v7.graphics.Palette;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.lineageos.lineageparts.R;
+import org.lineageos.lineageparts.SettingsPreferenceFragment;
+import org.lineageos.lineageparts.style.models.Accent;
+import org.lineageos.lineageparts.style.models.Style;
+import org.lineageos.lineageparts.style.models.StyleStatus;
+import org.lineageos.lineageparts.style.util.AccentAdapter;
+import org.lineageos.lineageparts.style.util.AccentUtils;
+import org.lineageos.lineageparts.style.util.OverlayManager;
+import org.lineageos.lineageparts.style.util.UIUtils;
+
+import java.util.List;
+
+import lineageos.providers.LineageSettings;
+
+public class StylePreferences extends SettingsPreferenceFragment {
+    private static final String TAG = "StylePreferences";
+    private static final int INDEX_WALLPAPER = 0;
+    private static final int INDEX_TIME = 1;
+    private static final int INDEX_LIGHT = 2;
+    private static final int INDEX_DARK = 3;
+
+    private Preference mStylePref;
+    private Preference mAccentPref;
+
+    private List<Accent> mAccents;
+
+    private StyleStatus mStyleStatus;
+
+    private byte mOkStatus = 0;
+
+    @Override
+    public void onCreate(Bundle savedInstance) {
+        super.onCreate(savedInstance);
+
+        addPreferencesFromResource(R.xml.style_preferences);
+
+
+        mStylePref = findPreference("berry_global_style");
+        mStylePref.setOnPreferenceChangeListener(this::onStyleChange);
+        setupStylePref();
+
+        mAccents = AccentUtils.getAccents(getContext(), mStyleStatus);
+        mAccentPref = findPreference("style_accent");
+        mAccentPref.setOnPreferenceClickListener(this::onAccentClick);
+        setupAccentPref();
+
+        Preference automagic = findPreference("style_automagic");
+        automagic.setOnPreferenceClickListener(p -> onAutomagicClick());
+    }
+
+    private boolean onAccentClick(Preference preference) {
+        mAccents = AccentUtils.getAccents(getContext(), mStyleStatus);
+
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.style_accent_title)
+                .setAdapter(new AccentAdapter(mAccents, getContext()),
+                        (dialog, i) -> onAccentSelected(mAccents.get(i)))
+                .setNegativeButton(android.R.string.cancel, null)
+                .show();
+
+        return true;
+    }
+
+    private void setupAccentPref() {
+        String currentAccent = LineageSettings.System.getString(getContext().getContentResolver(),
+                LineageSettings.System.BERRY_CURRENT_ACCENT);
+        try {
+            updateAccentPref(AccentUtils.getAccent(getContext(), currentAccent));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, currentAccent + ": package not found.");
+        }
+    }
+
+    private void onAccentSelected(Accent accent) {
+        String previousAccent = LineageSettings.System.getString(getContext().getContentResolver(),
+                LineageSettings.System.BERRY_CURRENT_ACCENT);
+
+        OverlayManager om = new OverlayManager(getContext());
+        if (!TextUtils.isEmpty(previousAccent)) {
+            // Disable previous theme
+            om.setEnabled(previousAccent, false);
+        }
+
+        LineageSettings.System.putString(getContext().getContentResolver(),
+                LineageSettings.System.BERRY_CURRENT_ACCENT, accent.getPackageName());
+
+        if (!TextUtils.isEmpty(accent.getPackageName())) {
+            // Enable new theme
+            om.setEnabled(accent.getPackageName(), true);
+        }
+        updateAccentPref(accent);
+    }
+
+    private void updateAccentPref(Accent accent) {
+        int size = getResources().getDimensionPixelSize(R.dimen.style_accent_icon);
+
+        mAccentPref.setSummary(accent.getName());
+        mAccentPref.setIcon(UIUtils.getAccentBitmap(getResources(), size, accent.getColor()));
+    }
+
+    private boolean onAutomagicClick() {
+        if (!hasStoragePermission()) {
+            Toast.makeText(getContext(), getString(R.string.style_permission_error),
+                    Toast.LENGTH_LONG).show();
+            return false;
+        }
+
+        Bitmap bitmap = getWallpaperBitmap();
+        if (bitmap == null) {
+            return false;
+        }
+
+        Accent[] accentsArray = new Accent[mAccents.size()];
+        mAccents.toArray(accentsArray);
+
+        Palette palette = Palette.from(bitmap).generate();
+        new AutomagicTask(palette, this::onAutomagicCompleted).execute(accentsArray);
+
+        return true;
+    }
+
+    private void onAutomagicCompleted(Style style) {
+        String styleType = getString(style.isLight() ?
+                R.string.style_global_entry_light : R.string.style_global_entry_dark).toLowerCase();
+        String accentName = style.getAccent().getName().toLowerCase();
+        String message = getString(R.string.style_automagic_dialog_content, styleType, accentName);
+
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.style_automagic_title)
+                .setMessage(message)
+                .setPositiveButton(R.string.style_automagic_dialog_positive,
+                        (dialog, i) -> applyStyle(style))
+                .setNegativeButton(android.R.string.cancel,
+                        (dialog, i) -> increaseOkStatus())
+                .show();
+    }
+
+    private void setupStylePref() {
+        int preference = LineageSettings.System.getInt(getContext().getContentResolver(),
+                LineageSettings.System.BERRY_GLOBAL_STYLE, INDEX_WALLPAPER);
+
+        setStyleIcon(preference);
+        switch (preference) {
+            case INDEX_LIGHT:
+                mStyleStatus = StyleStatus.LIGHT_ONLY;
+                break;
+            case INDEX_DARK:
+                mStyleStatus = StyleStatus.DARK_ONLY;
+                break;
+            default:
+                mStyleStatus = StyleStatus.DYNAMIC;
+                break;
+        }
+    }
+
+    private void applyStyle(Style style) {
+        int value = style.isLight() ? INDEX_LIGHT : INDEX_DARK;
+        LineageSettings.System.putInt(getContext().getContentResolver(),
+            LineageSettings.System.BERRY_GLOBAL_STYLE, value);
+
+        onStyleChange(mStylePref, value);
+        onAccentSelected(style.getAccent());
+    }
+
+    private boolean onStyleChange(Preference preference, Object newValue) {
+        Integer value;
+        if (newValue instanceof String) {
+            value = Integer.valueOf((String) newValue);
+        } else if (newValue instanceof Integer) {
+            value = (Integer) newValue;
+        } else {
+            return false;
+        }
+
+        boolean accentCompatibility = checkAccentCompatibility(value);
+        if (!accentCompatibility) {
+            new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.style_global_title)
+                .setMessage(R.string.style_accent_configuration_not_supported)
+                .setPositiveButton(R.string.style_accent_configuration_positive,
+                        (dialog, i) -> onAccentConflict(value))
+                .setNegativeButton(android.R.string.cancel, null)
+                .show();
+            return false;
+        }
+
+        LineageSettings.System.putInt(getContext().getContentResolver(),
+                LineageSettings.System.BERRY_GLOBAL_STYLE, value);
+
+        setStyleIcon(value);
+        return true;
+    }
+
+    private void setStyleIcon(int value) {
+        int icon;
+        switch (value) {
+            case INDEX_TIME:
+                icon = R.drawable.ic_style_time;
+                break;
+            case INDEX_LIGHT:
+                icon = R.drawable.ic_style_light;
+                break;
+            case INDEX_DARK:
+                icon = R.drawable.ic_style_dark;
+                break;
+            default:
+                icon = R.drawable.ic_style_auto;
+                break;
+        }
+
+        mStylePref.setIcon(icon);
+    }
+
+    private boolean checkAccentCompatibility(int value) {
+        String currentAccentPkg = LineageSettings.System.getString(
+                getContext().getContentResolver(), LineageSettings.System.BERRY_CURRENT_ACCENT);
+        StyleStatus supportedStatus;
+        try {
+            supportedStatus = AccentUtils.getAccent(getContext(), currentAccentPkg)
+                .getSupportedStatus();
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, e.getMessage());
+            supportedStatus = StyleStatus.DYNAMIC;
+        }
+
+        switch (supportedStatus) {
+            case LIGHT_ONLY:
+                return value == INDEX_LIGHT;
+            case DARK_ONLY:
+                return value == INDEX_DARK;
+            case DYNAMIC:
+            default: // Never happens, but compilation fails without this
+                return true;
+        }
+    }
+
+    private void onAccentConflict(int value) {
+        StyleStatus proposedStatus;
+        switch (value) {
+            case INDEX_LIGHT:
+                proposedStatus = StyleStatus.LIGHT_ONLY;
+                break;
+            case INDEX_DARK:
+                proposedStatus = StyleStatus.DARK_ONLY;
+                break;
+            default:
+                proposedStatus = StyleStatus.DYNAMIC;
+                break;
+        }
+
+        // Let the user pick the new accent
+        List<Accent> accents = AccentUtils.getAccents(getContext(), proposedStatus);
+
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.style_accent_title)
+                .setAdapter(new AccentAdapter(accents, getContext()),
+                        (dialog, i) -> {
+                            onAccentSelected(accents.get(i));
+                            onStyleChange(mStylePref, value);
+                })
+                .setNegativeButton(android.R.string.cancel, null)
+                .show();
+    }
+
+    @Nullable
+    private Bitmap getWallpaperBitmap() {
+        WallpaperManager manager = WallpaperManager.getInstance(getContext());
+        Drawable wallpaper = manager.getDrawable();
+
+        if (wallpaper == null) {
+            return null;
+        }
+
+        if (wallpaper instanceof BitmapDrawable) {
+            return ((BitmapDrawable) wallpaper).getBitmap();
+        }
+
+        Bitmap bitmap = Bitmap.createBitmap(wallpaper.getIntrinsicWidth(),
+                wallpaper.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        wallpaper.setBounds(0, 0 , canvas.getWidth(), canvas.getHeight());
+        wallpaper.draw(canvas);
+        return bitmap;
+    }
+
+    private boolean hasStoragePermission() {
+        Activity activity = getActivity();
+        return activity != null &&
+                activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) ==
+                        PackageManager.PERMISSION_GRANTED;
+    }
+
+    private void increaseOkStatus() {
+        mOkStatus++;
+        if (mOkStatus != 2) {
+            return;
+        }
+
+        mOkStatus = (byte) 0;
+        new AlertDialog.Builder(getActivity())
+            .setTitle(android.R.string.ok)
+            .setPositiveButton(android.R.string.ok, null)
+            .show();
+    }
+
+    private static final class AutomagicTask extends AsyncTask<Accent, Void, Style> {
+        private static final int COLOR_DEFAULT = Color.BLACK;
+
+        private final Palette mPalette;
+        private final Callback mCallback;
+
+        AutomagicTask(Palette palette, Callback callback) {
+            mPalette = palette;
+            mCallback = callback;
+        }
+
+        @NonNull
+        @Override
+        public Style doInBackground(Accent... accents) {
+            int wallpaperColor = mPalette.getVibrantColor(COLOR_DEFAULT);
+
+            // If vibrant color extraction failed, let's try muted color
+            if (wallpaperColor == COLOR_DEFAULT) {
+                wallpaperColor = mPalette.getMutedColor(COLOR_DEFAULT);
+            }
+
+            boolean isLight = UIUtils.isColorLight(wallpaperColor);
+            Accent bestAccent = getBestAccent(accents, wallpaperColor, isLight);
+
+            return new Style(bestAccent, isLight);
+        }
+
+        @Override
+        public void onPostExecute(Style style) {
+            mCallback.onDone(style);
+        }
+
+        private Accent getBestAccent(Accent[] accents, int wallpaperColor, boolean isLight) {
+            int bestIndex = 0;
+            double minDiff = Double.MAX_VALUE;
+            StyleStatus targetStatus = isLight ? StyleStatus.LIGHT_ONLY : StyleStatus.DARK_ONLY;
+
+            for (int i = 0; i < accents.length; i++) {
+                double diff = diff(accents[i].getColor(), wallpaperColor);
+                if (diff < minDiff && AccentUtils.isCompatible(targetStatus, accents[i])) {
+                    bestIndex = i;
+                    minDiff = diff;
+                }
+            }
+
+            return accents[bestIndex];
+        }
+
+        private double diff(@ColorInt int accent, @ColorInt int wallpaper) {
+            return Math.sqrt(Math.pow(Color.red(accent) - Color.red(wallpaper), 2) +
+                    Math.pow(Color.green(accent) - Color.green(wallpaper), 2) +
+                    Math.pow(Color.blue(accent) - Color.blue(wallpaper), 2));
+        }
+    }
+
+    private interface Callback {
+        void onDone(Style style);
+    }
+}
\ No newline at end of file
diff --git a/src/org/lineageos/lineageparts/style/models/Accent.java b/src/org/lineageos/lineageparts/style/models/Accent.java
new file mode 100644
index 0000000..e416571
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/models/Accent.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style.models;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+public class Accent {
+    @NonNull
+    private final String name;
+    @NonNull
+    private final String packageName;
+    @ColorInt
+    private final int color;
+    private StyleStatus supportedStatus;
+
+    public Accent(@NonNull String name, @NonNull String packageName, @ColorInt int color,
+            StyleStatus supportedStatus) {
+        this.name = name;
+        this.packageName = packageName;
+        this.color = color;
+        this.supportedStatus = supportedStatus;
+    }
+
+    @NonNull
+    public String getName() {
+        return name;
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return packageName;
+    }
+
+    @ColorInt
+    public int getColor() {
+        return color;
+    }
+
+    public StyleStatus getSupportedStatus() {
+        return supportedStatus;
+    }
+}
\ No newline at end of file
diff --git a/src/org/lineageos/lineageparts/style/models/Style.java b/src/org/lineageos/lineageparts/style/models/Style.java
new file mode 100644
index 0000000..8a602c9
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/models/Style.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style.models;
+
+import android.support.annotation.NonNull;
+
+public class Style {
+    @NonNull
+    private Accent accent;
+    private boolean isLight;
+
+    public Style(@NonNull Accent accent, boolean isLight) {
+        this.accent = accent;
+        this.isLight = isLight;
+    }
+
+    @NonNull
+    public Accent getAccent() {
+        return accent;
+    }
+
+    public boolean isLight() {
+        return isLight;
+    }
+}
\ No newline at end of file
diff --git a/src/org/lineageos/lineageparts/style/models/StyleStatus.java b/src/org/lineageos/lineageparts/style/models/StyleStatus.java
new file mode 100644
index 0000000..22968bf
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/models/StyleStatus.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style.models;
+
+public enum StyleStatus {
+    DYNAMIC,
+    LIGHT_ONLY,
+    DARK_ONLY
+}
\ No newline at end of file
diff --git a/src/org/lineageos/lineageparts/style/util/AccentAdapter.java b/src/org/lineageos/lineageparts/style/util/AccentAdapter.java
new file mode 100644
index 0000000..a0dfb5e
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/util/AccentAdapter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style.util;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.lineageos.lineageparts.R;
+import org.lineageos.lineageparts.style.models.Accent;
+
+import java.util.List;
+
+public class AccentAdapter extends BaseAdapter {
+    private List<Accent> mAccents;
+    private Context mContext;
+    private boolean isDark;
+
+    public AccentAdapter(List<Accent> accents, Context context) {
+        mAccents = accents;
+        mContext = context;
+    }
+
+    @Override
+    public int getCount() {
+        return mAccents.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return null;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return 0;
+    }
+
+    @Override
+    public View getView(int position, @Nullable View argView, @NonNull ViewGroup parent) {
+        View view = generateItemView(argView);
+        if (view == null) {
+            return null;
+        }
+
+        Resources r = mContext.getResources();
+        Accent accent = mAccents.get(position);
+
+        ImageView preview = view.findViewById(R.id.item_accent_preview);
+        TextView name = view.findViewById(R.id.item_accent_name);
+
+        preview.setImageDrawable(UIUtils.getAccentBitmap(r,
+                r.getDimensionPixelSize(R.dimen.style_accent_preview), accent.getColor()));
+        name.setText(accent.getName());
+
+        return view;
+    }
+
+    @Nullable
+    private View generateItemView(@Nullable View argView) {
+        if (argView == null) {
+            LayoutInflater inflater = LayoutInflater.from(mContext);
+            return (inflater == null) ? null :
+                    inflater.inflate(R.layout.style_item_accent_preview, null);
+        } else {
+            return argView;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/lineageos/lineageparts/style/util/AccentUtils.java b/src/org/lineageos/lineageparts/style/util/AccentUtils.java
new file mode 100644
index 0000000..6ad69ad
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/util/AccentUtils.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style.util;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.lineageos.lineageparts.R;
+import org.lineageos.lineageparts.style.models.Accent;
+import org.lineageos.lineageparts.style.models.StyleStatus;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class AccentUtils {
+    private static final String TAG = "AccentUtils";
+    private static final String METADATA_COLOR = "lineage_berry_accent_preview";
+    private static final String METADATA_SUPPORTED_STYLES = "lineage_berry_accent_supported_styles";
+    private static final String METADATA_STYLE_DEFAULT = "dark|light";
+    private static final String METADATA_STYLE_DARK = "dark";
+    private static final String METADATA_STYLE_LIGHT = "light";
+    private static final int DEFAULT_COLOR = Color.BLACK;
+
+    private AccentUtils() {
+    }
+
+    public static List<Accent> getAccents(Context context, StyleStatus status) {
+        List<Accent> accents = new ArrayList<>();
+
+        // Add default accent
+        accents.add(getDefaultAccent(context));
+
+        String[] targets = context.getResources().getStringArray(R.array.accent_packages);
+        for (String target : targets) {
+            try {
+                Accent accent = getAccent(context, target);
+                if (accent != null && isCompatible(status, accent)) {
+                    accents.add(accent);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, e.getMessage());
+            }
+        }
+
+        return accents;
+    }
+
+    @Nullable
+    public static Accent getAccent(Context context, @Nullable String target)
+            throws PackageManager.NameNotFoundException {
+        if (TextUtils.isEmpty(target)) {
+            return getDefaultAccent(context);
+        }
+
+        PackageManager pm = context.getPackageManager();
+        ApplicationInfo ai = pm.getApplicationInfo(target, PackageManager.GET_META_DATA);
+
+        String name = ai.loadLabel(pm).toString();
+        int color = ai.metaData == null ? DEFAULT_COLOR :
+                ai.metaData.getInt(METADATA_COLOR, DEFAULT_COLOR);
+
+        String supportedStyles = ai.metaData == null ? METADATA_STYLE_DEFAULT :
+                ai.metaData.getString(METADATA_SUPPORTED_STYLES, METADATA_STYLE_DEFAULT);
+        boolean supportsDark = supportedStyles.contains(METADATA_STYLE_DARK);
+        boolean supportsLight = supportedStyles.contains(METADATA_STYLE_LIGHT);
+        StyleStatus status = (supportsLight && supportsDark) ? StyleStatus.DYNAMIC :
+                supportsLight ? StyleStatus.LIGHT_ONLY : StyleStatus.DARK_ONLY;
+
+        return new Accent(name, ai.packageName, color, status);
+    }
+
+    @NonNull
+    private static Accent getDefaultAccent(Context context) {
+        return new Accent(context.getString(R.string.style_accent_default_name),
+                "", Color.parseColor("#167C80"), StyleStatus.DYNAMIC);
+    }
+
+    public static boolean isCompatible(StyleStatus currentStatus, Accent accent) {
+        StyleStatus accentStatus = accent.getSupportedStatus();
+        return accentStatus == StyleStatus.DYNAMIC || currentStatus == accentStatus;
+    }
+}
\ No newline at end of file
diff --git a/src/org/lineageos/lineageparts/style/util/OverlayManager.java b/src/org/lineageos/lineageparts/style/util/OverlayManager.java
new file mode 100644
index 0000000..353a571
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/util/OverlayManager.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style.util;
+
+import android.content.Context;
+import android.content.om.IOverlayManager;
+import android.content.om.OverlayInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.support.annotation.NonNull;
+
+public class OverlayManager {
+    private OverlayHelper mHelper;
+    private PackageManager mPackageManager;
+
+    public OverlayManager(Context context) {
+        this(context, ServiceManager.getService("overlay") == null ?
+                null : new OverlayHelper());
+    }
+
+    private OverlayManager(Context context, OverlayHelper helper) {
+        mHelper = helper;
+        mPackageManager = context.getPackageManager();
+    }
+
+    public void setEnabled(@NonNull String pkg, boolean enabled) {
+        if (!isChangeableOverlay(pkg)) {
+            return;
+        }
+
+        mHelper.setEnabled(pkg, enabled, UserHandle.myUserId());
+    }
+
+    public boolean isEnabled(@NonNull String pkg) {
+        return mHelper.isEnabled(pkg, UserHandle.myUserId());
+    }
+
+    private boolean isChangeableOverlay(@NonNull String pkg) {
+        try {
+            PackageInfo pi = mPackageManager.getPackageInfo(pkg, 0);
+            return pi != null && !pi.isStaticOverlay;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    static class OverlayHelper {
+        private final IOverlayManager mService;
+
+        OverlayHelper() {
+            mService = IOverlayManager.Stub.asInterface(ServiceManager.getService("overlay"));
+        }
+
+        public void setEnabled(@NonNull String pkg, boolean enabled, int userId) {
+            try {
+                mService.setEnabled(pkg, enabled, userId);
+            } catch (RemoteException ignored) {
+            }
+        }
+
+        public boolean isEnabled(@NonNull String pkg, int userId) {
+            try {
+                return mService.getOverlayInfo(pkg, userId).isEnabled();
+            } catch (RemoteException ignored) {
+            }
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/lineageos/lineageparts/style/util/UIUtils.java b/src/org/lineageos/lineageparts/style/util/UIUtils.java
new file mode 100644
index 0000000..bf2bba0
--- /dev/null
+++ b/src/org/lineageos/lineageparts/style/util/UIUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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 org.lineageos.lineageparts.style.util;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+
+public final class UIUtils {
+
+    private UIUtils() {
+    }
+
+    @NonNull
+    public static Drawable getAccentBitmap(Resources resources, int size, int color) {
+        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+
+        Canvas canvas = new Canvas(bitmap);
+        Paint paint = new Paint();
+        RectF rect = new RectF(new Rect(0, 0, size, size));
+        paint.setAntiAlias(true);
+        paint.setColor(color);
+        canvas.drawRoundRect(rect, size / 2, size / 2, paint);
+
+        return RoundedBitmapDrawableFactory.create(resources, bitmap);
+    }
+
+    public static boolean isColorLight(int color) {
+        int red = Color.red(color);
+        int green = Color.green(color);
+        int blue = Color.blue(color);
+
+        float hsl[] = new float[3];
+        ColorUtils.RGBToHSL(red, green, blue, hsl);
+        return hsl[2] > 0.5f;
+    }
+}
\ No newline at end of file