fix: Dialog progress tasks

This commit is contained in:
J-Jamet
2025-10-28 13:28:20 +01:00
parent 88a93829a9
commit 26daac4637
5 changed files with 104 additions and 89 deletions

View File

@@ -18,11 +18,11 @@ import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Co
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService
import com.kunzisoft.keepass.database.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
import com.kunzisoft.keepass.tasks.ProgressTaskViewModel
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
import kotlinx.coroutines.launch
@@ -32,6 +32,7 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
protected val mDatabase: ContextualDatabase?
get() = mDatabaseViewModel.database
private val progressTaskViewModel: ProgressTaskViewModel by viewModels()
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
@@ -81,13 +82,13 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
)
}
is DatabaseViewModel.ActionState.OnDatabaseActionStarted -> {
showDialog(uiState.progressMessage)
progressTaskViewModel.start(uiState.progressMessage)
}
is DatabaseViewModel.ActionState.OnDatabaseActionUpdated -> {
showDialog(uiState.progressMessage)
progressTaskViewModel.update(uiState.progressMessage)
}
is DatabaseViewModel.ActionState.OnDatabaseActionStopped -> {
stopDialog()
progressTaskViewModel.stop()
}
is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
@@ -95,12 +96,24 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
uiState.actionTask,
uiState.result
)
stopDialog()
progressTaskViewModel.stop()
}
}
}
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
progressTaskViewModel.progressTaskState.collect { state ->
when (state) {
ProgressTaskViewModel.ProgressTaskState.Start ->
showDialog()
ProgressTaskViewModel.ProgressTaskState.Stop ->
stopDialog()
}
}
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
mDatabaseViewModel.databaseState.collect { database ->
@@ -194,7 +207,7 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
}
}
private fun showDialog(progressMessage: ProgressMessage) {
private fun showDialog() {
lifecycleScope.launch {
if (showDatabaseDialog()) {
if (progressTaskDialogFragment == null) {
@@ -208,12 +221,6 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
PROGRESS_TASK_DIALOG_TAG
)
}
progressTaskDialogFragment?.apply {
updateTitle(progressMessage.titleId)
updateMessage(progressMessage.messageId)
updateWarning(progressMessage.warningId)
setCancellable(progressMessage.cancelable)
}
}
}
}

View File

@@ -1,13 +1,8 @@
package com.kunzisoft.keepass.database
import androidx.annotation.StringRes
data class ProgressMessage(
@StringRes
var titleId: Int,
@StringRes
var messageId: Int? = null,
@StringRes
var warningId: Int? = null,
var title: String,
var message: String? = null,
var warning: String? = null,
var cancelable: (() -> Unit)? = null
)

View File

