package com.etekcity.vesyncwidget.dialog.base
import android.content.Context import android.content.DialogInterface import android.content.res.Configuration import android.graphics.Point import android.os.Bundle import android.os.Handler import android.os.Parcelable import android.view.* import android.view.inputmethod.InputMethodManager import android.widget.EditText import androidx.annotation.DrawableRes import androidx.annotation.FloatRange import androidx.annotation.LayoutRes import androidx.annotation.StyleRes import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner import kotlinx.android.parcel.Parcelize
@Suppress("UNCHECKED_CAST") abstract class BaseDialog<T : BaseDialog> : DialogFragment() {
protected lateinit var baseParams: BaseDialogParams
protected var viewHandlerListener: ViewHandlerListener?
private var onDialogDismissListener: OnDialogDismissListener? = null
protected lateinit var mContext: Context
protected var dialogConfig: DialogConfig? = null
protected var level = DialogLevel.NORMAL
protected var cleanAllDialog: Boolean = false
private val MIN_SHOW_TIME = 500L
private val MIN_DELAY = 500L
private var mStartTime: Long = -1
private var mPostedHide = false
private var mPostedShow = false
private var mDismissed = false
private val mHandler: Handler = Handler()
private val mDelayedHide = Runnable {
mPostedHide = false
mStartTime = -1
dismiss()
}
private val mDelayedShow = Runnable {
mPostedShow = false
if (!mDismissed) {
mStartTime = System.currentTimeMillis()
initDialogConfig()
if(dialogConfig != null)
DialogManager.instance.show(dialogConfig!!)
else {
try {
//在每个add事务前增加一个remove事务,防止连续的add
baseParams.fragmentManager?.beginTransaction()?.remove(this)?.commit();
super.show(baseParams.fragmentManager!!, baseParams.tag)
} catch (e:Exception ) {
//同一实例使用不同的tag会异常,这里捕获一下
e.printStackTrace()
}
}
}
}
init {
baseParams = BaseDialogParams().apply {
layoutRes = layoutRes()
view = layoutView()
}
viewHandlerListener = this.viewHandler()
}
@LayoutRes
protected abstract fun layoutRes(): Int
protected abstract fun layoutView(): View?
protected abstract fun viewHandler(): ViewHandlerListener?
open fun initView(view: View) {}
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Restore UI status
savedInstanceState?.let {
baseParams = it.getParcelable(KEY_PARAMS) ?: BaseDialogParams()
viewHandlerListener = savedInstanceState.getParcelable(KEY_VIEW_HANDLER)
onDialogDismissListener = savedInstanceState.getParcelable(KEY_DISMISS_LISTENER)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
//Clear the title of Android4.4
dialog?.requestWindowFeature(Window.FEATURE_NO_TITLE)
return when {
baseParams.layoutRes > 0 -> inflater.inflate(baseParams.layoutRes, container)
baseParams.view != null -> baseParams.view!!
else ->
throw IllegalArgumentException("请先设置LayoutRes或View!")
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewHandlerListener?.convertView(ViewHolder.create(view), this)
initView(view)
//Set open Keyboard
if (this.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT && baseParams.needKeyboardViewId != 0) {
val editText = view.findViewById<EditText>(baseParams.needKeyboardViewId)
editText.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
?: return
editText.isFocusable = true
editText.isFocusableInTouchMode = true
editText.requestFocus()
if (imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)) {
editText.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
})
}
}
//save UI state
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.apply {
putParcelable(KEY_PARAMS, baseParams)
putParcelable(KEY_VIEW_HANDLER, viewHandlerListener)
putParcelable(KEY_DISMISS_LISTENER, onDialogDismissListener)
}
}
override fun onStart() {
super.onStart()
//Get screen size
val point = Point()
val windowManager = activity?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager
windowManager?.defaultDisplay?.getSize(point)
//Set window
dialog?.window?.let {
val params = it.attributes
params.dimAmount = baseParams.dimAmount
params.gravity = baseParams.gravity
it.attributes
//Set dialog width
when {
baseParams.widthScale > 0f -> {
if ((this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && baseParams.keepWidthScale)
|| this.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
//横屏并且保持比例 或者 竖屏
params.width = (point.x * baseParams.widthScale).toInt()
}
}
baseParams.widthDp > 0f -> params.width = dp2px(mContext, baseParams.widthDp)
}
//Set dialog height
when {
baseParams.heightScale > 0f -> {
if ((this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && baseParams.keepHeightScale)
|| this.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
//横屏并且保持比例 或者 竖屏
params.height = (point.y * baseParams.heightScale).toInt()
}
}
baseParams.heightDp > 0f -> params.height = dp2px(mContext, baseParams.heightDp)
}
//Set Window verticalMargin
params.verticalMargin = baseParams.verticalMargin
it.attributes = params
if (baseParams.backgroundDrawableRes == 0) {
it.setBackgroundDrawable(null)
} else {
it.setBackgroundDrawableResource(baseParams.backgroundDrawableRes)
}
it.setWindowAnimations(baseParams.animStyle)
}
//Set touch cancelable
if (!baseParams.cancelable) {
isCancelable = baseParams.cancelable
} else {
dialog?.setCanceledOnTouchOutside(baseParams.cancelableOutside)
}
}
fun setDialogLevel(level: DialogLevel) {
this.level = level
}
fun setCleanAll(cleanAll: Boolean) {
this.cleanAllDialog = cleanAll
}
override fun onCancel(dialog: DialogInterface) {
if(dialogConfig != null)
DialogManager.instance.remove(dialogConfig!!)
else
super.onCancel(dialog)
}
override fun onDismiss(dialog: DialogInterface) {
if (baseParams.needKeyboardViewId != 0) {
val editText = view?.findViewById<EditText>(baseParams.needKeyboardViewId)
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
?: return
imm.hideSoftInputFromWindow(editText?.windowToken, 0)
}
if(dialogConfig != null)
DialogManager.instance.remove(dialogConfig!!)
else
super.onDismiss(dialog)
onDialogDismissListener?.onDismiss(dialog)
}
protected fun setFragmentManager(fragmentManager: FragmentManager) {
baseParams.fragmentManager = fragmentManager
}
protected fun setLifecycleOwner(lifecycleOwner: LifecycleOwner?) {
baseParams.lifecycleOwner = lifecycleOwner
}
private fun initDialogConfig () {
if(baseParams.lifecycleOwner == null)
return
if(dialogConfig == null) {
dialogConfig = DialogConfig.builder(this, baseParams.fragmentManager!! , baseParams.lifecycleOwner!!, baseParams.tag)
dialogConfig!!.dialogLevel = level
dialogConfig!!.cleanAllDialog = cleanAllDialog
}
}
/*** Set Params (start) [External call]***/
fun setTag(tag: String): T {
baseParams.tag = tag
return this as T
}
fun setDismissListener(onDialogDismissListener: OnDialogDismissListener): T {
this.onDialogDismissListener = onDialogDismissListener
return this as T
}
fun setGravity(gravity: Int): T {
baseParams.gravity = gravity
return this as T
}
/**
* Dialog occupies the proportion of the screen
* {setWidthScale()} priority is higher than {setWidthDp()}
* @param scale Float
* @return T
*/
fun setWidthScale(@FloatRange(from = 0.0, to = 1.0) scale: Float): T {
baseParams.widthScale = scale
return this as T
}
fun setWidthDp(dp: Float): T {
baseParams.widthDp = dp
return this as T
}
fun setHeightScale(@FloatRange(from = 0.0, to = 1.0) scale: Float): T {
baseParams.heightScale = scale
return this as T
}
fun setHeightDp(dp: Float): T {
baseParams.heightDp = dp
return this as T
}
/**
* Whether to maintain the {setWidthScale()} when the screen is rotated
* If not set {setWidthScale()}, This item does not take effect
* @param isKeep Boolean [Default false]
* @return T
*/
fun setKeepWidthScale(isKeep: Boolean): T {
baseParams.keepWidthScale = isKeep
return this as T
}
/**
* Whether to maintain the {setHeightScale()} when the screen is rotated
* If not set {setHeightScale()}, This item does not take effect
* @param isKeep Boolean [Default false]
* @return T
*/
fun setKeepHeightScale(isKeep: Boolean): T {
baseParams.keepHeightScale = isKeep
return this as T
}
fun setVerticalMargin(@FloatRange(from = 0.0, to = 0.1) verticalMargin: Float): T {
baseParams.verticalMargin = verticalMargin
return this as T
}
fun setCancelableAll(cancelable: Boolean): T {
baseParams.cancelable = cancelable
return this as T
}
fun setCancelableOutside(cancelableOutside: Boolean): T {
baseParams.cancelableOutside = cancelableOutside
return this as T
}
fun setBackgroundDrawableRes(@DrawableRes resId: Int): T {
baseParams.backgroundDrawableRes = resId
return this as T
}
fun setAnimStyle(@StyleRes animStyleRes: Int): T {
baseParams.animStyle = animStyleRes
return this as T
}
/**
* 设置dialog外部背景的透明度
*/
fun setDimAmount(@FloatRange(from = 0.0, to = 1.0) dimAmount: Float): T {
baseParams.dimAmount = dimAmount
return this as T
}
/**
* auto open keyboard, (only EditText)
* @param id Int EditTextView ID
* @return T
*/
fun setNeedKeyboardEditTextId(id: Int): T {
baseParams.needKeyboardViewId = id
return this as T
}
open fun show(): T {
mStartTime = -1
mDismissed = false
mHandler.removeCallbacks(mDelayedHide)
mPostedHide = false
if (!mPostedShow) {
mHandler.postDelayed(mDelayedShow, MIN_DELAY)
mPostedShow = true
}
return this as T
}
open fun dismissDialog() {
mDismissed = true
mHandler.removeCallbacks(mDelayedShow)
mPostedShow = false
val diff = System.currentTimeMillis() - mStartTime
if (diff >= MIN_SHOW_TIME || mStartTime == -1L) {
dismiss()
} else {
if (!mPostedHide) {
mHandler.postDelayed(mDelayedHide, MIN_SHOW_TIME - diff)
mPostedHide = true
}
}
}
/*** Set Params (end)***/
companion object {
private const val KEY_PARAMS = "key_params"
private const val KEY_VIEW_HANDLER = "view_handler"
private const val KEY_DISMISS_LISTENER = "dismiss_listener"
private fun dp2px(context: Context, dipValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (dipValue * scale + 0.5f).toInt()
}
}
abstract class UnParcelableParams(var fragmentManager: FragmentManager? =null,
var lifecycleOwner: LifecycleOwner? = null,
var view: View? = null)
@Parcelize
class BaseDialogParams(
@LayoutRes var layoutRes: Int = 0,
var widthScale: Float = 0f,
var widthDp: Float = 0f,
var heightScale: Float = 0f,
var heightDp: Float = 0f,
var keepWidthScale: Boolean = false,
var keepHeightScale: Boolean = false,
var verticalMargin: Float = 0f,
var gravity: Int = Gravity.CENTER,
var tag: String = "LDialog",
var cancelable: Boolean = true,
var cancelableOutside: Boolean = true,
var backgroundDrawableRes: Int = 0,
var animStyle: Int = 0,
var needKeyboardViewId: Int = 0,
var dimAmount:Float = 0.4f
) : UnParcelableParams(), Parcelable
}