Last Updated: 6/9/2026
Viewing Event Details
Access comprehensive event information from the widget or app, including event time, location, attendees, RSVP status, and Zoom meeting links. The event details screen provides quick access to join meetings and respond to invitations.
Prerequisites
- ZCal Buddy installed with widget configured
- Zoom account connected
- At least one calendar event synced
Opening Event Details
From the Widget
Tap any event card in the home screen widget. The widget creates an intent with event data:
Intent(context, MainActivity::class.java).apply {
putExtra("screen", "event_details")
putExtra("event_id", event.eventId)
putExtra("event_calendar_id", event.calendarId)
putExtra("event_title", event.title)
putExtra("event_time", event.time)
putExtra("event_location", event.location)
putExtra("event_description", event.description)
putExtra("event_zoom_url", event.zoomUrl)
putExtra("event_start_millis", event.startMillis)
putExtra("event_end_millis", event.endMillis)
putExtra("event_all_day", event.allDay)
}The app opens to the EventDetailsScreen composable with the event data pre-filled.
From the App
- Open ZCal Buddy
- The home screen shows “Calendar synced.” if events are loaded
- Navigate to an event (implementation depends on app navigation)
- The event details screen displays
Event Information Display
The EventDetailsScreen composable shows event details in a card layout:
Event Title
The event title appears as a headline at the top of the details card:
Text(
text = title,
style = MaterialTheme.typography.headlineSmall
)If the event has no title, the widget displays “(No title)” but this placeholder isn’t shown in the details screen—the field would be blank.
Event Time
The time field displays the formatted time range from the event.time property. This is pre-formatted when the event is loaded from the calendar:
if (!time.isNullOrBlank()) {
Text(text = time)
}For all-day events, the time field may show “All day” or be omitted entirely depending on how the event was formatted during sync.
Event Location
The location appears below the time if present:
if (!location.isNullOrBlank()) {
Text(text = location)
}For Zoom Meeting events, the location field may contain the meeting room name or be empty. The Zoom join URL is displayed separately as a button.
Zoom Meeting Link
If the event has a Zoom URL (!zoomUrl.isNullOrBlank()), a Join Zoom button appears:
if (!zoomUrl.isNullOrBlank()) {
Button(
onClick = {
context.startActivity(
Intent(Intent.ACTION_VIEW, Uri.parse(zoomUrl))
)
}
) {
Text("Join Zoom")
}
}Tapping this button opens the Zoom meeting using the system’s default handler for Zoom URLs.
Joining Zoom Meetings
From Event Details
Tap the Join Zoom button in the event details card. The app creates an ACTION_VIEW intent with the Zoom URL:
Intent(Intent.ACTION_VIEW, Uri.parse(zoomUrl))Android routes this intent based on installed apps and URL handlers:
- Zoom app installed: The Zoom app opens directly to the meeting
- Zoom app not installed: The browser opens the Zoom web client
- Multiple handlers: Android shows an app chooser
From the Widget
Tap the blue Join button on any event card with a Zoom meeting. The widget creates a more specific intent that targets the Zoom app:
Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
setPackage("us.zoom.videomeetings")
}This intent explicitly targets the Zoom app package (us.zoom.videomeetings). If the Zoom app isn’t installed, the intent fails and the system may prompt you to install it.
Opening Zoom App Directly
The widget header’s date label acts as a button to open the Zoom app. The openZoomApp function in MainActivitySupport.kt handles this:
fun openZoomApp(context: Context) {
val packageName = "us.zoom.videomeetings"
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
if (launchIntent != null) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(launchIntent)
return
}
// Fallback to Zoom URL scheme
val zoomSchemeIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("zoomus://zoom.us")
).apply {
setPackage(packageName)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
runCatching {
context.startActivity(zoomSchemeIntent)
}.onFailure {
// Final fallback to Play Store
context.startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
)
}
}The function tries three approaches:
- Launch intent from package manager
- Custom Zoom URL scheme (
zoomus://zoom.us) - Play Store link if Zoom isn’t installed
RSVP Functionality
The event details screen includes an RSVP section when canRespond == true. This allows you to respond to meeting invitations.
RSVP Options
Three response buttons appear in the “Going?” section:
Yes: Sets responseStatus = "accepted"
Maybe: Sets responseStatus = "tentative"
No: Sets responseStatus = "declined"
The currently selected response is shown as a filled button, while unselected options appear as outlined buttons:
@Composable
fun RsvpButton(
label: String,
selected: Boolean,
onClick: () -> Unit
) {
if (selected) {
Button(onClick = onClick) {
Text(label)
}
} else {
OutlinedButton(onClick = onClick) {
Text(label)
}
}
}Submitting RSVP
When you tap an RSVP button, the app calls submitRsvpResponse:
val result = submitRsvpResponse(
context = context,
eventId = eventId,
calendarId = calendarId,
selfAttendeeEmail = selfAttendeeEmail,
responseStatus = "accepted" // or "tentative" or "declined"
)The function performs two operations:
1. Update via API (if selfAttendeeEmail is not blank):
ZoomAuthRepository().updateMyResponseStatus(
accessToken = accessToken,
calendarId = calendarId,
eventId = eventId,
selfEmail = attendeeEmail,
responseStatus = responseStatus
)This sends a PATCH request to https://api.zoom.us/v2/calendars/{calendarId}/events/{eventId}?sendUpdates=all with the updated attendee response.
2. Update local storage:
The function updates the cached event in local storage to reflect the new response status:
val updatedEvents = currentEvents.map { event ->
if (event.eventId == eventId && event.calendarId == calendarId) {
event.copy(
selfAttendeeEmail = effectiveAttendeeEmail,
selfResponseStatus = responseStatus,
attendees = event.attendees.map { attendee ->
if (attendee.self ||
attendee.email.equals(effectiveAttendeeEmail, true)) {
attendee.copy(responseStatus = responseStatus)
} else {
attendee
}
}
)
} else {
event
}
}This ensures the widget immediately reflects your response without waiting for the next API sync.
RSVP Status Display
The widget displays events differently based on your response status:
Accepted or Tentative (selfResponseStatus == "accepted" or "tentative"):
- Solid colored background
- Normal title text
- Event appears in your schedule
Declined (selfResponseStatus == "declined"):
- Hollow outline (colored border with dark background)
- Strikethrough title: ”━━━━ Event Title ━━━━”
- Muted appearance to indicate you’re not attending
The widget determines the display style in the EventCard composable:
val isDeclined = event.selfResponseStatus.equals("declined", true)
val isAccepted = event.selfResponseStatus.equals("accepted", true) ||
event.selfResponseStatus.equals("tentative", true)
val isHollow = !isAccepted || isDeclinedEvent Details Screen Actions
Back Button
Tap Back in the top-left corner to return to the previous screen. The onBack callback is provided by the parent composable:
Button(onClick = onBack) {
Text("Back")
}This typically returns to the home screen.
Edit Button
Tap Edit in the top-right corner to open the event editor. The onEdit callback navigates to the edit screen:
Button(onClick = onEdit) {
Text("Edit")
}This opens EditEventScreen with the current event data pre-filled.
Refresh After RSVP
After submitting an RSVP, the app calls onRefresh() to reload the event data:
if (result.success) {
selectedStatus.value = "accepted"
onRefresh()
}The refresh callback fetches updated events from local storage and refreshes the widget:
onRefresh = {
scope.launch {
val refreshedEvents = withContext(Dispatchers.IO) {
CalendarRepository.getEvents(context)
}
val refreshedCalendars = withContext(Dispatchers.IO) {
CalendarListRepository.getCalendars(context)
}
events.value = refreshedEvents
calendars.value = refreshedCalendars
forceWidgetRefresh(context)
}
}Event Properties Reference
The CalendarEvent data class contains these properties displayed in the details screen:
| Property | Type | Description |
|---|---|---|
eventId | String | Unique event identifier |
calendarId | String | Parent calendar identifier |
title | String | Event name/summary |
time | String | Formatted time range |
location | String | Event location |
description | String | Event notes/details |
zoomUrl | String? | Zoom meeting join URL (null if no meeting) |
startMillis | Long | Start time as Unix timestamp |
endMillis | Long | End time as Unix timestamp |
allDay | Boolean | Whether event spans entire day |
selfResponseStatus | String | Your RSVP status: “accepted”, “tentative”, “declined”, or “needsAction” |
attendees | List<EventAttendee> | List of invitees |
selfAttendeeEmail | String? | Your email address as an attendee |
Troubleshooting Details Screen
“Join Zoom” button doesn’t appear
The event doesn’t have a Zoom URL. This happens when:
- The event was created as “Offline” type
- The Zoom meeting creation failed during event creation
- The meeting was deleted but the event wasn’t updated
Edit the event and convert it to “Zoom Meeting” type to add a meeting.
Zoom app doesn’t open when tapping “Join Zoom”
The Zoom app may not be installed or the URL handler isn’t configured. Try:
- Install the Zoom app from the Play Store
- Open the Zoom app once to initialize URL handlers
- Try joining from the widget’s Join button instead
RSVP buttons don’t appear
The canRespond parameter is false. This happens when:
- The event data doesn’t include your attendee email
- You’re the organizer (organizers don’t RSVP to their own events)
- The event wasn’t loaded with full attendee data
RSVP update fails with “No saved Zoom access token”
Your Zoom connection expired. The app needs a valid access token to update your response via the API. Tap Connect Zoom on the home screen to re-authenticate.
Response updated but widget still shows old status
The local storage was updated but the widget hasn’t refreshed. Try:
- Wait a few seconds for the automatic refresh
- Tap the refresh button (↻) in the widget header
- Close and reopen the app
Event time shows in wrong time zone
The app displays events in your device’s local time zone. The event is stored with its original time zone, but the display converts it to your system time zone. Check your device’s date & time settings if the conversion appears incorrect.
What’s Next
You’ve learned how to view event details, join Zoom meetings, and respond to invitations. To customize how events appear in your widget, explore the widget settings to configure display options, calendar filters, and refresh behavior.