diff --git a/app/src/main/java/at/bitfire/icsdroid/model/EditCalendarModel.kt b/app/src/main/java/at/bitfire/icsdroid/model/EditCalendarModel.kt new file mode 100644 index 00000000..f083b837 --- /dev/null +++ b/app/src/main/java/at/bitfire/icsdroid/model/EditCalendarModel.kt @@ -0,0 +1,99 @@ +package at.bitfire.icsdroid.model + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import at.bitfire.icsdroid.db.dao.SubscriptionsDao +import at.bitfire.icsdroid.db.entity.Credential +import at.bitfire.icsdroid.db.entity.Subscription +import kotlinx.coroutines.launch + +class EditCalendarModel( + val editSubscriptionModel: EditSubscriptionModel, + val subscriptionSettingsModel: SubscriptionSettingsModel, + val credentialsModel: CredentialsModel +): ViewModel() { + + private var initialSubscription: Subscription? = null + private var initialCredential: Credential? = null + private var initialRequiresAuthValue: Boolean? = null + + init { + // Initialise view models and save their initial state + viewModelScope.launch { + editSubscriptionModel.subscriptionWithCredential.collect { data -> + if (data != null) + onSubscriptionLoaded(data) + } + } + } + + /** + * Whether user input is error free + */ + val inputValid: Boolean + @Composable + get() = remember(subscriptionSettingsModel.uiState, credentialsModel.uiState) { + val title = subscriptionSettingsModel.uiState.title + val requiresAuth = credentialsModel.uiState.requiresAuth + val username = credentialsModel.uiState.username + val password = credentialsModel.uiState.password + + val titleOK = !title.isNullOrBlank() + val authOK = if (requiresAuth) + !username.isNullOrBlank() && !password.isNullOrBlank() + else + true + titleOK && authOK + } + + /** + * Whether there are unsaved user changes + */ + val modelsDirty: Boolean + @Composable + get() = remember(subscriptionSettingsModel.uiState, credentialsModel.uiState) { + val requiresAuth = credentialsModel.uiState.requiresAuth + + val credentialsDirty = initialRequiresAuthValue != requiresAuth || + initialCredential?.let { !credentialsModel.equalsCredential(it) } ?: false + val subscriptionsDirty = initialSubscription?.let { + !subscriptionSettingsModel.equalsSubscription(it) + } ?: false + + credentialsDirty || subscriptionsDirty + } + + /** + * Initialise view models and remember their initial state + */ + private fun onSubscriptionLoaded(subscriptionWithCredential: SubscriptionsDao.SubscriptionWithCredential) { + val subscription = subscriptionWithCredential.subscription + + subscriptionSettingsModel.setUrl(subscription.url.toString()) + subscription.displayName.let { + subscriptionSettingsModel.setTitle(it) + } + subscription.color.let(subscriptionSettingsModel::setColor) + subscription.ignoreEmbeddedAlerts.let(subscriptionSettingsModel::setIgnoreAlerts) + subscription.defaultAlarmMinutes?.toString().let(subscriptionSettingsModel::setDefaultAlarmMinutes) + subscription.defaultAllDayAlarmMinutes?.toString()?.let(subscriptionSettingsModel::setDefaultAllDayAlarmMinutes) + subscription.ignoreDescription.let(subscriptionSettingsModel::setIgnoreDescription) + + val credential = subscriptionWithCredential.credential + val requiresAuth = credential != null + credentialsModel.setRequiresAuth(requiresAuth) + + if (credential != null) { + credential.username.let(credentialsModel::setUsername) + credential.password.let(credentialsModel::setPassword) + } + + // Save state, before user makes changes + initialSubscription = subscription + initialCredential = credential + initialRequiresAuthValue = credentialsModel.uiState.requiresAuth + } + +} diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/screen/EditCalendarScreen.kt b/app/src/main/java/at/bitfire/icsdroid/ui/screen/EditCalendarScreen.kt new file mode 100644 index 00000000..5dc41a42 --- /dev/null +++ b/app/src/main/java/at/bitfire/icsdroid/ui/screen/EditCalendarScreen.kt @@ -0,0 +1,303 @@ +package at.bitfire.icsdroid.ui.screen + +import android.app.Application +import android.widget.Toast +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Share +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import at.bitfire.icsdroid.R +import at.bitfire.icsdroid.db.entity.Subscription +import at.bitfire.icsdroid.model.CredentialsModel +import at.bitfire.icsdroid.model.EditCalendarModel +import at.bitfire.icsdroid.model.EditSubscriptionModel +import at.bitfire.icsdroid.model.SubscriptionSettingsModel +import at.bitfire.icsdroid.ui.partials.ExtendedTopAppBar +import at.bitfire.icsdroid.ui.partials.GenericAlertDialog +import at.bitfire.icsdroid.ui.theme.AppTheme +import at.bitfire.icsdroid.ui.views.LoginCredentialsComposable +import at.bitfire.icsdroid.ui.views.SubscriptionSettingsComposable + +@Composable +fun EditCalendarScreen( + application: Application, + subscriptionId: Long, + onShare: (subscription: Subscription) -> Unit, + onExit: () -> Unit = {} +) { + val credentialsModel: CredentialsModel = viewModel() + val subscriptionSettingsModel: SubscriptionSettingsModel = viewModel() + val editSubscriptionModel: EditSubscriptionModel = viewModel { + EditSubscriptionModel(application, subscriptionId) + } + val editCalendarModel: EditCalendarModel = viewModel { + EditCalendarModel(editSubscriptionModel, subscriptionSettingsModel, credentialsModel) + } + EditCalendarScreen( + inputValid = editCalendarModel.inputValid, + modelsDirty = editCalendarModel.modelsDirty, + successMessage = editCalendarModel.editSubscriptionModel.uiState.successMessage, + onDelete = editSubscriptionModel::removeSubscription, + onSave = { + editSubscriptionModel.updateSubscription(subscriptionSettingsModel, credentialsModel) + }, + onShare = { + editSubscriptionModel.subscriptionWithCredential.value?.let { + onShare(it.subscription) + } + }, + onExit = onExit, + supportsAuthentication = editCalendarModel.subscriptionSettingsModel.uiState.supportsAuthentication, + + // Subscription settings model + url = subscriptionSettingsModel.uiState.url, + title = subscriptionSettingsModel.uiState.title, + titleChanged = subscriptionSettingsModel::setTitle, + color = subscriptionSettingsModel.uiState.color, + colorChanged = subscriptionSettingsModel::setColor, + ignoreAlerts = subscriptionSettingsModel.uiState.ignoreAlerts, + ignoreAlertsChanged = subscriptionSettingsModel::setIgnoreAlerts, + defaultAlarmMinutes = subscriptionSettingsModel.uiState.defaultAlarmMinutes, + defaultAlarmMinutesChanged = subscriptionSettingsModel::setDefaultAlarmMinutes, + defaultAllDayAlarmMinutes = subscriptionSettingsModel.uiState.defaultAllDayAlarmMinutes, + defaultAllDayAlarmMinutesChanged = subscriptionSettingsModel::setDefaultAllDayAlarmMinutes, + ignoreDescription = subscriptionSettingsModel.uiState.ignoreDescription, + onIgnoreDescriptionChanged = subscriptionSettingsModel::setIgnoreDescription, + isCreating = false, + + // Credentials model + requiresAuth = credentialsModel.uiState.requiresAuth, + username = credentialsModel.uiState.username, + password = credentialsModel.uiState.password, + onRequiresAuthChange = credentialsModel::setRequiresAuth, + onUsernameChange = credentialsModel::setUsername, + onPasswordChange = credentialsModel::setPassword, + ) +} +@Composable +fun EditCalendarScreen( + inputValid: Boolean, + modelsDirty: Boolean, + successMessage: String?, + onDelete: () -> Unit, + onSave: () -> Unit, + onShare: () -> Unit, + onExit: () -> Unit, + supportsAuthentication: Boolean, + + // Subscription settings model + url: String?, + title: String?, + titleChanged: (String) -> Unit, + color: Int?, + colorChanged: (Int) -> Unit, + ignoreAlerts: Boolean, + ignoreAlertsChanged: (Boolean) -> Unit, + defaultAlarmMinutes: Long?, + defaultAlarmMinutesChanged: (String) -> Unit, + defaultAllDayAlarmMinutes: Long?, + defaultAllDayAlarmMinutesChanged: (String) -> Unit, + ignoreDescription: Boolean, + onIgnoreDescriptionChanged: (Boolean) -> Unit, + isCreating: Boolean, + + // Credentials model + requiresAuth: Boolean, + username: String? = null, + password: String? = null, + onRequiresAuthChange: (Boolean) -> Unit, + onUsernameChange: (String) -> Unit, + onPasswordChange: (String) -> Unit +) { + // show success message + successMessage?.let { + Toast.makeText(LocalContext.current, successMessage, Toast.LENGTH_LONG).show() + onExit() + } + + Scaffold( + topBar = { + AppBarComposable( + valid = inputValid, + modelsDirty = modelsDirty, + onDelete = onDelete, + onSave = onSave, + onShare = onShare, + onExit = onExit + ) + } + ) { paddingValues -> + Column( + Modifier + .verticalScroll(rememberScrollState()) + .padding(paddingValues) + .padding(16.dp) + ) { + SubscriptionSettingsComposable( + modifier = Modifier.fillMaxWidth(), + url = url, + title = title, + titleChanged = titleChanged, + color = color, + colorChanged = colorChanged, + ignoreAlerts = ignoreAlerts, + ignoreAlertsChanged = ignoreAlertsChanged, + defaultAlarmMinutes = defaultAlarmMinutes, + defaultAlarmMinutesChanged = defaultAlarmMinutesChanged, + defaultAllDayAlarmMinutes = defaultAllDayAlarmMinutes, + defaultAllDayAlarmMinutesChanged = defaultAllDayAlarmMinutesChanged, + ignoreDescription = ignoreDescription, + onIgnoreDescriptionChanged = onIgnoreDescriptionChanged, + isCreating = isCreating + ) + AnimatedVisibility( + visible = supportsAuthentication + ) { + LoginCredentialsComposable( + requiresAuth = requiresAuth, + username = username, + password = password, + onRequiresAuthChange = onRequiresAuthChange, + onUsernameChange = onUsernameChange, + onPasswordChange = onPasswordChange + ) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun AppBarComposable( + valid: Boolean, + modelsDirty: Boolean, + onDelete: () -> Unit, + onSave: () -> Unit, + onShare: () -> Unit, + onExit: () -> Unit +) { + var openDeleteDialog by remember { mutableStateOf(false) } + if (openDeleteDialog) + GenericAlertDialog( + content = { Text(stringResource(R.string.edit_calendar_really_delete)) }, + confirmButton = stringResource(R.string.edit_calendar_delete) to { + onDelete() + openDeleteDialog = false + }, + dismissButton = stringResource(R.string.edit_calendar_cancel) to { + openDeleteDialog = false + }, + ) { openDeleteDialog = false } + var openSaveDismissDialog by remember { mutableStateOf(false) } + if (openSaveDismissDialog) { + GenericAlertDialog( + content = { Text(text = if (valid) + stringResource(R.string.edit_calendar_unsaved_changes) + else + stringResource(R.string.edit_calendar_need_valid_credentials) + ) }, + confirmButton = if (valid) stringResource(R.string.edit_calendar_save) to { + onSave() + openSaveDismissDialog = false + } else stringResource(R.string.edit_calendar_edit) to { + openSaveDismissDialog = false + }, + dismissButton = stringResource(R.string.edit_calendar_dismiss) to onExit + ) { openSaveDismissDialog = false } + } + ExtendedTopAppBar( + navigationIcon = { + IconButton( + onClick = { + if (modelsDirty) + openSaveDismissDialog = true + else + onExit() + } + ) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, null) + } + }, + title = { Text(text = stringResource(R.string.activity_edit_calendar)) }, + actions = { + IconButton(onClick = { onShare() }) { + Icon( + Icons.Filled.Share, + stringResource(R.string.edit_calendar_send_url) + ) + } + IconButton(onClick = { openDeleteDialog = true }) { + Icon(Icons.Filled.Delete, stringResource(R.string.edit_calendar_delete)) + } + AnimatedVisibility(visible = valid && modelsDirty) { + IconButton(onClick = { onSave() }) { + Icon(Icons.Filled.Check, stringResource(R.string.edit_calendar_save)) + } + } + } + ) +} + +@Preview +@Composable +fun EditCalendarScreen_Preview() { + AppTheme { + EditCalendarScreen( + inputValid = true, + modelsDirty = false, + successMessage = "yay!", + onDelete = {}, + onSave = {}, + onShare = {}, + onExit = {}, + supportsAuthentication = true, + + // Subscription settings model + url = "url", + title = "title", + titleChanged = {}, + color = 0, + colorChanged = {}, + ignoreAlerts = true, + ignoreAlertsChanged = {}, + defaultAlarmMinutes = 20L, + defaultAlarmMinutesChanged = {}, + defaultAllDayAlarmMinutes = 10L, + defaultAllDayAlarmMinutesChanged = {}, + ignoreDescription = false, + onIgnoreDescriptionChanged = {}, + isCreating = true, + + // Credentials model + requiresAuth = true, + username = "", + password = "", + onRequiresAuthChange = {}, + onUsernameChange = {}, + onPasswordChange = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/views/EditCalendarActivity.kt b/app/src/main/java/at/bitfire/icsdroid/ui/views/EditCalendarActivity.kt index b3ac8361..721818c4 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/views/EditCalendarActivity.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/views/EditCalendarActivity.kt @@ -5,313 +5,38 @@ package at.bitfire.icsdroid.ui.views import android.os.Bundle -import android.util.Log -import android.widget.Toast -import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.Share -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.core.app.ShareCompat -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.coroutineScope -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.viewModelScope -import at.bitfire.icsdroid.Constants import at.bitfire.icsdroid.R -import at.bitfire.icsdroid.db.dao.SubscriptionsDao -import at.bitfire.icsdroid.db.entity.Credential import at.bitfire.icsdroid.db.entity.Subscription -import at.bitfire.icsdroid.model.CredentialsModel -import at.bitfire.icsdroid.model.EditSubscriptionModel -import at.bitfire.icsdroid.model.SubscriptionSettingsModel -import at.bitfire.icsdroid.ui.partials.ExtendedTopAppBar -import at.bitfire.icsdroid.ui.partials.GenericAlertDialog +import at.bitfire.icsdroid.ui.screen.EditCalendarScreen import at.bitfire.icsdroid.ui.theme.setContentThemed -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch class EditCalendarActivity: AppCompatActivity() { companion object { + // Used by intents only const val EXTRA_SUBSCRIPTION_ID = "subscriptionId" } - private val subscriptionSettingsModel by viewModels() - private var initialSubscription: Subscription? = null - private val credentialsModel by viewModels() - private var initialCredential: Credential? = null - private var initialRequiresAuthValue: Boolean? = null - - // Whether user made changes are legal - private val inputValid: Boolean - @Composable - get() = remember(subscriptionSettingsModel.uiState, credentialsModel.uiState) { - val title = subscriptionSettingsModel.uiState.title - val requiresAuth = credentialsModel.uiState.requiresAuth - val username = credentialsModel.uiState.username - val password = credentialsModel.uiState.password - - val titleOK = !title.isNullOrBlank() - val authOK = if (requiresAuth) - !username.isNullOrBlank() && !password.isNullOrBlank() - else - true - titleOK && authOK - } - - // Whether unsaved changes exist - private val modelsDirty: Boolean - @Composable - get() = remember(subscriptionSettingsModel.uiState, credentialsModel.uiState) { - val requiresAuth = credentialsModel.uiState.requiresAuth - - val credentialsDirty = initialRequiresAuthValue != requiresAuth || - initialCredential?.let { !credentialsModel.equalsCredential(it) } ?: false - val subscriptionsDirty = initialSubscription?.let { - !subscriptionSettingsModel.equalsSubscription(it) - } ?: false - - credentialsDirty || subscriptionsDirty - } - - - private val model by viewModels { - object: ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - val subscriptionId = intent.getLongExtra(EXTRA_SUBSCRIPTION_ID, -1) - return EditSubscriptionModel(application, subscriptionId) as T - } - } - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - // Initialise view models and save their initial state - lifecycleScope.launch { - model.subscriptionWithCredential.flowWithLifecycle(lifecycle).collect { data -> - if (data != null) - onSubscriptionLoaded(data) - } - } - setContentThemed { - val successMessage = model.uiState.successMessage - // show success message - successMessage?.let { - Toast.makeText(this, it, Toast.LENGTH_LONG).show() - finish() - } - - EditCalendarComposable() - } - } - - /** - * Initialise view models and remember their initial state - */ - private fun onSubscriptionLoaded(subscriptionWithCredential: SubscriptionsDao.SubscriptionWithCredential) { - val subscription = subscriptionWithCredential.subscription - - subscriptionSettingsModel.setUrl(subscription.url.toString()) - subscription.displayName.let { - subscriptionSettingsModel.setTitle(it) - } - subscription.color.let(subscriptionSettingsModel::setColor) - subscription.ignoreEmbeddedAlerts.let(subscriptionSettingsModel::setIgnoreAlerts) - subscription.defaultAlarmMinutes?.toString().let(subscriptionSettingsModel::setDefaultAlarmMinutes) - subscription.defaultAllDayAlarmMinutes?.toString()?.let(subscriptionSettingsModel::setDefaultAllDayAlarmMinutes) - subscription.ignoreDescription.let(subscriptionSettingsModel::setIgnoreDescription) - - val credential = subscriptionWithCredential.credential - val requiresAuth = credential != null - credentialsModel.setRequiresAuth(requiresAuth) - - if (credential != null) { - credential.username.let(credentialsModel::setUsername) - credential.password.let(credentialsModel::setPassword) - } - - // Save state, before user makes changes - initialSubscription = subscription - initialCredential = credential - initialRequiresAuthValue = credentialsModel.uiState.requiresAuth - } - - - /* user actions */ - - private fun onSave() = model.updateSubscription(subscriptionSettingsModel, credentialsModel) - - private fun onDelete() = model.removeSubscription() - - private fun onShare() { - lifecycleScope.launch { - model.subscriptionWithCredential.value?.let { (subscription, _) -> - ShareCompat.IntentBuilder(this@EditCalendarActivity) - .setSubject(subscription.displayName) - .setText(subscription.url.toString()) - .setType("text/plain") - .setChooserTitle(R.string.edit_calendar_send_url) - .startChooser() - } - } - } - - /* Composables */ - - @Composable - private fun EditCalendarComposable() { - val url = subscriptionSettingsModel.uiState.url - val title = subscriptionSettingsModel.uiState.title - val color = subscriptionSettingsModel.uiState.color - val ignoreAlerts = subscriptionSettingsModel.uiState.ignoreAlerts - val defaultAlarmMinutes = subscriptionSettingsModel.uiState.defaultAlarmMinutes - val defaultAllDayAlarmMinutes = subscriptionSettingsModel.uiState.defaultAllDayAlarmMinutes - val ignoreDescription = subscriptionSettingsModel.uiState.ignoreDescription - Scaffold( - topBar = { AppBarComposable(inputValid, modelsDirty) } - ) { paddingValues -> - Column( - Modifier - .verticalScroll(rememberScrollState()) - .padding(paddingValues) - .padding(16.dp) - ) { - SubscriptionSettingsComposable( - url = url, - title = title, - titleChanged = subscriptionSettingsModel::setTitle, - color = color, - colorChanged = subscriptionSettingsModel::setColor, - ignoreAlerts = ignoreAlerts, - ignoreAlertsChanged = subscriptionSettingsModel::setIgnoreAlerts, - defaultAlarmMinutes = defaultAlarmMinutes, - defaultAlarmMinutesChanged = subscriptionSettingsModel::setDefaultAlarmMinutes, - defaultAllDayAlarmMinutes = defaultAllDayAlarmMinutes, - defaultAllDayAlarmMinutesChanged = subscriptionSettingsModel::setDefaultAllDayAlarmMinutes, - ignoreDescription = ignoreDescription, - onIgnoreDescriptionChanged = subscriptionSettingsModel::setIgnoreDescription, - isCreating = false, - modifier = Modifier.fillMaxWidth() - ) - val supportsAuthentication = subscriptionSettingsModel.uiState.supportsAuthentication - val requiresAuth: Boolean = credentialsModel.uiState.requiresAuth - val username: String? = credentialsModel.uiState.username - val password: String? = credentialsModel.uiState.password - AnimatedVisibility(visible = supportsAuthentication) { - LoginCredentialsComposable( - requiresAuth, - username, - password, - onRequiresAuthChange = credentialsModel::setRequiresAuth, - onUsernameChange = credentialsModel::setUsername, - onPasswordChange = credentialsModel::setPassword - ) - } - } - } - } - - @OptIn(ExperimentalMaterial3Api::class) - @Composable - private fun AppBarComposable(valid: Boolean, modelsDirty: Boolean) { - var openDeleteDialog by remember { mutableStateOf(false) } - if (openDeleteDialog) - GenericAlertDialog( - content = { Text(stringResource(R.string.edit_calendar_really_delete)) }, - confirmButton = stringResource(R.string.edit_calendar_delete) to { - onDelete() - openDeleteDialog = false - }, - dismissButton = stringResource(R.string.edit_calendar_cancel) to { - openDeleteDialog = false - }, - ) { openDeleteDialog = false } - var openSaveDismissDialog by remember { mutableStateOf(false) } - if (openSaveDismissDialog) { - GenericAlertDialog( - content = { Text(text = if (valid) - stringResource(R.string.edit_calendar_unsaved_changes) - else - stringResource(R.string.edit_calendar_need_valid_credentials) - ) }, - confirmButton = if (valid) stringResource(R.string.edit_calendar_save) to { - onSave() - openSaveDismissDialog = false - } else stringResource(R.string.edit_calendar_edit) to { - openSaveDismissDialog = false - }, - dismissButton = stringResource(R.string.edit_calendar_dismiss) to ::finish - ) { openSaveDismissDialog = false } - } - ExtendedTopAppBar( - navigationIcon = { - IconButton( - onClick = { - if (modelsDirty) - openSaveDismissDialog = true - else - finish() - } - ) { - Icon(Icons.AutoMirrored.Filled.ArrowBack, null) - } - }, - title = { Text(text = stringResource(R.string.activity_edit_calendar)) }, - actions = { - IconButton(onClick = { onShare() }) { - Icon( - Icons.Filled.Share, - stringResource(R.string.edit_calendar_send_url) - ) - } - IconButton(onClick = { openDeleteDialog = true }) { - Icon(Icons.Filled.Delete, stringResource(R.string.edit_calendar_delete)) - } - AnimatedVisibility(visible = valid && modelsDirty) { - IconButton(onClick = { onSave() }) { - Icon(Icons.Filled.Check, stringResource(R.string.edit_calendar_save)) - } - } - } - ) - } - - @Preview - @Composable - fun TopBarComposable_Preview() { - AppBarComposable(valid = true, modelsDirty = true) - } + EditCalendarScreen( + application, + subscriptionId = intent.getLongExtra(EXTRA_SUBSCRIPTION_ID, -1), + { onShare(it) }, + { finish() } + ) + } + } + + private fun onShare(subscription: Subscription) = + ShareCompat.IntentBuilder(this) + .setSubject(subscription.displayName) + .setText(subscription.url.toString()) + .setType("text/plain") + .setChooserTitle(R.string.edit_calendar_send_url) + .startChooser() } \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/views/SubscriptionSettingsComposable.kt b/app/src/main/java/at/bitfire/icsdroid/ui/views/SubscriptionSettingsComposable.kt index be648bc8..25c50ca2 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/views/SubscriptionSettingsComposable.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/views/SubscriptionSettingsComposable.kt @@ -30,11 +30,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import at.bitfire.icsdroid.R import at.bitfire.icsdroid.calendar.LocalCalendar import at.bitfire.icsdroid.ui.partials.ColorPickerDialog import at.bitfire.icsdroid.ui.partials.SwitchSetting +import at.bitfire.icsdroid.ui.theme.AppTheme @Composable fun SubscriptionSettingsComposable( @@ -184,8 +186,31 @@ fun SubscriptionSettingsComposable( SwitchSetting( title = stringResource(R.string.add_calendar_description_title), description = stringResource(R.string.add_calendar_description_summary), - checked = ignoreDescription ?: false, + checked = ignoreDescription, onCheckedChange = onIgnoreDescriptionChanged ) } +} + +@Preview +@Composable +fun SubscriptionSettingsComposable_Preview() { + AppTheme { + SubscriptionSettingsComposable( + url = "url", + title = "title", + titleChanged = {}, + color = 0, + colorChanged = {}, + ignoreAlerts = true, + ignoreAlertsChanged = {}, + defaultAlarmMinutes = 20L, + defaultAlarmMinutesChanged = {}, + defaultAllDayAlarmMinutes = 10L, + defaultAllDayAlarmMinutesChanged = {}, + ignoreDescription = false, + onIgnoreDescriptionChanged = {}, + isCreating = true + ) + } } \ No newline at end of file