2023-01-13 13:22:25 +01:00
|
|
|
package jp.juggler.util.ui
|
2018-12-01 00:02:18 +01:00
|
|
|
|
|
|
|
import android.app.Activity
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.ContextWrapper
|
2020-09-27 12:59:54 +02:00
|
|
|
import android.graphics.Color
|
|
|
|
import android.os.Build
|
2018-12-01 00:02:18 +01:00
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
2020-09-27 12:59:54 +02:00
|
|
|
import android.view.WindowInsetsController
|
2018-12-01 00:02:18 +01:00
|
|
|
import android.view.inputmethod.InputMethodManager
|
2019-10-05 14:28:04 +02:00
|
|
|
import android.widget.CompoundButton
|
2021-12-04 09:55:28 +01:00
|
|
|
import android.widget.TextView
|
2021-06-28 09:09:00 +02:00
|
|
|
import androidx.annotation.ColorInt
|
2020-09-27 12:59:54 +02:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2023-01-13 13:22:25 +01:00
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
import jp.juggler.util.log.LogCategory
|
2020-09-27 12:59:54 +02:00
|
|
|
import kotlin.math.pow
|
2018-12-01 00:02:18 +01:00
|
|
|
|
2018-12-04 10:59:01 +01:00
|
|
|
private val log = LogCategory("ViewUtils")
|
2018-12-01 00:02:18 +01:00
|
|
|
|
2021-06-20 05:02:30 +02:00
|
|
|
fun View?.scan(callback: (view: View) -> Unit) {
|
|
|
|
this ?: return
|
|
|
|
callback(this)
|
|
|
|
if (this is ViewGroup) {
|
|
|
|
for (i in 0 until this.childCount) {
|
|
|
|
this.getChildAt(i)?.scan(callback)
|
|
|
|
}
|
|
|
|
}
|
2018-12-01 00:02:18 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 05:02:30 +02:00
|
|
|
val View?.activity: Activity?
|
|
|
|
get() {
|
|
|
|
var context = this?.context
|
|
|
|
while (context is ContextWrapper) {
|
|
|
|
if (context is Activity) return context
|
|
|
|
context = context.baseContext
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
2018-12-01 00:02:18 +01:00
|
|
|
|
|
|
|
fun View.hideKeyboard() {
|
2021-06-20 05:02:30 +02:00
|
|
|
try {
|
2022-12-27 03:54:52 +01:00
|
|
|
when (val imm = this.context?.getSystemService(Context.INPUT_METHOD_SERVICE)) {
|
|
|
|
is InputMethodManager ->
|
|
|
|
imm.hideSoftInputFromWindow(this.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
|
|
|
|
else -> log.e("hideKeyboard: can't get InputMethodManager")
|
2021-06-20 05:02:30 +02:00
|
|
|
}
|
|
|
|
} catch (ex: Throwable) {
|
2023-01-13 07:33:28 +01:00
|
|
|
log.e(ex, "hideKeyboard failed.")
|
2021-06-20 05:02:30 +02:00
|
|
|
}
|
2018-12-01 00:02:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fun View.showKeyboard() {
|
2021-06-20 05:02:30 +02:00
|
|
|
try {
|
2023-01-13 13:22:25 +01:00
|
|
|
ContextCompat.getSystemService(context, InputMethodManager::class.java)!!
|
|
|
|
.showSoftInput(this, InputMethodManager.HIDE_NOT_ALWAYS)
|
2021-06-20 05:02:30 +02:00
|
|
|
} catch (ex: Throwable) {
|
2023-01-13 07:33:28 +01:00
|
|
|
log.e(ex, "showKeyboard failed.")
|
2021-06-20 05:02:30 +02:00
|
|
|
}
|
2018-12-01 00:02:18 +01:00
|
|
|
}
|
|
|
|
|
2019-02-03 15:12:03 +01:00
|
|
|
// set visibility VISIBLE or GONE
|
2022-01-05 04:24:28 +01:00
|
|
|
// return this or null
|
|
|
|
// レシーバがnullableなのはplatform typeによるnull例外を避ける目的
|
2021-06-20 05:02:30 +02:00
|
|
|
fun <T : View> T?.vg(visible: Boolean): T? {
|
|
|
|
this?.visibility = if (visible) View.VISIBLE else View.GONE
|
|
|
|
return if (visible) this else null
|
2018-12-01 00:02:18 +01:00
|
|
|
}
|
2019-10-05 14:28:04 +02:00
|
|
|
|
2022-01-05 04:24:28 +01:00
|
|
|
// set visibility VISIBLE or INVISIBLE
|
|
|
|
// return this or null
|
|
|
|
// レシーバがnullableなのはplatform typeによるnull例外を避ける目的
|
|
|
|
fun <T : View> T?.visibleOrInvisible(visible: Boolean): T? {
|
|
|
|
this?.visibility = if (visible) View.VISIBLE else View.INVISIBLE
|
|
|
|
return if (visible) this else null
|
|
|
|
}
|
|
|
|
|
|
|
|
fun <T : View> T.visible(): T = apply { visibility = View.VISIBLE }
|
|
|
|
fun <T : View> T.invisible(): T = apply { visibility = View.INVISIBLE }
|
|
|
|
fun <T : View> T.gone(): T = apply { visibility = View.GONE }
|
|
|
|
|
2019-10-05 14:28:04 +02:00
|
|
|
// isChecked with skipping animation
|
2021-06-20 05:02:30 +02:00
|
|
|
var CompoundButton.isCheckedNoAnime: Boolean
|
|
|
|
get() = isChecked
|
|
|
|
set(value) {
|
|
|
|
isChecked = value
|
|
|
|
jumpDrawablesToCurrentState()
|
|
|
|
}
|
|
|
|
|
2023-01-13 13:22:25 +01:00
|
|
|
fun mixColor(col1: Int, col2: Int): Int = Color.rgb(
|
2021-06-20 05:02:30 +02:00
|
|
|
(Color.red(col1) + Color.red(col2)) ushr 1,
|
|
|
|
(Color.green(col1) + Color.green(col2)) ushr 1,
|
|
|
|
(Color.blue(col1) + Color.blue(col2)) ushr 1
|
2020-09-27 12:59:54 +02:00
|
|
|
)
|
|
|
|
|
2021-06-20 05:02:30 +02:00
|
|
|
private fun rgbToLab(rgb: Int): Triple<Float, Float, Float> {
|
|
|
|
|
|
|
|
fun Int.revGamma(): Float {
|
|
|
|
val v = toFloat() / 255f
|
|
|
|
return when {
|
|
|
|
v > 0.04045f -> ((v + 0.055f) / 1.055f).pow(2.4f)
|
|
|
|
else -> v / 12.92f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val r = Color.red(rgb).revGamma()
|
|
|
|
val g = Color.green(rgb).revGamma()
|
|
|
|
val b = Color.blue(rgb).revGamma()
|
|
|
|
|
|
|
|
//https://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions
|
|
|
|
|
|
|
|
fun f(src: Float, k: Float): Float {
|
|
|
|
val v = src * k
|
|
|
|
return when {
|
|
|
|
v > 0.008856f -> v.pow(1f / 3f)
|
|
|
|
else -> (7.787f * v) + (4f / 29f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val x = f(r * 0.4124f + g * 0.3576f + b * 0.1805f, 100f / 95.047f)
|
|
|
|
val y = f(r * 0.2126f + g * 0.7152f + b * 0.0722f, 100f / 100f)
|
|
|
|
val z = f(r * 0.0193f + g * 0.1192f + b * 0.9505f, 100f / 108.883f)
|
|
|
|
|
|
|
|
return Triple(
|
|
|
|
(116 * y) - 16, // L
|
|
|
|
500 * (x - y), // a
|
|
|
|
200 * (y - z) //b
|
|
|
|
)
|
2020-09-27 12:59:54 +02:00
|
|
|
}
|
|
|
|
|
2024-03-17 11:05:30 +01:00
|
|
|
fun Activity.setStatusBarColorCompat(@ColorInt c: Int) {
|
2021-06-28 09:09:00 +02:00
|
|
|
window?.apply {
|
|
|
|
statusBarColor = Color.BLACK or c
|
2021-06-20 05:02:30 +02:00
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= 30) {
|
|
|
|
decorView.windowInsetsController?.run {
|
|
|
|
val bit = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
|
|
|
|
setSystemBarsAppearance(if (rgbToLab(c).first >= 50f) bit else 0, bit)
|
|
|
|
}
|
2022-08-11 09:51:49 +02:00
|
|
|
} else {
|
2021-06-20 05:02:30 +02:00
|
|
|
@Suppress("DEPRECATION")
|
|
|
|
val bit = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
|
|
|
@Suppress("DEPRECATION")
|
|
|
|
decorView.systemUiVisibility =
|
2022-08-11 09:51:49 +02:00
|
|
|
when {
|
|
|
|
rgbToLab(c).first >= 50f -> {
|
|
|
|
//Dark Text to show up on your light status bar
|
|
|
|
decorView.systemUiVisibility or bit
|
|
|
|
}
|
|
|
|
else -> {
|
|
|
|
//Light Text to show up on your dark status bar
|
|
|
|
decorView.systemUiVisibility and bit.inv()
|
|
|
|
}
|
2021-06-20 05:02:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-28 09:09:00 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-20 05:02:30 +02:00
|
|
|
|
2024-03-17 11:05:30 +01:00
|
|
|
fun Activity.setNavigationBarColorCompat(@ColorInt c: Int) {
|
2021-06-28 09:09:00 +02:00
|
|
|
if (c == 0) {
|
|
|
|
// no way to restore to system default, need restart app.
|
|
|
|
return
|
|
|
|
}
|
2021-06-20 05:02:30 +02:00
|
|
|
|
2021-06-28 09:09:00 +02:00
|
|
|
window?.apply {
|
|
|
|
navigationBarColor = c or Color.BLACK
|
2021-06-20 05:02:30 +02:00
|
|
|
|
2021-06-28 09:09:00 +02:00
|
|
|
if (Build.VERSION.SDK_INT >= 30) {
|
|
|
|
decorView.windowInsetsController?.run {
|
|
|
|
val bit = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
|
|
|
|
setSystemBarsAppearance(if (rgbToLab(c).first >= 50f) bit else 0, bit)
|
|
|
|
}
|
2022-08-11 09:51:49 +02:00
|
|
|
} else {
|
2021-06-28 09:09:00 +02:00
|
|
|
@Suppress("DEPRECATION")
|
|
|
|
val bit = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
|
|
|
@Suppress("DEPRECATION")
|
|
|
|
decorView.systemUiVisibility = when {
|
2022-08-11 09:51:49 +02:00
|
|
|
//Dark Text to show up on your light status bar
|
|
|
|
rgbToLab(c).first >= 50f ->
|
2021-06-28 09:09:00 +02:00
|
|
|
decorView.systemUiVisibility or bit
|
2022-08-11 09:51:49 +02:00
|
|
|
//Light Text to show up on your dark status bar
|
|
|
|
else ->
|
2021-06-28 09:09:00 +02:00
|
|
|
decorView.systemUiVisibility and bit.inv()
|
2021-06-20 05:02:30 +02:00
|
|
|
}
|
2021-06-28 09:09:00 +02:00
|
|
|
}
|
2021-06-20 05:02:30 +02:00
|
|
|
}
|
2020-09-27 12:59:54 +02:00
|
|
|
}
|
2021-12-04 09:55:28 +01:00
|
|
|
|
2022-01-05 04:24:28 +01:00
|
|
|
var TextView.textOrGone: CharSequence?
|
2021-12-04 09:55:28 +01:00
|
|
|
get() = text
|
2022-01-05 04:24:28 +01:00
|
|
|
set(value) {
|
|
|
|
vg(value?.isNotEmpty() == true)?.text = value
|
2021-12-04 09:55:28 +01:00
|
|
|
}
|