759 lines
27 KiB
Kotlin
759 lines
27 KiB
Kotlin
/*
|
|
* Copyright (C) 2017 JRummy Apps Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.jrummyapps.android.colorpicker
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.Activity
|
|
import android.app.Dialog
|
|
import android.content.Context
|
|
import android.content.DialogInterface
|
|
import android.graphics.Color
|
|
import android.graphics.PorterDuff
|
|
import android.os.Bundle
|
|
import android.text.Editable
|
|
import android.text.InputFilter
|
|
import android.text.InputFilter.LengthFilter
|
|
import android.text.TextWatcher
|
|
import android.util.TypedValue
|
|
import android.view.MotionEvent
|
|
import android.view.View
|
|
import android.view.View.OnTouchListener
|
|
import android.view.ViewGroup.MarginLayoutParams
|
|
import android.view.WindowManager
|
|
import android.view.inputmethod.InputMethodManager
|
|
import android.widget.*
|
|
import android.widget.SeekBar.OnSeekBarChangeListener
|
|
import androidx.annotation.ColorInt
|
|
import androidx.annotation.IntDef
|
|
import androidx.annotation.StringRes
|
|
import androidx.appcompat.app.AlertDialog
|
|
import androidx.core.graphics.ColorUtils
|
|
import androidx.fragment.app.DialogFragment
|
|
import androidx.fragment.app.FragmentActivity
|
|
import com.jrummyapps.android.colorpicker.ColorPickerView.OnColorChangedListener
|
|
import java.util.*
|
|
import kotlin.math.roundToInt
|
|
|
|
/**
|
|
*
|
|
* A dialog to pick a color.
|
|
*
|
|
*
|
|
* The [activity][Activity] that shows this dialog should implement [ColorPickerDialogListener]
|
|
*
|
|
*
|
|
* Example usage:
|
|
*
|
|
* <pre>
|
|
* ColorPickerDialog.newBuilder().show(activity);
|
|
</pre> *
|
|
*/
|
|
class ColorPickerDialog :
|
|
DialogFragment(),
|
|
OnTouchListener,
|
|
OnColorChangedListener,
|
|
TextWatcher {
|
|
|
|
companion object {
|
|
private const val ARG_ID = "id"
|
|
private const val ARG_TYPE = "dialogType"
|
|
private const val ARG_COLOR = "color"
|
|
private const val ARG_ALPHA = "alpha"
|
|
private const val ARG_PRESETS = "presets"
|
|
private const val ARG_ALLOW_PRESETS = "allowPresets"
|
|
private const val ARG_ALLOW_CUSTOM = "allowCustom"
|
|
private const val ARG_DIALOG_TITLE = "dialogTitle"
|
|
private const val ARG_SHOW_COLOR_SHADES = "showColorShades"
|
|
private const val ARG_COLOR_SHAPE = "colorShape"
|
|
const val TYPE_CUSTOM = 0
|
|
const val TYPE_PRESETS = 1
|
|
const val ALPHA_THRESHOLD = 165
|
|
|
|
/**
|
|
* Material design colors used as the default color presets
|
|
*/
|
|
@JvmField
|
|
val MATERIAL_COLORS = intArrayOf(
|
|
-0xbbcca, // RED 500
|
|
-0x16e19d, // PINK 500
|
|
-0xd36d, // LIGHT PINK 500
|
|
-0x63d850, // PURPLE 500
|
|
-0x98c549, // DEEP PURPLE 500
|
|
-0xc0ae4b, // INDIGO 500
|
|
-0xde690d, // BLUE 500
|
|
-0xfc560c, // LIGHT BLUE 500
|
|
-0xff432c, // CYAN 500
|
|
-0xff6978, // TEAL 500
|
|
-0xb350b0, // GREEN 500
|
|
-0x743cb6, // LIGHT GREEN 500
|
|
-0x3223c7, // LIME 500
|
|
-0x14c5, // YELLOW 500
|
|
-0x3ef9, // AMBER 500
|
|
-0x6800, // ORANGE 500
|
|
-0x86aab8, // BROWN 500
|
|
-0x9f8275, // BLUE GREY 500
|
|
-0x616162
|
|
)
|
|
|
|
/**
|
|
* Create a new Builder for creating a [ColorPickerDialog] instance
|
|
*
|
|
* @return The [builder][Builder] to create the [ColorPickerDialog].
|
|
*/
|
|
@JvmStatic
|
|
fun newBuilder(): Builder {
|
|
return Builder()
|
|
}
|
|
|
|
fun unshiftIfNotExists(array: IntArray, value: Int): IntArray {
|
|
if (array.any { it == value }) {
|
|
return array
|
|
}
|
|
val newArray = IntArray(array.size + 1)
|
|
newArray[0] = value
|
|
System.arraycopy(array, 0, newArray, 1, newArray.size - 1)
|
|
return newArray
|
|
}
|
|
|
|
fun pushIfNotExists(array: IntArray, value: Int): IntArray {
|
|
if (array.any { it == value }) {
|
|
return array
|
|
}
|
|
val newArray = IntArray(array.size + 1)
|
|
newArray[newArray.size - 1] = value
|
|
System.arraycopy(array, 0, newArray, 0, newArray.size - 1)
|
|
return newArray
|
|
}
|
|
}
|
|
|
|
@Retention(AnnotationRetention.SOURCE)
|
|
@IntDef(TYPE_CUSTOM, TYPE_PRESETS)
|
|
annotation class DialogType
|
|
|
|
var colorPickerDialogListener: ColorPickerDialogListener? = null
|
|
|
|
var presets: IntArray = MATERIAL_COLORS
|
|
|
|
private var rootView: FrameLayout? = null
|
|
|
|
@ColorInt
|
|
var color = 0
|
|
private var dialogType = 0
|
|
var dialogId = 0
|
|
var showColorShades = false
|
|
private var colorShape = 0
|
|
|
|
// -- PRESETS --------------------------
|
|
internal var adapter: ColorPaletteAdapter? = null
|
|
private var shadesLayout: LinearLayout? = null
|
|
private var transparencySeekBar: SeekBar? = null
|
|
private var transparencyPercText: TextView? = null
|
|
|
|
// -- CUSTOM ---------------------------
|
|
var colorPicker: ColorPickerView? = null
|
|
private var newColorPanel: ColorPanelView? = null
|
|
private var hexEditText: EditText? = null
|
|
private var showAlphaSlider = false
|
|
private var fromEditText = false
|
|
|
|
private val selectedItemPosition: Int
|
|
get() = presets.indexOf(color)
|
|
|
|
override fun onAttach(context: Context) {
|
|
super.onAttach(context)
|
|
if (colorPickerDialogListener == null && context is ColorPickerDialogListener) {
|
|
colorPickerDialogListener = context
|
|
}
|
|
}
|
|
|
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
val args = arguments ?: error("onCreateDialog: args is null")
|
|
val activity = activity ?: error("onCreateDialog: activity is null")
|
|
dialogId = args.getInt(ARG_ID)
|
|
showAlphaSlider = args.getBoolean(ARG_ALPHA)
|
|
showColorShades = args.getBoolean(ARG_SHOW_COLOR_SHADES)
|
|
colorShape = args.getInt(ARG_COLOR_SHAPE)
|
|
if (savedInstanceState == null) {
|
|
color = args.getInt(ARG_COLOR)
|
|
dialogType = args.getInt(ARG_TYPE)
|
|
} else {
|
|
color = savedInstanceState.getInt(ARG_COLOR)
|
|
dialogType = savedInstanceState.getInt(ARG_TYPE)
|
|
}
|
|
val rootView = FrameLayout(activity).also { this.rootView = it }
|
|
when (dialogType) {
|
|
TYPE_CUSTOM -> rootView.addView(createPickerView())
|
|
TYPE_PRESETS -> rootView.addView(createPresetsView())
|
|
}
|
|
|
|
val builder = AlertDialog.Builder(activity)
|
|
.setView(rootView)
|
|
.setPositiveButton(R.string.cpv_select) { _, _ ->
|
|
colorPickerDialogListener?.onColorSelected(dialogId, color)
|
|
}
|
|
|
|
val dialogTitleStringRes = args.getInt(ARG_DIALOG_TITLE)
|
|
if (dialogTitleStringRes != 0) {
|
|
builder.setTitle(dialogTitleStringRes)
|
|
}
|
|
val neutralButtonStringRes: Int
|
|
neutralButtonStringRes =
|
|
if (dialogType == TYPE_CUSTOM && args.getBoolean(ARG_ALLOW_PRESETS)) {
|
|
R.string.cpv_presets
|
|
} else if (dialogType == TYPE_PRESETS && args.getBoolean(ARG_ALLOW_CUSTOM)) {
|
|
R.string.cpv_custom
|
|
} else {
|
|
0
|
|
}
|
|
if (neutralButtonStringRes != 0) {
|
|
builder.setNeutralButton(neutralButtonStringRes, null)
|
|
}
|
|
return builder.create()
|
|
}
|
|
|
|
override fun onStart() {
|
|
super.onStart()
|
|
val dialog = dialog as AlertDialog
|
|
|
|
// http://stackoverflow.com/a/16972670/1048340
|
|
dialog.window?.clearFlags(
|
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
|
|
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
|
|
)
|
|
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
|
|
|
|
// Do not dismiss the dialog when clicking the neutral button.
|
|
val neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
|
|
neutralButton?.setOnClickListener { v: View ->
|
|
rootView!!.removeAllViews()
|
|
when (dialogType) {
|
|
TYPE_CUSTOM -> {
|
|
dialogType = TYPE_PRESETS
|
|
(v as Button).setText(R.string.cpv_custom)
|
|
rootView!!.addView(createPresetsView())
|
|
}
|
|
TYPE_PRESETS -> {
|
|
dialogType = TYPE_CUSTOM
|
|
(v as Button).setText(R.string.cpv_presets)
|
|
rootView!!.addView(createPickerView())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onDismiss(dialog: DialogInterface) {
|
|
super.onDismiss(dialog)
|
|
colorPickerDialogListener!!.onDialogDismissed(dialogId)
|
|
}
|
|
|
|
override fun onSaveInstanceState(outState: Bundle) {
|
|
outState.putInt(ARG_COLOR, color)
|
|
outState.putInt(ARG_TYPE, dialogType)
|
|
super.onSaveInstanceState(outState)
|
|
}
|
|
|
|
// region Custom Picker
|
|
private fun createPickerView(): View {
|
|
val args = arguments ?: error("createPickerView: args is null")
|
|
val activity = activity ?: error("createPickerView: activity is null")
|
|
val contentView = View.inflate(getActivity(), R.layout.cpv_dialog_color_picker, null)
|
|
colorPicker = contentView.findViewById(R.id.cpv_color_picker_view)
|
|
val oldColorPanel: ColorPanelView = contentView.findViewById(R.id.cpv_color_panel_old)
|
|
newColorPanel = contentView.findViewById(R.id.cpv_color_panel_new)
|
|
val arrowRight = contentView.findViewById<ImageView>(R.id.cpv_arrow_right)
|
|
hexEditText = contentView.findViewById(R.id.cpv_hex)
|
|
try {
|
|
val value = TypedValue()
|
|
val typedArray = activity.obtainStyledAttributes(
|
|
value.data, intArrayOf(android.R.attr.textColorPrimary)
|
|
)
|
|
val arrowColor = typedArray.getColor(0, Color.BLACK)
|
|
typedArray.recycle()
|
|
arrowRight.setColorFilter(arrowColor)
|
|
} catch (ignored: Exception) {
|
|
}
|
|
oldColorPanel.color = args.getInt(ARG_COLOR)
|
|
|
|
val c = color
|
|
|
|
colorPicker?.apply {
|
|
setAlphaSliderVisible(showAlphaSlider)
|
|
setColor(c, true)
|
|
onColorChangedListener = this@ColorPickerDialog
|
|
}
|
|
|
|
newColorPanel?.apply {
|
|
color = c
|
|
setOnClickListener {
|
|
if (color == c) {
|
|
colorPickerDialogListener?.onColorSelected(dialogId, color)
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
|
|
hexEditText?.apply {
|
|
setHex(color)
|
|
addTextChangedListener(this@ColorPickerDialog)
|
|
setOnFocusChangeListener { _, hasFocus ->
|
|
if (hasFocus) showSoftInput(true)
|
|
}
|
|
if (!showAlphaSlider) {
|
|
filters = arrayOf<InputFilter>(LengthFilter(6))
|
|
}
|
|
}
|
|
|
|
contentView.setOnTouchListener(this)
|
|
|
|
return contentView
|
|
}
|
|
|
|
private fun View?.showSoftInput(show: Boolean) {
|
|
this ?: return
|
|
val imm = (activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)
|
|
?: return
|
|
if (show) {
|
|
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
|
|
} else {
|
|
imm.hideSoftInputFromWindow(this.windowToken, 0)
|
|
}
|
|
}
|
|
|
|
@SuppressLint("ClickableViewAccessibility")
|
|
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
|
val hexEditText = this.hexEditText
|
|
if (hexEditText?.hasFocus() == true && v !== hexEditText) {
|
|
hexEditText.clearFocus()
|
|
hexEditText.showSoftInput(false)
|
|
hexEditText.clearFocus()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
override fun onColorChanged(newColor: Int) {
|
|
color = newColor
|
|
newColorPanel!!.color = newColor
|
|
if (!fromEditText) {
|
|
setHex(newColor)
|
|
val hexEditText = this.hexEditText
|
|
if (hexEditText?.hasFocus() == true) {
|
|
hexEditText.showSoftInput(false)
|
|
hexEditText.clearFocus()
|
|
}
|
|
}
|
|
fromEditText = false
|
|
}
|
|
|
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
|
override fun afterTextChanged(s: Editable) {
|
|
if (hexEditText!!.isFocused) {
|
|
try {
|
|
val color = parseColorString(s.toString())
|
|
if (color != colorPicker!!.color) {
|
|
fromEditText = true
|
|
colorPicker!!.setColor(color, true)
|
|
}
|
|
} catch (ex: NumberFormatException) {
|
|
// nothing to do
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun setHex(color: Int) {
|
|
val hexText = when {
|
|
showAlphaSlider -> "%08X".format(color)
|
|
else -> "%06X".format(color and 0xFFFFFF)
|
|
}
|
|
hexEditText?.setText(hexText)
|
|
}
|
|
|
|
// -- endregion --
|
|
// region Presets Picker
|
|
private fun createPresetsView(): View {
|
|
val contentView = View.inflate(activity, R.layout.cpv_dialog_presets, null)
|
|
shadesLayout = contentView.findViewById(R.id.shades_layout)
|
|
transparencySeekBar = contentView.findViewById(R.id.transparency_seekbar)
|
|
transparencyPercText = contentView.findViewById(R.id.transparency_text)
|
|
val gridView = contentView.findViewById<GridView>(R.id.gridView)
|
|
loadPresets()
|
|
if (showColorShades) {
|
|
createColorShades(color)
|
|
} else {
|
|
shadesLayout?.visibility = View.GONE
|
|
contentView.findViewById<View>(R.id.shades_divider).visibility = View.GONE
|
|
}
|
|
adapter = ColorPaletteAdapter(presets, selectedItemPosition, colorShape) {
|
|
when (it) {
|
|
color -> {
|
|
colorPickerDialogListener?.onColorSelected(dialogId, color)
|
|
dismiss()
|
|
}
|
|
else -> {
|
|
color = it
|
|
if (showColorShades) {
|
|
createColorShades(color)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gridView.adapter = adapter
|
|
if (showAlphaSlider) {
|
|
setupTransparency()
|
|
} else {
|
|
contentView.findViewById<View>(R.id.transparency_layout).visibility = View.GONE
|
|
contentView.findViewById<View>(R.id.transparency_title).visibility = View.GONE
|
|
}
|
|
return contentView
|
|
}
|
|
|
|
private fun loadPresets() {
|
|
presets = arguments?.getIntArray(ARG_PRESETS) ?: MATERIAL_COLORS
|
|
presets = presets.copyOf()
|
|
|
|
val isMaterialColors = presets.contentEquals(MATERIAL_COLORS)
|
|
|
|
val alpha = Color.alpha(color)
|
|
|
|
// don't update the original array when modifying alpha
|
|
if (alpha != 255) {
|
|
// add alpha to the presets
|
|
for (i in presets.indices) {
|
|
val color = presets[i]
|
|
val red = Color.red(color)
|
|
val green = Color.green(color)
|
|
val blue = Color.blue(color)
|
|
presets[i] = Color.argb(alpha, red, green, blue)
|
|
}
|
|
}
|
|
presets = unshiftIfNotExists(presets, color)
|
|
if (isMaterialColors && presets.size == 19) {
|
|
// Add black to have a total of 20 colors if the current color is in the material color palette
|
|
presets = pushIfNotExists(presets, Color.argb(alpha, 0, 0, 0))
|
|
}
|
|
}
|
|
|
|
private fun createColorShades(@ColorInt color: Int) {
|
|
val colorShades = getColorShades(color)
|
|
if (shadesLayout!!.childCount != 0) {
|
|
for (i in 0 until shadesLayout!!.childCount) {
|
|
val layout = shadesLayout!!.getChildAt(i) as FrameLayout
|
|
val cpv: ColorPanelView = layout.findViewById(R.id.cpv_color_panel_view)
|
|
val iv = layout.findViewById<ImageView>(R.id.cpv_color_image_view)
|
|
cpv.color = colorShades[i]
|
|
cpv.tag = false
|
|
iv.setImageDrawable(null)
|
|
}
|
|
return
|
|
}
|
|
val horizontalPadding = resources
|
|
.getDimensionPixelSize(R.dimen.cpv_item_horizontal_padding)
|
|
for (colorShade in colorShades) {
|
|
var layoutResId: Int
|
|
layoutResId = if (colorShape == ColorShape.SQUARE) {
|
|
R.layout.cpv_color_item_square
|
|
} else {
|
|
R.layout.cpv_color_item_circle
|
|
}
|
|
val view = View.inflate(activity, layoutResId, null)
|
|
val colorPanelView: ColorPanelView = view.findViewById(R.id.cpv_color_panel_view)
|
|
val params = colorPanelView
|
|
.layoutParams as MarginLayoutParams
|
|
params.rightMargin = horizontalPadding
|
|
params.leftMargin = params.rightMargin
|
|
colorPanelView.layoutParams = params
|
|
colorPanelView.color = colorShade
|
|
shadesLayout!!.addView(view)
|
|
colorPanelView.post {
|
|
// The color is black when rotating the dialog. This is a dirty fix. WTF!?
|
|
colorPanelView.color = colorShade
|
|
}
|
|
colorPanelView.setOnClickListener { v: View ->
|
|
if (v.tag is Boolean && v.tag as Boolean) {
|
|
colorPickerDialogListener!!.onColorSelected(
|
|
dialogId,
|
|
this@ColorPickerDialog.color
|
|
)
|
|
dismiss()
|
|
return@setOnClickListener // already selected
|
|
}
|
|
this@ColorPickerDialog.color = colorPanelView.color
|
|
adapter!!.selectNone()
|
|
for (i in 0 until shadesLayout!!.childCount) {
|
|
val layout = shadesLayout!!.getChildAt(i) as FrameLayout
|
|
val cpv: ColorPanelView = layout.findViewById(R.id.cpv_color_panel_view)
|
|
val iv = layout.findViewById<ImageView>(R.id.cpv_color_image_view)
|
|
iv.setImageResource(if (cpv === v) R.drawable.cpv_preset_checked else 0)
|
|
if (cpv === v && ColorUtils.calculateLuminance(cpv.color) >= 0.65 ||
|
|
Color.alpha(cpv.color) <= ALPHA_THRESHOLD
|
|
) {
|
|
iv.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN)
|
|
} else {
|
|
iv.colorFilter = null
|
|
}
|
|
cpv.tag = cpv === v
|
|
}
|
|
}
|
|
colorPanelView.setOnLongClickListener {
|
|
colorPanelView.showHint()
|
|
true
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun shadeColor(@ColorInt color: Int, percent: Double): Int {
|
|
val hex = "#%06X".format(color and 0xFFFFFF)
|
|
val f = hex.substring(1).toLong(16)
|
|
val t = (if (percent < 0) 0 else 255).toDouble()
|
|
val p = if (percent < 0) percent * -1 else percent
|
|
val cR = f shr 16
|
|
val cG = f shr 8 and 0x00FF
|
|
val cB = f and 0x0000FF
|
|
return Color.argb(
|
|
Color.alpha(color),
|
|
((t - cR) * p).roundToInt() + cR.toInt(),
|
|
((t - cG) * p).roundToInt() + cG.toInt(),
|
|
((t - cB) * p).roundToInt() + cB.toInt(),
|
|
)
|
|
}
|
|
|
|
private fun getColorShades(@ColorInt color: Int): IntArray {
|
|
return intArrayOf(
|
|
shadeColor(color, 0.9),
|
|
shadeColor(color, 0.7),
|
|
shadeColor(color, 0.5),
|
|
shadeColor(color, 0.333),
|
|
shadeColor(color, 0.166),
|
|
shadeColor(color, -0.125),
|
|
shadeColor(color, -0.25),
|
|
shadeColor(color, -0.375),
|
|
shadeColor(color, -0.5),
|
|
shadeColor(color, -0.675),
|
|
shadeColor(color, -0.7),
|
|
shadeColor(color, -0.775)
|
|
)
|
|
}
|
|
|
|
private fun setupTransparency() {
|
|
|
|
val transparency = 255 - Color.alpha(color)
|
|
val percentage = (transparency.toDouble() * 100 / 255).toInt()
|
|
|
|
transparencyPercText?.text =
|
|
String.format(Locale.ENGLISH, "%d%%", percentage)
|
|
|
|
transparencySeekBar?.apply {
|
|
max = 255
|
|
progress = transparency
|
|
this.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
|
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
|
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
|
handleTransparencyChanged(progress)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
private fun handleTransparencyChanged(transparency: Int) {
|
|
|
|
val percentage = (transparency.toDouble() * 100 / 255).toInt()
|
|
val alpha = 255 - transparency
|
|
|
|
transparencyPercText?.text =
|
|
String.format(Locale.ENGLISH, "%d%%", percentage)
|
|
|
|
// update color:
|
|
val red = Color.red(color)
|
|
val green = Color.green(color)
|
|
val blue = Color.blue(color)
|
|
color = Color.argb(alpha, red, green, blue)
|
|
|
|
// update items in GridView:
|
|
adapter?.apply {
|
|
for (i in colors.indices) {
|
|
val color = colors[i]
|
|
colors[i] = Color.argb(
|
|
alpha,
|
|
Color.red(color),
|
|
Color.green(color),
|
|
Color.blue(color)
|
|
)
|
|
}
|
|
notifyDataSetChanged()
|
|
}
|
|
|
|
// update shades:
|
|
shadesLayout?.apply {
|
|
for (i in 0 until childCount) {
|
|
val layout = getChildAt(i) as FrameLayout
|
|
val cpv: ColorPanelView = layout.findViewById(R.id.cpv_color_panel_view)
|
|
val iv = layout.findViewById<ImageView>(R.id.cpv_color_image_view)
|
|
if (layout.tag == null) {
|
|
// save the original border color
|
|
layout.tag = cpv.borderColor
|
|
}
|
|
var color = cpv.color
|
|
color = Color.argb(
|
|
alpha,
|
|
Color.red(color),
|
|
Color.green(color),
|
|
Color.blue(color)
|
|
)
|
|
if (alpha <= ALPHA_THRESHOLD) {
|
|
cpv.borderColor = color or -0x1000000
|
|
} else {
|
|
cpv.borderColor = layout.tag as Int
|
|
}
|
|
if (cpv.tag != null && cpv.tag as Boolean) {
|
|
// The alpha changed on the selected shaded color. Update the checkmark color filter.
|
|
if (alpha <= ALPHA_THRESHOLD) {
|
|
iv.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN)
|
|
} else {
|
|
if (ColorUtils.calculateLuminance(color) >= 0.65) {
|
|
iv.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN)
|
|
} else {
|
|
iv.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
|
|
}
|
|
}
|
|
}
|
|
cpv.color = color
|
|
}
|
|
}
|
|
}
|
|
|
|
@Suppress("MemberVisibilityCanBePrivate")
|
|
class Builder(
|
|
// dialog title string resource id.
|
|
@StringRes
|
|
var dialogTitle: Int = R.string.cpv_default_title,
|
|
|
|
// which dialog view to show.
|
|
// Either [ColorPickerDialog.TYPE_CUSTOM] or [ColorPickerDialog.TYPE_PRESETS].
|
|
var dialogType: Int = TYPE_PRESETS,
|
|
|
|
// the colors used for the presets.
|
|
var presets: IntArray = MATERIAL_COLORS,
|
|
|
|
// the original color.
|
|
@ColorInt
|
|
var color: Int = Color.BLACK,
|
|
|
|
// the dialog id used for callbacks.
|
|
var dialogId: Int = 0,
|
|
|
|
// the alpha slider
|
|
// true to show the alpha slider.
|
|
// Currently only supported with the ColorPickerView.
|
|
var showAlphaSlider: Boolean = false,
|
|
|
|
// Show/Hide a neutral button to select preset colors.
|
|
// false to disable showing the presets button.
|
|
var allowPresets: Boolean = true,
|
|
|
|
// Show/Hide the neutral button to select a custom color.
|
|
// false to disable showing the custom button.
|
|
var allowCustom: Boolean = true,
|
|
|
|
// Show/Hide the color shades in the presets picker
|
|
// false to hide the color shades.
|
|
var showColorShades: Boolean = true,
|
|
|
|
// the shape of the color panel view.
|
|
// Either [ColorShape.CIRCLE] or [ColorShape.SQUARE].
|
|
var colorShape: Int = ColorShape.CIRCLE,
|
|
) {
|
|
fun setDialogTitle(@StringRes dialogTitle: Int): Builder {
|
|
this.dialogTitle = dialogTitle
|
|
return this
|
|
}
|
|
|
|
fun setDialogType(@DialogType dialogType: Int): Builder {
|
|
this.dialogType = dialogType
|
|
return this
|
|
}
|
|
|
|
fun setPresets(presets: IntArray): Builder {
|
|
this.presets = presets
|
|
return this
|
|
}
|
|
|
|
fun setColor(color: Int): Builder {
|
|
this.color = color
|
|
return this
|
|
}
|
|
|
|
fun setDialogId(dialogId: Int): Builder {
|
|
this.dialogId = dialogId
|
|
return this
|
|
}
|
|
|
|
fun setShowAlphaSlider(showAlphaSlider: Boolean): Builder {
|
|
this.showAlphaSlider = showAlphaSlider
|
|
return this
|
|
}
|
|
|
|
fun setAllowPresets(allowPresets: Boolean): Builder {
|
|
this.allowPresets = allowPresets
|
|
return this
|
|
}
|
|
|
|
fun setAllowCustom(allowCustom: Boolean): Builder {
|
|
this.allowCustom = allowCustom
|
|
return this
|
|
}
|
|
|
|
fun setShowColorShades(showColorShades: Boolean): Builder {
|
|
this.showColorShades = showColorShades
|
|
return this
|
|
}
|
|
|
|
fun setColorShape(colorShape: Int): Builder {
|
|
this.colorShape = colorShape
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Create the [ColorPickerDialog] instance.
|
|
*/
|
|
fun create(): ColorPickerDialog {
|
|
val dialog = ColorPickerDialog()
|
|
val args = Bundle()
|
|
args.putInt(ARG_ID, dialogId)
|
|
args.putInt(ARG_TYPE, dialogType)
|
|
args.putInt(ARG_COLOR, color)
|
|
args.putIntArray(ARG_PRESETS, presets)
|
|
args.putBoolean(ARG_ALPHA, showAlphaSlider)
|
|
args.putBoolean(ARG_ALLOW_CUSTOM, allowCustom)
|
|
args.putBoolean(ARG_ALLOW_PRESETS, allowPresets)
|
|
args.putInt(ARG_DIALOG_TITLE, dialogTitle)
|
|
args.putBoolean(ARG_SHOW_COLOR_SHADES, showColorShades)
|
|
args.putInt(ARG_COLOR_SHAPE, colorShape)
|
|
dialog.arguments = args
|
|
return dialog
|
|
}
|
|
|
|
/**
|
|
* Create and show the [ColorPickerDialog] created with this builder.
|
|
*/
|
|
fun show(activity: FragmentActivity) {
|
|
create().show(activity.supportFragmentManager, "color-picker-dialog")
|
|
}
|
|
}
|
|
}
|