OmniControl: use FabricatedOverlay for custom accent color
Change-Id: I489b27e6350e1b2100fcc1f02d5d23ed993e61ce
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index c08862a..0577e1d 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -24,9 +24,6 @@
</value>
</option>
</JavaCodeStyleSettings>
- <JetCodeStyleSettings>
- <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
- </JetCodeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
@@ -139,8 +136,5 @@
</rules>
</arrangement>
</codeStyleSettings>
- <codeStyleSettings language="kotlin">
- <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
- </codeStyleSettings>
</code_scheme>
</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index fbd59d8..4a39a00 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -6,16 +6,22 @@
<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/accent_color_item_background.xml" value="0.4306306306306306" />
+ <entry key="app/src/main/res/drawable/add_accent_color_item_background.xml" value="0.4436936936936937" />
<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/edit_accent_color_item_background.xml" value="0.4418918918918919" />
<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_add.xml" value="0.26756756756756755" />
<entry key="app/src/main/res/drawable/ic_bars_tile.xml" value="0.30885416666666665" />
+ <entry key="app/src/main/res/drawable/ic_baseline_check_circle_24.xml" value="0.4436936936936937" />
<entry key="app/src/main/res/drawable/ic_baseline_circle_24.xml" value="0.30885416666666665" />
+ <entry key="app/src/main/res/drawable/ic_edit_color.xml" value="0.4436936936936937" />
<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" />
@@ -32,13 +38,23 @@
<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-land/color_select_dialog.xml" value="0.42151675485008816" />
+ <entry key="app/src/main/res/layout/accent_colors_item.xml" value="0.4306306306306306" />
+ <entry key="app/src/main/res/layout/add_accent_colors_item.xml" value="0.4306306306306306" />
<entry key="app/src/main/res/layout/color_item.xml" value="0.3677536231884058" />
+ <entry key="app/src/main/res/layout/color_preset_item.xml" value="0.4519927536231884" />
+ <entry key="app/src/main/res/layout/color_select_dialog.xml" value="0.4519927536231884" />
<entry key="app/src/main/res/layout/colors_item.xml" value="0.75" />
+ <entry key="app/src/main/res/layout/dialog_battery_settings.xml" value="0.2942708333333333" />
+ <entry key="app/src/main/res/layout/edit_accent_colors_item.xml" value="0.4306306306306306" />
+ <entry key="app/src/main/res/layout/edit_dark_accent_colors_item.xml" value="0.1" />
+ <entry key="app/src/main/res/layout/edit_light_accent_colors_item.xml" value="0.4306306306306306" />
<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/primary_colors_item.xml" value="0.4306306306306306" />
<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/app/src/main/java/org/omnirom/control/OverlaysFragment.kt b/app/src/main/java/org/omnirom/control/OverlaysFragment.kt
index bf91a74..c5c8fd2 100644
--- a/app/src/main/java/org/omnirom/control/OverlaysFragment.kt
+++ b/app/src/main/java/org/omnirom/control/OverlaysFragment.kt
@@ -17,11 +17,9 @@
*/
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.Color
import android.graphics.Path
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.ColorDrawable
@@ -29,18 +27,19 @@
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.view.*
import android.widget.ImageView
import android.widget.LinearLayout
+import androidx.appcompat.app.AlertDialog
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 com.google.android.material.snackbar.Snackbar
+import org.json.JSONArray
+import org.json.JSONObject
+import org.omnirom.control.widget.ColorSelectDialog
import org.omnirom.control.widget.DynamicAdaptiveIconDrawable
@@ -53,34 +52,46 @@
private val iconShapeGridItems = ArrayList<IconShapeGridItem>()
private val primaryColorsGridItems = ArrayList<ColorsGridItem>()
- private val accentColorsGridItems = ArrayList<ColorsGridItem>()
+ private val accentColorsGridItems = ArrayList<AccentColorsGridItem>()
private val overlayItemList = ArrayList<View>()
+ private val customAccentColors = ArrayList<CustomAccentColor>()
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"
+ "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"
+ "system_accent1_100",
+ "system_accent1_600"
+ )
+
+ val CUSTOM_ACCENT_COLORS = arrayOf(
+ Color.parseColor("#a1c729"),
+ Color.parseColor("#ff8000"),
+ Color.parseColor("#a020f0"),
+ Color.parseColor("#ff005a"),
+ Color.parseColor("#e5141b"),
+ Color.parseColor("#f0b50b"),
+ Color.parseColor("#009ed8"),
+ Color.parseColor("#00897b"),
)
abstract class GridItem(
- packageName: String,
- isEnabled: Boolean,
- isSystem: Boolean,
- category: String
+ packageName: String,
+ isEnabled: Boolean,
+ isSystem: Boolean,
+ category: String
) {
val packageName: String = packageName
val isEnabled: Boolean = isEnabled
@@ -89,41 +100,70 @@
}
class IconShapeGridItem(
- packageName: String,
- iconShape: String,
- isEnabled: Boolean,
- isSystem: Boolean,
- category: String
+ 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
+ open class ColorsGridItem(
+ packageName: String,
+ colorList: ArrayList<Int>,
+ isEnabled: Boolean,
+ isSystem: Boolean,
+ category: String
) : GridItem(packageName, isEnabled, isSystem, category) {
val colorList: ArrayList<Int> = colorList
}
+ open class AccentColorsGridItem(
+ packageName: String,
+ colorList: ArrayList<Int>,
+ isEnabled: Boolean,
+ isSystem: Boolean,
+ category: String
+ ) : ColorsGridItem(packageName, colorList, isEnabled, isSystem, category) {
+ }
+
+ class FabricatedAccentColorsGridItem(packageName: String,
+ colorList: ArrayList<Int>,
+ isEnabled: Boolean,
+ isSystem: Boolean,
+ category: String,
+ customAccentColor: CustomAccentColor
+ ) : AccentColorsGridItem(packageName, colorList, isEnabled, isSystem, category) {
+ val customAccentColor = customAccentColor
+ }
+
+ class AddAccentColorsGridItem(
+ ) : AccentColorsGridItem("", ArrayList(), false, false, "") {
+ }
+
+ class CustomAccentColor(var identifier: String, var darkColor: Int, var lightColor: Int) {
+ fun toJSONObject(): JSONObject {
+ val color = JSONObject()
+ color.put("identifier", identifier)
+ color.put("system_accent1_100", darkColor)
+ color.put("system_accent1_600", lightColor)
+ return color
+ }
+ }
+
inner class IconShapeListViewAdapter(
- val context: Context,
- val gridItems: ArrayList<IconShapeGridItem>
+ val context: Context,
+ val gridItems: ArrayList<IconShapeGridItem>
) :
- RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+ 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
+ val shapeImage: ImageView = view.findViewById(R.id.icon_shape_icon)
+ val enabledImage: ImageView = view.findViewById(R.id.icon_shape_icon_enabled)
- init {
- shapeImage = view.findViewById(R.id.icon_shape_icon)
- enabledImage = view.findViewById(R.id.icon_shape_icon_enabled)
- }
}
override fun getItemCount(): Int {
@@ -132,7 +172,7 @@
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return IconShapeListItem(
- LayoutInflater.from(context).inflate(R.layout.icon_shape_item, parent, false)
+ LayoutInflater.from(context).inflate(R.layout.icon_shape_item, parent, false)
)
}
@@ -140,18 +180,18 @@
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)
- )
+ createShapeIconDrawable(
+ PathParser.createPathFromPathData(
+ gridItem.iconShape
+ ), context.resources.getDrawable(R.mipmap.ic_launcher, null)
+ )
)
if (gridItem.isEnabled || (gridItem.isSystem && !isOverlayEnabled())) {
holder.enabledImage.visibility =
- View.VISIBLE
+ View.VISIBLE
} else {
holder.enabledImage.visibility =
- View.GONE
+ View.GONE
}
holder.itemView.setOnClickListener {
if (gridItem.isSystem) {
@@ -176,8 +216,8 @@
private fun createShapeIconDrawable(path: Path, appIcon: Drawable): Drawable {
if (appIcon is AdaptiveIconDrawable) {
return DynamicAdaptiveIconDrawable(
- appIcon.background,
- appIcon.foreground, path
+ appIcon.background,
+ appIcon.foreground, path
)
}
return createShapeDrawable(path)
@@ -188,12 +228,11 @@
}
}
- inner class ColorsListViewAdapter(
- val context: Context,
- val gridItems: ArrayList<ColorsGridItem>,
- val isPrimary: Boolean
+ inner class PrimaryColorsListViewAdapter(
+ val context: Context,
+ private val gridItems: ArrayList<ColorsGridItem>
) :
- RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+ RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class ColorsListItem(view: View) : RecyclerView.ViewHolder(view) {
val colorList: LinearLayout = view.findViewById(R.id.colors_item_list)
@@ -206,7 +245,7 @@
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ColorsListItem(
- LayoutInflater.from(context).inflate(R.layout.colors_item, parent, false)
+ LayoutInflater.from(context).inflate(R.layout.primary_colors_item, parent, false)
)
}
@@ -217,34 +256,29 @@
for (color in gridItem.colorList) {
val colorItem: ImageView =
- LayoutInflater.from(context).inflate(R.layout.color_item, null) as 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)
+ 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
+ View.VISIBLE
} else {
holder.enabledImage.visibility =
- View.GONE
+ View.GONE
}
- holder.itemView.setOnClickListener {
- val gridItem: ColorsGridItem = gridItems.get(position)
+ holder.colorList.setOnClickListener {
if (gridItem.isSystem) {
overlayProvider.disableAllInCategory(gridItem.category)
} else if (!gridItem.isEnabled) {
overlayProvider.enableOverlayExclusive(gridItem.packageName)
}
- if (isPrimary) {
- loadPrimaryColorsOverlays()
- } else {
- loadAccentColorsOverlays()
- }
+ loadPrimaryColorsOverlays()
notifyDataSetChanged()
}
}
@@ -259,11 +293,150 @@
}
}
- inner class OverlayItemListViewAdapter(
- val context: Context,
- val overlayItems: ArrayList<View>
+ inner class AccentColorsListViewAdapter(
+ val context: Context,
+ private val gridItems: ArrayList<AccentColorsGridItem>
) :
- RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+ RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+
+ open inner class AccentColorsListItem(view: View) : RecyclerView.ViewHolder(view) {
+ val colorList: LinearLayout = view.findViewById(R.id.colors_item_list)
+ val enabledImage: ImageView = view.findViewById(R.id.colors_enabled)
+ }
+
+ inner class FabricatedAccentColorsListItem(view: View) : AccentColorsListItem(view) {
+ var accentColorDarkBackground: ImageView? = null
+ var accentColorLightBackground: ImageView? = null
+ }
+
+ inner class AddAccentColorsListItem(view: View) : RecyclerView.ViewHolder(view) {
+ }
+
+ override fun getItemCount(): Int {
+ return gridItems.size
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ if (position == gridItems.size - 1)
+ return 0
+ else if (gridItems[position] is FabricatedAccentColorsGridItem)
+ return 1
+ return 2
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ return if (viewType == 0) {
+ AddAccentColorsListItem(
+ LayoutInflater.from(context).inflate(R.layout.add_accent_colors_item, parent, false)
+ )
+ } else if (viewType == 1) {
+ FabricatedAccentColorsListItem(
+ LayoutInflater.from(context).inflate(R.layout.accent_colors_item, parent, false)
+ )
+ } else {
+ AccentColorsListItem(
+ LayoutInflater.from(context).inflate(R.layout.accent_colors_item, parent, false)
+ )
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ val gridItem: AccentColorsGridItem = gridItems[position]
+
+ if (holder is AddAccentColorsListItem) {
+ holder.itemView.setOnClickListener {
+ addNewCustomAccentColor()
+ loadAccentColorsOverlays()
+ notifyDataSetChanged()
+ }
+ } else if (holder is AccentColorsListItem) {
+ holder.colorList.removeAllViews()
+ if (holder is FabricatedAccentColorsListItem) {
+ val v = LayoutInflater.from(context).inflate(R.layout.edit_dark_accent_colors_item, null) as ImageView
+ holder.colorList.addView(
+ v,
+ context.resources.getDimensionPixelSize(R.dimen.color_item_width),
+ context.resources.getDimensionPixelSize(R.dimen.edit_accent_color_item_height)
+ )
+ holder.accentColorDarkBackground = v
+ }
+ 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.accent_color_item_height)
+ )
+ }
+ if (holder is FabricatedAccentColorsListItem) {
+ val v = LayoutInflater.from(context).inflate(R.layout.edit_light_accent_colors_item, null) as ImageView
+ holder.colorList.addView(
+ v,
+ context.resources.getDimensionPixelSize(R.dimen.color_item_width),
+ context.resources.getDimensionPixelSize(R.dimen.edit_accent_color_item_height)
+ )
+ holder.accentColorLightBackground = v
+ }
+
+ if (gridItem.isEnabled || (gridItem.isSystem && !isOverlayEnabled())) {
+ holder.enabledImage.visibility =
+ View.VISIBLE
+ } else {
+ holder.enabledImage.visibility =
+ View.GONE
+ }
+
+ if (holder is FabricatedAccentColorsListItem) {
+ holder.colorList.setOnClickListener {
+ if (!gridItem.isEnabled) {
+ overlayProvider.enableFabricatedAccentOverlayTransaction((gridItem as FabricatedAccentColorsGridItem).customAccentColor)
+ }
+ }
+ //registerForContextMenu(holder.colorList)
+ holder.colorList.setOnLongClickListener {
+ doDeleteCustomAccentColor(gridItem as FabricatedAccentColorsGridItem)
+ true
+ }
+ if (holder.accentColorDarkBackground != null) {
+ holder.accentColorDarkBackground!!.setOnClickListener {
+ setCustomAccentColor(gridItem as FabricatedAccentColorsGridItem, true)
+ }
+ }
+ if (holder.accentColorLightBackground != null) {
+ holder.accentColorLightBackground!!.setOnClickListener {
+ setCustomAccentColor(gridItem as FabricatedAccentColorsGridItem, false)
+ }
+ }
+ } else {
+ holder.colorList.setOnClickListener {
+ if (gridItem.isSystem) {
+ overlayProvider.enableAccentOverlayTransaction(null)
+ } else if (!gridItem.isEnabled) {
+ overlayProvider.enableAccentOverlayTransaction(gridItem.packageName)
+ }
+ 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) {
}
@@ -296,16 +469,16 @@
super.onResume()
(activity as? AppCompatActivity)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
(activity as? SettingsActivity)?.updateFragmentTitle(
- getFragmentTitle(),
- getFragmentSummary(),
- getFragmentIcon()
+ getFragmentTitle(),
+ getFragmentSummary(),
+ getFragmentIcon()
)
}
override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
): View? {
overlayProvider = OverlaysProvider(requireContext())
overlayProvider.init()
@@ -315,26 +488,26 @@
private fun loadIconShapeOverlays() {
iconShapeGridItems.clear();
iconShapeGridItems.add(
- IconShapeGridItem(
- "android",
- overlayProvider.getSystemIconMask(),
- false,
- true,
- overlayProvider.getIconShapeCategory()
- )
+ IconShapeGridItem(
+ "android",
+ overlayProvider.getSystemIconMask(),
+ false,
+ true,
+ overlayProvider.getIconShapeCategory()
+ )
)
- for (packageName in overlayProvider.getIconShapeOverlays()) {
+ for (packageName in overlayProvider.getIconShapeOverlays().sorted()) {
try {
val iconMask = overlayProvider.loadString("config_icon_mask", packageName)
iconShapeGridItems.add(
- IconShapeGridItem(
- packageName,
- iconMask,
- overlayProvider.isOverlayEnabled(packageName),
- false,
- overlayProvider.getIconShapeCategory()
+ IconShapeGridItem(
+ packageName,
+ iconMask,
+ overlayProvider.isOverlayEnabled(packageName),
+ false,
+ overlayProvider.getIconShapeCategory()
- )
+ )
)
} catch (e: NotFoundException) {
}
@@ -348,29 +521,29 @@
colorList.add(overlayProvider.getSystemColor(color))
}
primaryColorsGridItems.add(
- ColorsGridItem(
- "android",
- colorList,
- false,
- true,
- overlayProvider.getPrimaryColorCategory()
- )
+ ColorsGridItem(
+ "android",
+ colorList,
+ false,
+ true,
+ overlayProvider.getPrimaryColorCategory()
+ )
)
- for (packageName in overlayProvider.getPrimaryColorOverlays()) {
+ for (packageName in overlayProvider.getPrimaryColorOverlays().sorted()) {
try {
- val colorList = ArrayList<Int>()
+ val overlayColorList = ArrayList<Int>()
for (color in COLORS_NEUTRAL1) {
- colorList.add(overlayProvider.loadColor(color, packageName))
+ overlayColorList.add(overlayProvider.loadColor(color, packageName))
}
primaryColorsGridItems.add(
- ColorsGridItem(
- packageName,
- colorList,
- overlayProvider.isOverlayEnabled(packageName),
- false,
- overlayProvider.getPrimaryColorCategory()
- )
+ ColorsGridItem(
+ packageName,
+ overlayColorList,
+ overlayProvider.isOverlayEnabled(packageName),
+ false,
+ overlayProvider.getPrimaryColorCategory()
+ )
)
} catch (e: NotFoundException) {
}
@@ -384,67 +557,219 @@
colorList.add(overlayProvider.getSystemColor(color))
}
accentColorsGridItems.add(
- ColorsGridItem(
- "android",
- colorList,
- false,
- true,
- overlayProvider.getAccentColorCategory()
- )
+ AccentColorsGridItem(
+ "android",
+ colorList,
+ false,
+ true,
+ overlayProvider.getAccentColorCategory()
+ )
)
- for (packageName in overlayProvider.getAccentColorOverlays()) {
+ for (packageName in overlayProvider.getAccentColorOverlays().sorted()) {
try {
- val colorList = ArrayList<Int>()
+ val overlayColorList = ArrayList<Int>()
for (color in COLORS_ACCENT1) {
- colorList.add(overlayProvider.loadColor(color, packageName))
+ overlayColorList.add(overlayProvider.loadColor(color, packageName))
}
accentColorsGridItems.add(
- ColorsGridItem(
- packageName,
- colorList,
- overlayProvider.isOverlayEnabled(packageName),
- false,
- overlayProvider.getAccentColorCategory()
- )
+ AccentColorsGridItem(
+ packageName,
+ overlayColorList,
+ overlayProvider.isOverlayEnabled(packageName),
+ false,
+ overlayProvider.getAccentColorCategory()
+ )
)
} catch (e: NotFoundException) {
}
}
+ // add all custom colors
+ for (customColor in customAccentColors) {
+ val customColorList = ArrayList<Int>()
+ customColorList.add(customColor.darkColor)
+ customColorList.add(customColor.lightColor)
+ accentColorsGridItems.add(
+ FabricatedAccentColorsGridItem(
+ overlayProvider.getFabricatedOverlayIdentifier(customColor.identifier),
+ customColorList,
+ overlayProvider.isOverlayEnabled(overlayProvider.getFabricatedOverlayIdentifier(customColor.identifier)),
+ false,
+ overlayProvider.getAccentColorCategory(),
+ customColor
+ )
+ )
+ }
+ accentColorsGridItems.add(AddAccentColorsGridItem())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ loadCustomAccentColors()
loadIconShapeOverlays()
loadPrimaryColorsOverlays()
loadAccentColorsOverlays()
overlayItemList.clear()
val overlayItemView =
- LayoutInflater.from(context).inflate(R.layout.overlays_item, null, false)
+ 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)
+ 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)
+ LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
primaryColorsListView.adapter =
- ColorsListViewAdapter(requireContext(), primaryColorsGridItems, true)
+ PrimaryColorsListViewAdapter(requireContext(), primaryColorsGridItems)
accentColorsListView = overlayItemView.findViewById(R.id.accent_color_list_view)
accentColorsListView.layoutManager =
- LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
+ LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
accentColorsListView.adapter =
- ColorsListViewAdapter(requireContext(), accentColorsGridItems, false)
+ AccentColorsListViewAdapter(requireContext(), accentColorsGridItems)
overlayItemListView = view.findViewById(R.id.overlay_item_list)
overlayItemListView.adapter = OverlayItemListViewAdapter(requireContext(), overlayItemList)
overlayItemListView.layoutManager =
- LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
+ LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
+ }
+
+ private fun setCustomAccentColor(accentColor: FabricatedAccentColorsGridItem, changeDark: Boolean) {
+ val color = accentColor.customAccentColor
+ val oldColor = if (changeDark) color.darkColor else color.lightColor
+ val dialog = ColorSelectDialog(
+ requireContext(),
+ oldColor, false, CUSTOM_ACCENT_COLORS
+ )
+ dialog.setButton(
+ AlertDialog.BUTTON_POSITIVE, resources.getString(android.R.string.ok)
+ ) { _, _ ->
+ if (changeDark) color.darkColor = dialog.color
+ else color.lightColor = dialog.color
+ saveCustomAccentColors()
+
+ if (accentColor.isEnabled) {
+ overlayProvider.enableFabricatedAccentOverlayTransaction(color)
+ } else {
+ loadAccentColorsOverlays()
+ accentColorsListView.adapter!!.notifyDataSetChanged()
+ }
+ }
+ dialog.setButton(
+ AlertDialog.BUTTON_NEGATIVE, resources.getString(android.R.string.cancel)
+ ) { _, _ ->
+ dialog.dismiss()
+ }
+ dialog.show()
+ }
+
+ private fun doDeleteCustomAccentColor(accentColor: FabricatedAccentColorsGridItem) {
+ val dialog = AlertDialog.Builder(requireContext())
+ dialog.setMessage(getString(R.string.dialog_delete_accent_color_message))
+ dialog.setPositiveButton(resources.getString(android.R.string.ok)
+ ) { _, _ ->
+ deleteCustomAccentColor(accentColor)
+ loadAccentColorsOverlays()
+ accentColorsListView.adapter!!.notifyDataSetChanged()
+ }
+ dialog.setNegativeButton(resources.getString(android.R.string.cancel), null)
+
+ dialog.show()
+ }
+
+ private fun loadCustomAccentColors() {
+ val prefs = requireContext().getSharedPreferences("overlays", Context.MODE_PRIVATE)
+ val colorsString = prefs.getString("colors", "")
+ val deletedColor = prefs.getString("deletedAccentColor", "")
+
+ if (colorsString != null && colorsString.isNotEmpty()) {
+ val colorArray = JSONArray(colorsString)
+ for (i in 0..colorArray.length()) {
+ try {
+ val color = colorArray[i] as JSONObject
+ if (!deletedColor.isNullOrEmpty() && color["identifier"] == deletedColor) {
+ prefs.edit().remove("deletedAccentColor").commit()
+ continue
+ }
+ customAccentColors.add(
+ CustomAccentColor(
+ color["identifier"] as String,
+ color["system_accent1_100"] as Int,
+ color["system_accent1_600"] as Int
+ )
+ )
+ } catch (e: Exception) {
+ }
+ }
+ }
+ }
+
+ private fun saveCustomAccentColors() {
+ val prefs = requireContext().getSharedPreferences("overlays", Context.MODE_PRIVATE)
+ val colorArray = JSONArray()
+ for (color in customAccentColors) {
+ colorArray.put(color.toJSONObject())
+ }
+ prefs.edit().putString("colors", colorArray.toString()).commit()
+ }
+
+ private fun addNewCustomAccentColor() {
+ val customColor = getDefaultCustomAccentColor()
+ customAccentColors.add(customColor)
+ saveCustomAccentColors()
+ loadAccentColorsOverlays()
+ accentColorsListView.adapter!!.notifyDataSetChanged()
+
+ val prefs = requireContext().getSharedPreferences("overlays", Context.MODE_PRIVATE)
+ val firstTime = prefs.getBoolean("firstTimeAdd", true)
+ if (firstTime) {
+ Snackbar.make(requireView(), getString(R.string.delete_accent_color_info_message), Snackbar.LENGTH_LONG).setAction(android.R.string.ok, { prefs.edit().putBoolean("firstTimeAdd", false).commit() }).show()
+ }
+ }
+
+ private fun getDefaultCustomAccentColor(): CustomAccentColor {
+ val customColor = CustomAccentColor(
+ "",
+ resources.getColor(android.R.color.system_accent1_100, null),
+ resources.getColor(android.R.color.system_accent1_600, null)
+ )
+ customColor.identifier = "accent_color_" + customColor.hashCode()
+ return customColor
+ }
+
+ private fun deleteCustomAccentColor(accentColor: AccentColorsGridItem) {
+ if (accentColor !is FabricatedAccentColorsGridItem) return
+
+ if (accentColor.isEnabled) {
+ val prefs = requireContext().getSharedPreferences("overlays", Context.MODE_PRIVATE)
+ prefs.edit().putString("deletedAccentColor", accentColor.customAccentColor.identifier).commit()
+ // set to system default this will trigger a complete reload anyway
+ overlayProvider.enableAccentOverlayTransaction(null)
+ } else {
+ customAccentColors.remove(accentColor.customAccentColor)
+ saveCustomAccentColors()
+ loadAccentColorsOverlays()
+ accentColorsListView.adapter!!.notifyDataSetChanged()
+ }
+ }
+
+ override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
+ super.onCreateContextMenu(menu, v, menuInfo)
+ menu.setHeaderTitle(resources.getString(R.string.accent_color_title));
+ menu.add(0, 0, 0, "Delete");
+ }
+
+ override fun onContextItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == 0) {
+ /*deleteCustomAccentColor(gridItem)
+ loadAccentColorsOverlays()
+ accentColorsListView.adapter!!.notifyDataSetChanged()*/
+ }
+ return super.onContextItemSelected(item)
}
}
diff --git a/app/src/main/java/org/omnirom/control/OverlaysProvider.kt b/app/src/main/java/org/omnirom/control/OverlaysProvider.kt
index 2e46d03..f483595 100644
--- a/app/src/main/java/org/omnirom/control/OverlaysProvider.kt
+++ b/app/src/main/java/org/omnirom/control/OverlaysProvider.kt
@@ -18,11 +18,14 @@
package org.omnirom.control
import android.content.Context
+import android.content.om.FabricatedOverlay
import android.content.om.OverlayInfo
+import android.content.om.OverlayIdentifier
import android.content.om.OverlayManager
+import android.content.om.OverlayManagerTransaction
import android.content.res.Resources
import android.os.UserHandle
-import android.util.Log
+import android.util.TypedValue
class OverlaysProvider(context: Context) {
private val overlaysMap = HashMap<String, OverlayInfo>()
@@ -40,9 +43,8 @@
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)
+ overlaysMap.put(overlayInfo.getOverlayIdentifier().toString(), overlayInfo)
}
}
}
@@ -71,13 +73,20 @@
return "android.theme.customization.system_palette"
}
- fun getCategoryOverlays(category: String): ArrayList<String> {
+ private fun getCategoryOverlays(category: String): ArrayList<String> {
val packageList = ArrayList<String>()
overlaysMap.values.filter { it.getCategory() == category }
.forEach { packageList.add(it.getPackageName()) }
return packageList
}
+ private fun getCategoryOverlaysRaw(category: String): ArrayList<OverlayInfo> {
+ val overlayList = ArrayList<OverlayInfo>()
+ overlaysMap.values.filter { it.getCategory() == category }
+ .forEach { overlayList.add(it) }
+ return overlayList
+ }
+
@Throws(Resources.NotFoundException::class)
fun loadString(stringName: String, packageName: String): String {
val overlayRes: Resources = context.packageManager.getResourcesForApplication(
@@ -116,8 +125,8 @@
)
}
- fun isOverlayEnabled(packageName: String): Boolean {
- val overlayInfo = overlaysMap.get(packageName)
+ fun isOverlayEnabled(identifier: String): Boolean {
+ val overlayInfo = overlaysMap.get(OverlayIdentifier.fromString(identifier).toString())
if (overlayInfo != null) {
return overlayInfo.isEnabled()
}
@@ -130,14 +139,7 @@
}
}
- fun enableOverlay(packageName: String, reload: Boolean) {
- if (om != null) {
- om!!.setEnabled(packageName, true, user)
- if (reload) loadOverlayInfo()
- }
- }
-
- fun disableOverlay(packageName: String, reload: Boolean) {
+ private fun disableOverlay(packageName: String, reload: Boolean) {
if (om != null) {
om!!.setEnabled(packageName, false, user)
if (reload) loadOverlayInfo()
@@ -148,4 +150,118 @@
getCategoryOverlays(category).forEach { disableOverlay(it, false) }
loadOverlayInfo()
}
+
+ fun enableAccentOverlayTransaction(
+ overlay: String?
+ ) {
+ if (om != null) {
+ val transaction = OverlayManagerTransaction.Builder()
+ // disable farbricated
+ om!!.getOverlayInfosForTarget("android", user)
+ .forEach { info ->
+ if (info.isFabricated() && info.getPackageName() == context.packageName) {
+ transaction.unregisterFabricatedOverlay(info.getOverlayIdentifier())
+ }
+ }
+ // disable enabled
+ getCategoryOverlaysRaw(getAccentColorCategory()).filter { it.isEnabled() }
+ .forEach {
+ transaction.setEnabled(
+ it.getOverlayIdentifier(),
+ false,
+ UserHandle.myUserId()
+ )
+ }
+ // enable new if provided
+ if (overlay != null && overlaysMap.containsKey(OverlayIdentifier.fromString(overlay).toString())) {
+ transaction.setEnabled(
+ OverlayIdentifier.fromString(overlay),
+ true,
+ UserHandle.myUserId()
+ )
+ }
+ om!!.commit(transaction.build())
+ }
+ }
+
+ fun enableFabricatedAccentOverlayTransaction(
+ accentColor: OverlaysFragment.CustomAccentColor
+ ) {
+ if (om != null) {
+ val transaction = OverlayManagerTransaction.Builder()
+ // disable farbricated
+ om!!.getOverlayInfosForTarget("android", user)
+ .forEach { info ->
+ if (info.isFabricated() && info.getPackageName() == context.packageName) {
+ transaction.unregisterFabricatedOverlay(info.getOverlayIdentifier())
+ }
+ }
+ // disable enabled
+ getCategoryOverlaysRaw(getAccentColorCategory()).filter { it.isEnabled() }
+ .forEach {
+ transaction.setEnabled(
+ it.getOverlayIdentifier(),
+ false,
+ UserHandle.myUserId()
+ )
+ }
+ // enable fabricated
+ val accentColorOverlay = FabricatedOverlay.Builder(
+ context.packageName, accentColor.identifier, "android"
+ )
+ .setResourceValue(
+ "@android:color/system_accent1_100",
+ TypedValue.TYPE_INT_COLOR_ARGB8,
+ accentColor.darkColor
+ )
+ .setResourceValue(
+ "@android:color/system_accent1_600",
+ TypedValue.TYPE_INT_COLOR_ARGB8,
+ accentColor.lightColor
+ )
+ .build()
+
+
+ transaction.registerFabricatedOverlay(accentColorOverlay)
+ .setEnabled(accentColorOverlay!!.getIdentifier(), true, UserHandle.myUserId())
+ om!!.commit(transaction.build())
+ }
+ }
+
+ fun createFabricatedAccentOverlay(color: Int, name: String) {
+ if (om != null) {
+ val accentColorOverlay = FabricatedOverlay.Builder(
+ context.packageName, name, "android"
+ )
+ .setResourceValue(
+ "@android:color/system_accent1_100",
+ TypedValue.TYPE_INT_COLOR_ARGB8,
+ color
+ )
+ .setResourceValue(
+ "@android:color/system_accent1_600",
+ TypedValue.TYPE_INT_COLOR_ARGB8,
+ color
+ )
+ .build()
+
+ // disable all others
+ val transaction = OverlayManagerTransaction.Builder()
+ getCategoryOverlaysRaw(getAccentColorCategory()).filter { it.isEnabled() }
+ .forEach {
+ transaction.setEnabled(
+ it.getOverlayIdentifier(),
+ false,
+ UserHandle.myUserId()
+ )
+ }
+ transaction.registerFabricatedOverlay(accentColorOverlay)
+ .setEnabled(accentColorOverlay!!.getIdentifier(), true, UserHandle.myUserId())
+ om!!.commit(transaction.build())
+ }
+ }
+
+ fun getFabricatedOverlayIdentifier(overlayName: String) : String {
+ return OverlayIdentifier(context.packageName, overlayName).toString()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/omnirom/control/widget/AlphaPatternDrawable.java b/app/src/main/java/org/omnirom/control/widget/AlphaPatternDrawable.java
new file mode 100644
index 0000000..91be1e3
--- /dev/null
+++ b/app/src/main/java/org/omnirom/control/widget/AlphaPatternDrawable.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * Copyright (C) 2012 The CyanogenMod 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.omnirom.control.widget;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Bitmap.Config;
+import android.graphics.drawable.Drawable;
+
+/**
+ * This drawable that draws a simple white and gray chess board pattern. It's
+ * pattern you will often see as a background behind a partly transparent image
+ * in many applications.
+ *
+ * @author Daniel Nilsson
+ */
+public class AlphaPatternDrawable extends Drawable {
+
+ private int mRectangleSize = 10;
+
+ private Paint mPaint = new Paint();
+ private Paint mPaintWhite = new Paint();
+ private Paint mPaintGray = new Paint();
+
+ private int numRectanglesHorizontal;
+ private int numRectanglesVertical;
+
+ /**
+ * Bitmap in which the pattern will be cached.
+ */
+ private Bitmap mBitmap;
+
+ public AlphaPatternDrawable(int rectangleSize) {
+ mRectangleSize = rectangleSize;
+ mPaintWhite.setColor(0xffffffff);
+ mPaintGray.setColor(0xffcbcbcb);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mBitmap != null) {
+ canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
+ }
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ throw new UnsupportedOperationException("Alpha is not supported by this drawwable.");
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ throw new UnsupportedOperationException("ColorFilter is not supported by this drawwable.");
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+
+ int height = bounds.height();
+ int width = bounds.width();
+
+ numRectanglesHorizontal = (int) Math.ceil((width / mRectangleSize));
+ numRectanglesVertical = (int) Math.ceil(height / mRectangleSize);
+
+ generatePatternBitmap();
+ }
+
+ /**
+ * This will generate a bitmap with the pattern as big as the rectangle we
+ * were allow to draw on. We do this to cache the bitmap so we don't need
+ * to recreate it each time draw() is called since it takes a few
+ * milliseconds.
+ */
+ private void generatePatternBitmap() {
+
+ if (getBounds().width() <= 0 || getBounds().height() <= 0) {
+ return;
+ }
+
+ mBitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888);
+ Canvas canvas = new Canvas(mBitmap);
+
+ Rect r = new Rect();
+ boolean verticalStartWhite = true;
+ for (int i = 0; i <= numRectanglesVertical; i++) {
+ boolean isWhite = verticalStartWhite;
+ for (int j = 0; j <= numRectanglesHorizontal; j++) {
+ r.top = i * mRectangleSize;
+ r.left = j * mRectangleSize;
+ r.bottom = r.top + mRectangleSize;
+ r.right = r.left + mRectangleSize;
+
+ canvas.drawRect(r, isWhite ? mPaintWhite : mPaintGray);
+
+ isWhite = !isWhite;
+ }
+
+ verticalStartWhite = !verticalStartWhite;
+ }
+ }
+}
diff --git a/app/src/main/java/org/omnirom/control/widget/ColorPanelView.java b/app/src/main/java/org/omnirom/control/widget/ColorPanelView.java
new file mode 100644
index 0000000..9cd42cf
--- /dev/null
+++ b/app/src/main/java/org/omnirom/control/widget/ColorPanelView.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * Copyright (C) 2012 The CyanogenMod 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.omnirom.control.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * This class draws a panel which which will be filled with a color which can be
+ * set. It can be used to show the currently selected color which you will get
+ * from the {@link ColorPickerView}.
+ *
+ * @author Daniel Nilsson
+ */
+public class ColorPanelView extends View {
+
+ /**
+ * The width in pixels of the border surrounding the color panel.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ private static float mDensity = 1f;
+
+ private int mBorderColor = 0xff6E6E6E;
+ private int mColor = 0xff000000;
+
+ private Paint mBorderPaint;
+ private Paint mColorPaint;
+
+ private RectF mDrawingRect;
+ private RectF mColorRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+ public ColorPanelView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ init();
+ }
+
+ private void init() {
+ mBorderPaint = new Paint();
+ mColorPaint = new Paint();
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ final RectF rect = mColorRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect, mBorderPaint);
+ }
+
+ if (mAlphaPattern != null) {
+ mAlphaPattern.draw(canvas);
+ }
+
+ mColorPaint.setColor(mColor);
+
+ canvas.drawRect(rect, mColorPaint);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = getPaddingLeft();
+ mDrawingRect.right = w - getPaddingRight();
+ mDrawingRect.top = getPaddingTop();
+ mDrawingRect.bottom = h - getPaddingBottom();
+
+ setUpColorRect();
+
+ }
+
+ private void setUpColorRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mColorRect = new RectF(left, top, right, bottom);
+
+ mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity));
+
+ mAlphaPattern.setBounds(Math.round(mColorRect.left),
+ Math.round(mColorRect.top),
+ Math.round(mColorRect.right),
+ Math.round(mColorRect.bottom));
+
+ }
+
+ /**
+ * Set the color that should be shown by this view.
+ *
+ * @param color
+ */
+ public void setColor(int color) {
+ mColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color currently show by this view.
+ *
+ * @return
+ */
+ public int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Set the color of the border surrounding the panel.
+ *
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding the panel.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+}
diff --git a/app/src/main/java/org/omnirom/control/widget/ColorPickerView.java b/app/src/main/java/org/omnirom/control/widget/ColorPickerView.java
new file mode 100644
index 0000000..2465f3b
--- /dev/null
+++ b/app/src/main/java/org/omnirom/control/widget/ColorPickerView.java
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * Copyright (C) 2012 The CyanogenMod 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.omnirom.control.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Shader.TileMode;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Displays a color picker to the user and allow them to select a color. A
+ * slider for the alpha channel is also available. Enable it by setting
+ * setAlphaSliderVisible(boolean) to true.
+ *
+ * @author Daniel Nilsson
+ */
+public class ColorPickerView extends View {
+
+ public interface OnColorChangedListener {
+ public void onColorChanged(int color);
+ }
+
+ private final static int PANEL_SAT_VAL = 0;
+ private final static int PANEL_HUE = 1;
+ private final static int PANEL_ALPHA = 2;
+
+ /**
+ * The width in pixels of the border surrounding all color panels.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ /**
+ * The width in dp of the hue panel.
+ */
+ private float HUE_PANEL_WIDTH = 30f;
+ /**
+ * The height in dp of the alpha panel
+ */
+ private float ALPHA_PANEL_HEIGHT = 20f;
+ /**
+ * The distance in dp between the different color panels.
+ */
+ private float PANEL_SPACING = 10f;
+ /**
+ * The radius in dp of the color palette tracker circle.
+ */
+ private float PALETTE_CIRCLE_TRACKER_RADIUS = 5f;
+ /**
+ * The dp which the tracker of the hue or alpha panel will extend outside of
+ * its bounds.
+ */
+ private float RECTANGLE_TRACKER_OFFSET = 2f;
+
+ private static float mDensity = 1f;
+
+ private OnColorChangedListener mListener;
+
+ private Paint mSatValPaint;
+ private Paint mSatValTrackerPaint;
+
+ private Paint mHuePaint;
+ private Paint mHueTrackerPaint;
+
+ private Paint mAlphaPaint;
+ private Paint mAlphaTextPaint;
+
+ private Paint mBorderPaint;
+
+ private Shader mValShader;
+ private Shader mSatShader;
+ private Shader mHueShader;
+ private Shader mAlphaShader;
+
+ private int mAlpha = 0xff;
+ private float mHue = 360f;
+ private float mSat = 0f;
+ private float mVal = 0f;
+
+ private String mAlphaSliderText = "Alpha";
+ private int mSliderTrackerColor = 0xff1c1c1c;
+ private int mBorderColor = 0xff6E6E6E;
+ private boolean mShowAlphaPanel = false;
+
+ /*
+ * To remember which panel that has the "focus" when processing hardware
+ * button data.
+ */
+ private int mLastTouchedPanel = PANEL_SAT_VAL;
+
+ /**
+ * Offset from the edge we must have or else the finger tracker will get
+ * clipped when it is drawn outside of the view.
+ */
+ private float mDrawingOffset;
+
+ /*
+ * Distance form the edges of the view of where we are allowed to draw.
+ */
+ private RectF mDrawingRect;
+
+ private RectF mSatValRect;
+ private RectF mHueRect;
+ private RectF mAlphaRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+ private Point mStartTouchPoint = null;
+
+ public ColorPickerView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ PALETTE_CIRCLE_TRACKER_RADIUS *= mDensity;
+ RECTANGLE_TRACKER_OFFSET *= mDensity;
+ HUE_PANEL_WIDTH *= mDensity;
+ ALPHA_PANEL_HEIGHT *= mDensity;
+ PANEL_SPACING = PANEL_SPACING * mDensity;
+
+ mDrawingOffset = calculateRequiredOffset();
+ initPaintTools();
+
+ // Needed for receiving track ball motion events.
+ setFocusableInTouchMode(true);
+ setFocusable(true);
+ setClickable(true);
+ }
+
+ private void initPaintTools() {
+ mSatValPaint = new Paint();
+ mSatValTrackerPaint = new Paint();
+ mHuePaint = new Paint();
+ mHueTrackerPaint = new Paint();
+ mAlphaPaint = new Paint();
+ mAlphaTextPaint = new Paint();
+ mBorderPaint = new Paint();
+
+ mSatValTrackerPaint.setStyle(Style.STROKE);
+ mSatValTrackerPaint.setStrokeWidth(2f * mDensity);
+ mSatValTrackerPaint.setAntiAlias(true);
+
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+ mHueTrackerPaint.setStyle(Style.STROKE);
+ mHueTrackerPaint.setStrokeWidth(2f * mDensity);
+ mHueTrackerPaint.setAntiAlias(true);
+
+ mAlphaTextPaint.setColor(0xff1c1c1c);
+ mAlphaTextPaint.setTextSize(14f * mDensity);
+ mAlphaTextPaint.setAntiAlias(true);
+ mAlphaTextPaint.setTextAlign(Align.CENTER);
+ mAlphaTextPaint.setFakeBoldText(true);
+ }
+
+ private float calculateRequiredOffset() {
+ float offset = Math.max(PALETTE_CIRCLE_TRACKER_RADIUS, RECTANGLE_TRACKER_OFFSET);
+ offset = Math.max(offset, BORDER_WIDTH_PX * mDensity);
+
+ return offset * 1.5f;
+ }
+
+ private int[] buildHueColorArray() {
+ int[] hue = new int[361];
+
+ int count = 0;
+ for (int i = hue.length - 1; i >= 0; i--, count++) {
+ hue[count] = Color.HSVToColor(new float[] {
+ i, 1f, 1f
+ });
+ }
+ return hue;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) {
+ return;
+ }
+ drawSatValPanel(canvas);
+ drawHuePanel(canvas);
+ drawAlphaPanel(canvas);
+ }
+
+ private void drawSatValPanel(Canvas canvas) {
+ final RectF rect = mSatValRect;
+ int rgb = Color.HSVToColor(new float[] {
+ mHue, 1f, 1f
+ });
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect.left, mDrawingRect.top, rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX, mBorderPaint);
+ }
+
+ // On Honeycomb+ we need to use software rendering to create the shader properly
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+
+ // Get the overlaying gradients ready and create the ComposeShader
+ if (mValShader == null) {
+ mValShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom,
+ 0xffffffff, 0xff000000, TileMode.CLAMP);
+ }
+ mSatShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ 0xffffffff, rgb, TileMode.CLAMP);
+ ComposeShader mShader = new ComposeShader(mValShader, mSatShader, Mode.MULTIPLY);
+ mSatValPaint.setShader(mShader);
+ canvas.drawRect(rect, mSatValPaint);
+
+ Point p = satValToPoint(mSat, mVal);
+ mSatValTrackerPaint.setColor(0xff000000);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS - 1f * mDensity,
+ mSatValTrackerPaint);
+
+ mSatValTrackerPaint.setColor(0xffdddddd);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS, mSatValTrackerPaint);
+ }
+
+ private void drawHuePanel(Canvas canvas) {
+ final RectF rect = mHueRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+ if (mHueShader == null) {
+ mHueShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom,
+ buildHueColorArray(), null, TileMode.CLAMP);
+ mHuePaint.setShader(mHueShader);
+ }
+
+ canvas.drawRect(rect, mHuePaint);
+
+ float rectHeight = 4 * mDensity / 2;
+
+ Point p = hueToPoint(mHue);
+
+ RectF r = new RectF();
+ r.left = rect.left - RECTANGLE_TRACKER_OFFSET;
+ r.right = rect.right + RECTANGLE_TRACKER_OFFSET;
+ r.top = p.y - rectHeight;
+ r.bottom = p.y + rectHeight;
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+
+ }
+
+ private void drawAlphaPanel(Canvas canvas) {
+ if (!mShowAlphaPanel || mAlphaRect == null || mAlphaPattern == null) {
+ return;
+ }
+
+ final RectF rect = mAlphaRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+ mAlphaPattern.draw(canvas);
+
+ float[] hsv = new float[] {
+ mHue, mSat, mVal
+ };
+ int color = Color.HSVToColor(hsv);
+ int acolor = Color.HSVToColor(0, hsv);
+
+ mAlphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ color, acolor, TileMode.CLAMP);
+
+ mAlphaPaint.setShader(mAlphaShader);
+
+ canvas.drawRect(rect, mAlphaPaint);
+
+ if (mAlphaSliderText != null && mAlphaSliderText != "") {
+ canvas.drawText(mAlphaSliderText, rect.centerX(), rect.centerY() + 4 * mDensity,
+ mAlphaTextPaint);
+ }
+
+ float rectWidth = 4 * mDensity / 2;
+ Point p = alphaToPoint(mAlpha);
+
+ RectF r = new RectF();
+ r.left = p.x - rectWidth;
+ r.right = p.x + rectWidth;
+ r.top = rect.top - RECTANGLE_TRACKER_OFFSET;
+ r.bottom = rect.bottom + RECTANGLE_TRACKER_OFFSET;
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+ }
+
+ private Point hueToPoint(float hue) {
+ final RectF rect = mHueRect;
+ final float height = rect.height();
+
+ Point p = new Point();
+ p.y = (int) (height - (hue * height / 360f) + rect.top);
+ p.x = (int) rect.left;
+ return p;
+ }
+
+ private Point satValToPoint(float sat, float val) {
+
+ final RectF rect = mSatValRect;
+ final float height = rect.height();
+ final float width = rect.width();
+
+ Point p = new Point();
+
+ p.x = (int) (sat * width + rect.left);
+ p.y = (int) ((1f - val) * height + rect.top);
+
+ return p;
+ }
+
+ private Point alphaToPoint(int alpha) {
+ final RectF rect = mAlphaRect;
+ final float width = rect.width();
+
+ Point p = new Point();
+ p.x = (int) (width - (alpha * width / 0xff) + rect.left);
+ p.y = (int) rect.top;
+ return p;
+ }
+
+ private float[] pointToSatVal(float x, float y) {
+ final RectF rect = mSatValRect;
+ float[] result = new float[2];
+ float width = rect.width();
+ float height = rect.height();
+
+ if (x < rect.left) {
+ x = 0f;
+ } else if (x > rect.right) {
+ x = width;
+ } else {
+ x = x - rect.left;
+ }
+
+ if (y < rect.top) {
+ y = 0f;
+ } else if (y > rect.bottom) {
+ y = height;
+ } else {
+ y = y - rect.top;
+ }
+
+ result[0] = 1.f / width * x;
+ result[1] = 1.f - (1.f / height * y);
+ return result;
+ }
+
+ private float pointToHue(float y) {
+ final RectF rect = mHueRect;
+ float height = rect.height();
+
+ if (y < rect.top) {
+ y = 0f;
+ } else if (y > rect.bottom) {
+ y = height;
+ } else {
+ y = y - rect.top;
+ }
+ return 360f - (y * 360f / height);
+ }
+
+ private int pointToAlpha(int x) {
+ final RectF rect = mAlphaRect;
+ final int width = (int) rect.width();
+
+ if (x < rect.left) {
+ x = 0;
+ } else if (x > rect.right) {
+ x = width;
+ } else {
+ x = x - (int) rect.left;
+ }
+ return 0xff - (x * 0xff / width);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+ boolean update = false;
+
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ switch (mLastTouchedPanel) {
+ case PANEL_SAT_VAL:
+ float sat,
+ val;
+ sat = mSat + x / 50f;
+ val = mVal - y / 50f;
+ if (sat < 0f) {
+ sat = 0f;
+ } else if (sat > 1f) {
+ sat = 1f;
+ }
+
+ if (val < 0f) {
+ val = 0f;
+ } else if (val > 1f) {
+ val = 1f;
+ }
+ mSat = sat;
+ mVal = val;
+ update = true;
+ break;
+ case PANEL_HUE:
+ float hue = mHue - y * 10f;
+ if (hue < 0f) {
+ hue = 0f;
+ } else if (hue > 360f) {
+ hue = 360f;
+ }
+ mHue = hue;
+ update = true;
+ break;
+ case PANEL_ALPHA:
+ if (!mShowAlphaPanel || mAlphaRect == null) {
+ update = false;
+ } else {
+ int alpha = (int) (mAlpha - x * 10);
+ if (alpha < 0) {
+ alpha = 0;
+ } else if (alpha > 0xff) {
+ alpha = 0xff;
+ }
+ mAlpha = alpha;
+ update = true;
+ }
+ break;
+ }
+ }
+
+ if (update) {
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ }));
+ }
+ invalidate();
+ return true;
+ }
+ return super.onTrackballEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean update = false;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mStartTouchPoint = new Point((int) event.getX(), (int) event.getY());
+ update = moveTrackersIfNeeded(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ update = moveTrackersIfNeeded(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ mStartTouchPoint = null;
+ update = moveTrackersIfNeeded(event);
+ break;
+ }
+
+ if (update) {
+ requestFocus();
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ }));
+ }
+ invalidate();
+ return true;
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ private boolean moveTrackersIfNeeded(MotionEvent event) {
+
+ if (mStartTouchPoint == null)
+ return false;
+
+ boolean update = false;
+ int startX = mStartTouchPoint.x;
+ int startY = mStartTouchPoint.y;
+
+ if (mHueRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_HUE;
+ mHue = pointToHue(event.getY());
+ update = true;
+ } else if (mSatValRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_SAT_VAL;
+ float[] result = pointToSatVal(event.getX(), event.getY());
+ mSat = result[0];
+ mVal = result[1];
+ update = true;
+ } else if (mAlphaRect != null && mAlphaRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_ALPHA;
+ mAlpha = pointToAlpha((int) event.getX());
+ update = true;
+ }
+
+ return update;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = 0;
+ int height = 0;
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ int widthAllowed = MeasureSpec.getSize(widthMeasureSpec);
+ int heightAllowed = MeasureSpec.getSize(heightMeasureSpec);
+
+ widthAllowed = chooseWidth(widthMode, widthAllowed);
+ heightAllowed = chooseHeight(heightMode, heightAllowed);
+
+ if (!mShowAlphaPanel) {
+ height = (int) (widthAllowed - PANEL_SPACING - HUE_PANEL_WIDTH);
+
+ // If calculated height (based on the width) is more than the
+ // allowed height.
+ if (height > heightAllowed && heightMode != MeasureSpec.UNSPECIFIED) {
+ height = heightAllowed;
+ width = (int) (height + PANEL_SPACING + HUE_PANEL_WIDTH);
+ } else {
+ width = widthAllowed;
+ }
+ } else {
+
+ width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH);
+
+ if (width > widthAllowed && widthMode != MeasureSpec.UNSPECIFIED) {
+ width = widthAllowed;
+ height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT);
+ } else {
+ height = heightAllowed;
+ }
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ private int chooseWidth(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPrefferedWidth();
+ }
+ }
+
+ private int chooseHeight(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPrefferedHeight();
+ }
+ }
+
+ private int getPrefferedWidth() {
+ int width = getPrefferedHeight();
+ if (mShowAlphaPanel) {
+ width -= (PANEL_SPACING + ALPHA_PANEL_HEIGHT);
+ }
+ return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING);
+ }
+
+ private int getPrefferedHeight() {
+ int height = (int) (200 * mDensity);
+ if (mShowAlphaPanel) {
+ height += PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+ return height;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = mDrawingOffset + getPaddingLeft();
+ mDrawingRect.right = w - mDrawingOffset - getPaddingRight();
+ mDrawingRect.top = mDrawingOffset + getPaddingTop();
+ mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom();
+
+ setUpSatValRect();
+ setUpHueRect();
+ setUpAlphaRect();
+ }
+
+ private void setUpSatValRect() {
+ final RectF dRect = mDrawingRect;
+ float panelSide = dRect.height() - BORDER_WIDTH_PX * 2;
+
+ if (mShowAlphaPanel) {
+ panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = top + panelSide;
+ float right = left + panelSide;
+ mSatValRect = new RectF(left, top, right, bottom);
+ }
+
+ private void setUpHueRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX
+ - (mShowAlphaPanel ? (PANEL_SPACING + ALPHA_PANEL_HEIGHT) : 0);
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mHueRect = new RectF(left, top, right, bottom);
+ }
+
+ private void setUpAlphaRect() {
+ if (!mShowAlphaPanel) {
+ return;
+ }
+
+ final RectF dRect = mDrawingRect;
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mAlphaRect = new RectF(left, top, right, bottom);
+ mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity));
+ mAlphaPattern.setBounds(Math.round(mAlphaRect.left), Math
+ .round(mAlphaRect.top), Math.round(mAlphaRect.right), Math
+ .round(mAlphaRect.bottom));
+ }
+
+ /**
+ * Set a OnColorChangedListener to get notified when the color selected by
+ * the user has changed.
+ *
+ * @param listener
+ */
+ public void setOnColorChangedListener(OnColorChangedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Set the color of the border surrounding all panels.
+ *
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding all panels.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+ /**
+ * Get the current color this view is showing.
+ *
+ * @return the current color.
+ */
+ public int getColor() {
+ return Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ });
+ }
+
+ /**
+ * Set the color the view should show.
+ *
+ * @param color The color that should be selected.
+ */
+ public void setColor(int color) {
+ setColor(color, false);
+ }
+
+ /**
+ * Set the color this view should show.
+ *
+ * @param color The color that should be selected.
+ * @param callback If you want to get a callback to your
+ * OnColorChangedListener.
+ */
+ public void setColor(int color, boolean callback) {
+ int alpha = Color.alpha(color);
+ int red = Color.red(color);
+ int blue = Color.blue(color);
+ int green = Color.green(color);
+ float[] hsv = new float[3];
+
+ Color.RGBToHSV(red, green, blue, hsv);
+ mAlpha = alpha;
+ mHue = hsv[0];
+ mSat = hsv[1];
+ mVal = hsv[2];
+
+ if (callback && mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ }));
+ }
+ invalidate();
+ }
+
+ /**
+ * Get the drawing offset of the color picker view. The drawing offset is
+ * the distance from the side of a panel to the side of the view minus the
+ * padding. Useful if you want to have your own panel below showing the
+ * currently selected color and want to align it perfectly.
+ *
+ * @return The offset in pixels.
+ */
+ public float getDrawingOffset() {
+ return mDrawingOffset;
+ }
+
+ /**
+ * Set if the user is allowed to adjust the alpha panel. Default is false.
+ * If it is set to false no alpha will be set.
+ *
+ * @param visible
+ */
+ public void setAlphaSliderVisible(boolean visible) {
+ if (mShowAlphaPanel != visible) {
+ mShowAlphaPanel = visible;
+
+ /*
+ * Reset all shader to force a recreation. Otherwise they will not
+ * look right after the size of the view has changed.
+ */
+ mValShader = null;
+ mSatShader = null;
+ mHueShader = null;
+ mAlphaShader = null;
+ requestLayout();
+ }
+
+ }
+
+ public boolean isAlphaSliderVisible() {
+ return mShowAlphaPanel;
+ }
+
+ public void setSliderTrackerColor(int color) {
+ mSliderTrackerColor = color;
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+ invalidate();
+ }
+
+ public int getSliderTrackerColor() {
+ return mSliderTrackerColor;
+ }
+
+ /**
+ * Set the text that should be shown in the alpha slider. Set to null to
+ * disable text.
+ *
+ * @param res string resource id.
+ */
+ public void setAlphaSliderText(int res) {
+ String text = getContext().getString(res);
+ setAlphaSliderText(text);
+ }
+
+ /**
+ * Set the text that should be shown in the alpha slider. Set to null to
+ * disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setAlphaSliderText(String text) {
+ mAlphaSliderText = text;
+ invalidate();
+ }
+
+ /**
+ * Get the current value of the text that will be shown in the alpha slider.
+ *
+ * @return
+ */
+ public String getAlphaSliderText() {
+ return mAlphaSliderText;
+ }
+}
diff --git a/app/src/main/java/org/omnirom/control/widget/ColorSelectDialog.java b/app/src/main/java/org/omnirom/control/widget/ColorSelectDialog.java
new file mode 100644
index 0000000..ff5eba8
--- /dev/null
+++ b/app/src/main/java/org/omnirom/control/widget/ColorSelectDialog.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * Copyright (C) 2012 The CyanogenMod 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.omnirom.control.widget;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.GridLayout;
+import android.widget.LinearLayout;
+
+import org.omnirom.control.R;
+
+import java.util.Locale;
+
+import androidx.appcompat.app.AlertDialog;
+
+public class ColorSelectDialog extends AlertDialog implements
+ ColorPickerView.OnColorChangedListener, TextWatcher, OnFocusChangeListener {
+
+ private static final String TAG = "ColorSelectDialog";
+ private final static String STATE_KEY_COLOR = "color";
+
+ private ColorPickerView mColorPicker;
+
+ private EditText mHexColorInput;
+ private ColorPanelView mNewColor;
+ private LayoutInflater mInflater;
+ private LinearLayout mColorPanelView;
+ private boolean mWithAlpha;
+ private Context mContext;
+ private ViewGroup mColorPresetView;
+ private Integer[] mPresetColors;
+
+ public ColorSelectDialog(Context context, int initialColor, boolean withAlpha, Integer[] presetColors) {
+ super(context);
+ mContext = context;
+ mWithAlpha = withAlpha;
+ mPresetColors = presetColors;
+ init(initialColor);
+ }
+
+ private void init(int color) {
+ // To fight color banding.
+ getWindow().setFormat(PixelFormat.RGBA_8888);
+ setUp(color);
+ }
+
+ /**
+ * This function sets up the dialog with the proper values. If the speedOff parameters
+ * has a -1 value disable both spinners
+ *
+ * @param color - the color to set
+ */
+ private void setUp(int color) {
+ mInflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View layout = mInflater.inflate(R.layout.color_select_dialog, null);
+
+ mColorPicker = (ColorPickerView) layout.findViewById(R.id.color_picker_view);
+ mHexColorInput = (EditText) layout.findViewById(R.id.hex_color_input);
+ mNewColor = (ColorPanelView) layout.findViewById(R.id.color_panel);
+ mColorPanelView = (LinearLayout) layout.findViewById(R.id.color_panel_view);
+ mColorPresetView = layout.findViewById(R.id.color_preset_view);
+ if (mPresetColors != null && mPresetColors.length != 0) {
+ mColorPresetView.setVisibility(View.VISIBLE);
+ for (Integer presetColor : mPresetColors) {
+ Log.d(TAG, "presetColor = " + presetColor);
+ View colorPresetItem = mInflater.inflate(R.layout.color_preset_item, null);
+ colorPresetItem.findViewById(R.id.color_preset_item_view).setBackground(new ColorDrawable(presetColor));
+ int colorPresetItemSize = getContext().getResources().getDimensionPixelSize(R.dimen.color_preset_item_size);
+ mColorPresetView.addView(colorPresetItem, colorPresetItemSize, colorPresetItemSize);
+ colorPresetItem.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int presetColor = ((ColorDrawable) v.findViewById(R.id.color_preset_item_view).getBackground()).getColor();
+ mColorPicker.setColor(presetColor, true);
+ }
+ });
+ }
+ }
+
+ mColorPicker.setOnColorChangedListener(this);
+ mHexColorInput.setOnFocusChangeListener(this);
+ setAlphaSliderVisible(mWithAlpha);
+ mColorPicker.setColor(color, true);
+
+ setView(layout);
+
+ mColorPicker.setVisibility(View.VISIBLE);
+ mColorPanelView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public Bundle onSaveInstanceState() {
+ Bundle state = super.onSaveInstanceState();
+ state.putInt(STATE_KEY_COLOR, getColor());
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ mColorPicker.setColor(state.getInt(STATE_KEY_COLOR), true);
+ }
+
+ @Override
+ public void onColorChanged(int color) {
+ final boolean hasAlpha = mWithAlpha;
+ final String format = hasAlpha ? "%08x" : "%06x";
+ final int mask = hasAlpha ? 0xFFFFFFFF : 0x00FFFFFF;
+
+ mNewColor.setColor(color);
+ mHexColorInput.setText(String.format(Locale.US, format, color & mask));
+ }
+
+ public void setAlphaSliderVisible(boolean visible) {
+ mHexColorInput.setFilters(new InputFilter[]{new InputFilter.LengthFilter(visible ? 8 : 6)});
+ mColorPicker.setAlphaSliderVisible(visible);
+ }
+
+ public int getColor() {
+ return mColorPicker.getColor();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ String hexColor = mHexColorInput.getText().toString();
+ if (!hexColor.isEmpty()) {
+ try {
+ int color = Color.parseColor('#' + hexColor);
+ if (!mWithAlpha) {
+ color |= 0xFF000000; // set opaque
+ }
+ mColorPicker.setColor(color);
+ mNewColor.setColor(color);
+ } catch (IllegalArgumentException ex) {
+ // Number format is incorrect, ignore
+ }
+ }
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus) {
+ mHexColorInput.removeTextChangedListener(this);
+ InputMethodManager inputMethodManager = (InputMethodManager) getContext()
+ .getSystemService(Activity.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ } else {
+ mHexColorInput.addTextChangedListener(this);
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/add_accent_color_item_background.xml b/app/src/main/res/drawable/add_accent_color_item_background.xml
new file mode 100644
index 0000000..a3a0177
--- /dev/null
+++ b/app/src/main/res/drawable/add_accent_color_item_background.xml
@@ -0,0 +1,26 @@
+<?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">
+ android:shape="rectangle">
+ <stroke
+ android:width="2dp"
+ android:color="?attr/colorControlNormal"
+ android:dashWidth="8dp"
+ android:dashGap="4dp"/>
+
+</shape>
\ 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
index e1e3f25..df8097a 100644
--- a/app/src/main/res/drawable/check_circle.xml
+++ b/app/src/main/res/drawable/check_circle.xml
@@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:tint="?android:textColorPrimary"
+ android:tint="@color/colorAccent"
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" />
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>
diff --git a/app/src/main/res/drawable/check_circle_shape.xml b/app/src/main/res/drawable/check_circle_shape.xml
index 2e6add0..fe96cf0 100644
--- a/app/src/main/res/drawable/check_circle_shape.xml
+++ b/app/src/main/res/drawable/check_circle_shape.xml
@@ -1,7 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:tint="@color/colorAccent"
+ android:tint="@android:color/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml
new file mode 100644
index 0000000..eb23254
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml
new file mode 100644
index 0000000..5fb767e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_delete.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8,9h8v10L8,19L8,9zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_edit_color.xml b/app/src/main/res/drawable/ic_edit_color.xml
new file mode 100644
index 0000000..8adfae3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit_color.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3,16.25V21h4.75l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,-0.4 0.4,-1.03 0.01,-1.42zM6.92,19L5,17.08l8.06,-8.06 1.92,1.92L6.92,19z"/>
+</vector>
diff --git a/app/src/main/res/layout-land/color_select_dialog.xml b/app/src/main/res/layout-land/color_select_dialog.xml
new file mode 100644
index 0000000..09f76e7
--- /dev/null
+++ b/app/src/main/res/layout-land/color_select_dialog.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2010 Daniel Nilsson
+ Copyright (C) 2012 THe CyanogenMod Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:padding="@dimen/alert_dialog_padding_material">
+
+ <org.omnirom.control.widget.ColorPickerView
+ android:id="@+id/color_picker_view"
+ android:layout_width="280dp"
+ android:layout_height="280dp" />
+
+ <GridLayout
+ android:id="@+id/color_preset_view"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="top"
+ android:layout_margin="3dp"
+ android:columnCount="2"
+ android:rowCount="4"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:id="@+id/color_panel_view"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="top"
+ android:layout_margin="3dp"
+ android:orientation="vertical">
+
+ <EditText
+ android:id="@+id/hex_color_input"
+ android:layout_width="150dp"
+ android:layout_height="40dp"
+ android:digits="0123456789ABCDEFabcdef"
+ android:inputType="textNoSuggestions"
+ android:maxLength="6" />
+
+ <org.omnirom.control.widget.ColorPanelView
+ android:id="@+id/color_panel"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/app/src/main/res/layout/accent_colors_item.xml b/app/src/main/res/layout/accent_colors_item.xml
new file mode 100644
index 0000000..71068c4
--- /dev/null
+++ b/app/src/main/res/layout/accent_colors_item.xml
@@ -0,0 +1,54 @@
+<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="2dp"
+ 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="match_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:src="@android:color/system_neutral1_900" />
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:src="@android:color/system_neutral1_50" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/colors_item_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical" />
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_gravity="center_vertical"
+ android:src="@android:color/system_neutral1_100" />
+
+ <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:background="@drawable/check_circle_shape"
+ android:src="@drawable/check_circle"
+ android:visibility="gone" />
+ </FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/add_accent_colors_item.xml b/app/src/main/res/layout/add_accent_colors_item.xml
new file mode 100644
index 0000000..5f5ceab
--- /dev/null
+++ b/app/src/main/res/layout/add_accent_colors_item.xml
@@ -0,0 +1,20 @@
+<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"
+ android:background="@drawable/add_accent_color_item_background">
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_add"/>
+
+ </FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/color_preset_item.xml b/app/src/main/res/layout/color_preset_item.xml
new file mode 100644
index 0000000..9885f1e
--- /dev/null
+++ b/app/src/main/res/layout/color_preset_item.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/color_preset_item_size"
+ android:layout_height="@dimen/color_preset_item_size"
+ android:background="?android:attr/selectableItemBackground">
+
+ <View
+ android:id="@+id/color_preset_item_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="5dp"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/color_select_dialog.xml b/app/src/main/res/layout/color_select_dialog.xml
new file mode 100644
index 0000000..c527437
--- /dev/null
+++ b/app/src/main/res/layout/color_select_dialog.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2010 Daniel Nilsson
+ Copyright (C) 2012 THe CyanogenMod Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="@dimen/alert_dialog_padding_material">
+
+ <org.omnirom.control.widget.ColorPickerView
+ android:id="@+id/color_picker_view"
+ android:layout_width="330dp"
+ android:layout_height="330dp" />
+
+ <GridLayout
+ android:id="@+id/color_preset_view"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:layout_marginStart="3dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:columnCount="8"
+ android:rowCount="1"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:id="@+id/color_panel_view"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:layout_marginStart="3dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <EditText
+ android:id="@+id/hex_color_input"
+ android:layout_width="155dp"
+ android:layout_height="match_parent"
+ android:digits="0123456789ABCDEFabcdef"
+ android:inputType="textNoSuggestions"
+ android:maxLength="6" />
+
+ <org.omnirom.control.widget.ColorPanelView
+ android:id="@+id/color_panel"
+ android:layout_width="155dp"
+ android:layout_height="match_parent"
+ android:layout_marginStart="10dp" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/app/src/main/res/layout/edit_dark_accent_colors_item.xml b/app/src/main/res/layout/edit_dark_accent_colors_item.xml
new file mode 100644
index 0000000..58fbca0
--- /dev/null
+++ b/app/src/main/res/layout/edit_dark_accent_colors_item.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/accent_color_dark_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/system_neutral1_900"
+ android:foreground="?android:attr/selectableItemBackground"
+ android:scaleType="center"
+ app:tint="@android:color/white"
+ android:src="@drawable/ic_edit_color" />
diff --git a/app/src/main/res/layout/edit_light_accent_colors_item.xml b/app/src/main/res/layout/edit_light_accent_colors_item.xml
new file mode 100644
index 0000000..48556f4
--- /dev/null
+++ b/app/src/main/res/layout/edit_light_accent_colors_item.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/accent_color_light_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/system_neutral1_50"
+ android:foreground="?android:attr/selectableItemBackground"
+ android:scaleType="center"
+ app:tint="@android:color/black"
+ android:src="@drawable/ic_edit_color" />
diff --git a/app/src/main/res/layout/icon_shape_item.xml b/app/src/main/res/layout/icon_shape_item.xml
index 9b2f9c6..08f1c58 100644
--- a/app/src/main/res/layout/icon_shape_item.xml
+++ b/app/src/main/res/layout/icon_shape_item.xml
@@ -1,7 +1,7 @@
<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:layout_margin="2dp"
android:background="@drawable/overlay_item_background">
<FrameLayout
diff --git a/app/src/main/res/layout/colors_item.xml b/app/src/main/res/layout/primary_colors_item.xml
similarity index 97%
rename from app/src/main/res/layout/colors_item.xml
rename to app/src/main/res/layout/primary_colors_item.xml
index a449869..b3d0a7b 100644
--- a/app/src/main/res/layout/colors_item.xml
+++ b/app/src/main/res/layout/primary_colors_item.xml
@@ -1,7 +1,7 @@
<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:layout_margin="2dp"
android:background="@drawable/overlay_item_background">
<FrameLayout
diff --git a/app/src/main/res/values-sw600dp-land/dimens.xml b/app/src/main/res/values-sw600dp-land/dimens.xml
new file mode 100644
index 0000000..7e1a5d7
--- /dev/null
+++ b/app/src/main/res/values-sw600dp-land/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="color_item_column_width">@dimen/color_item_column_height</dimen>
+ <dimen name="color_item_container_width">@dimen/color_item_container_height</dimen>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..cecab27
--- /dev/null
+++ b/app/src/main/res/values-sw600dp/dimens.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="icon_shape_size">64dp</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_container_width">130dp</dimen>
+ <dimen name="color_item_container_height">160dp</dimen>
+ <dimen name="accent_color_item_height">30dp</dimen>
+ <!-- color_item_container_height / 2 - accent_color_item_height -->
+ <dimen name="edit_accent_color_item_height">50dp</dimen>
+ <dimen name="color_item_column_width">150dp</dimen>
+ <dimen name="color_item_column_height">180dp</dimen>
+</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 e7dea2f..d06af94 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -5,4 +5,6 @@
<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>
+ <color name="overlay_item_system_background">@color/colorAccent</color>
+
</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 4c7126d..5fabdfe 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -7,15 +7,20 @@
<dimen name="fragment_icon_margin_start">10dp</dimen>
<dimen name="grid_item_icon_size">32dp</dimen>
<dimen name="grid_icon_margin_start">0dp</dimen>
- <dimen name="icon_shape_size">64dp</dimen>
+ <dimen name="icon_shape_size">48dp</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="icon_shape_container_size">56dp</dimen>
+ <dimen name="icon_shape_column_size">64dp</dimen>
+ <dimen name="color_item_height">12dp</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="color_item_container_width">90dp</dimen>
+ <dimen name="color_item_container_height">120dp</dimen>
+ <dimen name="accent_color_item_height">20dp</dimen>
+ <!-- color_item_container_height / 2 - accent_color_item_height -->
+ <dimen name="edit_accent_color_item_height">40dp</dimen>
+ <dimen name="color_item_column_width">110dp</dimen>
+ <dimen name="color_item_column_height">140dp</dimen>
<dimen name="overlay_fragment_side_margin">10dp</dimen>
+ <dimen name="alert_dialog_padding_material">20dp</dimen>
+ <dimen name="color_preset_item_size">40dp</dimen>
</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d754c64..7810bf8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -70,6 +70,9 @@
<string name="device_registration_title">Google device registration</string>
<string name="device_registration_summary">Register device for Play Protect certification</string>
+ <string name="dialog_delete_accent_color_title">Delete</string>
+ <string name="dialog_delete_accent_color_message">Delete accent color?</string>
+ <string name="delete_accent_color_info_message">Long press to delete accent color</string>
<string name="navigation_bar_menu_arrow_keys_title">Arrow keys while typing</string>
<string name="navigation_bar_menu_arrow_keys_summary">Display left and right cursor buttons while typing</string>