Skip to content

Commit 8d12f5e

Browse files
committed
polish shell navigation and scene controls
1 parent d3d5f40 commit 8d12f5e

6 files changed

Lines changed: 166 additions & 53 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# OpenTasker
22

3-
[![Version](https://img.shields.io/badge/version-0.2.65-blue.svg)](https://github.com/SysAdminDoc/OpenTasker/releases)
3+
[![Version](https://img.shields.io/badge/version-0.2.66-blue.svg)](https://github.com/SysAdminDoc/OpenTasker/releases)
44
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
55
[![Platform](https://img.shields.io/badge/platform-Android%208.0%2B-brightgreen.svg)](https://developer.android.com)
66
[![Kotlin](https://img.shields.io/badge/kotlin-2.3.21-7f52ff.svg)](https://kotlinlang.org)
@@ -20,7 +20,7 @@
2020

2121
**Planned:** broad device-verified background geofence reliability, elevated (Shizuku) execution, Termux script dispatch, a visual flow authoring editor, and richer plugin UX. See [ROADMAP.md](ROADMAP.md).
2222

23-
> **Status:** the current source version is `0.2.65`. Device-evidence claims (location/calendar/sun) are single-device API 36 data points on `SM-S938B`, not broad background-geofence reliability guarantees. The latest polish pass improves IME handling, compact small-screen bottom navigation with a More menu, variable and scene-element deletion safety, saveable editor/context/template/scene-element state, numeric form keyboards, larger day-token touch targets, and accessibility roles for widget/flow/form switch targets.
23+
> **Status:** the current source version is `0.2.66`. Device-evidence claims (location/calendar/sun) are single-device API 36 data points on `SM-S938B`, not broad background-geofence reliability guarantees. The latest polish pass improves IME handling, compact small-screen bottom navigation with a More menu, squared-off navigation/FAB affordances, variable and scene-element deletion safety, saveable editor/context/template/scene-element state, scene-element nudge controls for non-drag movement, numeric form keyboards, larger day-token touch targets, and accessibility roles for widget/flow/form switch targets.
2424
2525
---
2626

RESEARCH.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Updated: 2026-06-15 (stricter release-polish pass; replaces previous research co
44

55
## Executive Summary
66

7-
OpenTasker is a Kotlin/Compose Android automation app at source version 0.2.65 with a Room-backed profile -> context -> task -> action engine, 49 registered runtime actions, engine-handled flow control, FOSS LocationManager geofencing, Locale/Tasker plugin hosting, Tasker XML import, OpenTasker JSON transfer, Quick Settings/widget triggers, OEM battery guidance, and F-Droid/Play release checks. The repo's best trait is explicit failure behavior: unsupported privileged actions fail honestly, imports are review-gated, sensitive run-log data is redacted, destructive variable and scene-element deletion are visually confirmed, and prior high-risk guards remain in place.
7+
OpenTasker is a Kotlin/Compose Android automation app at source version 0.2.66 with a Room-backed profile -> context -> task -> action engine, 49 registered runtime actions, engine-handled flow control, FOSS LocationManager geofencing, Locale/Tasker plugin hosting, Tasker XML import, OpenTasker JSON transfer, Quick Settings/widget triggers, OEM battery guidance, and F-Droid/Play release checks. The repo's best trait is explicit failure behavior: unsupported privileged actions fail honestly, imports are review-gated, sensitive run-log data is redacted, destructive variable and scene-element deletion are visually confirmed, and prior high-risk guards remain in place.
88

99
The highest-value direction is trust and scale before more action breadth. Existing roadmap items already cover target-SDK readiness, Android 17 audio gating, work/private-profile app automation, TalkBack accessibility, profile/task organization, action implementation tests, WorkManager resilience, diagnostics, Tasker XML export, scoped LAN cleartext, date/time templates, camera/mic privacy triggers, Wake-on-LAN, and optional BYO-key AI authoring.
1010

@@ -60,7 +60,7 @@ Recent continuation passes found three additional non-duplicate gaps; v0.2.62 cl
6060
- Target-SDK wording risk remains: live Google target-API pages checked on 2026-06-15 still publish the API 35 / August 31, 2025 requirement, not a public API 36 Play deadline. Target-36 code has shipped, but docs must not mark an unpublished deadline as verified.
6161
- Android 17 audio risk remains: `MediaActions.kt` and TTS/sound code can run from background profiles while `AutomationService` uses `specialUse|location`; background audio hardening can fail playback or volume changes silently unless a valid foreground-service/while-in-use path exists.
6262
- Work profile / Private Space risk remains: app-open contexts, package broadcasts, and app launch actions are package-name/current-profile based; no model currently records `UserHandle`, profile labels, hidden-profile state, or profile-local disclosure.
63-
- Accessibility gap is narrowed but remains: action checkbox rows, profile/context form switches, widget task rows, and flow graph targets now expose better roles/state; numeric fields request numeric keyboards, small day-token controls meet 48 dp minimum height, and scene element edit/delete dialog state is restored from stable scene id/index state. A full TalkBack sweep and accessibility-checks instrumentation gate are still missing.
63+
- Accessibility gap is narrowed but remains: action checkbox rows, profile/context form switches, widget task rows, flow graph targets, and the custom bottom navigation expose better roles/state; numeric fields request numeric keyboards, small day-token controls meet 48 dp minimum height, and scene element edit/delete dialog state is restored from stable scene id/index state with row-level nudge controls for non-drag movement. A full TalkBack sweep and accessibility-checks instrumentation gate are still missing.
6464
- Action test gap is narrowed but remains: `core/actions/ActionGuardsTest.kt` now covers key network, URL, wait, ping, and Wake-on-LAN guards, while file, media, settings, app, notification, and download edge cases still need coverage.
6565
- Network hardening gap closed in v0.2.62: `HttpPostAction` caps request bodies at 1 MB, uses fixed-length streaming, and keeps legacy `body` compatibility under the same cap.
6666
- New supply-chain gap: `.github/workflows/build.yml` and `release.yml` consume tag-pinned actions (`@v4`), the build workflow has no explicit `permissions`, `gradle/verification-metadata.xml` is absent, and no Dependabot config is present.

ROADMAP.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# OpenTasker Roadmap
22

3-
Source-backed product roadmap for OpenTasker v0.2.65 → v0.3.x. Reconciles current repo state with competitive research across Android automation apps, adjacent workflow engines, platform constraints (API 35–36), distribution policy, and dependency changelogs.
3+
Source-backed product roadmap for OpenTasker v0.2.66 → v0.3.x. Reconciles current repo state with competitive research across Android automation apps, adjacent workflow engines, platform constraints (API 35–36), distribution policy, and dependency changelogs.
44

55
**Last updated:** 2026-06-15
6-
**Roadmap version:** 2026.06.15 stricter release-polish pass (v0.2.65 scene-state/accessibility reconciliation)
7-
**Current app version:** 0.2.65
6+
**Roadmap version:** 2026.06.15 stricter release-polish pass (v0.2.66 shell and scene accessibility reconciliation)
7+
**Current app version:** 0.2.66
88
**Planning rule:** items marked "Now" must ship before the v0.3.0 public beta claim. Items already completed in 0.2.x are retained in the **Completed Backlog** appendix for traceability and explicitly removed from active tiers.
99

1010
## Reconciliation note (2026-05-06 pass)
@@ -133,7 +133,7 @@ Key local constraints:
133133

134134
### N9 (2026.05.06) - Documentation truth pass post-v0.2.59
135135

136-
**Status:** Partially refreshed through v0.2.65: README, ROADMAP.md, RESEARCH.md, Gradle version values, F-Droid metadata gates, compact More-menu phone navigation, saveable editor/context/template/scene-element state, numeric keyboard hints, row-level switch semantics, and destructive variable/scene-element affordances now reflect the target-SDK-36/action-metadata and stricter UI polish passes. Older architecture and readiness docs still need a current-version truth pass before this item closes. [L14]
136+
**Status:** Partially refreshed through v0.2.66: README, ROADMAP.md, RESEARCH.md, Gradle version values, F-Droid metadata gates, compact More-menu phone navigation, squared-off navigation/FAB affordances, saveable editor/context/template/scene-element state, scene-element nudge controls, numeric keyboard hints, row-level switch semantics, and destructive variable/scene-element affordances now reflect the target-SDK-36/action-metadata and stricter UI polish passes. Older architecture and readiness docs still need a current-version truth pass before this item closes. [L14]
137137
**Description:** Refresh README, CLAUDE.md, ARCHITECTURE.md, IMPROVEMENT_PLAN.md, and FDROID_READINESS.md to reflect the current release. Restructure long status copy into skimmable feature lists where needed. Sync version strings, toolchain versions, workflow filenames, fdroid metadata, Gradle properties, ROADMAP, and CHANGELOG.
138138
**Sources:** Local repo state [L1][L5], existing improvement plan [L7], dependency/release audit [L14].
139139
**Category:** docs, dev-experience.
@@ -163,7 +163,7 @@ Key local constraints:
163163

164164
### X1 (2026.05.06) - Scene editor finishing pass
165165

166-
**Status:** Element creation/editing, drag-to-move, and scaled previews shipped in v0.2.59. Resize handles, multi-select layout edits, alignment guides, and overlay launch (`SYSTEM_ALERT_WINDOW`) remain.
166+
**Status:** Element creation/editing, drag-to-move, scaled previews, and row-level 1 dp nudge controls shipped through v0.2.66. Resize handles, multi-select layout edits, alignment guides, and overlay launch (`SYSTEM_ALERT_WINDOW`) remain.
167167
**Description:** Add resize-handle drag, snap-to-grid + alignment guides, multi-select with bulk move/align/delete, then wire scene-launch action through the existing overlay-permission onboarding so a task can show a configured scene. Keep Android 15 background-FGS overlay restrictions in scope.
168168
**Sources:** Tasker scenes [S16], Android 15 overlay/FGS behavior [S36], Play foreground-service declaration requirements [S51], Android 16 target behavior [S52].
169169
**Category:** UX, platform/OS.

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ val releaseKeystorePath = System.getenv("OPEN_TASKER_RELEASE_KEYSTORE")
1010
val releaseKeystorePassword = System.getenv("OPEN_TASKER_RELEASE_KEYSTORE_PASSWORD")
1111
val releaseKeyAlias = System.getenv("OPEN_TASKER_RELEASE_KEY_ALIAS")
1212
val releaseKeyPassword = System.getenv("OPEN_TASKER_RELEASE_KEY_PASSWORD")
13-
val appVersionCode = 67
14-
val appVersionName = "0.2.65"
13+
val appVersionCode = 68
14+
val appVersionName = "0.2.66"
1515
val allowedDistributions = setOf("standard", "fdroid", "play")
1616
val selectedDistribution = providers.gradleProperty("openTaskerDistribution")
1717
.orElse("standard")

app/src/main/java/com/opentasker/ui/screens/ActiveAutomationUi.kt

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
66
import androidx.activity.result.contract.ActivityResultContracts
77
import androidx.compose.animation.animateContentSize
88
import androidx.compose.foundation.BorderStroke
9+
import androidx.compose.foundation.clickable
910
import androidx.compose.foundation.text.KeyboardOptions
1011
import androidx.compose.foundation.layout.Arrangement
1112
import androidx.compose.foundation.layout.Box
@@ -52,7 +53,6 @@ import androidx.compose.material3.Icon
5253
import androidx.compose.material3.IconButton
5354
import androidx.compose.material3.MaterialTheme
5455
import androidx.compose.material3.NavigationBar
55-
import androidx.compose.material3.NavigationBarItem
5656
import androidx.compose.material3.OutlinedButton
5757
import androidx.compose.material3.OutlinedTextField
5858
import androidx.compose.material3.Scaffold
@@ -81,6 +81,7 @@ import androidx.compose.ui.graphics.Color
8181
import androidx.compose.ui.graphics.vector.ImageVector
8282
import androidx.compose.ui.platform.LocalContext
8383
import androidx.compose.ui.semantics.Role
84+
import androidx.compose.ui.semantics.selected
8485
import androidx.compose.ui.semantics.stateDescription
8586
import androidx.compose.ui.semantics.semantics
8687
import androidx.compose.ui.text.style.TextAlign
@@ -855,11 +856,15 @@ fun ActiveAutomationUi(
855856
showCreateProfileDialog = true
856857
}
857858
},
859+
shape = RoundedCornerShape(12.dp),
858860
) {
859861
Icon(Icons.Filled.Add, contentDescription = if (tasks.isEmpty()) "Create task" else "Create profile")
860862
}
861863

862-
OpenTaskerScreen.Tasks -> FloatingActionButton(onClick = { showCreateTaskDialog = true }) {
864+
OpenTaskerScreen.Tasks -> FloatingActionButton(
865+
onClick = { showCreateTaskDialog = true },
866+
shape = RoundedCornerShape(12.dp),
867+
) {
863868
Icon(Icons.Filled.Add, contentDescription = "Create task")
864869
}
865870

@@ -873,30 +878,26 @@ fun ActiveAutomationUi(
873878
},
874879
bottomBar = {
875880
NavigationBar {
876-
val navigationBarScope = this
877881
primaryNavigationScreens.forEach { destination ->
878-
NavigationBarItem(
882+
OpenTaskerNavigationItem(
879883
selected = screen == destination,
880884
onClick = {
881885
screenOrdinal = destination.ordinal
882886
showMoreDestinations = false
883887
},
884-
icon = { Icon(destination.icon(), contentDescription = destination.label) },
885-
label = { Text(destination.label, maxLines = 1, overflow = TextOverflow.Ellipsis) },
886-
alwaysShowLabel = true,
888+
icon = destination.icon(),
889+
label = destination.label,
890+
modifier = Modifier.weight(1f),
887891
)
888892
}
889893
Box(Modifier.weight(1f)) {
890-
with(navigationBarScope) {
891-
NavigationBarItem(
892-
selected = screen in secondaryNavigationScreens,
893-
onClick = { showMoreDestinations = true },
894-
modifier = Modifier.fillMaxWidth(),
895-
icon = { Icon(Icons.Filled.Menu, contentDescription = "More screens") },
896-
label = { Text("More", maxLines = 1, overflow = TextOverflow.Ellipsis) },
897-
alwaysShowLabel = true,
898-
)
899-
}
894+
OpenTaskerNavigationItem(
895+
selected = screen in secondaryNavigationScreens,
896+
onClick = { showMoreDestinations = true },
897+
icon = Icons.Filled.Menu,
898+
label = "More",
899+
modifier = Modifier.fillMaxWidth(),
900+
)
900901
DropdownMenu(
901902
expanded = showMoreDestinations,
902903
onDismissRequest = { showMoreDestinations = false },
@@ -1203,6 +1204,53 @@ fun ActiveAutomationUi(
12031204
}
12041205
}
12051206

1207+
@Composable
1208+
private fun OpenTaskerNavigationItem(
1209+
selected: Boolean,
1210+
onClick: () -> Unit,
1211+
icon: ImageVector,
1212+
label: String,
1213+
modifier: Modifier = Modifier,
1214+
) {
1215+
val contentColor = if (selected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
1216+
Column(
1217+
modifier = modifier
1218+
.heightIn(min = 72.dp)
1219+
.clickable(role = Role.Tab, onClick = onClick)
1220+
.semantics(mergeDescendants = true) {
1221+
this.selected = selected
1222+
stateDescription = if (selected) "Selected" else "Not selected"
1223+
}
1224+
.padding(horizontal = 4.dp, vertical = 6.dp),
1225+
horizontalAlignment = Alignment.CenterHorizontally,
1226+
verticalArrangement = Arrangement.Center,
1227+
) {
1228+
Surface(
1229+
color = if (selected) MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.55f) else Color.Transparent,
1230+
shape = RoundedCornerShape(8.dp),
1231+
border = if (selected) BorderStroke(1.dp, MaterialTheme.colorScheme.primary.copy(alpha = 0.18f)) else null,
1232+
) {
1233+
Icon(
1234+
icon,
1235+
contentDescription = null,
1236+
tint = contentColor,
1237+
modifier = Modifier
1238+
.padding(horizontal = 16.dp, vertical = 5.dp)
1239+
.size(24.dp),
1240+
)
1241+
}
1242+
Spacer(Modifier.height(2.dp))
1243+
Text(
1244+
label,
1245+
style = MaterialTheme.typography.labelMedium,
1246+
color = contentColor,
1247+
maxLines = 1,
1248+
overflow = TextOverflow.Ellipsis,
1249+
textAlign = TextAlign.Center,
1250+
)
1251+
}
1252+
}
1253+
12061254
@Composable
12071255
private fun ProfilesScreen(
12081256
profiles: List<Profile>,

0 commit comments

Comments
 (0)