Fix state loss issues in list settings activities

pull/3207/head
Alex Baker 11 months ago
parent e48f7e61c3
commit 60a9b018ec

@ -6,16 +6,17 @@ import android.graphics.Bitmap
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Paint import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.colorInt
@ -51,39 +52,20 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
@Inject lateinit var defaultFilterProvider: DefaultFilterProvider @Inject lateinit var defaultFilterProvider: DefaultFilterProvider
@Inject lateinit var firebase: Firebase @Inject lateinit var firebase: Firebase
protected abstract val defaultIcon: String protected val baseViewModel: BaseListSettingsViewModel by viewModels()
protected var selectedColor = 0
protected var selectedIcon = mutableStateOf<String?>(null)
protected val textState = mutableStateOf("") protected abstract val defaultIcon: String
protected val errorState = mutableStateOf("")
protected val colorState = mutableStateOf(Color.Unspecified)
protected val showProgress = mutableStateOf(false)
protected val promptDelete = mutableStateOf(false)
protected val promptDiscard = mutableStateOf(false)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
/* defaultIcon is initialized in the descendant's constructor so it can not be used baseViewModel.setIcon(defaultIcon)
in constructor of the base class. So valid initial value for iconState is set here */
selectedIcon.value = defaultIcon
if (savedInstanceState != null) {
selectedColor = savedInstanceState.getInt(EXTRA_SELECTED_THEME)
selectedIcon.value = savedInstanceState.getString(EXTRA_SELECTED_ICON) ?: defaultIcon
}
addBackPressedCallback { addBackPressedCallback {
discard() discard()
} }
} }
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(EXTRA_SELECTED_THEME, selectedColor)
outState.putString(EXTRA_SELECTED_ICON, selectedIcon.value)
}
protected abstract fun hasChanges(): Boolean protected abstract fun hasChanges(): Boolean
protected abstract suspend fun save() protected abstract suspend fun save()
protected val isNew: Boolean protected val isNew: Boolean
@ -93,8 +75,11 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
protected abstract val toolbarTitle: String? protected abstract val toolbarTitle: String?
protected abstract suspend fun delete() protected abstract suspend fun delete()
protected open fun discard() { protected open fun discard() {
if (hasChanges()) promptDiscard.value = true if (hasChanges()) {
else finish() baseViewModel.promptDiscard(true)
} else {
finish()
}
} }
protected fun clearColor() { protected fun clearColor() {
@ -102,34 +87,36 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
} }
protected fun showThemePicker() { protected fun showThemePicker() {
newColorPalette(null, 0, selectedColor, Palette.COLORS) newColorPalette(null, 0, baseViewModel.color, Palette.COLORS)
.show(supportFragmentManager, FRAG_TAG_COLOR_PICKER) .show(supportFragmentManager, FRAG_TAG_COLOR_PICKER)
} }
val launcher = registerForIconPickerResult { selected -> val launcher = registerForIconPickerResult { selected ->
selectedIcon.value = selected baseViewModel.setIcon(selected)
} }
fun showIconPicker() { fun showIconPicker() {
launcher.launchIconPicker(this, selectedIcon.value) launcher.launchIconPicker(this, baseViewModel.icon)
} }
override fun onColorPicked(color: Int) { override fun onColorPicked(color: Int) {
selectedColor = color baseViewModel.setColor(color)
updateTheme() updateTheme()
} }
protected open fun promptDelete() { promptDelete.value = true } protected open fun promptDelete() { baseViewModel.promptDelete(true) }
protected fun updateTheme() { protected fun updateTheme() {
val selectedColor = baseViewModel.color
val themeColor: ThemeColor = val themeColor: ThemeColor =
if (selectedColor == 0) tasksTheme.themeColor if (selectedColor == 0) tasksTheme.themeColor
else colorProvider.getThemeColor(selectedColor, true) else colorProvider.getThemeColor(selectedColor, true)
colorState.value = baseViewModel.setColorState(
if (selectedColor == 0) Color.Unspecified if (selectedColor == 0) Color.Unspecified
else Color((colorProvider.getThemeColor(selectedColor, true)).primaryColor) else Color((colorProvider.getThemeColor(selectedColor, true)).primaryColor)
)
//iconState.intValue = (getIconResId(selectedIcon) ?: getIconResId(defaultIcon))!! //iconState.intValue = (getIconResId(selectedIcon) ?: getIconResId(defaultIcon))!!
@ -147,30 +134,31 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
fab: @Composable () -> Unit = {}, fab: @Composable () -> Unit = {},
extensionContent: @Composable ColumnScope.() -> Unit = {}, extensionContent: @Composable ColumnScope.() -> Unit = {},
) { ) {
val viewState = baseViewModel.viewState.collectAsStateWithLifecycle().value
ListSettingsScaffold( ListSettingsScaffold(
title = title, title = title,
theme = if (colorState.value == Color.Unspecified) theme = if (viewState.colorState == Color.Unspecified)
Color(tasksTheme.themeColor.primaryColor) Color(tasksTheme.themeColor.primaryColor)
else else
colorState.value, viewState.colorState,
promptDiscard = promptDiscard.value, promptDiscard = viewState.promptDiscard,
showProgress = showProgress.value, showProgress = viewState.showProgress,
dismissDiscardPrompt = { promptDiscard.value = false }, dismissDiscardPrompt = { baseViewModel.promptDiscard(false) },
save = { lifecycleScope.launch { save() } }, save = { lifecycleScope.launch { save() } },
discard = { finish() }, discard = { finish() },
actions = optionButton, actions = optionButton,
fab = fab, fab = fab,
) { ) {
ListSettingsContent( ListSettingsContent(
color = colorState.value, color = viewState.colorState,
icon = selectedIcon.value ?: defaultIcon, icon = viewState.icon ?: defaultIcon,
text = textState.value, text = viewState.title,
error = errorState.value, error = viewState.error,
requestKeyboard = requestKeyboard, requestKeyboard = requestKeyboard,
isNew = isNew, isNew = isNew,
setText = { setText = {
textState.value = it baseViewModel.setTitle(it)
errorState.value = "" baseViewModel.setError("")
}, },
pickColor = { showThemePicker() }, pickColor = { showThemePicker() },
clearColor = { clearColor() }, clearColor = { clearColor() },
@ -184,14 +172,14 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
protected fun createShortcut() { protected fun createShortcut() {
filter?.let { filter?.let {
val filterId = defaultFilterProvider.getFilterPreferenceValue(it) val filterId = defaultFilterProvider.getFilterPreferenceValue(it)
val iconColor = if (colorState.value == Color.Unspecified) val iconColor = if (baseViewModel.colorState == Color.Unspecified)
Color(tasksTheme.themeColor.primaryColor) Color(tasksTheme.themeColor.primaryColor)
else else
colorState.value baseViewModel.colorState
val shortcutInfo = ShortcutInfoCompat.Builder(this, UUIDHelper.newUUID()) val shortcutInfo = ShortcutInfoCompat.Builder(this, UUIDHelper.newUUID())
.setShortLabel(title) .setShortLabel(baseViewModel.title)
.setIcon( .setIcon(
selectedIcon.value baseViewModel.icon
?.let { icon -> ?.let { icon ->
try { try {
createShortcutIcon( createShortcutIcon(
@ -234,8 +222,6 @@ abstract class BaseListSettingsActivity : AppCompatActivity(), ColorPalettePicke
} }
companion object { companion object {
private const val EXTRA_SELECTED_THEME = "extra_selected_theme"
private const val EXTRA_SELECTED_ICON = "extra_selected_icon"
private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker" private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker"
fun createShortcutIcon(context: Context, backgroundColor: Color): IconCompat { fun createShortcutIcon(context: Context, backgroundColor: Color): IconCompat {

@ -0,0 +1,68 @@
package org.tasks.activities
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
class BaseListSettingsViewModel : ViewModel() {
data class ViewState(
val title: String = "",
val error: String = "",
val colorState: Color = Color.Unspecified,
val showProgress: Boolean = false,
val promptDelete: Boolean = false,
val promptDiscard: Boolean = false,
val icon: String? = null,
val color: Int = 0,
)
private val _viewState = MutableStateFlow(ViewState())
val viewState: StateFlow<ViewState> = _viewState
fun setTitle(title: String) {
_viewState.update { it.copy(title = title) }
}
fun setColor(color: Int) {
_viewState.update { it.copy(color = color) }
}
fun setIcon(icon: String) {
_viewState.update { it.copy(icon = icon) }
}
fun setError(error: String) {
_viewState.update { it.copy(error = error) }
}
fun showProgress(showProgress: Boolean) {
_viewState.update { it.copy(showProgress = showProgress) }
}
fun promptDiscard(promptDiscard: Boolean) {
_viewState.update { it.copy(promptDiscard = promptDiscard) }
}
fun promptDelete(promptDelete: Boolean) {
_viewState.update { it.copy(promptDelete = promptDelete) }
}
fun setColorState(colorState: Color) {
_viewState.update { it.copy(colorState = colorState) }
}
val title: String
get() = _viewState.value.title
val icon: String?
get() = _viewState.value.icon
val color: Int
get() = _viewState.value.color
val colorState: Color
get() = _viewState.value.colorState
}

@ -70,13 +70,11 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (savedInstanceState == null) { if (savedInstanceState == null) {
filter?.let { filter?.let {
selectedColor = it.tint baseViewModel.setColor(it.tint)
selectedIcon.value = it.icon baseViewModel.setIcon(it.icon)
textState.value = it.title ?: "" baseViewModel.setTitle(it.title ?: "")
} }
} intent.getStringExtra(EXTRA_TITLE)?.let { baseViewModel.setTitle(it) }
if (savedInstanceState != null) {
intent.getStringExtra(EXTRA_TITLE)?.let { textState.value = it }
} }
updateTheme() updateTheme()
@ -102,7 +100,7 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
override suspend fun save() { override suspend fun save() {
val newName = newName val newName = newName
if (Strings.isNullOrEmpty(newName)) { if (Strings.isNullOrEmpty(newName)) {
errorState.value = getString(R.string.name_cannot_be_empty) baseViewModel.setError(getString(R.string.name_cannot_be_empty))
return return
} }
@ -111,8 +109,8 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
var f = Filter( var f = Filter(
id = filter?.id ?: 0L, id = filter?.id ?: 0L,
title = newName, title = newName,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
values = criteria.values, values = criteria.values,
criterion = CriterionInstance.serialize(criteria), criterion = CriterionInstance.serialize(criteria),
sql = criteria.sql, sql = criteria.sql,
@ -138,16 +136,17 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
} }
private val newName: String private val newName: String
get() = textState.value.trim { it <= ' ' } get() = baseViewModel.title.trim { it <= ' ' }
override fun hasChanges(): Boolean { override fun hasChanges(): Boolean {
val viewState = baseViewModel.viewState.value
val criteria = viewModel.viewState.value.criteria val criteria = viewModel.viewState.value.criteria
return if (isNew) { return if (isNew) {
(!Strings.isNullOrEmpty(newName) (!Strings.isNullOrEmpty(newName)
|| selectedColor != 0 || selectedIcon.value?.isBlank() == false || criteria.size > 1) || viewState.color != 0 || viewState.icon?.isBlank() == false || criteria.size > 1)
} else newName != filter!!.title } else newName != filter!!.title
|| selectedColor != filter!!.tint || viewState.color != filter!!.tint
|| selectedIcon.value != filter!!.icon || viewState.icon != filter!!.icon
|| CriterionInstance.serialize(criteria) != filter!!.criterion!!.trim() || CriterionInstance.serialize(criteria) != filter!!.criterion!!.trim()
|| criteria.values != filter!!.valuesForNewTasks || criteria.values != filter!!.valuesForNewTasks
|| criteria.sql != filter!!.sql || criteria.sql != filter!!.sql

@ -53,11 +53,11 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
} }
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (savedInstanceState == null) { if (savedInstanceState == null) {
selectedColor = gtasksList.color baseViewModel.setColor(gtasksList.color)
selectedIcon.value = gtasksList.icon ?: defaultIcon baseViewModel.setIcon(gtasksList.icon ?: defaultIcon)
} }
if (!isNewList) textState.value = gtasksList.name!! if (!isNewList) baseViewModel.setTitle(gtasksList.name!!)
if (createListViewModel.inProgress if (createListViewModel.inProgress
|| renameListViewModel.inProgress || renameListViewModel.inProgress
@ -86,14 +86,14 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
get() = if (isNew) getString(R.string.new_list) else gtasksList.name!! get() = if (isNew) getString(R.string.new_list) else gtasksList.name!!
private fun showProgressIndicator() { private fun showProgressIndicator() {
showProgress.value = true baseViewModel.showProgress(true)
} }
private fun hideProgressIndicator() { private fun hideProgressIndicator() {
showProgress.value = false baseViewModel.showProgress(false)
} }
private fun requestInProgress() = showProgress.value private fun requestInProgress() = baseViewModel.viewState.value.showProgress
override suspend fun save() { override suspend fun save() {
if (requestInProgress()) { if (requestInProgress()) {
@ -101,7 +101,7 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
} }
val newName = newName val newName = newName
if (isNullOrEmpty(newName)) { if (isNullOrEmpty(newName)) {
errorState.value = getString(R.string.name_cannot_be_empty) baseViewModel.setError(getString(R.string.name_cannot_be_empty))
return return
} }
when { when {
@ -115,10 +115,10 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
} }
else -> { else -> {
if (colorChanged() || iconChanged()) { if (colorChanged() || iconChanged()) {
gtasksList.color = selectedColor gtasksList.color = baseViewModel.color
googleTaskListDao.insertOrReplace( googleTaskListDao.insertOrReplace(
gtasksList.copy( gtasksList.copy(
icon = selectedIcon.value icon = baseViewModel.icon
) )
) )
localBroadcastManager.broadcastRefresh() localBroadcastManager.broadcastRefresh()
@ -150,16 +150,16 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
} }
private val newName: String private val newName: String
get() = textState.value.trim { it <= ' ' } get() = baseViewModel.title.trim { it <= ' ' }
override fun hasChanges(): Boolean = override fun hasChanges(): Boolean =
if (isNewList) { if (isNewList) {
selectedColor >= 0 || !isNullOrEmpty(newName) baseViewModel.color != 0 || !isNullOrEmpty(newName)
} else colorChanged() || nameChanged() || iconChanged() } else colorChanged() || nameChanged() || iconChanged()
private fun colorChanged() = selectedColor != gtasksList.color private fun colorChanged() = baseViewModel.color != gtasksList.color
private fun iconChanged() = selectedIcon.value != (gtasksList.icon ?: TasksIcons.LIST) private fun iconChanged() = baseViewModel.icon != (gtasksList.icon ?: TasksIcons.LIST)
private fun nameChanged() = newName != gtasksList.name private fun nameChanged() = newName != gtasksList.name
@ -167,8 +167,8 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
val result = gtasksList.copy( val result = gtasksList.copy(
uuid = taskList.id, uuid = taskList.id,
name = taskList.title, name = taskList.title,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
) )
val id = googleTaskListDao.insertOrReplace(result) val id = googleTaskListDao.insertOrReplace(result)
@ -193,8 +193,8 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
private suspend fun onListRenamed(taskList: TaskList) { private suspend fun onListRenamed(taskList: TaskList) {
val result = gtasksList.copy( val result = gtasksList.copy(
name = taskList.title, name = taskList.title,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
) )
googleTaskListDao.insertOrReplace(result) googleTaskListDao.insertOrReplace(result)

@ -83,9 +83,9 @@ class PlaceSettingsActivity : BaseListSettingsActivity(),
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (savedInstanceState == null) { if (savedInstanceState == null) {
textState.value = place.displayName baseViewModel.setTitle(place.displayName)
selectedColor = place.color baseViewModel.setColor(place.color)
selectedIcon.value = place.icon ?: defaultIcon baseViewModel.setIcon(place.icon ?: defaultIcon)
} }
sliderPos.floatValue = (place.radius / STEP * STEP).toFloat() sliderPos.floatValue = (place.radius / STEP * STEP).toFloat()
@ -157,22 +157,22 @@ class PlaceSettingsActivity : BaseListSettingsActivity(),
} }
} }
override fun hasChanges() = textState.value != place.displayName override fun hasChanges() = baseViewModel.title != place.displayName
|| selectedColor != place.color || baseViewModel.color != place.color
|| selectedIcon.value != (place.icon ?: TasksIcons.PLACE) || baseViewModel.icon != (place.icon ?: TasksIcons.PLACE)
override suspend fun save() { override suspend fun save() {
val newName: String = textState.value val newName: String = baseViewModel.title
if (isNullOrEmpty(newName)) { if (isNullOrEmpty(newName)) {
errorState.value = getString(R.string.name_cannot_be_empty) baseViewModel.setError(getString(R.string.name_cannot_be_empty))
return return
} }
place = place.copy( place = place.copy(
name = newName, name = newName,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
radius = sliderPos.floatValue.roundToInt(), radius = sliderPos.floatValue.roundToInt(),
) )
locationDao.update(place) locationDao.update(place)

@ -39,13 +39,13 @@ class TagSettingsActivity : BaseListSettingsActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
tagData = intent.getParcelableExtra(EXTRA_TAG_DATA) ?: TagData() tagData = intent.getParcelableExtra(EXTRA_TAG_DATA) ?: TagData()
if (!isNewTag) textState.value = tagData.name!! if (!isNewTag) baseViewModel.setTitle(tagData.name!!)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (savedInstanceState == null) { if (savedInstanceState == null) {
selectedColor = tagData.color ?: 0 baseViewModel.setColor(tagData.color ?: 0)
selectedIcon.value = tagData.icon ?: defaultIcon baseViewModel.setIcon(tagData.icon ?: defaultIcon)
} }
setContent { setContent {
@ -63,7 +63,7 @@ class TagSettingsActivity : BaseListSettingsActivity() {
get() = if (isNew) getString(R.string.new_tag) else tagData.name!! get() = if (isNew) getString(R.string.new_tag) else tagData.name!!
private val newName: String private val newName: String
get() = textState.value.trim { it <= ' ' } get() = baseViewModel.title.trim { it <= ' ' }
private suspend fun clashes(newName: String): Boolean { private suspend fun clashes(newName: String): Boolean {
return ((isNewTag || !newName.equals(tagData.name, ignoreCase = true)) return ((isNewTag || !newName.equals(tagData.name, ignoreCase = true))
@ -73,19 +73,19 @@ class TagSettingsActivity : BaseListSettingsActivity() {
override suspend fun save() { override suspend fun save() {
val newName = newName val newName = newName
if (isNullOrEmpty(newName)) { if (isNullOrEmpty(newName)) {
errorState.value = getString(R.string.name_cannot_be_empty) baseViewModel.setError(getString(R.string.name_cannot_be_empty))
return return
} }
if (clashes(newName)) { if (clashes(newName)) {
errorState.value = getString(R.string.tag_already_exists) baseViewModel.setError(getString(R.string.tag_already_exists))
return return
} }
if (isNewTag) { if (isNewTag) {
tagData tagData
.copy( .copy(
name = newName, name = newName,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
) )
.let { it.copy(id = tagDataDao.insert(it)) } .let { it.copy(id = tagDataDao.insert(it)) }
.let { .let {
@ -99,8 +99,8 @@ class TagSettingsActivity : BaseListSettingsActivity() {
tagData tagData
.copy( .copy(
name = newName, name = newName,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
) )
.let { .let {
tagDataDao.update(it) tagDataDao.update(it)
@ -118,10 +118,10 @@ class TagSettingsActivity : BaseListSettingsActivity() {
override fun hasChanges(): Boolean { override fun hasChanges(): Boolean {
return if (isNewTag) { return if (isNewTag) {
selectedColor >= 0 || selectedIcon.value?.isBlank() == false || !isNullOrEmpty(newName) baseViewModel.color != 0 || baseViewModel.icon?.isBlank() == false || !isNullOrEmpty(newName)
} else { } else {
selectedColor != (tagData.color ?: 0) baseViewModel.color != (tagData.color ?: 0)
|| selectedIcon.value != (tagData.icon ?: TasksIcons.LABEL) || baseViewModel.icon != (tagData.icon ?: TasksIcons.LABEL)
|| newName != tagData.name || newName != tagData.name
} }
} }

@ -51,9 +51,9 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
} }
if (savedInstanceState == null) { if (savedInstanceState == null) {
if (caldavCalendar != null) { if (caldavCalendar != null) {
textState.value = caldavCalendar!!.name ?: "" baseViewModel.setTitle(caldavCalendar!!.name ?: "")
selectedColor = caldavCalendar!!.color baseViewModel.setColor(caldavCalendar!!.color)
selectedIcon.value = caldavCalendar?.icon ?: defaultIcon baseViewModel.setIcon(caldavCalendar?.icon ?: defaultIcon)
} }
} }
updateTheme() updateTheme()
@ -71,17 +71,17 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
} }
val name = newName val name = newName
if (isNullOrEmpty(name)) { if (isNullOrEmpty(name)) {
errorState.value = getString(R.string.name_cannot_be_empty) baseViewModel.setError(getString(R.string.name_cannot_be_empty))
return return
} }
when { when {
caldavCalendar == null -> { caldavCalendar == null -> {
showProgressIndicator() showProgressIndicator()
createCalendar(caldavAccount, name, selectedColor) createCalendar(caldavAccount, name, baseViewModel.color)
} }
nameChanged() || colorChanged() -> { nameChanged() || colorChanged() -> {
showProgressIndicator() showProgressIndicator()
updateNameAndColor(caldavAccount, caldavCalendar!!, name, selectedColor) updateNameAndColor(caldavAccount, caldavCalendar!!, name, baseViewModel.color)
} }
iconChanged() -> updateCalendar() iconChanged() -> updateCalendar()
else -> finish() else -> finish()
@ -97,11 +97,11 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
caldavAccount: CaldavAccount, caldavCalendar: CaldavCalendar caldavAccount: CaldavAccount, caldavCalendar: CaldavCalendar
) )
private fun showProgressIndicator() { showProgress.value = true } private fun showProgressIndicator() { baseViewModel.showProgress(true) }
private fun hideProgressIndicator() { showProgress.value = false } private fun hideProgressIndicator() { baseViewModel.showProgress(false) }
protected fun requestInProgress(): Boolean = showProgress.value protected fun requestInProgress(): Boolean = baseViewModel.viewState.value.showProgress
protected fun requestFailed(t: Throwable) { protected fun requestFailed(t: Throwable) {
hideProgressIndicator() hideProgressIndicator()
@ -129,8 +129,8 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
account = caldavAccount.uuid, account = caldavAccount.uuid,
url = url, url = url,
name = newName, name = newName,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
) )
caldavDao.insert(caldavCalendar) caldavDao.insert(caldavCalendar)
setResult( setResult(
@ -142,8 +142,8 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
protected suspend fun updateCalendar() { protected suspend fun updateCalendar() {
val result = caldavCalendar!!.copy( val result = caldavCalendar!!.copy(
name = newName, name = newName,
color = selectedColor, color = baseViewModel.color,
icon = selectedIcon.value, icon = baseViewModel.icon,
) )
caldavDao.update(result) caldavDao.update(result)
setResult( setResult(
@ -155,18 +155,18 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
override fun hasChanges(): Boolean = override fun hasChanges(): Boolean =
if (caldavCalendar == null) if (caldavCalendar == null)
!isNullOrEmpty(newName) || selectedColor != 0 || selectedIcon.value?.isBlank() == false !isNullOrEmpty(newName) || baseViewModel.color != 0 || baseViewModel.icon?.isBlank() == false
else else
nameChanged() || iconChanged() || colorChanged() nameChanged() || iconChanged() || colorChanged()
private fun nameChanged(): Boolean = caldavCalendar!!.name != newName private fun nameChanged(): Boolean = caldavCalendar!!.name != newName
private fun colorChanged(): Boolean = selectedColor != caldavCalendar!!.color private fun colorChanged(): Boolean = baseViewModel.color != caldavCalendar!!.color
private fun iconChanged(): Boolean = selectedIcon.value != (caldavCalendar!!.icon ?: TasksIcons.LIST) private fun iconChanged(): Boolean = baseViewModel.icon != (caldavCalendar!!.icon ?: TasksIcons.LIST)
private val newName: String private val newName: String
get() = textState.value.trim { it <= ' '} get() = baseViewModel.title.trim { it <= ' '}
override fun finish() { override fun finish() {
// hideKeyboard(name) // hideKeyboard(name)

@ -53,7 +53,7 @@ class CaldavCalendarSettingsActivity : BaseCaldavCalendarSettingsActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel.inFlight.observe(this) { showProgress.value = it } viewModel.inFlight.observe(this) { baseViewModel.showProgress(it) }
viewModel.error.observe(this) { throwable -> viewModel.error.observe(this) { throwable ->
throwable?.let { throwable?.let {
@ -163,7 +163,7 @@ class CaldavCalendarSettingsActivity : BaseCaldavCalendarSettingsActivity() {
} }
override suspend fun createCalendar(caldavAccount: CaldavAccount, name: String, color: Int) { override suspend fun createCalendar(caldavAccount: CaldavAccount, name: String, color: Int) {
caldavCalendar = viewModel.createCalendar(caldavAccount, name, color, selectedIcon.value) caldavCalendar = viewModel.createCalendar(caldavAccount, name, color, baseViewModel.icon)
} }
override suspend fun updateNameAndColor( override suspend fun updateNameAndColor(
@ -172,7 +172,7 @@ class CaldavCalendarSettingsActivity : BaseCaldavCalendarSettingsActivity() {
name: String, name: String,
color: Int color: Int
) { ) {
viewModel.updateCalendar(account, calendar, name, color, selectedIcon.value) viewModel.updateCalendar(account, calendar, name, color, baseViewModel.icon)
} }
override suspend fun deleteCalendar( override suspend fun deleteCalendar(

@ -3,6 +3,7 @@ package org.tasks.opentasks
import android.os.Bundle import android.os.Bundle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -23,19 +24,20 @@ class OpenTasksListSettingsActivity : BaseCaldavCalendarSettingsActivity() {
setContent { setContent {
TasksTheme { TasksTheme {
val viewState = baseViewModel.viewState.collectAsStateWithLifecycle().value
ListSettingsScaffold( ListSettingsScaffold(
title = toolbarTitle, title = toolbarTitle,
theme = if (colorState.value == Color.Unspecified) theme = if (viewState.colorState == Color.Unspecified)
Color(tasksTheme.themeColor.primaryColor) Color(tasksTheme.themeColor.primaryColor)
else else
colorState.value, viewState.colorState,
promptDiscard = promptDiscard.value, promptDiscard = viewState.promptDiscard,
showProgress = showProgress.value, showProgress = viewState.showProgress,
dismissDiscardPrompt = { promptDiscard.value = false }, dismissDiscardPrompt = { baseViewModel.promptDiscard(false) },
save = { lifecycleScope.launch { save() } }, save = { lifecycleScope.launch { save() } },
discard = { finish() }, discard = { finish() },
) { ) {
SelectIconRow(icon = selectedIcon.value?: defaultIcon) { showIconPicker() } SelectIconRow(icon = viewState.icon ?: defaultIcon) { showIconPicker() }
AddToHomeRow(onClick = { createShortcut() }) AddToHomeRow(onClick = { createShortcut() })
} }
Toaster(state = snackbar) Toaster(state = snackbar)

Loading…
Cancel
Save