@@ -33,6 +33,7 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.GroupActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.credentialprovider.activity.HardwareKeyActivity
import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.MainCredential
import com.kunzisoft.keepass.database.ProgressMessage
@@ -61,7 +62,6 @@ import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.credentialprovider.activity.HardwareKeyActivity
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.settings.PreferencesUtil
@@ -112,7 +112,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private var mTaskRemovedRequested = false
private var mSaveState = false
private var mProgressMessage: ProgressMessage = ProgressMessage(R.string.database_opened)
private lateinit var mProgressMessage: ProgressMessage
override fun retrieveChannelId(): String {
return CHANNEL_DATABASE_ID
@@ -305,6 +305,13 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
mResponseChallengeChannel = null
}
override fun onCreate() {
super.onCreate()
mProgressMessage = ProgressMessage(
title = getString(R.string.database_opened)
)
}
override fun onBind(intent: Intent): IBinder? {
super.onBind(intent)
return mActionTaskBinder
@@ -391,9 +398,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
TimeoutHelper.temporarilyDisableTimeout()
sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply {
putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId)
putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.messageId)
putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warningId)
putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.title)
putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.message)
putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warning)
})
mActionTaskListeners.forEach { actionTaskListener ->
@@ -506,8 +513,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
R.drawable.notification_ic_database_action
// Title depending on action
mProgressMessage.titleId =
if (intentAction == null) {
mProgressMessage.title =
getString(if (intentAction == null) {
R.string.database_opened
} else when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
@@ -522,24 +529,22 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
else
R.string.command_execution
}
}
})
// Updated later
mProgressMessage.messageId = null
mProgressMessage.message = null
// Warning if data is saved
mProgressMessage.warningId =
mProgressMessage.warning =
if (mSaveState)
R.string.do_not_kill_app
getString(R.string.do_not_kill_app)
else
null
val notificationBuilder = buildNewNotification().apply {
setSmallIcon(iconId)
intent?.let {
setContentTitle(getString(
intent.getIntExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId))
)
setContentTitle(intent.getStringExtra(DATABASE_TASK_TITLE_KEY))
}
setAutoCancel(false)
setContentIntent(null)
@@ -661,8 +666,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun updateMessage(resId: Int) {
mProgressMessage.messageId = resId
private fun updateMessage(@StringRes resId: Int) {
mProgressMessage.message = getString(resId)
notifyProgressMessage()
}
@@ -708,7 +713,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
initializeChallengeResponse()
val previousMessage = mProgressMessage.copy()
mProgressMessage.apply {
messageId = R.string.waiting_challenge_request
message = getString(R.string.waiting_challenge_request)
cancelable = {
cancelChallengeResponse(R.string.error_cancel_by_user)
}
@@ -723,7 +728,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
)
// Wait the response
mProgressMessage.apply {
messageId = R.string.waiting_challenge_response
message = getString(R.string.waiting_challenge_response)
}
notifyProgressMessage()
response = mResponseChallengeChannel?.receive() ?: byteArrayOf()

View File

@@ -27,32 +27,27 @@ import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.kunzisoft.keepass.R
import kotlinx.coroutines.launch
open class ProgressTaskDialogFragment : DialogFragment() {
@StringRes
private var title = UNDEFINED
@StringRes
private var message = UNDEFINED
@StringRes
private var warning = UNDEFINED
private var cancellable: (() -> Unit)? = null
private var titleView: TextView? = null
private var messageView: TextView? = null
private var warningView: TextView? = null
private var cancelButton: Button? = null
private var progressView: ProgressBar? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
private val progressTaskViewModel: ProgressTaskViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
try {
activity?.let {
val builder = AlertDialog.Builder(it)
@@ -71,68 +66,48 @@ open class ProgressTaskDialogFragment : DialogFragment() {
cancelButton = root.findViewById(R.id.progress_dialog_cancel)
progressView = root.findViewById(R.id.progress_dialog_bar)
updateTitle(title)
updateMessage(message)
updateWarning(warning)
setCancellable(cancellable)
isCancelable = false
return builder.create()
}
} catch (e: Exception) {
Log.e(TAG, "Unable to create progress dialog")
Log.e(TAG, "Unable to create progress dialog", e)
}
return super.onCreateDialog(savedInstanceState)
}
fun setTitle(@StringRes titleId: Int) {
this.title = titleId
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
progressTaskViewModel.progressMessageState.collect { state ->
updateView(titleView, state.title)
updateView(messageView, state.message)
updateView(warningView, state.warning)
activity?.lifecycleScope?.launch {
cancelButton?.isVisible = state.cancelable != null
cancelButton?.setOnClickListener {
state.cancelable?.invoke()
}
}
}
}
}
}
private fun updateView(textView: TextView?, @StringRes resId: Int) {
private fun updateView(textView: TextView?, value: String?) {
activity?.lifecycleScope?.launch {
if (resId == UNDEFINED) {
if (value == null) {
textView?.visibility = View.GONE
} else {
textView?.setText(resId)
textView?.text = value
textView?.visibility = View.VISIBLE
}
}
}
private fun updateCancelable() {
activity?.lifecycleScope?.launch {
cancelButton?.isVisible = cancellable != null
cancelButton?.setOnClickListener {
cancellable?.invoke()
}
}
}
fun updateTitle(@StringRes resId: Int?) {
this.title = resId ?: UNDEFINED
updateView(titleView, title)
}
fun updateMessage(@StringRes resId: Int?) {
this.message = resId ?: UNDEFINED
updateView(messageView, message)
}
fun updateWarning(@StringRes resId: Int?) {
this.warning = resId ?: UNDEFINED
updateView(warningView, warning)
}
fun setCancellable(cancellable: (() -> Unit)?) {
this.cancellable = cancellable
updateCancelable()
}
companion object {
private val TAG = ProgressTaskDialogFragment::class.java.simpleName
const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment"
const val UNDEFINED = -1
}
}

View File

@@ -0,0 +1,33 @@
package com.kunzisoft.keepass.tasks
import androidx.lifecycle.ViewModel
import com.kunzisoft.keepass.database.ProgressMessage
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class ProgressTaskViewModel: ViewModel() {
private val mProgressMessageState = MutableStateFlow(ProgressMessage(""))
val progressMessageState: StateFlow<ProgressMessage> = mProgressMessageState
private val mProgressTaskState = MutableStateFlow<ProgressTaskState>(ProgressTaskState.Stop)
val progressTaskState: StateFlow<ProgressTaskState> = mProgressTaskState
fun update(value: ProgressMessage) {
mProgressMessageState.value = value
}
fun start(value: ProgressMessage) {
mProgressTaskState.value = ProgressTaskState.Start
update(value)
}
fun stop() {
mProgressTaskState.value = ProgressTaskState.Stop
}
sealed class ProgressTaskState {
object Start: ProgressTaskState()
object Stop: ProgressTaskState()
}
}