diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt index 504211eca..7f9a581f4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/EventActivity.kt @@ -63,6 +63,7 @@ class EventActivity : SimpleActivity() { private val REPEAT_LIMIT = "REPEAT_LIMIT" private val REPEAT_RULE = "REPEAT_RULE" private val ATTENDEES = "ATTENDEES" + private val AVAILABILITY = "AVAILABILITY" private val EVENT_TYPE_ID = "EVENT_TYPE_ID" private val EVENT_CALENDAR_ID = "EVENT_CALENDAR_ID" private val SELECT_TIME_ZONE_INTENT = 1 @@ -89,6 +90,7 @@ class EventActivity : SimpleActivity() { private var mAttendeeAutoCompleteViews = ArrayList() private var mAvailableContacts = ArrayList() private var mSelectedContacts = ArrayList() + private var mAvailability = Attendees.AVAILABILITY_BUSY private var mStoredEventTypes = ArrayList() private var mOriginalTimeZone = DateTimeZone.getDefault().id private var mOriginalStartTS = 0L @@ -217,6 +219,14 @@ class EventActivity : SimpleActivity() { } } + event_availability.setOnClickListener { + showAvailabilityPicker(mAvailability) { + mAvailability = it + updateAvailabilityText() + updateAvailabilityImage() + } + } + event_type_holder.setOnClickListener { showEventTypeDialog() } event_all_day.apply { isChecked = mEvent.flags and FLAG_ALL_DAY != 0 @@ -350,6 +360,8 @@ class EventActivity : SimpleActivity() { putString(ATTENDEES, getAllAttendees(false)) + putInt(AVAILABILITY, mAvailability) + putLong(EVENT_TYPE_ID, mEventTypeId) putInt(EVENT_CALENDAR_ID, mEventCalendarId) } @@ -376,6 +388,8 @@ class EventActivity : SimpleActivity() { mReminder2Type = getInt(REMINDER_2_TYPE) mReminder3Type = getInt(REMINDER_3_TYPE) + mAvailability = getInt(AVAILABILITY) + mRepeatInterval = getInt(REPEAT_INTERVAL) mRepeatRule = getInt(REPEAT_RULE) mRepeatLimit = getLong(REPEAT_LIMIT) @@ -410,7 +424,9 @@ class EventActivity : SimpleActivity() { updateStartTexts() updateEndTexts() updateTimeZoneText() - updateAttendeesVisibility() + updateCalDAVVisibility() + updateAvailabilityText() + updateAvailabilityImage() } private fun setupEditEvent() { @@ -451,6 +467,7 @@ class EventActivity : SimpleActivity() { mRepeatRule = mEvent.repeatRule mEventTypeId = mEvent.eventType mEventCalendarId = mEvent.getCalDAVCalendarId() + mAvailability = mEvent.availability val token = object : TypeToken>() {}.type mAttendees = Gson().fromJson>(mEvent.attendees, token) ?: ArrayList() @@ -836,17 +853,30 @@ class EventActivity : SimpleActivity() { } } + private fun showAvailabilityPicker(currentValue: Int, callback: (Int) -> Unit) { + val items = arrayListOf( + RadioItem(Attendees.AVAILABILITY_BUSY, getString(R.string.status_busy)), + RadioItem(Attendees.AVAILABILITY_FREE, getString(R.string.status_free)) + ) + RadioGroupDialog(this, items, currentValue) { + callback(it as Int) + } + } + private fun updateReminderTypeImages() { updateReminderTypeImage(event_reminder_1_type, Reminder(mReminder1Minutes, mReminder1Type)) updateReminderTypeImage(event_reminder_2_type, Reminder(mReminder2Minutes, mReminder2Type)) updateReminderTypeImage(event_reminder_3_type, Reminder(mReminder3Minutes, mReminder3Type)) } - private fun updateAttendeesVisibility() { + private fun updateCalDAVVisibility() { val isSyncedEvent = mEventCalendarId != STORED_LOCALLY_ONLY event_attendees_image.beVisibleIf(isSyncedEvent) event_attendees_holder.beVisibleIf(isSyncedEvent) event_attendees_divider.beVisibleIf(isSyncedEvent) + event_availability_divider.beVisibleIf(isSyncedEvent) + event_availability_image.beVisibleIf(isSyncedEvent) + event_availability.beVisibleIf(isSyncedEvent) } private fun updateReminderTypeImage(view: ImageView, reminder: Reminder) { @@ -856,6 +886,16 @@ class EventActivity : SimpleActivity() { view.setImageDrawable(icon) } + private fun updateAvailabilityImage() { + val drawable = if (mAvailability == Attendees.AVAILABILITY_FREE) R.drawable.ic_event_available else R.drawable.ic_event_occupied + val icon = resources.getColoredDrawableWithColor(drawable, config.textColor) + event_availability_image.setImageDrawable(icon) + } + + private fun updateAvailabilityText() { + event_availability.text = if (mAvailability == Attendees.AVAILABILITY_FREE) getString(R.string.status_free) else getString(R.string.status_busy) + } + private fun updateRepetitionText() { event_repetition.text = getRepetitionText(mRepeatInterval) } @@ -895,7 +935,9 @@ class EventActivity : SimpleActivity() { config.lastUsedCaldavCalendarId = it updateCurrentCalendarInfo(getCalendarWithId(calendars, it)) updateReminderTypeImages() - updateAttendeesVisibility() + updateCalDAVVisibility() + updateAvailabilityText() + updateAvailabilityImage() } } } else { @@ -1122,6 +1164,7 @@ class EventActivity : SimpleActivity() { lastUpdated = System.currentTimeMillis() source = newSource location = event_location.value + availability = mAvailability } // recreate the event if it was moved in a different CalDAV calendar @@ -1648,7 +1691,7 @@ class EventActivity : SimpleActivity() { val textColor = config.textColor arrayOf( event_time_image, event_time_zone_image, event_repetition_image, event_reminder_image, event_type_image, event_caldav_calendar_image, - event_reminder_1_type, event_reminder_2_type, event_reminder_3_type, event_attendees_image + event_reminder_1_type, event_reminder_2_type, event_reminder_3_type, event_attendees_image, event_availability_image ).forEach { it.applyColorFilter(textColor) } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt index 7673b0d77..7c7844e21 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/databases/EventsDatabase.kt @@ -17,7 +17,7 @@ import com.simplemobiletools.calendar.pro.models.Event import com.simplemobiletools.calendar.pro.models.EventType import java.util.concurrent.Executors -@Database(entities = [Event::class, EventType::class], version = 3) +@Database(entities = [Event::class, EventType::class], version = 4) @TypeConverters(Converters::class) abstract class EventsDatabase : RoomDatabase() { @@ -41,6 +41,7 @@ abstract class EventsDatabase : RoomDatabase() { }) .addMigrations(MIGRATION_1_2) .addMigrations(MIGRATION_2_3) + .addMigrations(MIGRATION_3_4) .build() db!!.openHelper.setWriteAheadLoggingEnabled(true) } @@ -80,5 +81,13 @@ abstract class EventsDatabase : RoomDatabase() { } } } + + private val MIGRATION_3_4 = object : Migration(3, 4) { + override fun migrate(database: SupportSQLiteDatabase) { + database.apply { + execSQL("ALTER TABLE events ADD COLUMN availability INTEGER NOT NULL DEFAULT 0") + } + } + } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt index aab0321d4..381bd4318 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/CalDAVHelper.kt @@ -170,7 +170,9 @@ class CalDAVHelper(val context: Context) { Events.EVENT_LOCATION, Events.EVENT_TIMEZONE, Events.CALENDAR_TIME_ZONE, - Events.DELETED) + Events.DELETED, + Events.AVAILABILITY + ) val selection = "${Events.CALENDAR_ID} = $calendarId" context.queryCursor(uri, projection, selection, showErrors = showToasts) { cursor -> @@ -191,6 +193,7 @@ class CalDAVHelper(val context: Context) { val originalInstanceTime = cursor.getLongValue(Events.ORIGINAL_INSTANCE_TIME) val reminders = getCalDAVEventReminders(id) val attendees = Gson().toJson(getCalDAVEventAttendees(id)) + val availability = cursor.getIntValue(Events.AVAILABILITY) if (endTS == 0L) { val duration = cursor.getStringValue(Events.DURATION) ?: "" @@ -210,7 +213,7 @@ class CalDAVHelper(val context: Context) { reminder2?.minutes ?: REMINDER_OFF, reminder3?.minutes ?: REMINDER_OFF, reminder1?.type ?: REMINDER_NOTIFICATION, reminder2?.type ?: REMINDER_NOTIFICATION, reminder3?.type ?: REMINDER_NOTIFICATION, repeatRule.repeatInterval, repeatRule.repeatRule, - repeatRule.repeatLimit, ArrayList(), attendees, importId, eventTimeZone, allDay, eventTypeId, source = source) + repeatRule.repeatLimit, ArrayList(), attendees, importId, eventTimeZone, allDay, eventTypeId, source = source, availability = availability) if (event.getIsAllDay()) { event.startTS = Formatter.getShiftedImportTimestamp(event.startTS) @@ -382,6 +385,7 @@ class CalDAVHelper(val context: Context) { put(Events.EVENT_TIMEZONE, event.getTimeZoneString()) put(Events.EVENT_LOCATION, event.location) put(Events.STATUS, Events.STATUS_CONFIRMED) + put(Events.AVAILABILITY, event.availability) val repeatRule = Parser().getRepeatCode(event) if (repeatRule.isEmpty()) { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt index 0cfcda6ce..ad4ed8593 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/Constants.kt @@ -113,6 +113,7 @@ const val SUMMARY = "SUMMARY" const val DESCRIPTION = "DESCRIPTION:" const val UID = "UID:" const val ACTION = "ACTION:" +const val TRANSP = "TRANSP:" const val ATTENDEE = "ATTENDEE:" const val MAILTO = "mailto:" const val TRIGGER = "TRIGGER" @@ -155,6 +156,9 @@ const val FR = "FR" const val SA = "SA" const val SU = "SU" +const val OPAQUE = "OPAQUE" +const val TRANSPARENT = "TRANSPARENT" + const val SOURCE_SIMPLE_CALENDAR = "simple-calendar" const val SOURCE_IMPORTED_ICS = "imported-ics" const val SOURCE_CONTACT_BIRTHDAY = "contact-birthday" diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt index 84b41f7ee..6ba5f4a78 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsExporter.kt @@ -1,5 +1,6 @@ package com.simplemobiletools.calendar.pro.helpers +import android.provider.CalendarContract.Events import com.simplemobiletools.calendar.pro.R import com.simplemobiletools.calendar.pro.extensions.calDAVHelper import com.simplemobiletools.calendar.pro.extensions.eventTypesDB @@ -56,6 +57,7 @@ class IcsExporter { event.eventType.let { out.writeLn("$CATEGORIES${activity.eventTypesDB.getEventTypeWithId(it)?.title}") } event.lastUpdated.let { out.writeLn("$LAST_MODIFIED:${Formatter.getExportedTime(it)}") } event.location.let { out.writeLn("$LOCATION:$it") } + event.availability.let { out.writeLn("$TRANSP${if (it == Events.AVAILABILITY_FREE) TRANSPARENT else OPAQUE}") } if (event.getIsAllDay()) { out.writeLn("$DTSTART;$VALUE=$DATE:${Formatter.getDayCodeFromTS(event.startTS)}") diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt index 1b7b9a75b..7ab90a366 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/IcsImporter.kt @@ -1,5 +1,6 @@ package com.simplemobiletools.calendar.pro.helpers +import android.provider.CalendarContract.Events import com.simplemobiletools.calendar.pro.R import com.simplemobiletools.calendar.pro.activities.SimpleActivity import com.simplemobiletools.calendar.pro.extensions.eventsDB @@ -36,6 +37,7 @@ class IcsImporter(val activity: SimpleActivity) { private var curEventTypeId = REGULAR_EVENT_TYPE_ID private var curLastModified = 0L private var curCategoryColor = -2 + private var curAvailability = Events.AVAILABILITY_BUSY private var isNotificationDescription = false private var isProperReminderAction = false private var isSequence = false @@ -160,6 +162,8 @@ class IcsImporter(val activity: SimpleActivity) { curRecurrenceDayCode = Formatter.getDayCodeFromTS(timestamp) } else if (line.startsWith(SEQUENCE)) { isSequence = true + } else if (line.startsWith(TRANSP)) { + line.substring(TRANSP.length).let { curAvailability = if (it == TRANSPARENT) Events.AVAILABILITY_FREE else Events.AVAILABILITY_BUSY } } else if (line.trim() == BEGIN_ALARM) { isNotificationDescription = true } else if (line.trim() == END_ALARM) { @@ -222,7 +226,8 @@ class IcsImporter(val activity: SimpleActivity) { curEventTypeId, 0, curLastModified, - source + source, + curAvailability ) if (event.getIsAllDay() && curEnd > curStart) { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt index d6020ae47..bd595c4d2 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/Event.kt @@ -37,7 +37,8 @@ data class Event( @ColumnInfo(name = "event_type") var eventType: Long = REGULAR_EVENT_TYPE_ID, @ColumnInfo(name = "parent_id") var parentId: Long = 0, @ColumnInfo(name = "last_updated") var lastUpdated: Long = 0L, - @ColumnInfo(name = "source") var source: String = SOURCE_SIMPLE_CALENDAR) + @ColumnInfo(name = "source") var source: String = SOURCE_SIMPLE_CALENDAR, + @ColumnInfo(name = "availability") var availability: Int = 0) : Serializable { companion object { diff --git a/app/src/main/res/drawable/ic_event_available.xml b/app/src/main/res/drawable/ic_event_available.xml new file mode 100644 index 000000000..67a9d039f --- /dev/null +++ b/app/src/main/res/drawable/ic_event_available.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_event_occupied.xml b/app/src/main/res/drawable/ic_event_occupied.xml new file mode 100644 index 000000000..be00e9377 --- /dev/null +++ b/app/src/main/res/drawable/ic_event_occupied.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_event.xml b/app/src/main/res/layout/activity_event.xml index 32f40d01b..19d676003 100644 --- a/app/src/main/res/layout/activity_event.xml +++ b/app/src/main/res/layout/activity_event.xml @@ -430,11 +430,45 @@ android:background="@color/divider_grey" android:importantForAccessibility="no" /> + + + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d1eeb81c2..853850aba 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -217,8 +217,8 @@ Dir fehlt die Berechtigung zum Ändern des gewählten Kalenders Der Termin wurde nicht gefunden. Bitte aktiviere die Synchronisierung für den Kalender in den Einstellungen. Es wurden keine synchronisierbaren Kalender gefunden - Free - Busy + Verfügbar + Beschäftigt diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e69b4b00f..ad51f3408 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -217,8 +217,8 @@     您不被允许对选择的日历写入 未找到活动。请在应用程序设定中为对应的日历启用 CalDAV 同步。 未发现可同步的日历 - Free - Busy + 空闲 + 繁忙           @@ -302,9 +302,9 @@         Reddit:         https://www.reddit.com/r/SimpleMobileTools -     + -     diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 111f7b2c5..ae16a5580 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -217,8 +217,8 @@ 你不被允許對選擇的行事曆寫入 Event not found. Please enable CalDAV sync for the appropriate calendar in the app settings. No synchronizable calendars have been found - Free - Busy + 空閒 + 繁忙 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 870333543..1d8feadc0 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -217,8 +217,8 @@ 你不被允許對選擇的行事曆寫入 未發現活動。請在程式設定中為合適的行事曆啟用CalDAV同步。 No synchronizable calendars have been found - Free - Busy + 空閒 + 繁忙