Files
Magisk/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt

294 lines
9.5 KiB
Kotlin
Raw Normal View History

package com.topjohnwu.magisk.utils
import android.view.View
import android.view.ViewGroup
2019-10-04 17:56:30 +02:00
import android.widget.TextSwitcher
2019-04-24 20:28:41 +02:00
import android.widget.TextView
2019-10-04 17:56:30 +02:00
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
2019-04-22 14:11:41 +02:00
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import androidx.drawerlayout.widget.DrawerLayout
2019-04-24 20:28:41 +02:00
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
2019-06-12 16:08:02 +02:00
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
2019-04-22 14:11:41 +02:00
import androidx.viewpager.widget.ViewPager
2019-06-12 16:08:02 +02:00
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.navigation.NavigationView
2019-04-24 20:28:41 +02:00
import com.skoumal.teanity.extensions.subscribeK
2019-04-19 16:32:01 +02:00
import com.topjohnwu.magisk.R
2019-07-28 02:10:22 -07:00
import com.topjohnwu.magisk.extensions.replaceRandomWithSpecial
2019-04-19 16:32:01 +02:00
import com.topjohnwu.magisk.model.entity.state.IndeterminateState
2019-04-24 20:28:41 +02:00
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)
}
2019-04-19 16:32:01 +02:00
@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
}
)
}
2019-04-22 14:11:41 +02:00
@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()
})
2019-04-24 20:28:41 +02:00
}
@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)
2019-05-23 16:50:31 +02:00
view.setTag(R.id.recyclerScrollListener, observer)
}
fun RecyclerView.Adapter<*>.removeListener() {
2019-05-23 16:50:31 +02:00
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()
}
2019-06-12 16:08:02 +02:00
}
@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)
}
2019-06-12 16:08:02 +02:00
}
@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
}
}
}
}
2019-10-04 17:56:30 +02:00
}
@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
}