From fa466ba9c145e6792303eeba55a706090256473b Mon Sep 17 00:00:00 2001 From: mapgiedev <4250209+mapgie@users.noreply.github.com> Date: Sun, 31 May 2026 00:46:22 +0100 Subject: [PATCH 1/7] feat: build infrastructure and data layer Gradle wrapper (8.13), version catalog, app build config, data models, repositories, SupabaseClientProvider, SettingsRepository. --- .../dash/data/repository/ChoreRepository.kt | 1 - .../dash/data/repository/TaskRepository.kt | 1 - gradlew | 63 ++++++++++--------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/mapgie/dash/data/repository/ChoreRepository.kt b/app/src/main/java/com/mapgie/dash/data/repository/ChoreRepository.kt index 5d8665d..0950611 100644 --- a/app/src/main/java/com/mapgie/dash/data/repository/ChoreRepository.kt +++ b/app/src/main/java/com/mapgie/dash/data/repository/ChoreRepository.kt @@ -106,7 +106,6 @@ class ChoreRepository @Inject constructor( } } -// Used only internally for the owners query; owners table has just a handle column. @kotlinx.serialization.Serializable private data class OwnerDto(@kotlinx.serialization.SerialName("handle") val handle: String) diff --git a/app/src/main/java/com/mapgie/dash/data/repository/TaskRepository.kt b/app/src/main/java/com/mapgie/dash/data/repository/TaskRepository.kt index c4198b4..36c20f0 100644 --- a/app/src/main/java/com/mapgie/dash/data/repository/TaskRepository.kt +++ b/app/src/main/java/com/mapgie/dash/data/repository/TaskRepository.kt @@ -62,7 +62,6 @@ class TaskRepository @Inject constructor( client.from("todos").delete { filter { eq("id", taskId) } } } - /** Returns tasks with a future reminder_at that have not yet been reminded. */ suspend fun pendingReminders(): List { val client = requireClient() return client.from("todos") diff --git a/gradlew b/gradlew index fc7f6a3..ce48113 100644 --- a/gradlew +++ b/gradlew @@ -1,10 +1,31 @@ #!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# Gradle start up script for POSIX generated by "Gradle Init". +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# Gradle start up script for POSIX generated by "Gradle init" +############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link app_path=$0 + +# Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] @@ -22,7 +43,7 @@ APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} -# Add default JVM options here. +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. @@ -57,6 +78,7 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java @@ -69,54 +91,33 @@ location of your Java installation." fi else JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" + ;; esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS '"$JAVA_OPTS"' '"$GRADLE_OPTS"' '"\"-Dorg.gradle.appname=$APP_BASE_NAME\""' -classpath '"$CLASSPATH"' org.gradle.wrapper.GradleWrapperMain '"$@"' exec "$JAVACMD" "$@" From 9d5ba27e6afbe59a52576b282a3fd734ff840fac Mon Sep 17 00:00:00 2001 From: mapgiedev <4250209+mapgie@users.noreply.github.com> Date: Sun, 31 May 2026 00:54:18 +0100 Subject: [PATCH 2/7] feat: add app scaffolding, alarm layer, NFC, theme, nav, chore UI (Batch 2) --- app/src/main/AndroidManifest.xml | 66 ++++--------------- .../java/com/mapgie/dash/DashApplication.kt | 16 +---- .../main/java/com/mapgie/dash/MainActivity.kt | 2 +- .../com/mapgie/dash/alarm/AlarmReceiver.kt | 2 +- .../com/mapgie/dash/alarm/AlarmScheduler.kt | 2 +- .../com/mapgie/dash/alarm/BootReceiver.kt | 2 +- .../java/com/mapgie/dash/alarm/BootWorker.kt | 2 +- .../dash/alarm/DailyStaleChoreWorker.kt | 2 +- .../main/java/com/mapgie/dash/di/AppModule.kt | 7 +- .../java/com/mapgie/dash/di/SupabaseModule.kt | 6 +- .../java/com/mapgie/dash/nfc/NfcHandler.kt | 2 +- .../dash/notification/NotificationHelper.kt | 2 +- .../mapgie/dash/ui/components/ChoreCard.kt | 2 +- .../dash/ui/components/EditChoreSheet.kt | 2 +- .../dash/ui/components/LogBottomSheet.kt | 2 +- .../mapgie/dash/ui/navigation/DashNavGraph.kt | 2 +- .../dash/ui/screens/chores/ChoreListScreen.kt | 2 +- .../ui/screens/chores/ChoreListViewModel.kt | 2 +- .../java/com/mapgie/dash/ui/theme/Color.kt | 2 +- .../java/com/mapgie/dash/ui/theme/Theme.kt | 2 +- .../java/com/mapgie/dash/ui/theme/Type.kt | 2 +- 21 files changed, 35 insertions(+), 94 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 03822b3..c0eb0a1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ - @@ -9,10 +8,7 @@ - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - + + + - - - - + android:exported="false" tools:node="merge"> + - - - + \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/DashApplication.kt b/app/src/main/java/com/mapgie/dash/DashApplication.kt index d87fa41..352cea8 100644 --- a/app/src/main/java/com/mapgie/dash/DashApplication.kt +++ b/app/src/main/java/com/mapgie/dash/DashApplication.kt @@ -1,5 +1,4 @@ package com.mapgie.dash - import android.app.Application import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration @@ -14,26 +13,17 @@ import javax.inject.Inject @HiltAndroidApp class DashApplication : Application(), Configuration.Provider { - @Inject lateinit var workerFactory: HiltWorkerFactory - override val workManagerConfiguration: Configuration - get() = Configuration.Builder() - .setWorkerFactory(workerFactory) - .build() - + get() = Configuration.Builder().setWorkerFactory(workerFactory).build() override fun onCreate() { super.onCreate() NotificationHelper.createChannels(this) scheduleDailyChoreCheck() } - private fun scheduleDailyChoreCheck() { val request = PeriodicWorkRequestBuilder(1, TimeUnit.DAYS).build() WorkManager.getInstance(this).enqueueUniquePeriodicWork( - "daily_chore_check", - ExistingPeriodicWorkPolicy.KEEP, - request - ) + "daily_chore_check", ExistingPeriodicWorkPolicy.KEEP, request) } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/MainActivity.kt b/app/src/main/java/com/mapgie/dash/MainActivity.kt index 80eced8..93c5953 100644 --- a/app/src/main/java/com/mapgie/dash/MainActivity.kt +++ b/app/src/main/java/com/mapgie/dash/MainActivity.kt @@ -107,4 +107,4 @@ class MainActivity : ComponentActivity() { super.onPause() nfcAdapter?.disableForegroundDispatch(this) } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/alarm/AlarmReceiver.kt b/app/src/main/java/com/mapgie/dash/alarm/AlarmReceiver.kt index 6a8d816..32fab99 100644 --- a/app/src/main/java/com/mapgie/dash/alarm/AlarmReceiver.kt +++ b/app/src/main/java/com/mapgie/dash/alarm/AlarmReceiver.kt @@ -34,4 +34,4 @@ class AlarmReceiver : BroadcastReceiver() { } } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/alarm/AlarmScheduler.kt b/app/src/main/java/com/mapgie/dash/alarm/AlarmScheduler.kt index c9f91ef..b1c8456 100644 --- a/app/src/main/java/com/mapgie/dash/alarm/AlarmScheduler.kt +++ b/app/src/main/java/com/mapgie/dash/alarm/AlarmScheduler.kt @@ -55,4 +55,4 @@ class AlarmScheduler @Inject constructor( PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/alarm/BootReceiver.kt b/app/src/main/java/com/mapgie/dash/alarm/BootReceiver.kt index fc721c4..5ffd250 100644 --- a/app/src/main/java/com/mapgie/dash/alarm/BootReceiver.kt +++ b/app/src/main/java/com/mapgie/dash/alarm/BootReceiver.kt @@ -17,4 +17,4 @@ class BootReceiver : BroadcastReceiver() { WorkManager.getInstance(context) .enqueue(OneTimeWorkRequestBuilder().build()) } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/alarm/BootWorker.kt b/app/src/main/java/com/mapgie/dash/alarm/BootWorker.kt index 110bd91..4c8a579 100644 --- a/app/src/main/java/com/mapgie/dash/alarm/BootWorker.kt +++ b/app/src/main/java/com/mapgie/dash/alarm/BootWorker.kt @@ -25,4 +25,4 @@ class BootWorker @AssistedInject constructor( } Result.success() }.getOrElse { Result.retry() } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/alarm/DailyStaleChoreWorker.kt b/app/src/main/java/com/mapgie/dash/alarm/DailyStaleChoreWorker.kt index e43e678..97197ef 100644 --- a/app/src/main/java/com/mapgie/dash/alarm/DailyStaleChoreWorker.kt +++ b/app/src/main/java/com/mapgie/dash/alarm/DailyStaleChoreWorker.kt @@ -25,4 +25,4 @@ class DailyStaleChoreWorker @AssistedInject constructor( NotificationHelper.showStaleChoresSummary(applicationContext, staleLabels) Result.success() }.getOrElse { Result.retry() } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/di/AppModule.kt b/app/src/main/java/com/mapgie/dash/di/AppModule.kt index 1f59e6a..dc96e65 100644 --- a/app/src/main/java/com/mapgie/dash/di/AppModule.kt +++ b/app/src/main/java/com/mapgie/dash/di/AppModule.kt @@ -1,12 +1,7 @@ package com.mapgie.dash.di - import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent - -// All repositories and infrastructure classes use @Inject constructor and -// @Singleton, so Hilt auto-provides them without explicit @Provides methods. -// This module exists as an extension point for future additions. @Module @InstallIn(SingletonComponent::class) -object AppModule +object AppModule \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/di/SupabaseModule.kt b/app/src/main/java/com/mapgie/dash/di/SupabaseModule.kt index 9f82262..9597e40 100644 --- a/app/src/main/java/com/mapgie/dash/di/SupabaseModule.kt +++ b/app/src/main/java/com/mapgie/dash/di/SupabaseModule.kt @@ -1,11 +1,7 @@ package com.mapgie.dash.di - import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent - -// SupabaseClientProvider is @Singleton + @Inject; no manual @Provides needed. -// This module exists as an extension point for mock clients in tests. @Module @InstallIn(SingletonComponent::class) -object SupabaseModule +object SupabaseModule \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/nfc/NfcHandler.kt b/app/src/main/java/com/mapgie/dash/nfc/NfcHandler.kt index 766d227..33fa658 100644 --- a/app/src/main/java/com/mapgie/dash/nfc/NfcHandler.kt +++ b/app/src/main/java/com/mapgie/dash/nfc/NfcHandler.kt @@ -55,4 +55,4 @@ object NfcHandler { } else -> null } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/notification/NotificationHelper.kt b/app/src/main/java/com/mapgie/dash/notification/NotificationHelper.kt index ea9b421..102a624 100644 --- a/app/src/main/java/com/mapgie/dash/notification/NotificationHelper.kt +++ b/app/src/main/java/com/mapgie/dash/notification/NotificationHelper.kt @@ -92,4 +92,4 @@ object NotificationHelper { NotificationManagerCompat.from(context).notify(1, notification) } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/components/ChoreCard.kt b/app/src/main/java/com/mapgie/dash/ui/components/ChoreCard.kt index 088b83c..7b07ec9 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/ChoreCard.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/ChoreCard.kt @@ -114,4 +114,4 @@ private fun lastScannedText(lastScanned: Instant?): String { days == 1L -> "Yesterday" else -> "${days}d ago" } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/components/EditChoreSheet.kt b/app/src/main/java/com/mapgie/dash/ui/components/EditChoreSheet.kt index a9ce87b..a00f092 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/EditChoreSheet.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/EditChoreSheet.kt @@ -162,4 +162,4 @@ fun EditChoreSheet( } ) } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/components/LogBottomSheet.kt b/app/src/main/java/com/mapgie/dash/ui/components/LogBottomSheet.kt index 824c11d..5818818 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/LogBottomSheet.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/LogBottomSheet.kt @@ -152,4 +152,4 @@ fun LogBottomSheet( DatePicker(state = datePickerState) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/navigation/DashNavGraph.kt b/app/src/main/java/com/mapgie/dash/ui/navigation/DashNavGraph.kt index f96fb2b..d6925ac 100644 --- a/app/src/main/java/com/mapgie/dash/ui/navigation/DashNavGraph.kt +++ b/app/src/main/java/com/mapgie/dash/ui/navigation/DashNavGraph.kt @@ -103,4 +103,4 @@ fun DashNavGraph( } } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt index ffd977e..a68ec4d 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListScreen.kt @@ -365,4 +365,4 @@ private fun EmptyState(message: String) { modifier = Modifier.padding(32.dp) ) } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt index 48cc4fd..72849f0 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/chores/ChoreListViewModel.kt @@ -158,4 +158,4 @@ class ChoreListViewModel @Inject constructor( fun setOwnerFilter(ownerFilter: OwnerFilter) { _uiState.update { it.copy(ownerFilter = ownerFilter) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/theme/Color.kt b/app/src/main/java/com/mapgie/dash/ui/theme/Color.kt index c29118d..7ca7a19 100644 --- a/app/src/main/java/com/mapgie/dash/ui/theme/Color.kt +++ b/app/src/main/java/com/mapgie/dash/ui/theme/Color.kt @@ -67,4 +67,4 @@ val md_theme_dark_scrim = Color(0xFF000000) // Semantic status colours referenced by card left-bars val StatusStale = Color(0xFFBA1A1A) val StatusAging = Color(0xFFF29900) -val StatusFresh = Color(0xFF4A7C59) +val StatusFresh = Color(0xFF4A7C59) \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/theme/Theme.kt b/app/src/main/java/com/mapgie/dash/ui/theme/Theme.kt index 70b2747..f9bb34e 100644 --- a/app/src/main/java/com/mapgie/dash/ui/theme/Theme.kt +++ b/app/src/main/java/com/mapgie/dash/ui/theme/Theme.kt @@ -95,4 +95,4 @@ fun DashTheme( typography = DashTypography, content = content ) -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/theme/Type.kt b/app/src/main/java/com/mapgie/dash/ui/theme/Type.kt index 52befef..a6b6714 100644 --- a/app/src/main/java/com/mapgie/dash/ui/theme/Type.kt +++ b/app/src/main/java/com/mapgie/dash/ui/theme/Type.kt @@ -57,4 +57,4 @@ val DashTypography = Typography( lineHeight = 16.sp, letterSpacing = 0.5.sp ) -) +) \ No newline at end of file From bb6c35ab945cc23d67bd2ecc1f850baff66eabde Mon Sep 17 00:00:00 2001 From: mapgiedev <4250209+mapgie@users.noreply.github.com> Date: Sun, 31 May 2026 00:59:06 +0100 Subject: [PATCH 3/7] feat: add task UI, settings, licenses, resources, docs, and CI workflows (Batch 3) --- .github/workflows/build.yml | 52 +++++++++ .github/workflows/changelog-check.yml | 27 +++++ .github/workflows/codeql.yml | 34 ++++++ .github/workflows/license-sync.yml | 24 ++++ README.md | 107 ++++++++++++++++++ .../dash/ui/components/EditTaskSheet.kt | 2 +- .../com/mapgie/dash/ui/components/TaskCard.kt | 2 +- .../ui/screens/licenses/LicensesScreen.kt | 2 +- .../ui/screens/settings/SettingsScreen.kt | 2 +- .../ui/screens/settings/SettingsViewModel.kt | 2 +- .../dash/ui/screens/tasks/TaskListScreen.kt | 2 +- .../ui/screens/tasks/TaskListViewModel.kt | 2 +- .../res/mipmap-anydpi-v26/ic_launcher.xml | 2 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 2 +- app/src/main/res/values-night/themes.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- app/src/main/res/values/themes.xml | 2 +- 17 files changed, 256 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/changelog-check.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/license-sync.yml create mode 100644 README.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c28dfe5 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,52 @@ +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: + cache-read-only: false + - run: echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties + - run: ./gradlew :app:assembleDebug --no-daemon + - run: ./gradlew :app:test --no-daemon + - run: ./gradlew :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." diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml new file mode 100644 index 0000000..c4034b5 --- /dev/null +++ b/.github/workflows/changelog-check.yml @@ -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 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..ddef407 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,34 @@ +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: + cache-read-only: false + - run: echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties + - run: ./gradlew :app:assembleDebug --no-daemon + - uses: github/codeql-action/analyze@v4 + with: + category: /language:java-kotlin diff --git a/.github/workflows/license-sync.yml b/.github/workflows/license-sync.yml new file mode 100644 index 0000000..01aedac --- /dev/null +++ b/.github/workflows/license-sync.yml @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f3a415 --- /dev/null +++ b/README.md @@ -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. diff --git a/app/src/main/java/com/mapgie/dash/ui/components/EditTaskSheet.kt b/app/src/main/java/com/mapgie/dash/ui/components/EditTaskSheet.kt index 842ec15..7e9ff22 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/EditTaskSheet.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/EditTaskSheet.kt @@ -446,4 +446,4 @@ fun EditTaskSheet( } ) { DatePicker(state = reminderPickerState) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt b/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt index e1b061a..3e96de2 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt @@ -172,4 +172,4 @@ private fun DueBadge(task: TaskDto) { style = MaterialTheme.typography.labelSmall, color = color ) -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/licenses/LicensesScreen.kt b/app/src/main/java/com/mapgie/dash/ui/screens/licenses/LicensesScreen.kt index 568ebba..6e86668 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/licenses/LicensesScreen.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/licenses/LicensesScreen.kt @@ -76,4 +76,4 @@ fun LicensesScreen(onBack: () -> Unit) { } } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsScreen.kt index 2857d25..891e5d3 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsScreen.kt @@ -212,4 +212,4 @@ private val ThemeMode.label: String ThemeMode.SYSTEM -> "System" ThemeMode.LIGHT -> "Light" ThemeMode.DARK -> "Dark" - } + } \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsViewModel.kt b/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsViewModel.kt index 5640877..562950f 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/settings/SettingsViewModel.kt @@ -48,4 +48,4 @@ class SettingsViewModel @Inject constructor( } fun clearSaveError() = _saveError.value = null -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListScreen.kt b/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListScreen.kt index c4e1ff9..693111f 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListScreen.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListScreen.kt @@ -275,4 +275,4 @@ private fun TaskSort.next(): TaskSort = when (this) { TaskSort.PRIORITY -> TaskSort.DUE TaskSort.DUE -> TaskSort.CREATED TaskSort.CREATED -> TaskSort.PRIORITY -} +} \ No newline at end of file diff --git a/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListViewModel.kt b/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListViewModel.kt index 65237f3..985d092 100644 --- a/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListViewModel.kt +++ b/app/src/main/java/com/mapgie/dash/ui/screens/tasks/TaskListViewModel.kt @@ -192,4 +192,4 @@ class TaskListViewModel @Inject constructor( fun setGroupBy(enabled: Boolean) = _uiState.update { it.copy(groupByCategory = enabled) } fun setOwnerFilter(f: OwnerFilter) = _uiState.update { it.copy(ownerFilter = f) } fun clearError() = _uiState.update { it.copy(error = null) } -} +} \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index d378acd..bbd3e02 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index d378acd..bbd3e02 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,4 +2,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 79f5cc8..659588b 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,4 +1,4 @@