From c4066f78d83b1440155248f9bc724b905aa88697 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Mon, 13 Jul 2026 17:52:10 +0000 Subject: feat(widget): tap event to open its source directly, drop detail popup Tapping a calendar event (or any event-type item) now opens the event's URL (Google Calendar, Plan to Eat, etc.) directly instead of showing an intermediate popup with an "Open in Calendar" button. Removes EventDetailActivity and the recurrence-schedule lookup it was the only consumer of: WidgetRepository.getRecurrence, the Go /api/widget/recurrence endpoint, HandleWidgetRecurrence, GoogleCalendarAPI.GetRecurrenceRule, and formatRecurrence, plus their tests. RecurringEventID itself stays -- it's general calendar-sync metadata used elsewhere in the timeline pipeline, not exclusive to the removed popup. --- android/app/src/main/AndroidManifest.xml | 7 - .../org/terst/doot/widget/data/WidgetRepository.kt | 20 --- .../java/org/terst/doot/widget/ui/DootWidget.kt | 37 ++---- .../terst/doot/widget/ui/EventDetailActivity.kt | 145 --------------------- 4 files changed, 10 insertions(+), 199 deletions(-) delete mode 100644 android/app/src/main/java/org/terst/doot/widget/ui/EventDetailActivity.kt (limited to 'android/app/src') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 74d19ea..ca96e71 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -33,13 +33,6 @@ android:exported="false" android:windowSoftInputMode="adjustResize" /> - - - = - withContext(Dispatchers.IO) { - val encodedId = java.net.URLEncoder.encode(recurringEventId, "UTF-8") - val request = Request.Builder() - .url("$serverUrl/api/widget/recurrence?recurring_event_id=$encodedId") - .header("Authorization", "Bearer $token") - .build() - runCatching { - val response = client.newCall(request).execute() - check(response.isSuccessful) { "HTTP ${response.code}" } - val body = checkNotNull(response.body?.string()) { "Empty body" } - val parsed = json.decodeFromString(body) - parsed.recurrence - } - } } diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt b/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt index 85a93d6..1983383 100644 --- a/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt +++ b/android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt @@ -2,6 +2,7 @@ package org.terst.doot.widget.ui import android.content.Context import android.content.Intent +import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @@ -37,6 +38,12 @@ fun sourceColor(source: String): Color = when (source) { else -> Color(0xFF888888) } +/** Opens the event's source URL (Google Calendar, Plan to Eat, etc.) directly. */ +fun calendarViewIntent(url: String): Intent = + Intent(Intent.ACTION_VIEW, Uri.parse(url.ifEmpty { "https://calendar.google.com" })).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + private val json = Json { ignoreUnknownKeys = true } class DootWidget : GlanceAppWidget() { @@ -154,20 +161,12 @@ fun WidgetRoot(items: List, now: Instant, isRefreshing: Boolean, tex @Composable fun AllDayRow(event: WidgetItem, textSize: WidgetTextSize) { - val context = LocalContext.current - val detailIntent = Intent(context, EventDetailActivity::class.java).apply { - putExtra(EventDetailActivity.EXTRA_TITLE, event.title) - putExtra(EventDetailActivity.EXTRA_SOURCE, event.source) - putExtra(EventDetailActivity.EXTRA_URL, event.url) - putExtra(EventDetailActivity.EXTRA_RECURRING_EVENT_ID, event.recurringEventId) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } val color = sourceColor(event.source) Row( modifier = GlanceModifier .fillMaxWidth() .padding(vertical = 3.dp) - .clickable(actionStartActivity(detailIntent)), + .clickable(actionStartActivity(calendarViewIntent(event.url))), verticalAlignment = Alignment.CenterVertically ) { Box(modifier = GlanceModifier.width(3.dp).height(16.dp).background(color)) {} @@ -291,21 +290,13 @@ fun HourRow( @Composable fun EventBlock(event: WidgetItem, isPast: Boolean, textSize: WidgetTextSize) { - val context = LocalContext.current - val detailIntent = Intent(context, EventDetailActivity::class.java).apply { - putExtra(EventDetailActivity.EXTRA_TITLE, event.title) - putExtra(EventDetailActivity.EXTRA_SOURCE, event.source) - putExtra(EventDetailActivity.EXTRA_URL, event.url) - putExtra(EventDetailActivity.EXTRA_RECURRING_EVENT_ID, event.recurringEventId) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } val color = sourceColor(event.source) val alpha = if (isPast) 0.5f else 1f Row( modifier = GlanceModifier .fillMaxWidth() .padding(vertical = 4.dp) - .clickable(actionStartActivity(detailIntent)), + .clickable(actionStartActivity(calendarViewIntent(event.url))), verticalAlignment = Alignment.CenterVertically ) { Box( @@ -453,14 +444,6 @@ fun TomorrowSection(items: List, fragments: List, allD @Composable fun TomorrowEventRow(event: WidgetItem, zone: ZoneId, textSize: WidgetTextSize) { - val context = LocalContext.current - val detailIntent = Intent(context, EventDetailActivity::class.java).apply { - putExtra(EventDetailActivity.EXTRA_TITLE, event.title) - putExtra(EventDetailActivity.EXTRA_SOURCE, event.source) - putExtra(EventDetailActivity.EXTRA_URL, event.url) - putExtra(EventDetailActivity.EXTRA_RECURRING_EVENT_ID, event.recurringEventId) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } val color = sourceColor(event.source) val timeLabel = event.start?.let { val t = Instant.parse(it).atZone(zone) @@ -470,7 +453,7 @@ fun TomorrowEventRow(event: WidgetItem, zone: ZoneId, textSize: WidgetTextSize) modifier = GlanceModifier .fillMaxWidth() .padding(vertical = 3.dp) - .clickable(actionStartActivity(detailIntent)), + .clickable(actionStartActivity(calendarViewIntent(event.url))), verticalAlignment = Alignment.CenterVertically ) { Text( diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/EventDetailActivity.kt b/android/app/src/main/java/org/terst/doot/widget/ui/EventDetailActivity.kt deleted file mode 100644 index b8423ce..0000000 --- a/android/app/src/main/java/org/terst/doot/widget/ui/EventDetailActivity.kt +++ /dev/null @@ -1,145 +0,0 @@ -package org.terst.doot.widget.ui - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import okhttp3.OkHttpClient -import org.terst.doot.widget.data.Keys -import org.terst.doot.widget.data.WidgetRepository -import org.terst.doot.widget.data.dataStore - -class EventDetailActivity : ComponentActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val title = intent.getStringExtra(EXTRA_TITLE) ?: "" - val source = intent.getStringExtra(EXTRA_SOURCE) ?: "calendar" - val url = intent.getStringExtra(EXTRA_URL) ?: "" - val recurringEventId = intent.getStringExtra(EXTRA_RECURRING_EVENT_ID) - - setContent { - MaterialTheme(colorScheme = darkColorScheme()) { - EventDetailSheet( - title = title, - source = source, - recurringEventId = recurringEventId, - onOpenCalendar = { - startActivity( - Intent(Intent.ACTION_VIEW, Uri.parse(url.ifEmpty { "https://calendar.google.com" })) - ) - finish() - }, - onDismiss = ::finish - ) - } - } - } - - companion object { - const val EXTRA_TITLE = "event_title" - const val EXTRA_SOURCE = "event_source" - const val EXTRA_URL = "event_url" - const val EXTRA_RECURRING_EVENT_ID = "event_recurring_id" - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun EventDetailSheet( - title: String, - source: String, - recurringEventId: String?, - onOpenCalendar: () -> Unit, - onDismiss: () -> Unit -) { - val context = LocalContext.current - var recurrenceText by remember { mutableStateOf(null) } - - LaunchedEffect(recurringEventId) { - if (recurringEventId == null) return@LaunchedEffect - recurrenceText = "Loading…" - val prefs = context.dataStore.data.first() - val url = prefs[Keys.SERVER_URL]?.trimEnd('/') ?: return@LaunchedEffect - val token = prefs[Keys.TOKEN] ?: return@LaunchedEffect - val repo = WidgetRepository(OkHttpClient(), url, token) - repo.getRecurrence(recurringEventId).onSuccess { text -> - recurrenceText = text - }.onFailure { - recurrenceText = null - } - } - - ModalBottomSheet( - onDismissRequest = onDismiss, - sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), - containerColor = Color(0xFF1E293B), - tonalElevation = 0.dp, - dragHandle = { - Box( - modifier = Modifier - .padding(vertical = 12.dp) - .width(36.dp) - .height(4.dp) - .background(Color.White.copy(alpha = 0.25f), RoundedCornerShape(2.dp)) - ) - } - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp) - .navigationBarsPadding() - ) { - Row(verticalAlignment = androidx.compose.ui.Alignment.CenterVertically) { - Box( - modifier = Modifier - .size(10.dp) - .background(sourceColor(source), RoundedCornerShape(5.dp)) - ) - Spacer(Modifier.width(10.dp)) - Text( - text = title, - fontSize = 17.sp, - fontWeight = FontWeight.SemiBold, - color = Color.White, - modifier = Modifier.weight(1f) - ) - } - recurrenceText?.let { text -> - Spacer(Modifier.height(8.dp)) - Text( - text = text, - fontSize = 13.sp, - color = Color.White.copy(alpha = 0.6f) - ) - } - Spacer(Modifier.height(20.dp)) - OutlinedButton( - onClick = onOpenCalendar, - modifier = Modifier.fillMaxWidth(), - colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White), - border = androidx.compose.foundation.BorderStroke(1.dp, Color.White.copy(alpha = 0.3f)) - ) { - Text("Open in Calendar", fontSize = 15.sp) - } - Spacer(Modifier.height(20.dp)) - } - } -} -- cgit v1.2.3