nordiccalendar

A simple Material 3 native calendar app fitting my personal requirements
git clone git://archive.git.mtrnord.blog/MTRNord/nordiccalendar.git
Log | Files | Refs | README | LICENSE

commit 2a296910f9dad1f75fbecf40756c4e2c78e01245
parent f97215511c556baa01e1cc60c6536586cb143e88
Author: MTRNord <MTRNord@users.noreply.github.com>
Date:   Wed, 30 Jul 2025 18:02:59 +0200

More info for events

Diffstat:
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/screens/DayView.kt | 5+++--
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/util/CalendarData.kt | 165+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
2 files changed, 106 insertions(+), 64 deletions(-)

diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/DayView.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/DayView.kt @@ -225,7 +225,8 @@ fun DayView( // Events (wie gehabt, optimiert) val eventColumns = remember(events.value, maxWidthPx) { assignColumns(events.value) } events.value.forEach { event -> - val triple = eventColumns.find { it.first.id == event.id } + val triple = + eventColumns.find { it.first.eventId == event.eventId && it.first.calendar.id == event.calendar.id } if (triple != null) { val (event, col, maxColumns) = triple val shownStart = maxOf(event.startTime, dayStart.value) @@ -247,7 +248,7 @@ fun DayView( val showStartTimeAsMidnight = shownStart == dayStart.value val showEndTimeAsMidnight = shownEnd == dayEnd.value - key(event.id) { + key(event.eventId, event.calendar.id) { Box( modifier = Modifier .offset { IntOffset(offsetX, offsetY.toInt()) } diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/util/CalendarData.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/util/CalendarData.kt @@ -19,15 +19,24 @@ data class Calendar( ) data class Event( - val id: Long, // Instanz-ID (Instances._ID) - val eventId: Long, // Event-ID (Instances.EVENT_ID) val title: String, val description: String?, + val location: String?, + val eventColor: Long, + /// One of STATUS_TENTATIVE, STATUS_CONFIRMED, STATUS_CANCELED + val status: Int, + val selfAttendeeStatus: Int, + val duration: String?, + val eventTimezone: String?, + val eventEndTimezone: String?, + val allDay: Boolean, + val accessLevel: Int, + val availability: Int, + val hasAlarm: Boolean, + val eventId: Long, val startTime: Long, val endTime: Long, val calendarId: Long, - val location: String?, - val allDay: Boolean, val organizer: String?, val attendees: List<String> = emptyList(), // Reference to the calendar this event belongs to @@ -36,12 +45,51 @@ data class Event( // Helper for the android calendar provider/CalendarContract class CalendarData { + + val INSTANCE_PROJECTION = arrayOf( + CalendarContract.Instances.CALENDAR_ID, + CalendarContract.Instances.TITLE, + CalendarContract.Instances.DESCRIPTION, + CalendarContract.Instances.EVENT_LOCATION, + CalendarContract.Instances.EVENT_COLOR, + CalendarContract.Instances.STATUS, + CalendarContract.Instances.SELF_ATTENDEE_STATUS, + CalendarContract.Instances.DURATION, + CalendarContract.Instances.EVENT_TIMEZONE, + CalendarContract.Instances.EVENT_END_TIMEZONE, + CalendarContract.Instances.ALL_DAY, + CalendarContract.Instances.ACCESS_LEVEL, + CalendarContract.Instances.AVAILABILITY, + CalendarContract.Instances.HAS_ALARM, + CalendarContract.Instances.EVENT_ID, + CalendarContract.Instances.BEGIN, + CalendarContract.Instances.END, + CalendarContract.Instances.ORGANIZER + ) + val PROJECTION_CALENDAR_ID_INDEX = 0 + val PROJECTION_TITLE_INDEX = 1 + val PROJECTION_DESCRIPTION_INDEX = 2 + val PROJECTION_EVENT_LOCATION_INDEX = 3 + val PROJECTION_EVENT_COLOR_INDEX = 4 + val PROJECTION_STATUS_INDEX = 5 + val PROJECTION_SELF_ATTENDEE_STATUS_INDEX = 6 + val PROJECTION_DURATION_INDEX = 7 + val PROJECTION_EVENT_TIMEZONE_INDEX = 8 + val PROJECTION_EVENT_END_TIMEZONE_INDEX = 9 + val PROJECTION_ALL_DAY_INDEX = 10 + val PROJECTION_ACCESS_LEVEL_INDEX = 11 + val PROJECTION_AVAILABILITY_INDEX = 12 + val PROJECTION_HAS_ALARM_INDEX = 13 + val PROJECTION_EVENT_ID_INDEX = 14 + val PROJECTION_BEGIN_INDEX = 15 + val PROJECTION_END_INDEX = 16 + val PROJECTION_ORGANIZER_INDEX = 17 + // Get available calendars from the Android Calendar Provider fun getCalendars(contentResolver: ContentResolver): List<Calendar> { val calendars = mutableListOf<Calendar>() - - val EVENT_PROJECTION = arrayOf( + val CALENDAR_PROJECTION = arrayOf( CalendarContract.Calendars._ID, CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, CalendarContract.Calendars.NAME, @@ -65,7 +113,7 @@ class CalendarData { val selectionArgs = emptyArray<String>() val cur = contentResolver.query( uri, - EVENT_PROJECTION, + CALENDAR_PROJECTION, selection, selectionArgs, null @@ -119,29 +167,6 @@ class CalendarData { ): List<Event> { val events = mutableListOf<Event>() - val INSTANCE_PROJECTION = arrayOf( - CalendarContract.Instances._ID, - CalendarContract.Instances.EVENT_ID, - CalendarContract.Instances.TITLE, - CalendarContract.Instances.DESCRIPTION, - CalendarContract.Instances.BEGIN, - CalendarContract.Instances.END, - CalendarContract.Instances.CALENDAR_ID, - CalendarContract.Instances.EVENT_LOCATION, - CalendarContract.Instances.ALL_DAY, - CalendarContract.Instances.ORGANIZER - ) - val PROJECTION_ID_INDEX = 0 - val PROJECTION_EVENT_ID_INDEX = 1 - val PROJECTION_TITLE_INDEX = 2 - val PROJECTION_DESCRIPTION_INDEX = 3 - val PROJECTION_BEGIN_INDEX = 4 - val PROJECTION_END_INDEX = 5 - val PROJECTION_CALENDAR_ID_INDEX = 6 - val PROJECTION_EVENT_LOCATION_INDEX = 7 - val PROJECTION_ALL_DAY_INDEX = 8 - val PROJECTION_ORGANIZER_INDEX = 9 - val builder: Uri.Builder = CalendarContract.Instances.CONTENT_URI.buildUpon() ContentUris.appendId(builder, startMillis) ContentUris.appendId(builder, endMillis) @@ -154,20 +179,27 @@ class CalendarData { "${CalendarContract.Instances.BEGIN} ASC" ) while (cur?.moveToNext() == true) { - val id = cur.getLong(PROJECTION_ID_INDEX) - val eventId = cur.getLong(PROJECTION_EVENT_ID_INDEX) + val calId = cur.getLong(PROJECTION_CALENDAR_ID_INDEX) val title = cur.getString(PROJECTION_TITLE_INDEX) ?: "No Title" val description = cur.getString(PROJECTION_DESCRIPTION_INDEX) - val startTime = cur.getLong(PROJECTION_BEGIN_INDEX) - val endTime = cur.getLong(PROJECTION_END_INDEX) - val calId = cur.getLong(PROJECTION_CALENDAR_ID_INDEX) val location = cur.getString(PROJECTION_EVENT_LOCATION_INDEX) + val event_color = cur.getLong(PROJECTION_EVENT_COLOR_INDEX) + val status = cur.getInt(PROJECTION_STATUS_INDEX) + val selfAttendeeStatus = cur.getInt(PROJECTION_SELF_ATTENDEE_STATUS_INDEX) + val duration = cur.getString(PROJECTION_DURATION_INDEX) + val eventTimezone = cur.getString(PROJECTION_EVENT_TIMEZONE_INDEX) + val eventEndTimezone = cur.getString(PROJECTION_EVENT_END_TIMEZONE_INDEX) val allDay = cur.getInt(PROJECTION_ALL_DAY_INDEX) == 1 + val accessLevel = cur.getInt(PROJECTION_ACCESS_LEVEL_INDEX) + val availability = cur.getInt(PROJECTION_AVAILABILITY_INDEX) + val hasAlarm = cur.getInt(PROJECTION_HAS_ALARM_INDEX) == 1 + val eventId = cur.getLong(PROJECTION_EVENT_ID_INDEX) + val startTime = cur.getLong(PROJECTION_BEGIN_INDEX) + val endTime = cur.getLong(PROJECTION_END_INDEX) val organizer = cur.getString(PROJECTION_ORGANIZER_INDEX) events.add( Event( - id = id, eventId = eventId, title = title, description = description, @@ -187,7 +219,16 @@ class CalendarData { syncEvents = false, visible = false, displayName = "Unknown Calendar" // Fallback if calendar not found - ) // Fallback if calendar not found + ), + eventColor = event_color, + status = status, + selfAttendeeStatus = selfAttendeeStatus, + duration = duration, + eventTimezone = eventTimezone, + eventEndTimezone = eventTimezone, + accessLevel = accessLevel, + availability = availability, + hasAlarm = hasAlarm, ) ) } @@ -254,18 +295,6 @@ class CalendarData { } fun getEventById(contentResolver: ContentResolver, eventId: Long): Event? { - val INSTANCE_PROJECTION = arrayOf( - CalendarContract.Instances._ID, - CalendarContract.Instances.EVENT_ID, - CalendarContract.Instances.TITLE, - CalendarContract.Instances.DESCRIPTION, - CalendarContract.Instances.BEGIN, - CalendarContract.Instances.END, - CalendarContract.Instances.CALENDAR_ID, - CalendarContract.Instances.EVENT_LOCATION, - CalendarContract.Instances.ALL_DAY, - CalendarContract.Instances.ORGANIZER - ) val selection = "Instances.event_id = ?" val selectionArgs = arrayOf(eventId.toString()) val builder = CalendarContract.Instances.CONTENT_URI.buildUpon() @@ -282,19 +311,22 @@ class CalendarData { ) val event = if (cur?.moveToFirst() == true) { Event( - id = cur.getLong(0), - eventId = cur.getLong(1), - title = cur.getString(2) ?: "No Title", - description = cur.getString(3), - startTime = cur.getLong(4), - endTime = cur.getLong(5), - calendarId = cur.getLong(6), - location = cur.getString(7), - allDay = cur.getInt(8) == 1, - organizer = cur.getString(9), - calendar = getCalendars(contentResolver).firstOrNull { it.id == cur.getLong(6) } + eventId = cur.getLong(PROJECTION_EVENT_ID_INDEX), + title = cur.getString(PROJECTION_TITLE_INDEX) ?: "No Title", + description = cur.getString(PROJECTION_DESCRIPTION_INDEX), + startTime = cur.getLong(PROJECTION_BEGIN_INDEX), + endTime = cur.getLong(PROJECTION_END_INDEX), + calendarId = cur.getLong(PROJECTION_CALENDAR_ID_INDEX), + location = cur.getString(PROJECTION_EVENT_LOCATION_INDEX), + allDay = cur.getInt(PROJECTION_ALL_DAY_INDEX) == 1, + organizer = cur.getString(PROJECTION_ORGANIZER_INDEX), + calendar = getCalendars(contentResolver).firstOrNull { + it.id == cur.getLong( + PROJECTION_CALENDAR_ID_INDEX + ) + } ?: Calendar( - id = cur.getLong(6), + id = cur.getLong(PROJECTION_CALENDAR_ID_INDEX), name = "Unknown Calendar", color = 0xFF000000, // Default black color accountName = "Unknown Account", @@ -302,7 +334,16 @@ class CalendarData { syncEvents = false, visible = false, displayName = "Unknown Calendar" // Fallback if calendar not found - ) // Fallback if calendar not found + ), // Fallback if calendar not found, + eventColor = cur.getLong(PROJECTION_EVENT_COLOR_INDEX), + status = cur.getInt(PROJECTION_STATUS_INDEX), + selfAttendeeStatus = cur.getInt(PROJECTION_SELF_ATTENDEE_STATUS_INDEX), + duration = cur.getString(PROJECTION_DURATION_INDEX), + eventTimezone = cur.getString(PROJECTION_EVENT_TIMEZONE_INDEX), + eventEndTimezone = cur.getString(PROJECTION_EVENT_END_TIMEZONE_INDEX), + accessLevel = cur.getInt(PROJECTION_ACCESS_LEVEL_INDEX), + availability = cur.getInt(PROJECTION_AVAILABILITY_INDEX), + hasAlarm = cur.getInt(PROJECTION_HAS_ALARM_INDEX) == 1, ) } else null cur?.close()