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() 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(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)