conventionschedule-android/app/src/main/java/com/adlerosn/brasilfurfest/helper/PeekAndPop.kt

207 lines
9.1 KiB
Kotlin

package com.adlerosn.brasilfurfest.helper
import android.app.Activity
import android.graphics.Point
import android.graphics.Rect
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AlertDialog
import android.util.DisplayMetrics
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import kotlin.math.roundToInt
class PeekAndPop(builder: Builder) {
class Builder private constructor(val activity: Activity, val builderData: BuilderData) {
constructor(activity: Activity) : this(activity, BuilderData(
null,
null,
null,
false,
NullObjects.nullGeneralActionListener,
NullObjects.nullHoldAndReleaseListener
))
class BuilderData(
val peekLayout: Int?,
val longClickViews: View?,
val parentViewGroupToDisallowTouchEvents: ViewGroup?,
val blurs: Boolean,
val onGeneralActionListener: OnGeneralActionListener,
val onHoldAndReleaseListener: OnHoldAndReleaseListener
)
fun peekLayout(@LayoutRes layoutId: Int) = Builder(activity, BuilderData(
layoutId,
builderData.longClickViews,
builderData.parentViewGroupToDisallowTouchEvents,
builderData.blurs,
builderData.onGeneralActionListener,
builderData.onHoldAndReleaseListener
))
fun longClickViews(view: View): Builder = Builder(activity, BuilderData(
builderData.peekLayout,
view,
builderData.parentViewGroupToDisallowTouchEvents,
builderData.blurs,
builderData.onGeneralActionListener,
builderData.onHoldAndReleaseListener
))
fun parentViewGroupToDisallowTouchEvents(viewGroup: ViewGroup) = Builder(activity, BuilderData(
builderData.peekLayout,
builderData.longClickViews,
viewGroup,
builderData.blurs,
builderData.onGeneralActionListener,
builderData.onHoldAndReleaseListener
))
fun onGeneralActionListener(onGeneralActionListener: OnGeneralActionListener) = Builder(activity, BuilderData(
builderData.peekLayout,
builderData.longClickViews,
builderData.parentViewGroupToDisallowTouchEvents,
builderData.blurs,
onGeneralActionListener,
builderData.onHoldAndReleaseListener
))
fun onHoldAndReleaseListener(onHoldAndReleaseListener: PeekAndPop.OnHoldAndReleaseListener) = Builder(activity, BuilderData(
builderData.peekLayout,
builderData.longClickViews,
builderData.parentViewGroupToDisallowTouchEvents,
builderData.blurs,
builderData.onGeneralActionListener,
onHoldAndReleaseListener
))
fun blurBackground(blur: Boolean) = Builder(activity, BuilderData(
builderData.peekLayout,
builderData.longClickViews,
builderData.parentViewGroupToDisallowTouchEvents,
blur,
builderData.onGeneralActionListener,
builderData.onHoldAndReleaseListener
))
fun build(): PeekAndPop = PeekAndPop(this)
}
private val activity = builder.activity
private val peekLayout = builder.builderData.peekLayout!!
private val longClickViews = builder.builderData.longClickViews!!
private val parentViewGroupToDisallowTouchEvents = ( builder.builderData.parentViewGroupToDisallowTouchEvents ?: longClickViews ) as ViewGroup
private val onGeneralActionListener = builder.builderData.onGeneralActionListener
private val onHoldAndReleaseListener = builder.builderData.onHoldAndReleaseListener
val peekView = activity.layoutInflater.inflate(peekLayout)
init {
longClickViews.isLongClickable = true
longClickViews.setOnLongClickListener {
onGeneralActionListener.onPeek(longClickViews, -1)
parentViewGroupToDisallowTouchEvents.requestDisallowInterceptTouchEvent(true)
AlertDialog.Builder(activity)
.setView(peekView)
.setOnDismissListener {
(peekView.parent as ViewGroup).removeView(peekView)
longClickViews.setOnTouchListener(null)
cursorIsOver = null
onGeneralActionListener.onPop(longClickViews, -1)
}
.show()
.apply {
val dialog = this
window?.setBackgroundDrawableResource(android.R.color.transparent)
longClickViews.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View, event: MotionEvent): Boolean {
when {
event.action == MotionEvent.ACTION_CANCEL -> dialog.dismiss()
event.action == MotionEvent.ACTION_MOVE -> {
val metrics = DisplayMetrics()
activity.windowManager.defaultDisplay.getMetrics(metrics)
val winSize = Point(metrics.widthPixels, metrics.heightPixels+(40*metrics.density).roundToInt())
val peekSize = peekView.let { Point(it.width, it.height) }
val offset = Point(
((winSize.x/2) - (peekSize.x/2)),
((winSize.y/2) - (peekSize.y/2))
)
val newCursorIsOver = holdAndReleaseViews.firstOrNull { view ->
val relativePosition = Rect()
view.getGlobalVisibleRect(relativePosition)
val actualPosition = Rect(
relativePosition.left+offset.x,
relativePosition.top+offset.y,
relativePosition.right+offset.x,
relativePosition.bottom+offset.y
)
Point(event.rawX.roundToInt(), event.rawY.roundToInt()) in actualPosition
}
updateCursorIsOver(newCursorIsOver)
}
event.action == MotionEvent.ACTION_UP -> {
cursorIsOver?.let { releasedView ->
onHoldAndReleaseListener.onRelease(releasedView, -1)
}
dialog.dismiss()
}
else -> dialog.dismiss()
}
return false
}
})
}
true
}
}
private var cursorIsOver: View? = null
private val holdAndReleaseViews = mutableListOf<View>()
private fun updateCursorIsOver(view: View?){
if(view != cursorIsOver){
cursorIsOver?.let {
onHoldAndReleaseListener.onLeave(it, -1)
}
view?.let {
onHoldAndReleaseListener.onHold(it, -1)
}
cursorIsOver = view
}
}
fun addHoldAndReleaseView(@IdRes idRes: Int) {
peekView.findViewById<View?>(idRes)?.let {
holdAndReleaseViews.add(it)
}
}
interface OnGeneralActionListener {
fun onPeek(longClickView: View, position: Int)
fun onPop(longClickView: View, position: Int)
}
interface OnHoldAndReleaseListener {
fun onHold(view: View, position: Int)
fun onLeave(view: View, position: Int)
fun onRelease(view: View, position: Int)
}
private object NullObjects {
val nullGeneralActionListener = object : OnGeneralActionListener{
override fun onPeek(longClickView: View, position: Int) {}
override fun onPop(longClickView: View, position: Int) {}
}
val nullHoldAndReleaseListener = object : OnHoldAndReleaseListener{
override fun onHold(view: View, position: Int) {}
override fun onLeave(view: View, position: Int) {}
override fun onRelease(view: View, position: Int) {}
}
}
}
private operator fun Rect.contains(point: Point): Boolean = contains(point.x, point.y)