| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- @file:Suppress("unused")
- package io.legato.kazusa.utils
- import android.annotation.SuppressLint
- import android.content.Context
- import android.graphics.Bitmap
- import android.graphics.Canvas
- import android.graphics.Color
- import android.graphics.Picture
- import android.os.Build
- import android.text.Html
- import android.view.MotionEvent
- import android.view.View
- import android.view.View.GONE
- import android.view.View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
- import android.view.View.INVISIBLE
- import android.view.View.VISIBLE
- import android.view.ViewGroup
- import android.view.inputmethod.InputMethodManager
- import android.widget.EdgeEffect
- import android.widget.EditText
- import android.widget.RadioGroup
- import android.widget.SeekBar
- import android.widget.TextView
- import androidx.annotation.ColorInt
- import androidx.annotation.DrawableRes
- import androidx.appcompat.app.AppCompatActivity
- import androidx.appcompat.view.menu.MenuPopupHelper
- import androidx.appcompat.widget.PopupMenu
- import androidx.core.graphics.createBitmap
- import androidx.core.graphics.record
- import androidx.core.graphics.withTranslation
- import androidx.core.view.ViewCompat
- import androidx.core.view.WindowInsetsCompat
- import androidx.core.view.get
- import androidx.core.view.isVisible
- import androidx.core.view.marginBottom
- import androidx.core.view.updateLayoutParams
- import androidx.recyclerview.widget.RecyclerView
- import androidx.viewpager.widget.ViewPager
- import io.legato.kazusa.help.config.AppConfig
- import io.legato.kazusa.lib.theme.TintHelper
- import io.legato.kazusa.utils.canvasrecorder.CanvasRecorder
- import io.legato.kazusa.utils.canvasrecorder.record
- import splitties.systemservices.inputMethodManager
- import splitties.views.bottomPadding
- import splitties.views.topPadding
- import java.lang.reflect.Field
- private tailrec fun getCompatActivity(context: Context?): AppCompatActivity? {
- return when (context) {
- is AppCompatActivity -> context
- is androidx.appcompat.view.ContextThemeWrapper -> getCompatActivity(context.baseContext)
- is android.view.ContextThemeWrapper -> getCompatActivity(context.baseContext)
- else -> null
- }
- }
- val View.activity: AppCompatActivity?
- get() = getCompatActivity(context)
- fun View.hideSoftInput() = run {
- inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
- }
- fun EditText.showSoftInput() = run {
- requestFocus()
- inputMethodManager.showSoftInput(this, InputMethodManager.RESULT_SHOWN)
- }
- fun View.disableAutoFill() = run {
- this.importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
- }
- fun View.applyTint(
- @ColorInt color: Int,
- isDark: Boolean = AppConfig.isNightTheme
- ) {
- TintHelper.setTintAuto(this, color, false, isDark)
- }
- fun View.applyBackgroundTint(
- @ColorInt color: Int,
- isDark: Boolean = AppConfig.isNightTheme
- ) {
- if (background == null) {
- setBackgroundColor(color)
- } else {
- TintHelper.setTintAuto(this, color, true, isDark)
- }
- }
- fun RecyclerView.setEdgeEffectColor(@ColorInt color: Int) {
- edgeEffectFactory = object : RecyclerView.EdgeEffectFactory() {
- override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
- val edgeEffect = super.createEdgeEffect(view, direction)
- edgeEffect.color = color
- return edgeEffect
- }
- }
- }
- fun ViewPager.setEdgeEffectColor(@ColorInt color: Int) {
- try {
- val clazz = ViewPager::class.java
- for (name in arrayOf("mLeftEdge", "mRightEdge")) {
- val field = clazz.getDeclaredField(name)
- field.isAccessible = true
- val edge = field.get(this)
- (edge as EdgeEffect).color = color
- }
- } catch (ignored: Exception) {
- }
- }
- fun EditText.disableEdit() {
- keyListener = null
- }
- fun View.gone() {
- if (visibility != GONE) {
- visibility = GONE
- }
- }
- fun View.gone(gone: Boolean) {
- if (gone) {
- gone()
- } else {
- visibility = VISIBLE
- }
- }
- fun View.invisible() {
- if (visibility != INVISIBLE) {
- visibility = INVISIBLE
- }
- }
- fun View.visible() {
- if (visibility != VISIBLE) {
- visibility = VISIBLE
- }
- }
- fun View.visible(visible: Boolean) {
- if (visible && visibility != VISIBLE) {
- visibility = VISIBLE
- } else if (!visible && isVisible) {
- visibility = INVISIBLE
- }
- }
- fun View.screenshot(bitmap: Bitmap? = null, canvas: Canvas? = null): Bitmap? {
- return if (width > 0 && height > 0) {
- val screenshot = if (bitmap != null && bitmap.width == width && bitmap.height == height) {
- bitmap.eraseColor(Color.TRANSPARENT)
- bitmap
- } else {
- bitmap?.recycle()
- createBitmap(width, height)
- }
- val c = canvas ?: Canvas()
- c.setBitmap(screenshot)
- c.save()
- c.translate(-scrollX.toFloat(), -scrollY.toFloat())
- this.draw(c)
- c.restore()
- c.setBitmap(null)
- screenshot.prepareToDraw()
- screenshot
- } else {
- null
- }
- }
- fun View.screenshot(picture: Picture) {
- if (width > 0 && height > 0) {
- picture.record(width, height) {
- withTranslation(-scrollX.toFloat(), -scrollY.toFloat()) {
- draw(this)
- }
- }
- }
- }
- fun View.screenshot(canvasRecorder: CanvasRecorder) {
- if (width > 0 && height > 0) {
- canvasRecorder.record(width, height) {
- draw(this)
- }
- }
- }
- fun View.setPaddingBottom(bottom: Int) {
- setPadding(paddingLeft, paddingTop, paddingRight, bottom)
- }
- fun SeekBar.progressAdd(int: Int) {
- progress += int
- }
- fun RadioGroup.getIndexById(id: Int): Int {
- for (i in 0 until this.childCount) {
- if (id == get(i).id) {
- return i
- }
- }
- return 0
- }
- fun RadioGroup.getCheckedIndex(): Int {
- for (i in 0 until this.childCount) {
- if (checkedRadioButtonId == get(i).id) {
- return i
- }
- }
- return 0
- }
- fun RadioGroup.checkByIndex(index: Int) {
- check(get(index).id)
- }
- @SuppressLint("ObsoleteSdkInt")
- fun TextView.setHtml(html: String) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- text = Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT)
- } else {
- @Suppress("DEPRECATION")
- text = Html.fromHtml(html)
- }
- }
- fun TextView.setTextIfNotEqual(charSequence: CharSequence?) {
- if (text != charSequence) {
- text = charSequence
- }
- }
- @SuppressLint("RestrictedApi")
- fun PopupMenu.show(x: Int, y: Int) {
- kotlin.runCatching {
- val field: Field = this.javaClass.getDeclaredField("mPopup")
- field.isAccessible = true
- (field.get(this) as MenuPopupHelper).show(x, y)
- }.onFailure {
- it.printOnDebug()
- }
- }
- fun View.shouldHideSoftInput(event: MotionEvent): Boolean {
- if (this is EditText) {
- val l = intArrayOf(0, 0)
- getLocationInWindow(l)
- val left = l[0]
- val top = l[1]
- val bottom = top + getHeight()
- val right = left + getWidth()
- return !(event.x > left && event.x < right && event.y > top && event.y < bottom)
- }
- return false
- }
- fun View.applyStatusBarPadding(withInitialPadding: Boolean = false) {
- val initialPadding = if (withInitialPadding) topPadding else 0
- setOnApplyWindowInsetsListenerCompat { _, windowInsets ->
- val insets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars())
- topPadding = initialPadding + insets.top
- windowInsets
- }
- }
- fun View.applyNavigationBarPadding(withInitialPadding: Boolean = false) {
- val initialPadding = if (withInitialPadding) bottomPadding else 0
- setOnApplyWindowInsetsListenerCompat { _, windowInsets ->
- bottomPadding = initialPadding + windowInsets.navigationBarHeight
- windowInsets
- }
- }
- fun View.applyNavigationBarMargin(withInitialMargin: Boolean = false) {
- val initialMargin = if (withInitialMargin) marginBottom else 0
- setOnApplyWindowInsetsListenerCompat { _, windowInsets ->
- updateLayoutParams<ViewGroup.MarginLayoutParams> {
- bottomMargin = initialMargin + windowInsets.navigationBarHeight
- }
- windowInsets
- }
- }
- fun View.setBackgroundKeepPadding(@DrawableRes backgroundResId: Int) {
- val paddingLeft = paddingLeft
- val paddingTop = paddingTop
- val paddingRight = paddingRight
- val paddingBottom = paddingBottom
- setBackgroundResource(backgroundResId)
- setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
- }
- fun View.canScroll(direction: Int): Boolean {
- return canScrollVertically(direction) || canScrollHorizontally(direction)
- }
- private val requestLayoutBroken =
- Build.VERSION.SDK_INT in Build.VERSION_CODES.O..Build.VERSION_CODES.Q
- fun View.setOnApplyWindowInsetsListenerCompat(listener: (View, WindowInsetsCompat) -> WindowInsetsCompat) {
- ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
- val windowInsets = listener(view, insets)
- if (requestLayoutBroken && isLayoutRequested) {
- post {
- requestLayout()
- }
- }
- windowInsets
- }
- }
|