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
53 changes: 53 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Build & Release APK
on:
pull_request:
branches: [main]
paths:
- "**/*.kt"
- "**/*.xml"
- "**/*.gradle.kts"
- "gradle/libs.versions.toml"
- "gradle/wrapper/**"
- "gradle.properties"
workflow_dispatch:
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check version is new
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION=$(grep 'versionName' app/build.gradle.kts | grep -oP '"[^"]+"' | tr -d '"')
echo "VERSION=${VERSION}" >> $GITHUB_ENV
if gh release view "v${VERSION}" &>/dev/null; then
echo "::error::v${VERSION} already released."
exit 1
fi
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
with:
gradle-version: '8.13'
cache-read-only: false
- run: echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties
- run: gradle :app:assembleDebug --no-daemon
- run: gradle :app:test --no-daemon
- run: gradle :app:lintDebug --no-daemon
- name: Create GitHub Release
if: github.event_name == 'workflow_dispatch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
APK_NAME="Dash-${{ env.VERSION }}-debug.apk"
mv app/build/outputs/apk/debug/app-debug.apk \
app/build/outputs/apk/debug/${APK_NAME}
gh release create "v${{ env.VERSION }}" \
"app/build/outputs/apk/debug/${APK_NAME}" \
--title "Dash v${{ env.VERSION }}" \
--notes "Debug build — enable 'Install unknown apps' in Android settings to sideload."
27 changes: 27 additions & 0 deletions .github/workflows/changelog-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Changelog bump check
on:
pull_request:
branches: [main]
paths:
- "**/*.kt"
- "**/*.xml"
- "**/*.gradle.kts"
- "gradle/libs.versions.toml"
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fail if CHANGELOG.md was not updated
run: |
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
if echo "$CHANGED" | grep -q "^CHANGELOG.md$"; then
echo "CHANGELOG.md updated — good."
else
echo "::error::Code changed but CHANGELOG.md was not updated."
exit 1
fi
35 changes: 35 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: "CodeQL"
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
- cron: "37 19 * * 2"
permissions:
security-events: write
actions: read
contents: read
jobs:
analyze:
name: Analyze (java-kotlin)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v4
with:
languages: java-kotlin
queries: security-extended
- uses: actions/setup-java@v4
with:
java-version: "17"
distribution: temurin
- uses: gradle/actions/setup-gradle@v3
with:
gradle-version: '8.13'
cache-read-only: false
- run: echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties
- run: gradle :app:assembleDebug --no-daemon
- uses: github/codeql-action/analyze@v4
with:
category: /language:java-kotlin
24 changes: 24 additions & 0 deletions .github/workflows/license-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Licence screen sync check
on:
pull_request:
branches: [main]
paths:
- "gradle/libs.versions.toml"
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fail if LicensesScreen.kt was not updated
run: |
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
if echo "$CHANGED" | grep -q "LicensesScreen.kt"; then
echo "LicensesScreen.kt updated — good."
else
echo "::error::gradle/libs.versions.toml changed but LicensesScreen.kt was not updated."
exit 1
fi
44 changes: 30 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
# Changelog

All notable changes to the Android app are documented here.
All notable changes to this project will be documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

---

## [Unreleased]

### Fixed
- CI: provision Gradle 8.13 directly via `gradle/actions/setup-gradle` instead of
relying on `gradle-wrapper.jar`, which cannot be committed through the GitHub API
- Compose BOM bumped to 2024.09.00 (Material3 1.3.0) to gain `PullToRefreshBox`
and align `ModalBottomSheetProperties` signature (removed `securePolicy` param)
- Added missing `lifecycle-runtime-compose` dependency for `collectAsStateWithLifecycle`
- `TaskRepository.pendingReminders`: replaced unavailable `isNull()` DSL call with
a Kotlin-side `completedAt == null` filter
- `EditTaskSheet`: fixed `SecureFlagPolicy` import (`material3` → `compose.ui.window`)
- `SettingsViewModel.clearSaveError`: fixed assignment-as-expression syntax error
- Removed custom `debug.keystore` signing config; CI now uses the default Android
debug keystore so packaging does not fail in a clean environment
- `menuAnchor()` calls updated to `menuAnchor(MenuAnchorType)` in `EditChoreSheet`,
`SettingsScreen`, and `EditTaskSheet` — Material3 1.3.0 deprecated the
parameterless overload at `DeprecationLevel.ERROR`

## [1.0.0] — 2026-05-30

