SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/dialog/SuspendProgress.kt

97 lines
3.1 KiB
Kotlin

package jp.juggler.subwaytooter.dialog
import android.app.Dialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import jp.juggler.subwaytooter.databinding.DlgSuspendProgressBinding
import jp.juggler.util.coroutine.AppDispatchers
import jp.juggler.util.log.LogCategory
import jp.juggler.util.ui.dismissSafe
import jp.juggler.util.ui.vg
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
private val log = LogCategory("SuspendProgress")
class SuspendProgress(val activity: AppCompatActivity) {
private val views = DlgSuspendProgressBinding.inflate(activity.layoutInflater)
private val dialog = Dialog(activity)
suspend fun <T : Any?> run(
message: String,
title: String,
cancellable: Boolean,
block: suspend (Reporter) -> T,
): T = Reporter().use { reporter ->
try {
dialog.setContentView(views.root)
reporter.setMessage(message)
reporter.setTitle(title)
dialog.setCancelable(cancellable)
dialog.setCanceledOnTouchOutside(cancellable)
dialog.show()
block(reporter)
} finally {
dialog.dismissSafe()
}
}
inner class Reporter : AutoCloseable {
private val flowMessage = MutableStateFlow<CharSequence>("")
private val flowTitle = MutableStateFlow<CharSequence>("")
private val jobMessage = activity.lifecycleScope.launch(AppDispatchers.MainImmediate) {
try {
flowMessage.collect {
views.tvMessage.vg(it.isNotEmpty())?.text = it
}
} catch (ex: Throwable) {
when (ex) {
is CancellationException, is ClosedReceiveChannelException -> Unit
else -> log.w(ex, "error.")
}
}
}
private val jobTitle = activity.lifecycleScope.launch(AppDispatchers.MainImmediate) {
try {
flowTitle.collect {
views.tvTitle.vg(it.isNotEmpty())?.text = it
}
} catch (ex: Throwable) {
when (ex) {
is CancellationException, is ClosedReceiveChannelException -> Unit
else -> log.w(ex, "error.")
}
}
}
override fun close() {
jobMessage.cancel()
jobTitle.cancel()
}
fun setMessage(msg: CharSequence) {
flowMessage.value = msg
}
fun setTitle(title: CharSequence) {
flowTitle.value = title
}
}
}
suspend fun <T : Any?> AppCompatActivity.runInProgress(
message: String = "please wait…",
title: String = "",
cancellable: Boolean = true,
block: suspend (SuspendProgress.Reporter) -> T,
): T = SuspendProgress(this).run(
message = message,
title = title,
cancellable = cancellable,
block = block
)