diff --git a/app/src/main/java/io/legado/app/base/BaseOverlayDialogFragment.kt b/app/src/main/java/io/legado/app/base/BaseOverlayDialogFragment.kt new file mode 100644 index 000000000..323d3e9de --- /dev/null +++ b/app/src/main/java/io/legado/app/base/BaseOverlayDialogFragment.kt @@ -0,0 +1,62 @@ +package io.legado.app.base + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.view.View +import androidx.annotation.LayoutRes +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.lifecycleScope +import io.legado.app.constant.AppLog +import io.legado.app.help.coroutine.Coroutine +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlin.coroutines.CoroutineContext + +abstract class BaseOverlayDialogFragment( + @LayoutRes layoutID: Int +) : DialogFragment(layoutID) { + + private var onDismissListener: DialogInterface.OnDismissListener? = null + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return Dialog(requireContext(), theme) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + onFragmentCreated(view, savedInstanceState) + observeLiveBus() + } + + abstract fun onFragmentCreated(view: View, savedInstanceState: Bundle?) + + open fun observeLiveBus() { + } + + fun setOnDismissListener(onDismissListener: DialogInterface.OnDismissListener?) { + this.onDismissListener = onDismissListener + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + onDismissListener?.onDismiss(dialog) + } + + override fun show(manager: FragmentManager, tag: String?) { + if (isAdded) return + kotlin.runCatching { + manager.beginTransaction().remove(this).commitAllowingStateLoss() + super.show(manager, tag) + }.onFailure { + AppLog.put("显示对话框失败 tag:$tag", it) + } + } + + fun execute( + scope: CoroutineScope = lifecycleScope, + context: CoroutineContext = Dispatchers.IO, + block: suspend CoroutineScope.() -> T + ) = Coroutine.async(scope, context) { block() } +} diff --git a/app/src/main/java/io/legado/app/ui/book/manga/config/MangaClickActionConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/manga/config/MangaClickActionConfigDialog.kt index 4005cd1a2..ca9f3f13d 100644 --- a/app/src/main/java/io/legado/app/ui/book/manga/config/MangaClickActionConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/manga/config/MangaClickActionConfigDialog.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import io.legado.app.R -import io.legado.app.base.BaseDialogFragment +import io.legado.app.base.BaseOverlayDialogFragment import io.legado.app.constant.PreferKey import io.legado.app.databinding.DialogClickActionConfigBinding import io.legado.app.help.config.AppConfig @@ -15,7 +15,7 @@ import io.legado.app.utils.getCompatColor import io.legado.app.utils.putPrefInt import io.legado.app.utils.viewbindingdelegate.viewBinding -class MangaClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_action_config) { +class MangaClickActionConfigDialog : BaseOverlayDialogFragment(R.layout.dialog_click_action_config) { private val binding by viewBinding(DialogClickActionConfigBinding::bind) private val actions by lazy { @@ -142,4 +142,3 @@ class MangaClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_ac AppConfig.detectMangaClickArea() } } - diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ClickActionConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ClickActionConfigDialog.kt index 6f95ecf06..39c526c12 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ClickActionConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ClickActionConfigDialog.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import io.legado.app.R -import io.legado.app.base.BaseDialogFragment +import io.legado.app.base.BaseOverlayDialogFragment import io.legado.app.constant.PreferKey import io.legado.app.databinding.DialogClickActionConfigBinding import io.legado.app.help.config.AppConfig @@ -19,7 +19,7 @@ import io.legado.app.utils.viewbindingdelegate.viewBinding /** * 点击区域设置 */ -class ClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_action_config) { +class ClickActionConfigDialog : BaseOverlayDialogFragment(R.layout.dialog_click_action_config) { private val binding by viewBinding(DialogClickActionConfigBinding::bind) private val actions by lazy { linkedMapOf( @@ -147,4 +147,4 @@ class ClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_action_ AppConfig.detectClickArea() } -} \ No newline at end of file +} diff --git a/app/src/test/java/io/legado/app/ui/book/read/config/ClickActionConfigDialogTest.kt b/app/src/test/java/io/legado/app/ui/book/read/config/ClickActionConfigDialogTest.kt new file mode 100644 index 000000000..416b9fded --- /dev/null +++ b/app/src/test/java/io/legado/app/ui/book/read/config/ClickActionConfigDialogTest.kt @@ -0,0 +1,94 @@ +package io.legado.app.ui.book.read.config + +import android.app.Application +import android.os.Bundle +import android.view.View +import android.view.View.MeasureSpec +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.appcompat.app.AppCompatActivity +import io.legado.app.base.BaseOverlayDialogFragment +import io.legado.app.R +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import splitties.init.injectAsAppCtx + +@RunWith(RobolectricTestRunner::class) +@Config( + sdk = [34], + application = ClickActionConfigDialogTest.TestApplication::class +) +class ClickActionConfigDialogTest { + + @Test + fun clickActionConfigDialog_shouldFillWindowHeight() { + val activity = Robolectric.buildActivity(TestActivity::class.java) + .setup() + .get() + val dialog = TestFullscreenDialog() + + dialog.show(activity.supportFragmentManager, "click-config") + shadowOf(activity.mainLooper).idle() + + val windowHeight = 1920 + val windowWidth = 1080 + val decorView = dialog.requireDialog().window!!.decorView + layoutView(decorView, windowWidth, windowHeight) + + assertEquals( + "ClickActionConfigDialog root view should cover the full window height", + windowHeight, + dialog.requireView().height + ) + } + + private fun layoutView(view: View, width: Int, height: Int) { + view.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) + ) + view.layout(0, 0, width, height) + } + + class TestApplication : Application() { + override fun onCreate() { + injectAsAppCtx() + super.onCreate() + } + } + + class TestFullscreenDialog : BaseOverlayDialogFragment(R.layout.dialog_click_action_config) { + override fun onStart() { + super.onStart() + dialog?.window?.run { + setBackgroundDrawableResource(R.color.transparent) + setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + } + + override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) = Unit + } + + class TestActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(R.style.Theme_Base_WH) + super.onCreate(savedInstanceState) + setContentView( + FrameLayout(this).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + ) + } + } +}