Files
Magisk/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt
2019-10-05 12:53:40 +02:00

294 lines
9.5 KiB
Kotlin

package com.topjohnwu.magisk.utils
import android.view.View
import android.view.ViewGroup
import android.widget.TextSwitcher
import android.widget.TextView
import android.widget.ViewSwitcher
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.Toolbar
import androidx.core.view.postDelayed
import androidx.core.view.updateLayoutParams
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import androidx.drawerlayout.widget.DrawerLayout
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.navigation.NavigationView
import com.skoumal.teanity.extensions.subscribeK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.replaceRandomWithSpecial
import com.topjohnwu.magisk.model.entity.state.IndeterminateState
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import java.util.concurrent.TimeUnit
@BindingAdapter("onNavigationClick")
fun setOnNavigationClickedListener(view: Toolbar, listener: View.OnClickListener) {
view.setNavigationOnClickListener(listener)
}
@BindingAdapter("onNavigationClick")
fun setOnNavigationClickedListener(
view: NavigationView,
listener: NavigationView.OnNavigationItemSelectedListener
) {
view.setNavigationItemSelectedListener {
(view.parent as? DrawerLayout)?.closeDrawers()
listener.onNavigationItemSelected(it)
}
}
@BindingAdapter("srcCompat")
fun setImageResource(view: AppCompatImageView, @DrawableRes resId: Int) {
view.setImageResource(resId)
}
@BindingAdapter("app:tint")
fun setTint(view: AppCompatImageView, @ColorInt tint: Int) {
view.setColorFilter(tint)
}
@BindingAdapter("isChecked")
fun setChecked(view: AppCompatImageView, isChecked: Boolean) {
val state = when (isChecked) {
true -> IndeterminateState.CHECKED
else -> IndeterminateState.UNCHECKED
}
setChecked(view, state)
}
@BindingAdapter("isChecked")
fun setChecked(view: AppCompatImageView, isChecked: IndeterminateState) {
view.setImageResource(
when (isChecked) {
IndeterminateState.INDETERMINATE -> R.drawable.ic_indeterminate
IndeterminateState.CHECKED -> R.drawable.ic_checked
IndeterminateState.UNCHECKED -> R.drawable.ic_unchecked
}
)
}
@BindingAdapter("position")
fun setPosition(view: ViewPager, position: Int) {
view.currentItem = position
}
@InverseBindingAdapter(attribute = "position", event = "positionChanged")
fun getPosition(view: ViewPager) = view.currentItem
@BindingAdapter("positionChanged")
fun setPositionChangedListener(view: ViewPager, listener: InverseBindingListener) {
view.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageSelected(position: Int) = listener.onChange()
override fun onPageScrollStateChanged(state: Int) = listener.onChange()
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) = listener.onChange()
})
}
@BindingAdapter("invisibleScale")
fun setInvisibleWithScale(view: View, isInvisible: Boolean) {
view.animate()
.scaleX(if (isInvisible) 0f else 1f)
.scaleY(if (isInvisible) 0f else 1f)
.setInterpolator(FastOutSlowInInterpolator())
.start()
}
@BindingAdapter("movieBehavior", "movieBehaviorText")
fun setMovieBehavior(view: TextView, isMovieBehavior: Boolean, text: String) {
(view.tag as? Disposable)?.dispose()
if (isMovieBehavior) {
val observer = Observable
.interval(150, TimeUnit.MILLISECONDS)
.subscribeK {
view.text = text.replaceRandomWithSpecial()
}
view.tag = observer
} else {
view.text = text
}
}
/*@BindingAdapter("selection"*//*, "selectionAttrChanged", "adapter"*//*)
fun setSelectedItemPosition(view: Spinner, position: Int) {
view.setSelection(position)
}
@InverseBindingAdapter(
attribute = "android:selectedItemPosition",
event = "android:selectedItemPositionAttrChanged"
)
fun getSelectedItemPosition(view: Spinner) = view.selectedItemPosition
@BindingAdapter("selectedItemPositionAttrChanged")
fun setSelectedItemPositionListener(view: Spinner, listener: InverseBindingListener) {
view.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(p0: AdapterView<*>?) {
listener.onChange()
}
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
listener.onChange()
}
}
}*/
@BindingAdapter("onTouch")
fun setOnTouchListener(view: View, listener: View.OnTouchListener) {
view.setOnTouchListener(listener)
}
@BindingAdapter("scrollToLast")
fun setScrollToLast(view: RecyclerView, shouldScrollToLast: Boolean) {
fun scrollToLast() = view.post {
view.scrollToPosition(view.adapter?.itemCount?.minus(1) ?: 0)
}
fun wait(callback: () -> Unit) {
Observable.timer(1, TimeUnit.SECONDS).subscribeK { callback() }
}
fun RecyclerView.Adapter<*>.setListener() {
val observer = object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
scrollToLast()
}
}
registerAdapterDataObserver(observer)
view.setTag(R.id.recyclerScrollListener, observer)
}
fun RecyclerView.Adapter<*>.removeListener() {
val observer =
view.getTag(R.id.recyclerScrollListener) as? RecyclerView.AdapterDataObserver ?: return
unregisterAdapterDataObserver(observer)
}
fun trySetListener(): Unit = view.adapter?.setListener() ?: wait { trySetListener() }
if (shouldScrollToLast) {
trySetListener()
} else {
view.adapter?.removeListener()
}
}
@BindingAdapter("hide")
fun setHidden(view: FloatingActionButton, hide: Boolean) {
if (hide) view.hide() else view.show()
}
@BindingAdapter("scrollPosition", "scrollPositionSmooth", requireAll = false)
fun setScrollPosition(view: RecyclerView, position: Int, smoothScroll: Boolean) {
val adapterItemCount = view.adapter?.itemCount ?: -1
if (position !in 0 until adapterItemCount) {
// the position is not in adapter bounds, adapter will throw exception for invalid positions
return
}
when {
smoothScroll -> view.smoothScrollToPosition(position)
else -> view.scrollToPosition(position)
}
}
@BindingAdapter("recyclerScrollEvent")
fun setScrollListener(view: RecyclerView, listener: InverseBindingListener) {
view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
// don't change this or the recycler will stop at every line, effectively disabling smooth scroll
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
listener.onChange()
}
}
})
}
@InverseBindingAdapter(attribute = "scrollPosition", event = "recyclerScrollEvent")
fun getScrollPosition(view: RecyclerView) = (view.layoutManager as? LinearLayoutManager)
?.findLastCompletelyVisibleItemPosition()
?: -1
@BindingAdapter("isEnabled")
fun setEnabled(view: View, isEnabled: Boolean) {
view.isEnabled = isEnabled
}
// md2
@BindingAdapter("onSelectClick", "onSelectReset", requireAll = false)
fun View.setOnSelectClickListener(listener: View.OnClickListener, resetTime: Long) {
setOnClickListener {
when {
it.isSelected -> {
listener.onClick(it)
(it.tag as? Runnable)?.let { task ->
it.handler.removeCallbacks(task)
}
it.isSelected = false
}
else -> {
it.isSelected = true
it.tag = it.postDelayed(resetTime) {
it.tag = null
it.isSelected = false
}
}
}
}
}
@BindingAdapter("textCaptionVariant")
fun TextSwitcher.setTextBinding(text: CharSequence) {
tag as? ViewSwitcher.ViewFactory ?: ViewSwitcher.ViewFactory {
View.inflate(context, R.layout.swicher_caption_variant, null)
}.also {
tag = it
setFactory(it)
setInAnimation(context, R.anim.switcher_bottom_up)
setOutAnimation(context, R.anim.switcher_center_up)
}
val currentText = (currentView as? TextView)?.text
if (currentText != text) {
setText(text)
}
}
@BindingAdapter(
"android:layout_marginLeft",
"android:layout_marginTop",
"android:layout_marginRight",
"android:layout_marginBottom",
requireAll = false
)
fun View.setMargins(
marginLeft: Int?,
marginTop: Int?,
marginRight: Int?,
marginBottom: Int?
) = updateLayoutParams<ViewGroup.MarginLayoutParams> {
marginLeft?.let { leftMargin = it }
marginTop?.let { topMargin = it }
marginRight?.let { rightMargin = it }
marginBottom?.let { bottomMargin = it }
}
@BindingAdapter("nestedScrollingEnabled")
fun RecyclerView.setNestedScrolling(enabled: Boolean) {
isNestedScrollingEnabled = enabled
}