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.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService
import com.kunzisoft.keepass.database.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
import com.kunzisoft.keepass.tasks.ProgressTaskViewModel
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -32,6 +32,7 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
protected val mDatabase: ContextualDatabase? protected val mDatabase: ContextualDatabase?
get() = mDatabaseViewModel.database get() = mDatabaseViewModel.database
private val progressTaskViewModel: ProgressTaskViewModel by viewModels()
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
@@ -81,13 +82,13 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
) )
} }
is DatabaseViewModel.ActionState.OnDatabaseActionStarted -> { is DatabaseViewModel.ActionState.OnDatabaseActionStarted -> {
showDialog(uiState.progressMessage) progressTaskViewModel.start(uiState.progressMessage)
} }
is DatabaseViewModel.ActionState.OnDatabaseActionUpdated -> { is DatabaseViewModel.ActionState.OnDatabaseActionUpdated -> {
showDialog(uiState.progressMessage) progressTaskViewModel.update(uiState.progressMessage)
} }
is DatabaseViewModel.ActionState.OnDatabaseActionStopped -> { is DatabaseViewModel.ActionState.OnDatabaseActionStopped -> {
stopDialog() progressTaskViewModel.stop()
} }
is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> { is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> {
onDatabaseActionFinished( onDatabaseActionFinished(
@@ -95,12 +96,24 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
uiState.actionTask, uiState.actionTask,
uiState.result 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 { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) { repeatOnLifecycle(Lifecycle.State.RESUMED) {
mDatabaseViewModel.databaseState.collect { database -> mDatabaseViewModel.databaseState.collect { database ->
@@ -194,7 +207,7 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
} }
} }
private fun showDialog(progressMessage: ProgressMessage) { private fun showDialog() {
lifecycleScope.launch { lifecycleScope.launch {
if (showDatabaseDialog()) { if (showDatabaseDialog()) {
if (progressTaskDialogFragment == null) { if (progressTaskDialogFragment == null) {
@@ -208,12 +221,6 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
PROGRESS_TASK_DIALOG_TAG 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 package com.kunzisoft.keepass.database
import androidx.annotation.StringRes
data class ProgressMessage( data class ProgressMessage(
@StringRes var title: String,
var titleId: Int, var message: String? = null,
@StringRes var warning: String? = null,
var messageId: Int? = null,
@StringRes
var warningId: Int? = null,
var cancelable: (() -> Unit)? = 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.activities.GroupActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseAction import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction 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.ContextualDatabase
import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.MainCredential
import com.kunzisoft.keepass.database.ProgressMessage 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.NodeId
import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.credentialprovider.activity.HardwareKeyActivity
import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
@@ -112,7 +112,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private var mTaskRemovedRequested = false private var mTaskRemovedRequested = false
private var mSaveState = false private var mSaveState = false
private var mProgressMessage: ProgressMessage = ProgressMessage(R.string.database_opened) private lateinit var mProgressMessage: ProgressMessage
override fun retrieveChannelId(): String { override fun retrieveChannelId(): String {
return CHANNEL_DATABASE_ID return CHANNEL_DATABASE_ID
@@ -305,6 +305,13 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
mResponseChallengeChannel = null mResponseChallengeChannel = null
} }
override fun onCreate() {
super.onCreate()
mProgressMessage = ProgressMessage(
title = getString(R.string.database_opened)
)
}
override fun onBind(intent: Intent): IBinder? { override fun onBind(intent: Intent): IBinder? {
super.onBind(intent) super.onBind(intent)
return mActionTaskBinder return mActionTaskBinder
@@ -391,9 +398,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
TimeoutHelper.temporarilyDisableTimeout() TimeoutHelper.temporarilyDisableTimeout()
sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply { sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply {
putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId) putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.title)
putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.messageId) putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.message)
putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warningId) putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warning)
}) })
mActionTaskListeners.forEach { actionTaskListener -> mActionTaskListeners.forEach { actionTaskListener ->
@@ -506,8 +513,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
R.drawable.notification_ic_database_action R.drawable.notification_ic_database_action
// Title depending on action // Title depending on action
mProgressMessage.titleId = mProgressMessage.title =
if (intentAction == null) { getString(if (intentAction == null) {
R.string.database_opened R.string.database_opened
} else when (intentAction) { } else when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> R.string.creating_database ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
@@ -522,24 +529,22 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
else else
R.string.command_execution R.string.command_execution
} }
} })
// Updated later // Updated later
mProgressMessage.messageId = null mProgressMessage.message = null
// Warning if data is saved // Warning if data is saved
mProgressMessage.warningId = mProgressMessage.warning =
if (mSaveState) if (mSaveState)
R.string.do_not_kill_app getString(R.string.do_not_kill_app)
else else
null null
val notificationBuilder = buildNewNotification().apply { val notificationBuilder = buildNewNotification().apply {
setSmallIcon(iconId) setSmallIcon(iconId)
intent?.let { intent?.let {
setContentTitle(getString( setContentTitle(intent.getStringExtra(DATABASE_TASK_TITLE_KEY))
intent.getIntExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId))
)
} }
setAutoCancel(false) setAutoCancel(false)
setContentIntent(null) setContentIntent(null)
@@ -661,8 +666,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
} }
private fun updateMessage(resId: Int) { private fun updateMessage(@StringRes resId: Int) {
mProgressMessage.messageId = resId mProgressMessage.message = getString(resId)
notifyProgressMessage() notifyProgressMessage()
} }
@@ -708,7 +713,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
initializeChallengeResponse() initializeChallengeResponse()
val previousMessage = mProgressMessage.copy() val previousMessage = mProgressMessage.copy()
mProgressMessage.apply { mProgressMessage.apply {
messageId = R.string.waiting_challenge_request message = getString(R.string.waiting_challenge_request)
cancelable = { cancelable = {
cancelChallengeResponse(R.string.error_cancel_by_user) cancelChallengeResponse(R.string.error_cancel_by_user)
} }
@@ -723,7 +728,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
) )
// Wait the response // Wait the response
mProgressMessage.apply { mProgressMessage.apply {
messageId = R.string.waiting_challenge_response message = getString(R.string.waiting_challenge_response)
} }
notifyProgressMessage() notifyProgressMessage()
response = mResponseChallengeChannel?.receive() ?: byteArrayOf() response = mResponseChallengeChannel?.receive() ?: byteArrayOf()

View File

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