### Added
- choreDash tab: chore list with staleness indicators, NFC tap-to-log,
swipe-to-log with undo, group-by-category, archive/unarchive
- taskDash tab: task list with priority/due/created sort, filter chips
(All / Active / Done), group-by-category, collapsible done section,
task reminders via AlarmManager (exact alarms)
- Settings tab: Supabase URL + anon-key entry, owner selection,
light/dark/system theme toggle
- Initial Android app combining choreDash (NFC chore tracker) and taskDash
(shared to-do list), connecting to the same Supabase project as the web app
- choreDash tab: chore list with staleness colour bars, NFC tap-to-log,
swipe-to-log with undo snackbar, group-by-category, archive/unarchive
- taskDash tab: task list with priority/due/created sort, All/Active/Done
filter chips, group-by-category, collapsible done section, per-task
AlarmManager reminders, owner filter
- Settings tab: Supabase URL + anon-key, owner dropdown, light/dark/system
theme toggle, open-source licenses screen
- NFC foreground dispatch in MainActivity; NDEF text/URI/raw-hex tag-ID extraction
- DailyStaleChoreWorker: once-a-day notification summarising overdue chores
- BootWorker: re-schedules pending task reminders after device reboot
- Open-source licenses screen
- Material3 dynamic-color theming seeded from #4A7C59 (sage green)
- DailyStaleChoreWorker (WorkManager periodic): daily overdue-chore notification
- BootWorker: re-schedules pending task reminders after reboot
- Material3 theme seeded from `#4A7C59` (sage green)
- CI workflows: build + release APK, changelog check, license-sync check, CodeQL
107 changes: 107 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# choreDash + taskDash — Android

Native Android app combining two household tools:

- **choreDash** — chore tracker with NFC tag support. Tap a tag on the fridge, washing machine, etc. to log that chore as done.
- **taskDash** — shared to-do list with categories, priority, due dates, and per-task reminder notifications.

Both tools read from / write to the same Supabase project used by the web app.
Credentials are entered once in the **Settings** tab and persisted in DataStore.

---

## Requirements

| Tool | Version |
|------|---------|
| Android Studio | Hedgehog 2023.1+ |
| JDK | 17 |
| Android Gradle Plugin | 8.13.x |
| Compile SDK | 35 |
| Min SDK | 26 (Android 8) |

---

## Build

```bash
# Debug APK
./gradlew assembleDebug

# Run unit tests
./gradlew test

# Lint
./gradlew lintDebug
```

---

## Project structure

```
app/src/main/java/com/mapgie/dash/
DashApplication.kt # HiltAndroidApp + WorkManager Configuration.Provider
MainActivity.kt # NFC foreground dispatch, Compose entry point
alarm/
AlarmReceiver.kt # BroadcastReceiver for task reminders
AlarmScheduler.kt # AlarmManager wrapper
BootReceiver.kt # Re-schedules alarms after reboot
BootWorker.kt # HiltWorker: queries pending reminders on boot
DailyStaleChoreWorker.kt # HiltWorker: daily stale-chore notification
data/
model/ # Chore.kt, Task.kt, Owner.kt + enums/extension fns
preferences/ # SettingsRepository (DataStore)
repository/ # ChoreRepository, TaskRepository (Supabase)
supabase/ # SupabaseClientProvider
di/
AppModule.kt # Hilt modules
notification/
NotificationHelper.kt # Channel creation + show helpers
nfc/
NfcHandler.kt # NDEF/URI/raw-hex tag-ID extraction
ui/
theme/ # Color.kt, Theme.kt, Type.kt
navigation/ # DashNavGraph.kt
screens/
chores/ # ChoreListViewModel + ChoreListScreen
tasks/ # TaskListViewModel + TaskListScreen
settings/ # SettingsViewModel + SettingsScreen
licenses/ # LicensesScreen
components/ # ChoreCard, LogBottomSheet, EditChoreSheet,
# TaskCard, EditTaskSheet
```

---

## NFC setup

1. Write NDEF Text records to your NFC tags (any NFC writer app).
2. The tag ID is used to identify a chore — see `NfcHandler.extractTagId()`.
3. When the app receives an NFC intent, `LogBottomSheet` opens pre-filled with the matching chore (or shows "Unknown tag" if no chore matches).

---

## Supabase schema

This app connects to the same Supabase project as the web app.
Expected tables: `chores`, `chore_logs`, `todos`, `owners`.

---

## Binary files

`app/debug.keystore` and `gradle/wrapper/gradle-wrapper.jar` are binary files.
After cloning, copy them from a local choreDash checkout or generate a new debug keystore with:

```bash
keytool -genkey -v -keystore app/debug.keystore -alias androiddebugkey \
-keyalg RSA -keysize 2048 -validity 10000 \
-storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US"
```

---

## Open-source licenses

See **Settings → Open-source licenses** inside the app.
13 changes: 1 addition & 12 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,7 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

signingConfigs {
named("debug") {
storeFile = file("debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
}
}

buildTypes {
debug {
signingConfig = signingConfigs.getByName("debug")
}
release {
isMinifyEnabled = true
proguardFiles(
Expand Down Expand Up @@ -60,6 +48,7 @@ android {
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
Expand Down
Loading
Loading