feat(detail): full event read — surface every readable field (v0.6.0)
Round out the read-only model so the detail view shows everything CalendarContract actually stores, ahead of write support. Data layer: - New domain types: Reminder, EventStatus, Availability, AccessLevel, AttendeeRelationship, AttendeeType; EventDetail gains reminders, status, availability, accessLevel, eventTimezone, selfStatus and Attendee gains relationship + type (all defaulted so existing callers compile) - EventDetailProjection reads STATUS / AVAILABILITY / ACCESS_LEVEL / EVENT_TIMEZONE / SELF_ATTENDEE_STATUS; AttendeeProjection reads RELATIONSHIP + TYPE; new ReminderProjection queries CalendarContract.Reminders - Mappers translate each provider integer code, guarding STATUS's null-vs-0 ambiguity (0 == TENTATIVE) so an absent status reads as Confirmed - Mapper unit tests cover every new column's codes Detail UI: - Status / availability / access chips under the title; cancelled also strikes the title through - Reminders card with humanised lead times (plurals, DE + EN) - Foreign-timezone card, shown only for timed events in a non-device zone - Attendee role badges + the user's own "Your response: …" line - http(s) URLs in the description are now tappable URL field cut: CalendarContract exposes no Events.URL column (only the CUSTOM_APP_URI app deep-link), so URLs are surfaced by linkifying the description instead. Recorded in ROADMAP/CHANGELOG. Version bumped to 0.6.0 / 6. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,12 +29,34 @@ data class EventDetail(
|
||||
val organizer: String?,
|
||||
val attendees: List<Attendee>,
|
||||
val rrule: String?,
|
||||
/** Reminders (VALARM) configured on the event, ascending lead time. */
|
||||
val reminders: List<Reminder> = emptyList(),
|
||||
/** Confirmed / Tentative / Cancelled (`Events.STATUS`). */
|
||||
val status: EventStatus = EventStatus.Confirmed,
|
||||
/** Busy / Free (`Events.AVAILABILITY`, the iCal TRANSP field). */
|
||||
val availability: Availability = Availability.Busy,
|
||||
/** Default / Private / Confidential / Public (`Events.ACCESS_LEVEL`). */
|
||||
val accessLevel: AccessLevel = AccessLevel.Default,
|
||||
/** Raw zone id (`Events.EVENT_TIMEZONE`), e.g. "Europe/Berlin"; null if unset. */
|
||||
val eventTimezone: String? = null,
|
||||
/** This device user's own response (`Events.SELF_ATTENDEE_STATUS`). */
|
||||
val selfStatus: AttendeeStatus = AttendeeStatus.Unknown,
|
||||
)
|
||||
|
||||
data class Attendee(
|
||||
val name: String,
|
||||
val email: String?,
|
||||
val status: AttendeeStatus,
|
||||
/** Organizer / performer / speaker / plain attendee (`ATTENDEE_RELATIONSHIP`). */
|
||||
val relationship: AttendeeRelationship = AttendeeRelationship.None,
|
||||
/** Required / optional / resource (`ATTENDEE_TYPE`). */
|
||||
val type: AttendeeType = AttendeeType.None,
|
||||
)
|
||||
|
||||
data class Reminder(
|
||||
/** Lead time before the event start, in minutes. `-1` means the provider default. */
|
||||
val minutes: Int,
|
||||
val method: ReminderMethod,
|
||||
)
|
||||
|
||||
enum class AttendeeStatus {
|
||||
@@ -45,6 +67,48 @@ enum class AttendeeStatus {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
enum class AttendeeRelationship {
|
||||
Organizer,
|
||||
Attendee,
|
||||
Performer,
|
||||
Speaker,
|
||||
None,
|
||||
}
|
||||
|
||||
enum class AttendeeType {
|
||||
Required,
|
||||
Optional,
|
||||
Resource,
|
||||
None,
|
||||
}
|
||||
|
||||
enum class ReminderMethod {
|
||||
Alert,
|
||||
Email,
|
||||
Sms,
|
||||
Alarm,
|
||||
Default,
|
||||
}
|
||||
|
||||
enum class EventStatus {
|
||||
Confirmed,
|
||||
Tentative,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
enum class Availability {
|
||||
Busy,
|
||||
Free,
|
||||
Tentative,
|
||||
}
|
||||
|
||||
enum class AccessLevel {
|
||||
Default,
|
||||
Public,
|
||||
Private,
|
||||
Confidential,
|
||||
}
|
||||
|
||||
enum class FailureReason {
|
||||
PermissionRevoked,
|
||||
NoCalendarsConfigured,
|
||||
|
||||
Reference in New Issue
Block a user