diff --git a/.editorconfig b/.editorconfig index c79c990fb..0744096cb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,6 +18,7 @@ charset = utf-8 indent_style = space indent_size = 4 continuation_indent_size = 4 +max_line_length = 160 [*.xml] continuation_indent_size = 4 diff --git a/app/build.gradle b/app/build.gradle index ce22316a8..c0cc358c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,7 +63,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:1b72d7ccff' + implementation 'com.github.SimpleMobileTools:Simple-Commons:16ae1d2c03' implementation 'joda-time:joda-time:2.10.3' implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 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/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/MainActivity.kt index 029897abf..7356f9b25 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/activities/MainActivity.kt @@ -606,13 +606,14 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { val selectionArgs = arrayOf(CommonDataKinds.Event.CONTENT_ITEM_TYPE, type.toString()) val dateFormats = getDateFormats() + val yearDateFormats = getDateFormatsWithYear() val existingEvents = if (birthdays) eventsDB.getBirthdays() else eventsDB.getAnniversaries() val importIDs = HashMap() existingEvents.forEach { importIDs[it.importId] = it.startTS } - val eventTypeId = if (birthdays) getBirthdaysEventTypeId() else getAnniversariesEventTypeId() + val eventTypeId = if (birthdays) eventsHelper.getBirthdaysEventTypeId() else eventsHelper.getAnniversariesEventTypeId() val source = if (birthdays) SOURCE_CONTACT_BIRTHDAY else SOURCE_CONTACT_ANNIVERSARY queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> @@ -624,15 +625,17 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { try { val formatter = SimpleDateFormat(format, Locale.getDefault()) val date = formatter.parse(startDate) - if (date.year < 70) { - date.year = 70 + val flags = if (format in yearDateFormats) { + FLAG_ALL_DAY + } else { + FLAG_ALL_DAY or FLAG_MISSING_YEAR } val timestamp = date.time / 1000L val lastUpdated = cursor.getLongValue(CommonDataKinds.Event.CONTACT_LAST_UPDATED_TIMESTAMP) val event = Event( null, timestamp, timestamp, name, reminder1Minutes = reminders[0], reminder2Minutes = reminders[1], - reminder3Minutes = reminders[2], importId = contactId, timeZone = DateTimeZone.getDefault().id, flags = FLAG_ALL_DAY, + reminder3Minutes = reminders[2], importId = contactId, timeZone = DateTimeZone.getDefault().id, flags = flags, repeatInterval = YEAR, repeatRule = REPEAT_SAME_DAY, eventType = eventTypeId, source = source, lastUpdated = lastUpdated ) @@ -681,7 +684,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { } try { - val eventTypeId = if (birthdays) getBirthdaysEventTypeId() else getAnniversariesEventTypeId() + val eventTypeId = if (birthdays) eventsHelper.getBirthdaysEventTypeId() else eventsHelper.getAnniversariesEventTypeId() val source = if (birthdays) SOURCE_CONTACT_BIRTHDAY else SOURCE_CONTACT_ANNIVERSARY val existingEvents = if (birthdays) eventsDB.getBirthdays() else eventsDB.getAnniversaries() @@ -743,26 +746,6 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { callback(eventsFound, eventsAdded) } - private fun getBirthdaysEventTypeId(): Long { - val birthdays = getString(R.string.birthdays) - var eventTypeId = eventsHelper.getEventTypeIdWithTitle(birthdays) - if (eventTypeId == -1L) { - val eventType = EventType(null, birthdays, resources.getColor(R.color.default_birthdays_color)) - eventTypeId = eventsHelper.insertOrUpdateEventTypeSync(eventType) - } - return eventTypeId - } - - private fun getAnniversariesEventTypeId(): Long { - val anniversaries = getString(R.string.anniversaries) - var eventTypeId = eventsHelper.getEventTypeIdWithTitle(anniversaries) - if (eventTypeId == -1L) { - val eventType = EventType(null, anniversaries, resources.getColor(R.color.default_anniversaries_color)) - eventTypeId = eventsHelper.insertOrUpdateEventTypeSync(eventType) - } - return eventTypeId - } - private fun updateView(view: Int) { calendar_fab.beVisibleIf(view != YEARLY_VIEW && view != WEEKLY_VIEW) config.storedView = view 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 17c5ce7e8..9e3d612be 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 @@ -94,6 +94,7 @@ const val REPEAT_ORDER_WEEKDAY = 4 // i.e. every 4th sunday // special event flags const val FLAG_ALL_DAY = 1 const val FLAG_IS_PAST_EVENT = 2 +const val FLAG_MISSING_YEAR = 4 // constants related to ICS file exporting / importing const val BEGIN_CALENDAR = "BEGIN:VCALENDAR" @@ -113,6 +114,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" @@ -130,6 +132,7 @@ const val SEQUENCE = "SEQUENCE" // this tag isn't a standard ICS tag, but there's no official way of adding a category color in an ics file const val CATEGORY_COLOR = "X-SMT-CATEGORY-COLOR:" const val CATEGORY_COLOR_LEGACY = "CATEGORY_COLOR:" +const val MISSING_YEAR = "X-SMT-MISSING-YEAR:" const val DISPLAY = "DISPLAY" const val EMAIL = "EMAIL" @@ -154,6 +157,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/EventsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt index f5445cc1c..3a4224e64 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/helpers/EventsHelper.kt @@ -3,6 +3,7 @@ package com.simplemobiletools.calendar.pro.helpers import android.app.Activity import android.content.Context import androidx.collection.LongSparseArray +import com.simplemobiletools.calendar.pro.R import com.simplemobiletools.calendar.pro.extensions.* import com.simplemobiletools.calendar.pro.models.Event import com.simplemobiletools.calendar.pro.models.EventType @@ -259,6 +260,9 @@ class EventsHelper(val context: Context) { } fun getEventsSync(fromTS: Long, toTS: Long, eventId: Long = -1L, applyTypeFilter: Boolean, callback: (events: ArrayList) -> Unit) { + val birthDayEventId = getBirthdaysEventTypeId(createIfNotExists = false) + val anniversaryEventId = getAnniversariesEventTypeId(createIfNotExists = false) + var events = if (applyTypeFilter) { val displayEventTypes = context.config.displayEventTypes if (displayEventTypes.isEmpty()) { @@ -294,12 +298,46 @@ class EventsHelper(val context: Context) { events.forEach { it.updateIsPastEvent() + val originalEvent = eventsDB.getEventWithId(it.id!!) + if (originalEvent != null && + (birthDayEventId != -1L && it.eventType == birthDayEventId) or + (anniversaryEventId != -1L && it.eventType == anniversaryEventId) + ) { + val eventStartDate = Formatter.getDateFromTS(it.startTS) + val originalEventStartDate = Formatter.getDateFromTS(originalEvent.startTS) + if (it.hasMissingYear().not()) { + val years = (eventStartDate.year - originalEventStartDate.year).coerceAtLeast(0) + if (years > 0) { + it.title = "${it.title} ($years)" + } + } + } it.color = eventTypeColors.get(it.eventType) ?: config.primaryColor } callback(events) } + fun getBirthdaysEventTypeId(createIfNotExists: Boolean = true): Long { + val birthdays = context.getString(R.string.birthdays) + var eventTypeId = getEventTypeIdWithTitle(birthdays) + if (eventTypeId == -1L && createIfNotExists) { + val eventType = EventType(null, birthdays, context.resources.getColor(R.color.default_birthdays_color)) + eventTypeId = insertOrUpdateEventTypeSync(eventType) + } + return eventTypeId + } + + fun getAnniversariesEventTypeId(createIfNotExists: Boolean = true): Long { + val anniversaries = context.getString(R.string.anniversaries) + var eventTypeId = getEventTypeIdWithTitle(anniversaries) + if (eventTypeId == -1L && createIfNotExists) { + val eventType = EventType(null, anniversaries, context.resources.getColor(R.color.default_anniversaries_color)) + eventTypeId = insertOrUpdateEventTypeSync(eventType) + } + return eventTypeId + } + fun getRepeatableEventsFor(fromTS: Long, toTS: Long, eventId: Long = -1L, applyTypeFilter: Boolean = false): List { val events = if (applyTypeFilter) { val displayEventTypes = context.config.displayEventTypes 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 ced32bae0..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 @@ -23,7 +24,13 @@ class IcsExporter { private var eventsFailed = 0 private var calendars = ArrayList() - fun exportEvents(activity: BaseSimpleActivity, outputStream: OutputStream?, events: ArrayList, showExportingToast: Boolean, callback: (result: ExportResult) -> Unit) { + fun exportEvents( + activity: BaseSimpleActivity, + outputStream: OutputStream?, + events: ArrayList, + showExportingToast: Boolean, + callback: (result: ExportResult) -> Unit + ) { if (outputStream == null) { callback(EXPORT_FAIL) return @@ -50,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)}") @@ -58,6 +66,7 @@ class IcsExporter { event.startTS.let { out.writeLn("$DTSTART:${Formatter.getExportedTime(it * 1000L)}") } event.endTS.let { out.writeLn("$DTEND:${Formatter.getExportedTime(it * 1000L)}") } } + event.hasMissingYear().let { out.writeLn("$MISSING_YEAR${if (it) 1 else 0}") } out.writeLn("$DTSTAMP$exportTime") out.writeLn("$STATUS$CONFIRMED") @@ -73,11 +82,13 @@ class IcsExporter { out.writeLn(END_CALENDAR) } - callback(when { - eventsExported == 0 -> EXPORT_FAIL - eventsFailed > 0 -> EXPORT_PARTIAL - else -> EXPORT_OK - }) + callback( + when { + eventsExported == 0 -> EXPORT_FAIL + eventsFailed > 0 -> EXPORT_PARTIAL + else -> EXPORT_OK + } + ) } } 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 b884e1632..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 @@ -128,6 +130,10 @@ class IcsImporter(val activity: SimpleActivity) { if (color.trimStart('-').areDigitsOnly()) { curCategoryColor = Integer.parseInt(color) } + } else if (line.startsWith(MISSING_YEAR)) { + if (line.substring(MISSING_YEAR.length) == "1") { + curFlags = curFlags or FLAG_MISSING_YEAR + } } else if (line.startsWith(CATEGORIES) && !overrideFileEventTypes) { val categories = line.substring(CATEGORIES.length) tryAddCategories(categories) @@ -156,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) { @@ -218,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 8f08aec5f..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 { @@ -138,6 +139,7 @@ data class Event( } fun getIsAllDay() = flags and FLAG_ALL_DAY != 0 + fun hasMissingYear() = flags and FLAG_MISSING_YEAR != 0 fun getReminders() = setOf( Reminder(reminder1Minutes, reminder1Type), diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/MonthViewEvent.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/MonthViewEvent.kt index 24c861338..e4a8890d5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/MonthViewEvent.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/models/MonthViewEvent.kt @@ -1,4 +1,4 @@ package com.simplemobiletools.calendar.pro.models -data class MonthViewEvent(val id: Long, val title: String, val startTS: Long, val color: Int, val startDayIndex: Int, val daysCnt: Int, val originalStartDayIndex: Int, +data class MonthViewEvent(val id: Long, val title: String, val startTS: Long, val endTS: Long, val color: Int, val startDayIndex: Int, val daysCnt: Int, val originalStartDayIndex: Int, val isAllDay: Boolean, val isPastEvent: Boolean) diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/views/MonthView.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/views/MonthView.kt index b63d83475..4d9b29de9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/pro/views/MonthView.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/pro/views/MonthView.kt @@ -121,14 +121,14 @@ class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(con val daysCnt = getEventLastingDaysCount(event) val validDayEvent = isDayValid(event, day.code) if ((lastEvent == null || lastEvent.startDayIndex + daysCnt <= day.indexOnMonthView) && !validDayEvent) { - val monthViewEvent = MonthViewEvent(event.id!!, event.title, event.startTS, event.color, day.indexOnMonthView, + val monthViewEvent = MonthViewEvent(event.id!!, event.title, event.startTS, event.endTS, event.color, day.indexOnMonthView, daysCnt, day.indexOnMonthView, event.getIsAllDay(), event.isPastEvent) allEvents.add(monthViewEvent) } } } - allEvents = allEvents.asSequence().sortedWith(compareBy({ -it.daysCnt }, { !it.isAllDay }, { it.startTS }, { it.startDayIndex }, { it.title })) + allEvents = allEvents.asSequence().sortedWith(compareBy({ -it.daysCnt }, { !it.isAllDay }, { it.startTS }, { it.endTS }, { it.startDayIndex }, { it.title })) .toMutableList() as ArrayList } 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 + 空閒 + 繁忙