summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/AndroidManifest.xml6
-rw-r--r--android/app/src/main/java/org/terst/doot/widget/ui/DootWidget.kt39
-rw-r--r--android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt110
-rw-r--r--android/app/src/main/res/values/themes.xml11
4 files changed, 146 insertions, 20 deletions
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index f6bf015..3fc5603 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -10,6 +10,12 @@
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:allowBackup="false">
+ <!-- Task detail bottom sheet (opened by widget task taps) -->
+ <activity
+ android:name=".ui.TaskDetailActivity"
+ android:theme="@style/Theme.TaskDetail"
+ android:exported="false" />
+
<!-- Settings activity (also the widget config screen) -->
<activity
android:name=".ui.SettingsActivity"
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 dcb73d5..72fcccc 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
@@ -46,10 +46,9 @@ class DootWidget : GlanceAppWidget() {
val items = parseItems(prefs)
val now = prefs[Keys.NOW]?.let { runCatching { Instant.parse(it) }.getOrNull() }
?: Instant.now()
- val serverUrl = prefs[Keys.SERVER_URL]?.trimEnd('/') ?: ""
provideContent {
- WidgetRoot(items, now, serverUrl)
+ WidgetRoot(items, now)
}
}
@@ -60,7 +59,7 @@ class DootWidget : GlanceAppWidget() {
}
@Composable
-fun WidgetRoot(items: List<WidgetItem>, now: Instant, serverUrl: String = "") {
+fun WidgetRoot(items: List<WidgetItem>, now: Instant) {
val zone = ZoneId.systemDefault()
val nowZoned: ZonedDateTime = now.atZone(zone)
val allScheduled = items.filter { it.start != null }.sortedBy { Instant.parse(it.start!!) }
@@ -90,7 +89,7 @@ fun WidgetRoot(items: List<WidgetItem>, now: Instant, serverUrl: String = "") {
}
for (hour in gridStart..gridEnd) {
- HourRow(hour, nowZoned, scheduledEvents, fragments, zone, serverUrl)
+ HourRow(hour, nowZoned, scheduledEvents, fragments, zone)
}
}
}
@@ -101,8 +100,7 @@ fun HourRow(
nowZoned: ZonedDateTime,
scheduled: List<WidgetItem>,
fragments: List<TaskFragment>,
- @Suppress("UNUSED_PARAMETER") zone: ZoneId,
- serverUrl: String = ""
+ @Suppress("UNUSED_PARAMETER") zone: ZoneId
) {
val hourStart = nowZoned.withHour(hour).withMinute(0).withSecond(0).withNano(0).toInstant()
val hourEnd = hourStart.plus(1, ChronoUnit.HOURS)
@@ -149,7 +147,7 @@ fun HourRow(
}
fragsHere.forEach { frag ->
- TaskFragmentBlock(frag, serverUrl)
+ TaskFragmentBlock(frag)
}
}
}
@@ -185,7 +183,7 @@ fun EventBlock(event: WidgetItem, isPast: Boolean) {
}
@Composable
-fun TaskFragmentBlock(fragment: TaskFragment, serverUrl: String = "") {
+fun TaskFragmentBlock(fragment: TaskFragment) {
Column(
modifier = GlanceModifier
.fillMaxWidth()
@@ -201,25 +199,26 @@ fun TaskFragmentBlock(fragment: TaskFragment, serverUrl: String = "") {
modifier = GlanceModifier.padding(start = 8.dp, top = 3.dp, bottom = 1.dp)
)
fragment.slots.forEach { slot ->
- TaskRow(slot.task, serverUrl)
+ TaskRow(slot.task)
}
}
}
@Composable
-fun TaskRow(task: WidgetItem, serverUrl: String = "") {
- val tapAction = if (serverUrl.isNotEmpty()) {
- val url = "$serverUrl/task?id=${task.id}&source=${task.source}"
- actionStartActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
- } else {
- null
+fun TaskRow(task: WidgetItem) {
+ val context = LocalContext.current
+ val detailIntent = Intent(context, TaskDetailActivity::class.java).apply {
+ putExtra(TaskDetailActivity.EXTRA_ID, task.id)
+ putExtra(TaskDetailActivity.EXTRA_SOURCE, task.source)
+ putExtra(TaskDetailActivity.EXTRA_TITLE, task.title)
+ putExtra(TaskDetailActivity.EXTRA_COMPLETABLE, task.completable)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
- val rowModifier = GlanceModifier
- .fillMaxWidth()
- .padding(horizontal = 8.dp, vertical = 5.dp)
- .let { if (tapAction != null) it.clickable(tapAction) else it }
Row(
- modifier = rowModifier,
+ modifier = GlanceModifier
+ .fillMaxWidth()
+ .padding(horizontal = 8.dp, vertical = 5.dp)
+ .clickable(actionStartActivity(detailIntent)),
verticalAlignment = Alignment.CenterVertically
) {
if (task.completable) {
diff --git a/android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt b/android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt
new file mode 100644
index 0000000..2a42871
--- /dev/null
+++ b/android/app/src/main/java/org/terst/doot/widget/ui/TaskDetailActivity.kt
@@ -0,0 +1,110 @@
+package org.terst.doot.widget.ui
+
+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.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import org.terst.doot.widget.work.CompleteWorker
+
+class TaskDetailActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val id = intent.getStringExtra(EXTRA_ID) ?: return finish()
+ val source = intent.getStringExtra(EXTRA_SOURCE) ?: return finish()
+ val title = intent.getStringExtra(EXTRA_TITLE) ?: ""
+ val completable = intent.getBooleanExtra(EXTRA_COMPLETABLE, false)
+
+ setContent {
+ MaterialTheme(colorScheme = darkColorScheme()) {
+ TaskDetailSheet(
+ title = title,
+ source = source,
+ completable = completable,
+ onComplete = {
+ CompleteWorker.enqueue(this, id, source)
+ finish()
+ },
+ onDismiss = ::finish
+ )
+ }
+ }
+ }
+
+ companion object {
+ const val EXTRA_ID = "task_id"
+ const val EXTRA_SOURCE = "task_source"
+ const val EXTRA_TITLE = "task_title"
+ const val EXTRA_COMPLETABLE = "task_completable"
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun TaskDetailSheet(
+ title: String,
+ source: String,
+ completable: Boolean,
+ onComplete: () -> Unit,
+ onDismiss: () -> Unit
+) {
+ 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)
+ )
+ }
+ Spacer(Modifier.height(24.dp))
+ if (completable) {
+ Button(
+ onClick = onComplete,
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF3B82F6))
+ ) {
+ Text("Mark Complete", fontSize = 15.sp)
+ }
+ }
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+}
diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..ef88823
--- /dev/null
+++ b/android/app/src/main/res/values/themes.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Transparent window for TaskDetailActivity so the launcher shows behind the bottom sheet -->
+ <style name="Theme.TaskDetail" parent="android:Theme.DeviceDefault.NoActionBar">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ </style>
+</resources>