Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ or on your own NAS (Ollama). No third-party cloud, ever.
**Spec:** [`docs/PRODUCTION_PLAN.md`](docs/PRODUCTION_PLAN.md). Every module
and rule traces back to a section (and often a community demand source) there.

## Status — v0.1.0-alpha.16
## Status — v0.1.0-alpha.17

| Area | State |
|---|---|
Expand Down Expand Up @@ -43,6 +43,7 @@ and rule traces back to a section (and often a community demand source) there.
| Focus timer: tap the ring for a custom HH:MM:SS time, plus an "Overlay" button that floats the countdown over any app (tap once for a close X that leaves it running); Overwhelm overlay now follows the theme | ✅ |
| Screen Time: mirrors Android digital-wellbeing into LifeOS and keeps it forever (survives Samsung's ~monthly purge) — weekly bars + average, week scrolling, per-app breakdown, unlocks, JSON export | ✅ |
| Screen Time · Plants (custom photos + care atlas) · News · Downloader on Home; NAS server apps redesigned as an app-store list; Jarvis answers from a live data snapshot (note bodies included) and a Developer Options "Jarvis Debugging" toggle exposes snapshot/output/tool-calls with a copy button | ✅ |
| Brick (§Module Brick): tap-to-block modes — pick blocked apps + optional per-app daily allowances, turn a mode on/off by NFC tag, time window or by hand, strict mode refuses early exits; blocked apps hit a full-screen wall via an accessibility blocker (the only route Android gives a sideloaded app). Blocking rules covered by unit tests | ✅ |
| Deferred post-alpha: Glance home-screen widgets, HA WebSocket live state/zones, Vault unlock UI, first-run onboarding checklist (grants live in Settings → System access), FinTS bank sync | 🔜 |

**Google-free by design:** no Google service is ever called at runtime (no Play Services, no Google recognizer, no Google Maps). Remaining Google-*authored* open-source, fully on-device libraries: AndroidX/Jetpack (unavoidable on Android), MediaPipe (Gemma inference), ML Kit on-device OCR/barcode (no network) — swap candidates documented in the plan.
Expand All @@ -52,7 +53,7 @@ and rule traces back to a section (and often a community demand source) there.
Grab `lifeos-v*.apk` from [Releases](../../releases), then:

```
adb install -r -g lifeos-v0.1.0-alpha.16.apk
adb install -r -g lifeos-v0.1.0-alpha.17.apk
```

or copy to the phone and allow *Install unknown apps*. Android 13+ (minSdk 33).
Expand Down Expand Up @@ -80,7 +81,8 @@ app/ Shell: theme, bottom bar, NavHost, Home grid + planner ca
core/{model,common,designsystem,database,datastore,network,ai,service,vault,ui}
feature/{chat,capture,notes,reminders,todo,calendar,messagecenter,dhl,
imagereasoning,finance,email,nas,books,route,smarthome,planner,
clock,adhd,memex,agentic,evolution,downloader,plants,news,vault}
clock,adhd,memex,agentic,evolution,downloader,plants,news,vault,
screentime,brick}
build-logic/ Convention plugins
docs/PRODUCTION_PLAN.md
```
Expand Down
5 changes: 3 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ android {

defaultConfig {
applicationId = "com.lifeos"
versionCode = 16
versionName = "0.1.0-alpha.16"
versionCode = 17
versionName = "0.1.0-alpha.17"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down Expand Up @@ -86,6 +86,7 @@ dependencies {
implementation(projects.feature.news)
implementation(projects.feature.vault)
implementation(projects.feature.screentime)
implementation(projects.feature.brick)

implementation(projects.core.model)
implementation(projects.core.network)
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/kotlin/com/lifeos/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import com.lifeos.app.ui.LifeOsApp
import com.lifeos.core.datastore.SettingsRepository
import com.lifeos.core.designsystem.theme.LifeOsTheme
import com.lifeos.core.designsystem.theme.PALETTE_DYNAMIC
import com.lifeos.core.service.LifeOsForegroundService
import com.lifeos.feature.brick.data.BrickRepository
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

Expand All @@ -26,6 +29,9 @@ class MainActivity : FragmentActivity() {
@Inject
lateinit var settingsRepository: SettingsRepository

@Inject
lateinit var brickRepository: BrickRepository

/** Bumped each time the assistant gesture asks for quick capture. */
private val captureRequests = mutableStateOf(0)

Expand All @@ -37,6 +43,8 @@ class MainActivity : FragmentActivity() {
enableEdgeToEdge()

LifeOsForegroundService.start(this)
// Reload any live Brick mode and re-arm its time windows (survives reboots).
lifecycleScope.launch { brickRepository.refresh() }
requestNotificationsPermission()
if (isCaptureIntent(intent)) captureRequests.value++

Expand Down
2 changes: 2 additions & 0 deletions app/src/main/kotlin/com/lifeos/app/ui/LifeOsApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import com.lifeos.feature.plants.PlantsRoute
import com.lifeos.feature.news.NewsRoute
import com.lifeos.feature.vault.VaultRoute
import com.lifeos.feature.screentime.ScreenTimeRoute
import com.lifeos.feature.brick.BrickRoute

/**
* Single-activity app shell (§1.3): adaptive scaffold, short M3E bottom bar,
Expand Down Expand Up @@ -191,6 +192,7 @@ fun LifeOsApp(captureRequests: Int = 0, navBarIds: List<String> = emptyList()) {
composable<LifeDestination.News> { NewsRoute() }
composable<LifeDestination.Vault> { VaultRoute() }
composable<LifeDestination.ScreenTime> { ScreenTimeRoute() }
composable<LifeDestination.Brick> { BrickRoute() }
}
}

Expand Down
17 changes: 12 additions & 5 deletions app/src/main/kotlin/com/lifeos/app/ui/screen/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.compose.material.icons.filled.GridView
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.LocalFlorist
import androidx.compose.material.icons.filled.Newspaper
import androidx.compose.material.icons.filled.Shield
import androidx.compose.material.icons.filled.Timelapse
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.automirrored.filled.ViewList
Expand Down Expand Up @@ -198,6 +199,12 @@ fun HomeScreen(
icon = Icons.Filled.Newspaper,
destination = LifeDestination.News,
),
AppGridItem(
label = "Brick",
description = "Tap-to-block modes for distracting apps",
icon = Icons.Filled.Shield,
destination = LifeDestination.Brick,
),
AppGridItem(
label = "Screen Time",
description = "Digital wellbeing, kept forever",
Expand Down Expand Up @@ -227,15 +234,15 @@ fun HomeScreen(
Text(
"LifeOS",
style = MaterialTheme.typography.headlineMedium,
// Hold for a full 5 seconds (not the ~0.5s system long-press) to
// reveal the hidden Vault — deliberate, hard to trigger by accident.
// Hold for 3 seconds (not the ~0.5s system long-press) to reveal
// the hidden Vault — deliberate, hard to trigger by accident.
modifier = Modifier.pointerInput(Unit) {
awaitEachGesture {
awaitFirstDown()
val revealed = withTimeoutOrNull(5_000L) {
val revealed = withTimeoutOrNull(3_000L) {
waitForUpOrCancellation()
false // released before 5s
} ?: true // 5s elapsed while still held
false // released before 3s
} ?: true // 3s elapsed while still held
if (revealed) vaultRevealed = true
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ fun SettingsRoute(
}
}

SystemSettingRow(
title = "App blocking (Brick)",
subtitle = "Enable \"LifeOS Brick\" so blocking modes can stop blocked apps from opening",
) {
context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
}

SectionHeader(title = "Storage")
SystemSettingRow(
title = "Readable /LifeOS folder",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class SettingsViewModel @Inject constructor(
val DEFAULT_HOME_ORDER = listOf(
"Notes", "Logger", "Packages", "Finance", "Scan", "Planner", "Books",
"Routes", "Smart home", "NAS", "Clock", "Focus", "Memex", "Macros", "Evolution",
"Downloader", "Plants", "News", "Screen Time",
"Downloader", "Plants", "News", "Brick", "Screen Time",
)
}
}
Expand Down
Loading
Loading