From eb9bd7a2562aa2461cd7e1b7ee57691ad9cb465e Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 25 Mar 2024 16:45:47 +0100 Subject: [PATCH 1/4] Added option to ignore description Signed-off-by: Arnau Mora Gras --- .../at.bitfire.icsdroid.db.AppDatabase/4.json | 151 ++++++++++++++++++ .../at/bitfire/icsdroid/db/AppDatabase.kt | 6 +- .../icsdroid/db/entity/Subscription.kt | 5 + .../icsdroid/model/CreateSubscriptionModel.kt | 1 + .../icsdroid/model/EditSubscriptionModel.kt | 3 +- .../model/SubscriptionSettingsModel.kt | 14 +- .../icsdroid/ui/views/AddCalendarActivity.kt | 7 +- .../icsdroid/ui/views/EditCalendarActivity.kt | 7 + .../views/SubscriptionSettingsComposable.kt | 16 ++ app/src/main/res/values/strings.xml | 3 + 10 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 app/schemas/at.bitfire.icsdroid.db.AppDatabase/4.json diff --git a/app/schemas/at.bitfire.icsdroid.db.AppDatabase/4.json b/app/schemas/at.bitfire.icsdroid.db.AppDatabase/4.json new file mode 100644 index 00000000..5907691a --- /dev/null +++ b/app/schemas/at.bitfire.icsdroid.db.AppDatabase/4.json @@ -0,0 +1,151 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "1eec5a75f63cbbca3716bc789fd91375", + "entities": [ + { + "tableName": "subscriptions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `calendarId` INTEGER, `url` TEXT NOT NULL, `eTag` TEXT, `displayName` TEXT NOT NULL, `lastModified` INTEGER, `lastSync` INTEGER, `errorMessage` TEXT, `ignoreEmbeddedAlerts` INTEGER NOT NULL, `defaultAlarmMinutes` INTEGER, `defaultAllDayAlarmMinutes` INTEGER, `ignoreDescription` INTEGER NOT NULL DEFAULT 0, `color` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "calendarId", + "columnName": "calendarId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eTag", + "columnName": "eTag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastModified", + "columnName": "lastModified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSync", + "columnName": "lastSync", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "errorMessage", + "columnName": "errorMessage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ignoreEmbeddedAlerts", + "columnName": "ignoreEmbeddedAlerts", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "defaultAlarmMinutes", + "columnName": "defaultAlarmMinutes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "defaultAllDayAlarmMinutes", + "columnName": "defaultAllDayAlarmMinutes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ignoreDescription", + "columnName": "ignoreDescription", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "credentials", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`subscriptionId` INTEGER NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, PRIMARY KEY(`subscriptionId`), FOREIGN KEY(`subscriptionId`) REFERENCES `subscriptions`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "subscriptionId", + "columnName": "subscriptionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "subscriptionId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "subscriptions", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "subscriptionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1eec5a75f63cbbca3716bc789fd91375')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/icsdroid/db/AppDatabase.kt b/app/src/main/java/at/bitfire/icsdroid/db/AppDatabase.kt index af9aa729..e78114e9 100644 --- a/app/src/main/java/at/bitfire/icsdroid/db/AppDatabase.kt +++ b/app/src/main/java/at/bitfire/icsdroid/db/AppDatabase.kt @@ -25,7 +25,7 @@ import at.bitfire.icsdroid.db.entity.Subscription @TypeConverters(Converters::class) @Database( entities = [Subscription::class, Credential::class], - version = 3, + version = 4, autoMigrations = [ AutoMigration ( from = 1, @@ -34,6 +34,10 @@ import at.bitfire.icsdroid.db.entity.Subscription AutoMigration ( from = 2, to = 3 + ), + AutoMigration ( + from = 3, + to = 4 ) ] ) diff --git a/app/src/main/java/at/bitfire/icsdroid/db/entity/Subscription.kt b/app/src/main/java/at/bitfire/icsdroid/db/entity/Subscription.kt index 4f3e28be..85fd0002 100644 --- a/app/src/main/java/at/bitfire/icsdroid/db/entity/Subscription.kt +++ b/app/src/main/java/at/bitfire/icsdroid/db/entity/Subscription.kt @@ -7,6 +7,7 @@ package at.bitfire.icsdroid.db.entity import android.net.Uri import android.provider.CalendarContract.Calendars import androidx.core.content.contentValuesOf +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import at.bitfire.icsdroid.calendar.LocalCalendar @@ -42,6 +43,10 @@ data class Subscription( /** setting: Shall a default alarm be added to every all-day event in the calendar? If yes, this field contains the minutes before the event. If no, it is `null`. */ val defaultAllDayAlarmMinutes: Long? = null, + /** If true, the `DESCRIPTION` field of events will be dropped when synchronization runs. */ + @ColumnInfo(defaultValue = "0") + val ignoreDescription: Boolean = false, + /** The color that represents the subscription. */ val color: Int? = null ) { diff --git a/app/src/main/java/at/bitfire/icsdroid/model/CreateSubscriptionModel.kt b/app/src/main/java/at/bitfire/icsdroid/model/CreateSubscriptionModel.kt index af1da87e..4bc1f694 100644 --- a/app/src/main/java/at/bitfire/icsdroid/model/CreateSubscriptionModel.kt +++ b/app/src/main/java/at/bitfire/icsdroid/model/CreateSubscriptionModel.kt @@ -41,6 +41,7 @@ class CreateSubscriptionModel(application: Application) : AndroidViewModel(appli ignoreEmbeddedAlerts = subscriptionSettingsModel.ignoreAlerts.value ?: false, defaultAlarmMinutes = subscriptionSettingsModel.defaultAlarmMinutes.value, defaultAllDayAlarmMinutes = subscriptionSettingsModel.defaultAllDayAlarmMinutes.value, + ignoreDescription = subscriptionSettingsModel.ignoreDescription.value ?: false, ) /** A list of all the ids of the inserted rows */ diff --git a/app/src/main/java/at/bitfire/icsdroid/model/EditSubscriptionModel.kt b/app/src/main/java/at/bitfire/icsdroid/model/EditSubscriptionModel.kt index 2dce32d8..62e4eb16 100644 --- a/app/src/main/java/at/bitfire/icsdroid/model/EditSubscriptionModel.kt +++ b/app/src/main/java/at/bitfire/icsdroid/model/EditSubscriptionModel.kt @@ -40,7 +40,8 @@ class EditSubscriptionModel( color = subscriptionSettingsModel.color.value, defaultAlarmMinutes = subscriptionSettingsModel.defaultAlarmMinutes.value, defaultAllDayAlarmMinutes = subscriptionSettingsModel.defaultAllDayAlarmMinutes.value, - ignoreEmbeddedAlerts = subscriptionSettingsModel.ignoreAlerts.value ?: false + ignoreEmbeddedAlerts = subscriptionSettingsModel.ignoreAlerts.value ?: false, + ignoreDescription = subscriptionSettingsModel.ignoreDescription.value ?: false ) subscriptionsDao.update(newSubscription) diff --git a/app/src/main/java/at/bitfire/icsdroid/model/SubscriptionSettingsModel.kt b/app/src/main/java/at/bitfire/icsdroid/model/SubscriptionSettingsModel.kt index 44da91ef..c7b38c6e 100644 --- a/app/src/main/java/at/bitfire/icsdroid/model/SubscriptionSettingsModel.kt +++ b/app/src/main/java/at/bitfire/icsdroid/model/SubscriptionSettingsModel.kt @@ -17,6 +17,9 @@ class SubscriptionSettingsModel : ViewModel() { val defaultAlarmMinutes = MutableLiveData(null) val defaultAllDayAlarmMinutes = MutableLiveData(null) + // advanced + val ignoreDescription = MutableLiveData(false) + val supportsAuthentication = MediatorLiveData(false).apply { addSource(url) { val uri = try { @@ -32,9 +35,10 @@ class SubscriptionSettingsModel : ViewModel() { fun equalsSubscription(subscription: Subscription) = url.value == subscription.url.toString() - && title.value == subscription.displayName - && color.value == subscription.color - && ignoreAlerts.value == subscription.ignoreEmbeddedAlerts - && defaultAlarmMinutes.value == subscription.defaultAlarmMinutes - && defaultAllDayAlarmMinutes.value == subscription.defaultAllDayAlarmMinutes + && title.value == subscription.displayName + && color.value == subscription.color + && ignoreAlerts.value == subscription.ignoreEmbeddedAlerts + && defaultAlarmMinutes.value == subscription.defaultAlarmMinutes + && defaultAllDayAlarmMinutes.value == subscription.defaultAllDayAlarmMinutes + && ignoreDescription.value == subscription.ignoreDescription } \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/views/AddCalendarActivity.kt b/app/src/main/java/at/bitfire/icsdroid/ui/views/AddCalendarActivity.kt index 34f11e8b..cd7c7aec 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/views/AddCalendarActivity.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/views/AddCalendarActivity.kt @@ -54,10 +54,10 @@ import at.bitfire.icsdroid.ui.ResourceInfo import at.bitfire.icsdroid.ui.partials.ExtendedTopAppBar import at.bitfire.icsdroid.ui.theme.lightblue import at.bitfire.icsdroid.ui.theme.setContentThemed -import kotlinx.coroutines.launch -import okhttp3.HttpUrl.Companion.toHttpUrl import java.net.URI import java.net.URISyntaxException +import kotlinx.coroutines.launch +import okhttp3.HttpUrl.Companion.toHttpUrl @OptIn(ExperimentalFoundationApi::class) class AddCalendarActivity : AppCompatActivity() { @@ -125,6 +125,7 @@ class AddCalendarActivity : AppCompatActivity() { val ignoreAlerts by subscriptionSettingsModel.ignoreAlerts.observeAsState(false) val defaultAlarmMinutes by subscriptionSettingsModel.defaultAlarmMinutes.observeAsState(null) val defaultAllDayAlarmMinutes by subscriptionSettingsModel.defaultAllDayAlarmMinutes.observeAsState(null) + val ignoreDescription by subscriptionSettingsModel.ignoreDescription.observeAsState(false) val requiresAuth: Boolean by credentialsModel.requiresAuth.observeAsState(false) val username: String? by credentialsModel.username.observeAsState(null) @@ -229,6 +230,8 @@ class AddCalendarActivity : AppCompatActivity() { it.toLongOrNull() ) }, + ignoreDescription = ignoreDescription, + onIgnoreDescriptionChanged = subscriptionSettingsModel.ignoreDescription::setValue, isCreating = isCreating, modifier = Modifier .fillMaxSize() 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 6712e9a0..7b073f70 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 @@ -97,6 +97,7 @@ class EditCalendarActivity: AppCompatActivity() { addSource(subscriptionSettingsModel.ignoreAlerts) { value = subscriptionDirty() } addSource(subscriptionSettingsModel.defaultAlarmMinutes) { value = subscriptionDirty() } addSource(subscriptionSettingsModel.defaultAllDayAlarmMinutes) { value = subscriptionDirty() } + addSource(subscriptionSettingsModel.ignoreDescription) { value = subscriptionDirty() } addSource(credentialsModel.requiresAuth) { value = credentialDirty() } addSource(credentialsModel.username) { value = credentialDirty() } addSource(credentialsModel.password) { value = credentialDirty() } @@ -171,6 +172,9 @@ class EditCalendarActivity: AppCompatActivity() { subscription.defaultAllDayAlarmMinutes.let { subscriptionSettingsModel.defaultAllDayAlarmMinutes.postValue(it) } + subscription.ignoreDescription.let { + subscriptionSettingsModel.ignoreDescription.postValue(it) + } val credential = subscriptionWithCredential.credential val requiresAuth = credential != null @@ -217,6 +221,7 @@ class EditCalendarActivity: AppCompatActivity() { val ignoreAlerts by subscriptionSettingsModel.ignoreAlerts.observeAsState(false) val defaultAlarmMinutes by subscriptionSettingsModel.defaultAlarmMinutes.observeAsState() val defaultAllDayAlarmMinutes by subscriptionSettingsModel.defaultAllDayAlarmMinutes.observeAsState() + val ignoreDescription by subscriptionSettingsModel.ignoreDescription.observeAsState(false) val inputValid by inputValid.observeAsState(false) val modelsDirty by modelsDirty.observeAsState(false) Scaffold( @@ -248,6 +253,8 @@ class EditCalendarActivity: AppCompatActivity() { it.toLongOrNull() ) }, + ignoreDescription = ignoreDescription, + onIgnoreDescriptionChanged = subscriptionSettingsModel.ignoreDescription::postValue, isCreating = false, modifier = Modifier.fillMaxWidth() ) 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 b1c89ccb..be648bc8 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 @@ -49,6 +49,8 @@ fun SubscriptionSettingsComposable( defaultAlarmMinutesChanged: (String) -> Unit, defaultAllDayAlarmMinutes: Long?, defaultAllDayAlarmMinutesChanged: (String) -> Unit, + ignoreDescription: Boolean, + onIgnoreDescriptionChanged: (Boolean) -> Unit, isCreating: Boolean, modifier: Modifier = Modifier ) { @@ -171,5 +173,19 @@ fun SubscriptionSettingsComposable( ) Spacer(modifier = Modifier.padding(12.dp)) + + // Advanced + + Text( + text = stringResource(R.string.add_calendar_advanced_title), + style = MaterialTheme.typography.headlineSmall, + ) + + SwitchSetting( + title = stringResource(R.string.add_calendar_description_title), + description = stringResource(R.string.add_calendar_description_summary), + checked = ignoreDescription ?: false, + onCheckedChange = onIgnoreDescriptionChanged + ) } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d1bc0df6..551bbd89 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,6 +78,9 @@ Add default alarm This will add an alarm for all events Minutes before event + Advanced + Ignore DESCRIPTION field + If enabled, the DESCRIPTION field will be dropped from each event. This is normally used for adding spammy information to events. Share details From 80bb85963eb100979fe3d0f94a0452ef4b6dca18 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 25 Mar 2024 16:53:32 +0100 Subject: [PATCH 2/4] Implemented description removal Signed-off-by: Arnau Mora Gras --- .../at/bitfire/icsdroid/ProcessEventsTask.kt | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/at/bitfire/icsdroid/ProcessEventsTask.kt b/app/src/main/java/at/bitfire/icsdroid/ProcessEventsTask.kt index fad5017c..b9f0ee21 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ProcessEventsTask.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ProcessEventsTask.kt @@ -16,17 +16,17 @@ import at.bitfire.icsdroid.calendar.LocalCalendar import at.bitfire.icsdroid.calendar.LocalEvent import at.bitfire.icsdroid.db.AppDatabase import at.bitfire.icsdroid.db.entity.Subscription -import at.bitfire.icsdroid.ui.views.EditCalendarActivity import at.bitfire.icsdroid.ui.NotificationUtils +import at.bitfire.icsdroid.ui.views.EditCalendarActivity +import java.io.InputStream +import java.io.InputStreamReader +import java.time.Duration import net.fortuna.ical4j.model.Property import net.fortuna.ical4j.model.PropertyList import net.fortuna.ical4j.model.component.VAlarm import net.fortuna.ical4j.model.property.Action import net.fortuna.ical4j.model.property.Trigger import okhttp3.MediaType -import java.io.InputStream -import java.io.InputStreamReader -import java.time.Duration /** * Fetches the .ics for a given Webcal subscription and stores the events @@ -103,6 +103,18 @@ class ProcessEventsTask( } } + /** + * Updates the advanced preferences of the given event according to the [subscription]'s: + * - [Subscription.ignoreDescription] + */ + private fun updateAdvancedPreferences(event: Event): Event = event.apply { + if (subscription.ignoreDescription) { + // Remove the description + Log.d(Constants.TAG, "Removing the description from $uid") + description = null + } + } + private suspend fun processEvents() { val uri = subscription.url Log.i(Constants.TAG, "Synchronizing $uri, forceResync=$forceResync") @@ -209,7 +221,7 @@ class ProcessEventsTask( val uids = HashSet(events.size) for (ev in events) { - val event = updateAlarms(ev) + val event = updateAlarms(ev).let(::updateAdvancedPreferences) val uid = event.uid!! Log.d(Constants.TAG, "Found VEVENT $uid: $event") uids += uid From 28a21b730d8f2d15028265b30dadf2b6ef1fe12c Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 25 Mar 2024 16:57:04 +0100 Subject: [PATCH 3/4] Added title to authentication Signed-off-by: Arnau Mora Gras --- .../icsdroid/ui/views/CredentialsComposable.kt | 14 +++++++++++--- app/src/main/res/values/strings.xml | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/views/CredentialsComposable.kt b/app/src/main/java/at/bitfire/icsdroid/ui/views/CredentialsComposable.kt index 2fa7d18b..3fd1b463 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/views/CredentialsComposable.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/views/CredentialsComposable.kt @@ -8,16 +8,17 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Visibility +import androidx.compose.material.icons.rounded.VisibilityOff import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Switch import androidx.compose.material3.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Visibility -import androidx.compose.material.icons.rounded.VisibilityOff import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -30,6 +31,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import at.bitfire.icsdroid.R @Composable @@ -50,6 +52,12 @@ fun LoginCredentialsComposable( Column( Modifier.fillMaxWidth() ) { + Text( + text = stringResource(R.string.add_calendar_authentication_title), + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.padding(top = 8.dp) + ) + Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 551bbd89..1bf44abd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -56,6 +56,7 @@ Subscribe to calendar Valid URI required + Authentication Third parties can easily intercept your credentials. Use HTTPS for secure authentication. Subscribed successfully Password From 23263fe42c43269cb8451e85a6bedf920e0617fa Mon Sep 17 00:00:00 2001 From: Sunik Kupfer Date: Wed, 27 Mar 2024 14:52:42 +0100 Subject: [PATCH 4/4] Update setting explanation --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1bf44abd..42659d99 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,7 +81,7 @@ Minutes before event Advanced Ignore DESCRIPTION field - If enabled, the DESCRIPTION field will be dropped from each event. This is normally used for adding spammy information to events. + If enabled, the DESCRIPTION field will be removed from events. Helpful if it contains spammy information. Share details