aboutsummaryrefslogtreecommitdiff
path: root/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt
blob: 8e396c7a0d7a4a59ccbe30fd4ea85c3070404bc3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package ca.allanwang.kau.utils

import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.os.Build
import android.support.annotation.ColorInt
import android.support.annotation.IntRange
import android.support.v4.content.ContextCompat
import android.support.v4.graphics.drawable.DrawableCompat
import android.support.v7.widget.AppCompatEditText
import android.widget.*
import com.afollestad.materialdialogs.R

/**
 * Created by Allan Wang on 2017-06-08.
 */
fun Int.isColorDark(): Boolean
        = (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255.0 < 0.5

fun Int.toHexString(withAlpha: Boolean = false, withHexPrefix:Boolean = true): String {
    val hex = if (withAlpha) String.format("#%08X", this)
    else String.format("#%06X", 0xFFFFFF and this)
    return if (withHexPrefix) hex else hex.substring(1)
}

fun Int.isColorVisibleOn(@ColorInt color: Int, @IntRange(from = 0L, to = 255L) delta: Int = 8,
                         @IntRange(from = 0L, to = 255L) minAlpha: Int = 50): Boolean =
        if (Color.alpha(this) < minAlpha) false
        else (Math.abs(Color.red(this) - Color.red(color)) < delta
                && Math.abs(Color.green(this) - Color.green(color)) < delta
                && Math.abs(Color.blue(this) - Color.blue(color)) < delta)


@ColorInt
fun Context.getDisabledColor(): Int {
    val primaryColor = resolveColor(android.R.attr.textColorPrimary)
    val disabledColor = if (primaryColor.isColorDark()) Color.BLACK else Color.WHITE
    return disabledColor.adjustAlpha(0.3f)
}

@ColorInt
fun Int.adjustAlpha(factor: Float): Int {
    val alpha = Math.round(Color.alpha(this) * factor)
    return Color.argb(alpha, Color.red(this), Color.green(this), Color.blue(this))
}

@Throws(IllegalArgumentException::class)
fun String.toColor(): Int {
    val toParse: String
    if (startsWith("#") && length == 4)
        toParse = "#${this[1]}${this[1]}${this[2]}${this[2]}${this[3]}${this[3]}"
    else
        toParse = this
    return Color.parseColor(toParse)
}

//Get ColorStateList
fun Context.colorStateList(@ColorInt color: Int): ColorStateList {
    val disabledColor = getDisabledColor()
    return ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked),
            intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked),
            intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked),
            intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked)),
            intArrayOf(this.resolveColor(R.attr.colorControlNormal), color, disabledColor, disabledColor))
}

/*
 * Tint Helpers
 * Kotlin tint bindings that start with 'tint' so it doesn't conflict with existing methods
 * Largely based on MDTintHelper
 * https://github.com/afollestad/material-dialogs/blob/master/core/src/main/java/com/afollestad/materialdialogs/internal/MDTintHelper.java
 */
fun RadioButton.tint(colors: ColorStateList) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        buttonTintList = colors
    } else {
        val radioDrawable = context.drawable(R.drawable.abc_btn_radio_material)
        val d = DrawableCompat.wrap(radioDrawable)
        DrawableCompat.setTintList(d, colors)
        buttonDrawable = d
    }
}

fun RadioButton.tint(@ColorInt color: Int) = tint(context.colorStateList(color))

fun CheckBox.tint(colors: ColorStateList) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        buttonTintList = colors
    } else {
        val checkDrawable = context.drawable(R.drawable.abc_btn_check_material)
        val drawable = DrawableCompat.wrap(checkDrawable)
        DrawableCompat.setTintList(drawable, colors)
        buttonDrawable = drawable
    }
}

fun CheckBox.tint(@ColorInt color: Int) = tint(context.colorStateList(color))

fun SeekBar.tint(@ColorInt color: Int) {
    val s1 = ColorStateList.valueOf(color)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        thumbTintList = s1
        progressTintList = s1
    } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
        val progressDrawable = DrawableCompat.wrap(progressDrawable)
        this.progressDrawable = progressDrawable
        DrawableCompat.setTintList(progressDrawable, s1)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            val thumbDrawable = DrawableCompat.wrap(thumb)
            DrawableCompat.setTintList(thumbDrawable, s1)
            thumb = thumbDrawable
        }
    } else {
        val mode: PorterDuff.Mode = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
            PorterDuff.Mode.MULTIPLY else PorterDuff.Mode.SRC_IN
        indeterminateDrawable?.setColorFilter(color, mode)
        progressDrawable?.setColorFilter(color, mode)
    }
}

fun ProgressBar.tint(@ColorInt color: Int, skipIndeterminate: Boolean = false) {
    val sl = ColorStateList.valueOf(color)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        progressTintList = sl
        secondaryProgressTintList = sl
        if (!skipIndeterminate) indeterminateTintList = sl
    } else {
        val mode: PorterDuff.Mode = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
            PorterDuff.Mode.MULTIPLY else PorterDuff.Mode.SRC_IN
        indeterminateDrawable?.setColorFilter(color, mode)
        progressDrawable?.setColorFilter(color, mode)
    }
}

fun Context.textColorStateList(@ColorInt color: Int): ColorStateList {
    val states = arrayOf(
            intArrayOf(-android.R.attr.state_enabled),
            intArrayOf(-android.R.attr.state_pressed, -android.R.attr.state_focused),
            intArrayOf()
    )
    val colors = intArrayOf(
            resolveColor(R.attr.colorControlNormal),
            resolveColor(R.attr.colorControlNormal),
            color
    )
    return ColorStateList(states, colors)
}

fun EditText.tint(@ColorInt color: Int) {
    val editTextColorStateList = context.textColorStateList(color)
    if (this is AppCompatEditText) {
        supportBackgroundTintList = editTextColorStateList
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        backgroundTintList = editTextColorStateList
    }
    tintCursor(color)
}

fun EditText.tintCursor(@ColorInt color: Int) {
    try {
        val fCursorDrawableRes = TextView::class.java.getDeclaredField("mCursorDrawableRes")
        fCursorDrawableRes.isAccessible = true
        val mCursorDrawableRes = fCursorDrawableRes.getInt(this)
        val fEditor = TextView::class.java.getDeclaredField("mEditor")
        fEditor.isAccessible = true
        val editor = fEditor.get(this)
        val clazz = editor.javaClass
        val fCursorDrawable = clazz.getDeclaredField("mCursorDrawable")
        fCursorDrawable.isAccessible = true
        val drawables: Array<Drawable> = Array(2, {
            val drawable = ContextCompat.getDrawable(context, mCursorDrawableRes)
            drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN)
            drawable
        })
        fCursorDrawable.set(editor, drawables)
    } catch (e: Exception) {
        e.printStackTrace()
    }

}