Compare commits
163 Commits
70acfe6d5e
...
redesign
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3314aa06b4 | ||
|
|
12c909d6a4 | ||
|
|
c2594fd330 | ||
|
|
00b08e2f92 | ||
|
|
93da9fbcbe | ||
|
|
82dcb1da40 | ||
|
|
e82c1d9b0a | ||
|
|
c1c648de74 | ||
|
|
7523023be8 | ||
|
|
2103e9c1e8 | ||
|
|
7aadb5e215 | ||
|
|
76d7000735 | ||
|
|
51e59e85ef | ||
|
|
0475e3084d | ||
|
|
9889430109 | ||
|
|
cb89fdd911 | ||
|
|
281c25b05d | ||
|
|
1beb5c2eee | ||
|
|
461f2de8dc | ||
|
|
75a7c409cd | ||
|
|
751c302036 | ||
|
|
f0e1ad39b0 | ||
|
|
1dc84023d0 | ||
|
|
30465c7833 | ||
|
|
d260f00756 | ||
|
|
f0ac4b7e56 | ||
|
|
bd45beadd5 | ||
|
|
f060c9771f | ||
|
|
0ab71166aa | ||
|
|
d9c5d242ec | ||
|
|
cb1e7a2fed | ||
|
|
3586704cce | ||
|
|
7f3b58c5bc | ||
|
|
f29e8a4dac | ||
|
|
b69130c2b2 | ||
|
|
77aabef4a9 | ||
|
|
4b699de853 | ||
|
|
e3cb7282bc | ||
|
|
e537839e28 | ||
|
|
44d441811c | ||
|
|
da1efa5a74 | ||
| c7a306e8fa | |||
|
|
14cd5f04d5 | ||
|
|
7e533fae49 | ||
|
|
8393f154e5 | ||
|
|
c956869e8a | ||
|
|
4eabb3b766 | ||
|
|
146dedd9b4 | ||
|
|
6076194497 | ||
|
|
9cde1ab78b | ||
|
|
f5facaa811 | ||
|
|
78015298ec | ||
|
|
b4e1c50ea3 | ||
|
|
5da11d2e4d | ||
|
|
e8a50e55ca | ||
|
|
530e544542 | ||
|
|
300dce9835 | ||
|
|
a98def915e | ||
|
|
7aa7970f2e | ||
|
|
e517720b03 | ||
|
|
402913d877 | ||
|
|
2014abe660 | ||
|
|
80834d8652 | ||
| 45de2a576c | |||
|
|
7bc98c03e4 | ||
|
|
bf1caefde4 | ||
|
|
6983186caf | ||
| e1ad5f7205 | |||
|
|
7c93966878 | ||
| 14341b7a63 | |||
|
|
31d20f1bff | ||
|
|
6507afea5f | ||
|
|
387ce6e08e | ||
|
|
f822fc8a8e | ||
|
|
67c746f18b | ||
| 803481ca8e | |||
|
|
1e65cb47da | ||
|
|
ec96e21517 | ||
|
|
46f80df165 | ||
|
|
5c3d78245f | ||
|
|
f5045c3cf0 | ||
|
|
696c50f0fc | ||
|
|
b8bdbf0a8d | ||
|
|
1ea9596006 | ||
|
|
cdbe71eaec | ||
|
|
125a00819d | ||
|
|
b4c31374c0 | ||
|
|
c8b7f5ec2e | ||
| 46a8d5a77a | |||
|
|
0528334eb4 | ||
|
|
e033d716dd | ||
| 96c1d0260b | |||
|
|
e195d5d189 | ||
| 6e00a1b829 | |||
|
|
533e89c405 | ||
|
|
083244bc83 | ||
|
|
9def0fdc32 | ||
|
|
512756cb48 | ||
|
|
19d2f21fc3 | ||
|
|
5074e8f8f8 | ||
|
|
18a08b0e7a | ||
|
|
026e4ba3e4 | ||
|
|
58fd7fa5c6 | ||
| 755442fb70 | |||
| d31515bc79 | |||
|
|
1710c9349d | ||
|
|
cd025b1f92 | ||
|
|
2c681cf65c | ||
|
|
92099685e6 | ||
|
|
7e0c92368e | ||
|
|
817f5e02ca | ||
|
|
d3be0a269f | ||
|
|
8e318b0b52 | ||
| b729e90e38 | |||
|
|
acfcf773f7 | ||
|
|
1ea7f7a63f | ||
|
|
7289adb7a0 | ||
|
|
19d7aeefb0 | ||
| 92c0610dcb | |||
|
|
5fce08ab75 | ||
|
|
1c9dff0932 | ||
|
|
fd83b18642 | ||
| 6db05c1dc6 | |||
|
|
84536956c4 | ||
|
|
459da00820 | ||
|
|
f883d45452 | ||
|
|
11ff823fe2 | ||
|
|
cfd9365d07 | ||
|
|
f2f76dec56 | ||
|
|
2d42ef3ecd | ||
|
|
e19158558d | ||
|
|
bf4db389ff | ||
|
|
964a68cdfd | ||
|
|
21b39d4e5c | ||
| 68b6ab593a | |||
|
|
3658589d94 | ||
| 4e42ddfee4 | |||
|
|
a80ea7b892 | ||
| 6a6b8e0a1b | |||
|
|
f7c6caebc1 | ||
| ddb170cffc | |||
|
|
79e2ae9b87 | ||
| 66f2718714 | |||
|
|
4891e965bb | ||
| f2f5ddb8ce | |||
|
|
2fab6be58a | ||
|
|
549f4a1510 | ||
| 4d13ea5de6 | |||
|
|
02ee4af49d | ||
|
|
cc253c24db | ||
| 45d343b1c1 | |||
|
|
2eeed60387 | ||
|
|
63a1034e3d | ||
| 7b9bdb9a36 | |||
|
|
1561e1b861 | ||
|
|
3f7149d622 | ||
|
|
bc9cc265e1 | ||
| a48c65bfb8 | |||
|
|
fdf72bdb96 | ||
|
|
dbcc55d7fc | ||
|
|
f8a0370910 | ||
| 9a59fa5525 | |||
|
|
41374da6ea |
56
.gitea/workflows/code-analysis.yml
Normal file → Executable file
56
.gitea/workflows/code-analysis.yml
Normal file → Executable file
@@ -1,20 +1,44 @@
|
|||||||
name: Code Analysis and Production Deploy
|
name: Code Analysis and Production Deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
|
||||||
push:
|
push:
|
||||||
branches: [ main, dev ]
|
branches: [ main, dev ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||||
|
with:
|
||||||
|
clean: true
|
||||||
|
# Der Analyse-Workflow benötigt keine Historie. Ein flacher Checkout
|
||||||
|
# verhindert, dass der Runner das große Repository-Pack vollständig lädt.
|
||||||
|
fetch-depth: 1
|
||||||
|
# Der Web-Workflow braucht keine Android-Artefakte. Insbesondere liegt
|
||||||
|
# dort ein großer Heap-Dump im aktuellen Commit, der auch bei einem
|
||||||
|
# flachen Checkout übertragen würde und den Runner-Checkout abbricht.
|
||||||
|
sparse-checkout: |
|
||||||
|
/*
|
||||||
|
!/android-app/
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
|
||||||
|
- name: Ensure clean workspace
|
||||||
|
run: |
|
||||||
|
git reset --hard HEAD
|
||||||
|
git clean -fdx
|
||||||
|
|
||||||
|
- name: Debug dependency files
|
||||||
|
run: |
|
||||||
|
echo "commit: $(git rev-parse HEAD)"
|
||||||
|
echo "branch ref: ${GITHUB_REF:-unknown}"
|
||||||
|
echo "package.json checksum:" && sha256sum package.json
|
||||||
|
echo "package-lock.json checksum:" && sha256sum package-lock.json
|
||||||
|
echo "eslint entries in package.json:" && rg '"eslint"' package.json || true
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 22
|
||||||
cache: npm
|
cache: npm
|
||||||
@@ -32,6 +56,15 @@ jobs:
|
|||||||
node -v
|
node -v
|
||||||
npm -v
|
npm -v
|
||||||
|
|
||||||
|
- name: Check package.json version vs production
|
||||||
|
if: "github.ref == 'refs/heads/main'"
|
||||||
|
env:
|
||||||
|
PROD_HOST: ${{ vars.PROD_HOST }}
|
||||||
|
PROD_USER: ${{ vars.PROD_USER }}
|
||||||
|
PROD_PORT: ${{ vars.PROD_PORT }}
|
||||||
|
PROD_SSH_KEY: ${{ secrets.PROD_SSH_KEY }}
|
||||||
|
run: bash scripts/check-version-against-prod.sh
|
||||||
|
|
||||||
- name: gitleaks (Secrets Scanning)
|
- name: gitleaks (Secrets Scanning)
|
||||||
run: |
|
run: |
|
||||||
# Try to get the latest release asset URL
|
# Try to get the latest release asset URL
|
||||||
@@ -65,7 +98,11 @@ jobs:
|
|||||||
rm -f gitleaks.tar.gz
|
rm -f gitleaks.tar.gz
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: |
|
||||||
|
if ! npm ci; then
|
||||||
|
echo "WARNING: npm ci fehlgeschlagen (Lockfile-Drift?). Fallback auf npm install."
|
||||||
|
npm install
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
@@ -88,7 +125,7 @@ jobs:
|
|||||||
chmod +x osv-scanner
|
chmod +x osv-scanner
|
||||||
./osv-scanner --version
|
./osv-scanner --version
|
||||||
test -f ./package-lock.json
|
test -f ./package-lock.json
|
||||||
./osv-scanner --lockfile ./package-lock.json
|
./osv-scanner scan -L ./package-lock.json --config ./.osv-scanner.toml
|
||||||
|
|
||||||
deploy-production:
|
deploy-production:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -118,11 +155,12 @@ jobs:
|
|||||||
-o BatchMode=yes \
|
-o BatchMode=yes \
|
||||||
-p "${{ vars.PROD_PORT }}" \
|
-p "${{ vars.PROD_PORT }}" \
|
||||||
"${{ vars.PROD_USER }}@${{ vars.PROD_HOST }}" \
|
"${{ vars.PROD_USER }}@${{ vars.PROD_HOST }}" \
|
||||||
"bash -lc 'cd /var/www/harheimertc && git fetch origin main && git checkout -B main origin/main && git reset --hard origin/main && ./deploy-production.sh'"
|
"bash -lc 'cd /var/www/harheimertc && git reset --hard HEAD && git fetch origin main && git checkout -B main origin/main && git reset --hard origin/main && ./deploy-production.sh'"
|
||||||
|
|
||||||
deploy-test:
|
deploy-test:
|
||||||
|
needs: analyze
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare SSH
|
- name: Prepare SSH
|
||||||
run: |
|
run: |
|
||||||
@@ -148,4 +186,4 @@ jobs:
|
|||||||
-o BatchMode=yes \
|
-o BatchMode=yes \
|
||||||
-p "${{ vars.PROD_PORT }}" \
|
-p "${{ vars.PROD_PORT }}" \
|
||||||
"${{ vars.PROD_USER }}@${{ vars.PROD_HOST }}" \
|
"${{ vars.PROD_USER }}@${{ vars.PROD_HOST }}" \
|
||||||
"bash -lc 'cd /var/www/harheimertc.test && git fetch origin dev && git checkout -B dev origin/dev && git reset --hard origin/dev && ./deploy-test.sh'"
|
"bash -lc 'cd /var/www/harheimertc.test && git reset --hard HEAD && git fetch origin dev && git checkout -B dev origin/dev && git reset --hard origin/dev && ./deploy-test.sh'"
|
||||||
|
|||||||
19
.gitea/workflows/version-gate.yml
Normal file → Executable file
19
.gitea/workflows/version-gate.yml
Normal file → Executable file
@@ -1,20 +1,11 @@
|
|||||||
name: Require Package Version Change
|
name: Require Package Version Change (disabled)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
workflow_dispatch: {}
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
noop:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Disabled
|
||||||
uses: actions/checkout@v4
|
run: echo "version-gate workflow disabled — gating is handled in code-analysis.yml"
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
|
|
||||||
- name: Check package.json version changed
|
|
||||||
run: scripts/check-package-version-changed.sh origin/main
|
|
||||||
|
|||||||
28
.github/workflows/android-ci.yml
vendored
Executable file
28
.github/workflows/android-ci.yml
vendored
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Android CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper/
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
./gradlew :app:assembleDebug
|
||||||
9
.gitignore
vendored
Normal file → Executable file
9
.gitignore
vendored
Normal file → Executable file
@@ -88,6 +88,15 @@ out
|
|||||||
.nuxt
|
.nuxt
|
||||||
dist
|
dist
|
||||||
|
|
||||||
|
# Android / Gradle generated and machine-local files
|
||||||
|
/android-app/.gradle/
|
||||||
|
/android-app/.kotlin/
|
||||||
|
/android-app/**/build/
|
||||||
|
/android-app/local.properties
|
||||||
|
/android-app/gradle-local.properties
|
||||||
|
# JVM Heap-Dumps sind lokale Diagnoseartefakte und dürfen nie ins Repository.
|
||||||
|
*.hprof
|
||||||
|
|
||||||
# Build output (but keep production data!)
|
# Build output (but keep production data!)
|
||||||
.output
|
.output
|
||||||
!.output/.gitkeep
|
!.output/.gitkeep
|
||||||
|
|||||||
7
.gitleaks.toml
Executable file
7
.gitleaks.toml
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
[[allowlists]]
|
||||||
|
description = "generated/imported non-secret data"
|
||||||
|
paths = [
|
||||||
|
'''server/data/spielplan-import/harheimer_tc_spielplan\.(html|json)$''',
|
||||||
|
'''android-app/app/build/.*''',
|
||||||
|
'''android-app/\.idea/planningMode\.xml$''',
|
||||||
|
]
|
||||||
3
.gitleaksignore
Executable file
3
.gitleaksignore
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
google-services.json:gcp-api-key:18
|
||||||
|
google-services.json:gcp-api-key:37
|
||||||
|
google-services.json:gcp-api-key:56
|
||||||
4
.osv-scanner.toml
Normal file
4
.osv-scanner.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[[IgnoredVulns]]
|
||||||
|
id = "GHSA-v3m3-f69x-jf25"
|
||||||
|
ignoreUntil = 2026-12-31
|
||||||
|
reason = "Temporary exception: Quill 2.0.3 is required by the current RichTextEditor implementation, and OSV currently reports no fixed version. Track upstream fix and remove this ignore once a patched release is available."
|
||||||
0
.semgrepignore
Normal file → Executable file
0
.semgrepignore
Normal file → Executable file
36
ANDROID_ARCHITECTURE.md
Executable file
36
ANDROID_ARCHITECTURE.md
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
Android Architektur (Kotlin + Jetpack Compose) — Vorschlag
|
||||||
|
|
||||||
|
Packages/Module Struktur:
|
||||||
|
- app/ (Android-App module)
|
||||||
|
- src/main/java/de/harheimertc/
|
||||||
|
- ui/
|
||||||
|
- navigation/ (NavGraph, Routes)
|
||||||
|
- screens/ (HomeScreen, TermineScreen, SpielplanScreen, GalleryScreen, ContactScreen, AuthScreens, CMS Screens)
|
||||||
|
- components/ (TopBar, BottomNav, Cards, Modals)
|
||||||
|
- theme/ (Color.kt, Typography.kt, Theme.kt)
|
||||||
|
- data/
|
||||||
|
- api/ (Retrofit interfaces, DTOs)
|
||||||
|
- repository/ (Repositories für Domain-Modelle)
|
||||||
|
- local/ (Room DAOs, Entities)
|
||||||
|
- di/ (Hilt Modules)
|
||||||
|
- domain/ (UseCases, Business-Logic)
|
||||||
|
- util/ (Extensions, DateUtils, ImageUtils)
|
||||||
|
- auth/ (AuthManager, Passkeys helper)
|
||||||
|
|
||||||
|
Wichtige Dateien:
|
||||||
|
- `MainActivity.kt` — Hosts Compose NavHost
|
||||||
|
- `AppTheme.kt` — Compose Material3 Theme mit Token-Mapping
|
||||||
|
- `NetworkModule` (Hilt) — Retrofit + OkHttp + Auth Interceptor
|
||||||
|
- `Repository` Layer — entkoppelt UI von Netz
|
||||||
|
- `Room` Entities — für Caching von Termine/News/Galerie
|
||||||
|
|
||||||
|
Auth-Strategie:
|
||||||
|
- AuthRepository verwaltet Login/Logout, `checkAuth()` (mirroring `/api/auth/status`).
|
||||||
|
- Token/Cookie-Speicherung: `EncryptedSharedPreferences` für Tokens oder `CookieJar` mit OKHttp-Client.
|
||||||
|
- Passkeys: `Fido2Client` wrapper + Bridge zu Server-API (Formate prüfen).
|
||||||
|
|
||||||
|
Build / Module Tipps:
|
||||||
|
- Start mit Single Module `app/` und später evtl. `:data`, `:domain` Trennung.
|
||||||
|
- Verwende Gradle Kotlin DSL (build.gradle.kts).
|
||||||
|
|
||||||
|
Diese Architekturdatei wurde generiert; ich kann nun ein initiales Gradle-Kotlin-Scaffold erzeugen. Soll ich das direkt in `android-app/` ablegen? (Ja/Nein)
|
||||||
372
ANDROID_KOTLIN_PLAN.md
Executable file
372
ANDROID_KOTLIN_PLAN.md
Executable file
@@ -0,0 +1,372 @@
|
|||||||
|
Android App — Kotlin (Jetpack Compose) Plan und Abhakliste
|
||||||
|
|
||||||
|
Kurz: Ziel ist eine native Android-App mit Kotlin + Jetpack Compose, die die Web-UI 1:1 abbildet (Farben, Typografie, Funktionalitäten). Diese Datei enthält Architekturentscheidungen, empfohlene Bibliotheken und eine detaillierte Abhakliste (schrittweise).
|
||||||
|
|
||||||
|
1) Zusammenfassung der Entscheidungen
|
||||||
|
- Plattform: Native Android
|
||||||
|
- Sprache: Kotlin
|
||||||
|
- UI-Toolkit: Jetpack Compose (Compose Material3)
|
||||||
|
- Architektur: MVVM mit `ViewModel` + Kotlin Coroutines + Flow
|
||||||
|
- DI: Hilt
|
||||||
|
- HTTP-Client: Ktor Client oder Retrofit + OkHttp (empfohlen: Retrofit für breite Community-Docs)
|
||||||
|
- Bild-Loading: Coil
|
||||||
|
- Lokale DB / Caching: Room + DataStore (Preferences)
|
||||||
|
- Background/Sync: WorkManager
|
||||||
|
- Auth: kurzlebiges JWT-Access-Token plus rotierendes, widerrufbares Refresh-Token pro Android-Gerätesitzung; Speicherung in `EncryptedSharedPreferences`/Android Keystore; Unterstützung für Passkeys (Android Passkeys / WebAuthn Interop über FIDO2 APIs)
|
||||||
|
- Auth-Sicherheitsentscheidung: kein statischer App-Key bzw. kein in der APK hinterlegtes Client-Secret. Native Apps können ein gemeinsames Secret nicht vertraulich halten. Optional später: Refresh-Sitzung an ein pro Installation im Android Keystore erzeugtes Schlüsselpaar binden.
|
||||||
|
- Rich-Text: WebView-basierte Anzeige; Editoren: ggf. hybride Lösung (Server-side HTML editor + WebView) oder `RichEditor`-Libs
|
||||||
|
- Crash-Reporting & Monitoring: Firebase Crashlytics oder Sentry
|
||||||
|
|
||||||
|
2) Design & Farben
|
||||||
|
- Material Theme (Material3) mit Farben aus `tailwind.config.js` (Primary + Accent).
|
||||||
|
- Fonts: Inter & Montserrat via Google Fonts (Download/Bundle oder Play-Services-Download at runtime).
|
||||||
|
- Mapping: Tailwind-Token → `colors.xml` / Compose `Color` tokens.
|
||||||
|
|
||||||
|
3) Empfohlene Abhängigkeiten (erste Implementierung)
|
||||||
|
- androidx.compose.* (ui, material3, navigation)
|
||||||
|
- androidx.lifecycle:lifecycle-viewmodel-ktx
|
||||||
|
- com.google.dagger:hilt-android
|
||||||
|
- retrofit2 + converter-moshi / kotlinx-serialization
|
||||||
|
- io.coil-kt:coil-compose
|
||||||
|
- androidx.room:room-runtime + room-ktx
|
||||||
|
- androidx.work:work-runtime-ktx
|
||||||
|
- androidx.datastore:datastore-preferences
|
||||||
|
- com.google.android.gms:play-services-auth (für passkeys falls nötig)
|
||||||
|
- io.sentry:sentry-android (optional)
|
||||||
|
|
||||||
|
4) Detaillierte Abhakliste (Schritte)
|
||||||
|
[x] 1. Repo-Analyse: Liste der externen Endpunkte und Auth-Anforderungen exportieren
|
||||||
|
[x] 2. Projekt-Scaffold: Android Studio Projekt mit Kotlin + Compose anlegen
|
||||||
|
[x] 3. App-Architektur: Module / Packages anlegen (ui, data, domain, di, util)
|
||||||
|
[x] 4. CI-Build: Gradle-Config und GitHub Actions Skeleton
|
||||||
|
[x] 5. Theme: `Color.kt`, `Typography.kt`, `Theme.kt` erstellen und Tailwind-Farben mappen
|
||||||
|
[x] 6. Fonts: Inter + Montserrat einbinden (res/font oder GoogleFonts)
|
||||||
|
[x] 7. Navigation: Compose Navigation-Graph mit Routen für alle Web-Seiten anlegen
|
||||||
|
[x] 7a. Umgebungen: Android-Varianten fuer lokal, Test-Instanz und Produktion mit eigener API-Basis konfigurieren
|
||||||
|
[x] 7b. Adaptive Navigation: Tablet mit persistentem Header/Hauptmenue, Smartphone mit bestehender screenbezogener Navigation
|
||||||
|
[x] 7c. Branding: vorhandenes Web-Logo als optimierte Android-Ressource in der App-Navigation verwenden
|
||||||
|
[x] 7d. Tablet-Navigation: öffentliche Haupt- und Subnavigation der Web-UI mit Portierungszielen abbilden
|
||||||
|
[x] 7e. Navigation: dynamische Mannschaftslinks, Galerie-Sichtbarkeit und rollenabhängiges `Intern` wie in der Web-UI anbinden
|
||||||
|
[x] 8. Start-Screen: `HomeScreen` webnah mit Hero, Navigation, Termine, Spielen, News und Aktionen umsetzen
|
||||||
|
[x] 9. Komponenten: NavBar, Footer, Cards, ImageGrid und News-Dialog implementieren
|
||||||
|
[x] 10. Öffentliche Screens aus der Web-Navigation portieren
|
||||||
|
- [x] `/` Startseite
|
||||||
|
- [x] `/termine`: öffentliche Terminliste mit Lade-, Leer- und Fehlerzustand
|
||||||
|
- [x] `/mannschaften/spielplaene`: Saison-, Wettbewerbs- und Mannschaftsfilter mit Spielkarten
|
||||||
|
- [x] `/verein/galerie`: Anzeige-Screen vorhanden
|
||||||
|
- [x] `/kontakt`: Formular-Screen vorhanden
|
||||||
|
- [x] `/mitgliedschaft`: Antrag, Validierung, PDF-Erzeugung und PDF-Öffnen
|
||||||
|
- [x] `/verein/ueber-uns`: CMS-Inhalt aus der öffentlichen Konfiguration
|
||||||
|
- [x] `/vorstand`: öffentliche Vorstandsangaben aus der Konfiguration
|
||||||
|
- [x] `/verein/geschichte`: CMS-Inhalt aus der öffentlichen Konfiguration
|
||||||
|
- [x] `/verein/satzung`: CMS-Inhalt und PDF-Aufruf aus der öffentlichen Konfiguration
|
||||||
|
- [x] `/vereinsmeisterschaften`: Ergebnisliste mit Jahresfilter und Statistik
|
||||||
|
- [x] `/links`: strukturierte CMS-Links mit Fallback-Verweisen
|
||||||
|
- [x] `/mannschaften`: Übersicht aus saisonaler Mannschafts-CSV
|
||||||
|
- [x] `/mannschaften/[slug]`: dynamische Mannschaftsdetails mit aktuellem Spielplan und Umschaltung `Matches`/`Tabelle`
|
||||||
|
- [x] `/spielsysteme`: Spielsystemkarten mit Kategoriefilter aus CSV
|
||||||
|
- [x] `/training`: Trainingsort und gruppierte Trainingszeiten aus der Konfiguration
|
||||||
|
- [x] `/training/trainer`
|
||||||
|
- [x] `/training/anfaenger`
|
||||||
|
- [x] `/tt-regeln`: Regelübersicht mit DTTB- und PDF-Aufruf
|
||||||
|
[x] 10a. Weitere öffentliche bzw. bestehende Web-Routen prüfen und portieren
|
||||||
|
- [x] `/impressum`
|
||||||
|
- [x] Legacy-/Doppelrouten geklärt: `/galerie`, `/geschichte`, `/satzung`, `/ueber-uns`, `/spielplan`, `/verein/tt-regeln`, `/mannschaft/[slug]`, `/mannschaften/herren`, `/mannschaften/damen`, `/mannschaften/jugend`
|
||||||
|
[x] 10b. Newsletter-Screens portieren
|
||||||
|
- [x] `/newsletter/subscribe`
|
||||||
|
- [x] `/newsletter/unsubscribe`
|
||||||
|
- [x] `/newsletter/confirm`, `/newsletter/confirmed`, `/newsletter/unsubscribed`
|
||||||
|
[x] 10c. Auth-Screens portieren
|
||||||
|
- [x] `/login`: Passwort-Login und Logout in der laufenden Sitzung
|
||||||
|
- [x] `/registrieren`
|
||||||
|
- [x] `/passwort-vergessen`
|
||||||
|
[x] 10d. Mitgliederbereich portieren
|
||||||
|
- [x] `/mitgliederbereich`: Übersicht
|
||||||
|
- [x] `/mitgliederbereich/mitglieder`
|
||||||
|
- [x] `/mitgliederbereich/news`
|
||||||
|
- [x] `/mitgliederbereich/profil`
|
||||||
|
- [x] `/mitgliederbereich/api`
|
||||||
|
[x] 10e. CMS-Screens nach Rollenberechtigung portieren
|
||||||
|
- [x] `/cms`, `/cms/startseite`, `/cms/inhalte`, `/cms/vereinsmeisterschaften`
|
||||||
|
- [x] `/cms/sportbetrieb`, `/cms/mitgliederverwaltung`, `/cms/kontaktanfragen`
|
||||||
|
- [x] `/cms/newsletter`, `/cms/einstellungen`, `/cms/benutzer`
|
||||||
|
[x] 11. API-Client: Retrofit/Ktor-Client implementieren, Auth-Interceptor (Token Refresh)
|
||||||
|
- [x] Retrofit/OkHttp/Moshi und Hilt-Verdrahtung
|
||||||
|
- [x] Öffentliche Endpunkte für Startseite, Termine, Spielplan, Galerie und Kontakt
|
||||||
|
- [x] Mitgliedschafts-PDF-Endpunkte mit Cookie-Jar und `FileProvider`
|
||||||
|
- [x] Passwort-Login-Endpunkt und Token-Übergabe an den Interceptor
|
||||||
|
- [x] Verschlüsselte Token-Persistenz sowie Status/Logout per Bearer-Token
|
||||||
|
- [x] Bearer-Unterstützung aller später portierten geschützten Bereiche (`/api/profile`, `/api/birthdays`, `/api/members`, `/api/news`, CMS-/Newsletter-/Galerie-Endpunkte erledigt); Web bleibt bewusst Cookie-basiert
|
||||||
|
- [x] `POST /api/auth/refresh` anbinden und Access-Token bei Ablauf automatisch erneuern
|
||||||
|
- [x] OkHttp-`Authenticator` mit genau einem synchronisierten Refresh-Versuch pro fehlgeschlagenem Request ergänzen
|
||||||
|
[x] 12. Auth: Login/Register/Logout + sichere Token-Speicherung (EncryptedSharedPreferences)
|
||||||
|
- [x] Login/Logout und verschlüsselte Token-Speicherung
|
||||||
|
- [x] Registrierung und Passwort-Reset
|
||||||
|
- [x] Backend: Android-JWT-Access-Token auf ca. 15 Minuten reduzieren; bestehende Web-Cookie-Sitzungen bis zur Web-Refresh-Integration kompatibel weiterführen
|
||||||
|
- [x] Backend: langlebige, zufällige Refresh-Token pro Gerätesitzung mit serverseitig gespeichertem Token-Hash einführen
|
||||||
|
- [x] Backend: Refresh-Token bei jeder Erneuerung rotieren und Wiederverwendung eines verbrauchten Tokens als Sitzungsdiebstahl behandeln
|
||||||
|
- [x] Backend: Logout, Kontodeaktivierung und Passwortänderung widerrufen betroffene Refresh-Sitzungen
|
||||||
|
- [x] App: Access- und Refresh-Token verschlüsselt speichern, Sitzung beim App-Start durch Refresh wiederherstellen
|
||||||
|
- [ ] Optional nach MVP: pro Installation ein nicht exportierbares Keystore-Schlüsselpaar erzeugen und Refresh-Requests daran binden
|
||||||
|
[x] 13. Passkeys: Integration prüfen (FIDO2 / Passkeys) und Fallback auf Passwort
|
||||||
|
- [x] Passkey-Login über Android Credential Manager an bestehende WebAuthn-Endpunkte anbinden
|
||||||
|
- [x] Passkey-Erstellung und -Entfernung im nativen Profil ergänzen
|
||||||
|
- [x] Backend-Passkey-Endpunkte für Android-Bearer-Token und Android-Refresh-Sitzungen erweitern
|
||||||
|
- [ ] Für Produktion `/.well-known/assetlinks.json` mit Package `de.harheimertc` und Release-Zertifikat-Fingerprint veröffentlichen
|
||||||
|
[x] 14. Image-Upload: Multipart-Upload + Coil für Anzeige + Bildkompression (u. a. Sharp-Äquivalent evtl. serverseitig)
|
||||||
|
[x] 15. Rich-Text: Anzeige von HTML (Compose + WebView) und ggf. Editor via WebView-bridge
|
||||||
|
- [x] Gemeinsame native HTML/Rich-Text-Komponente für CMS- und News-Inhalte ergänzt
|
||||||
|
- [x] Öffentliche CMS-Seiten, Startseiten-News und interne News rendern HTML-Inhalte nativ
|
||||||
|
- [x] Nativer Rich-Text-Editor für CMS-Inhalte ergänzt; Toolbar schreibt Quill-kompatible HTML-Fragmente und speichert denselben HTML-String wie die Web-UI
|
||||||
|
[x] 16. Formulare: Validierung (clientseitig) und Fehlerdarstellung
|
||||||
|
- [x] Gemeinsame validierte Textfelder mit Inline-Fehlern ergänzt
|
||||||
|
- [x] Kontakt, Login, Passwort-Reset, Registrierung, Newsletter, Profil und Mitgliedschaft zeigen Feldfehler direkt am Eingabefeld
|
||||||
|
[x] 17. Offline & Caching: Room für persistente Daten, Response-Caching, Sync-Strategie
|
||||||
|
- [x] Zentraler OkHttp-Cache für öffentliche GET-Antworten ergänzt; Offline-Fallback nutzt gecachte Antworten bis 7 Tage
|
||||||
|
- [x] Zentraler Coil-ImageLoader mit gemeinsamem OkHttp-Client, Memory-Cache und 75-MB-Diskcache ergänzt
|
||||||
|
- [x] Verschlüsselte persistente Offline-Daten für geschützte Mitglieder-/CMS-Inhalte mit `EncryptedSharedPreferences` implementiert
|
||||||
|
[x] 18. Lokalisierung: `strings.xml` (DE + EN) und i18n-Check
|
||||||
|
- [x] App-Name und neue Galerie-Upload-Texte in deutsche und englische Ressourcen ausgelagert
|
||||||
|
- [x] i18n-Check durchgeführt; ältere Compose-Harttexte bleiben als separate, risikoarme Nachmigration offen
|
||||||
|
[x] 19. Accessibility: ContentDescription, Focus, Farben/Kontrast prüfen
|
||||||
|
[ ] 20. Tests: Unit-Tests für ViewModels + UI-Tests mit Compose Testing
|
||||||
|
- [x] Erste JVM-Unit-Tests für gemeinsame Formularvalidierung ergänzt
|
||||||
|
- [x] ViewModel-Tests für Auth-/CMS-/Galerie-Flows ergänzen
|
||||||
|
- [ ] Compose-UI-Tests für kritische Screens ergänzen
|
||||||
|
- [x] Hilt androidTest dependencies und `kspAndroidTest` konfiguriert
|
||||||
|
- [x] `HiltTestApplication` in `androidTest`-Manifest gesetzt
|
||||||
|
- [x] `LoginScreenTest` zu `@HiltAndroidTest` migriert und `HiltAndroidRule` hinzugefügt
|
||||||
|
- [x] `TestHiltModules.kt` für androidTest hinzugefügt (Test‑Bindings bereitgestellt)
|
||||||
|
[x] 21. Performance: Bildoptimierung, LazyLists, Paging (falls große Daten)
|
||||||
|
[ ] 22. Analytics: Firebase / Matomo Integration (je nach Datenschutz)
|
||||||
|
[x] 23. Crash-Reporting: Sentry / Crashlytics integrieren
|
||||||
|
[ ] 24. Build/Release: App signing, Release-Notes, Play-Store-Metadaten vorbereiten
|
||||||
|
- [x] Technische Release-Basis vorbereitet: `ANDROID_VERSION_CODE` und `ANDROID_VERSION_NAME` als Gradle-Properties eingeführt (`android-app/gradle.properties`) und im App-Gradle verdrahtet.
|
||||||
|
- [x] Production-Release-Flavor auf Produktiv-Backend parametrisierbar gemacht (`PRODUCTION_API_BASE_URL`, Default `https://harheimertc.de/`).
|
||||||
|
- [x] Release-Signing per sicheren Gradle-Properties vorbereitet (`RELEASE_STORE_FILE`, `RELEASE_STORE_PASSWORD`, `RELEASE_KEY_ALIAS`, `RELEASE_KEY_PASSWORD`) statt Hardcoding.
|
||||||
|
- [x] `:app:assembleProductionRelease` erfolgreich gebaut (Stand 2026-05-29).
|
||||||
|
- [x] Play-Store-Listing-Basis ergänzt: Datenschutzseite unter `/datenschutz` sowie Skripte für Icon/Feature-Graphic-Export und Screenshot-Anonymisierung inklusive Anleitung (`android-app/PLAYSTORE_ASSETS.md`).
|
||||||
|
- [x] Konto-Lösch-URL für Play Store ergänzt: öffentliche Seite unter `/konto-loeschen` inklusive Prozessbeschreibung.
|
||||||
|
- [ ] Offen: Finales Upload-Keystore + Credentials in CI/Build-Host hinterlegen, Play-Store-Release-Notes und Store-Metadaten pflegen.
|
||||||
|
[ ] 25. Dokumentation: `README-android.md` mit Setup, Architektur und Release-Anleitung
|
||||||
|
|
||||||
|
5) Kurzzeit-MVP (Priorität für erste Version)
|
||||||
|
- [x] A. Auth (Login/Logout)
|
||||||
|
- [x] Passwort-Login und Logout in der aktuellen App-Sitzung
|
||||||
|
- [x] Persistente Statuswiederherstellung/Logout für die Auth-Endpunkte
|
||||||
|
- [x] Dauerhaftes Eingeloggtbleiben durch rotierendes Refresh-Token pro Android-Gerätesitzung
|
||||||
|
- [x] B. Home, Termine, Spielplan, Galerie (anzeigen)
|
||||||
|
- [x] C. Kontaktformular (absenden)
|
||||||
|
- [x] D. Bildanzeige + Caching
|
||||||
|
- [x] E. Theme & Fonts
|
||||||
|
|
||||||
|
6) Nächste Aktionen (sofort)
|
||||||
|
- Web-Login bei Bedarf auf denselben Refresh-Flow migrieren; bis dahin bleiben Web-Cookie-Sitzungen bewusst kompatibel.
|
||||||
|
- Release-Zertifikat erzeugen und Digital Asset Links für native Android-Passkeys veröffentlichen.
|
||||||
|
- Die noch fehlenden öffentlichen Routen aus `10a` und die Newsletter-Screens aus `10b` nativ portieren.
|
||||||
|
- Saisonwahl für Mannschaftsübersicht/-details wie in der Web-UI ergänzen.
|
||||||
|
- Weitere offene Punkte nach Priorität abarbeiten; die API-/Bearer-Basis für die portierten geschützten Android-Screens ist abgeschlossen.
|
||||||
|
|
||||||
|
7) Umsetzungsprotokoll
|
||||||
|
- 2026-05-27: Webnahe Startseite mit öffentlichen Live-Daten umgesetzt; Hilt/Moshi-App-Verdrahtung ergänzt.
|
||||||
|
- 2026-05-27: `Termine` und `Spielplan` als native Screens umgesetzt; Spielplan unterstützt Saison, Wettbewerb, Mannschaft, Ergebnis und zweizeilige Gruppeninformation.
|
||||||
|
- 2026-05-27: `Mitgliedschaft` mit Antrag/PDF-Abruf sowie Passwort-`Login`/`Logout` umgesetzt; offene Auth-Härtung separat ausgewiesen.
|
||||||
|
- 2026-05-27: Tokens verschlüsselt persistiert; Session-Wiederherstellung sowie Logout per Bearer-Token in den Auth-Endpunkten ermöglicht.
|
||||||
|
- 2026-05-27: Registrierung und Passwort-Reset an die vorhandenen Auth-Endpunkte angebunden.
|
||||||
|
- 2026-05-27: Product-Flavors `local`, `instantTest` und `production` eingerichtet; lokale Basis-URL ist per Gradle-Parameter überschreibbar.
|
||||||
|
- 2026-05-27: Gradle-Heap/Worker für Flavor-Builds festgelegt, nachdem paralleles D8/KSP mit dem 512-MiB-Standardheap nicht ausreichend Speicher hatte.
|
||||||
|
- 2026-05-27: Lokales Testsetup gegen Emulator geprüft; bei IPv6-gebundenem Nuxt-Dev-Server wird die von Nuxt ausgegebene Network-URL per `LOCAL_API_BASE_URL` verwendet.
|
||||||
|
- 2026-05-27: Adaptive Navigation umgesetzt; Tablet-Layouts ab `600dp` zeigen Header und Hauptmenue dauerhaft, Smartphone-Layouts behalten die vorhandene Navigation.
|
||||||
|
- 2026-05-27: Platzhalterlogo in der Android-Navigation durch das vorhandene Harheimer-TC-Weblogo als skalierte lokale PNG-Ressource ersetzt.
|
||||||
|
- 2026-05-27: Web-Navigation und `pages/` vollständig inventarisiert; Tablet-Haupt-/Subnavigation für die öffentlichen Bereiche strukturell angeglichen und alle fehlenden Screens einzeln in die Portierungsliste aufgenommen.
|
||||||
|
- 2026-05-27: Tablet-Header auf Web-Verhalten angepasst (Bereichswechsel öffnet Startseite und Submenü) und die native ActionBar zugunsten des App-Headers entfernt.
|
||||||
|
- 2026-05-27: Navigation mit Live-Mannschaftslinks, öffentlicher Galerie-Sichtbarkeit und rollenabhängigem `Intern` ergänzt; Mannschaftsübersicht/-detail sowie Training, Trainer und Anfänger nativ portiert.
|
||||||
|
- 2026-05-27: Mannschaftsdetail um die Web-Untertabs `Matches` und `Tabelle` erweitert; Tabellenzeilen werden aus `/api/spielplan/table` geladen und die eigene Mannschaft hervorgehoben.
|
||||||
|
- 2026-05-27: Tabellenraster in den Mannschaftsdetails mit gemeinsamen Spaltenbreiten für Tablet und Smartphone ausgerichtet; die Zustandswiederherstellung dynamischer Mannschaftslinks korrigiert.
|
||||||
|
- 2026-05-27: Die verbleibenden öffentlichen Screens aus Punkt 10 portiert: Verein/CMS-Inhalte, Vorstand, Satzung/PDF, Links, Vereinsmeisterschaften mit Personenbild-Dialog, Spielsysteme und TT-Regeln.
|
||||||
|
- 2026-05-27: Architektur für dauerhaftes Android-Login festgelegt: kein eingebetteter App-Key, sondern kurzlebige Access-Tokens und rotierende, widerrufbare Refresh-Tokens pro Gerätesitzung; optionale spätere Gerätebindung per Keystore-Schlüsselpaar.
|
||||||
|
- 2026-05-27: Dauerhaftes Android-Login umgesetzt: Android-Logins erhalten 15-Minuten-Access-Tokens und rotierende Refresh-Tokens; Token-Hashes, Wiederverwendungswiderruf, Logout-/Reset-/Deaktivierungswiderruf sowie verschlüsselte App-Speicherung und automatischer OkHttp-Refresh sind implementiert.
|
||||||
|
- 2026-05-27: Native Profilseite des Mitgliederbereichs umgesetzt; `/api/profile` akzeptiert nun Bearer-Tokens, die App kann Profil-/Sichtbarkeitsdaten bearbeiten und Passwortänderungen auslösen.
|
||||||
|
- 2026-05-27: Login leitet in der Android-App nun direkt zur Mitgliederbereich-Übersicht weiter; `/mitgliederbereich` ist nativ mit Kacheln und Geburtstagsliste umgesetzt, `/api/birthdays` akzeptiert Bearer-Tokens.
|
||||||
|
- 2026-05-28: 10d und 10e abgeschlossen: Mitgliederliste, interne News, API-Doku sowie alle CMS-Routen sind nativ angebunden; relevante Mitglieder-/News-/CMS-Endpunkte akzeptieren Bearer-Tokens.
|
||||||
|
- 2026-05-28: 10a und 10b abgeschlossen: Impressum, Newsletter-An-/Abmeldung und Newsletter-Statusseiten sind nativ umgesetzt; Legacy-/Doppelrouten im Android-NavGraph auf native Screens abgebildet. Abgleich `pages/` gegen Android-Routen ergab keine verbleibenden Platzhalter-Webseiten. `/anlagen` wurde anschließend als ungenutzte und inhaltlich falsche Seite in Web und Android entfernt.
|
||||||
|
- 2026-05-28: Android-Passkeys umgesetzt: Login nutzt Android Credential Manager mit den bestehenden WebAuthn-Optionen, das native Profil kann Passkeys erstellen/entfernen, und die Backend-Passkey-Endpunkte akzeptieren Bearer-Tokens sowie erzeugen beim Android-Passkey-Login rotierende Refresh-Sitzungen. Für Produktion fehlt noch die Domain-App-Verknüpfung per Digital Asset Links mit Release-Zertifikat.
|
||||||
|
- 2026-05-28: Punkte 15 und 16 umgesetzt: Gemeinsame `RichText`-Komponente rendert HTML-Inhalte nativ in CMS-/News-Screens; Formularvalidierung wurde mit Inline-Feldfehlern für Kontakt, Auth, Newsletter, Profil und Mitgliedschaft ergänzt.
|
||||||
|
- 2026-05-28: Rich-Text-Editor nativ umgesetzt: `/cms/inhalte` kann Über-uns, Geschichte, TT-Regeln und Satzung mit Android-Toolbar bearbeiten; gespeichert wird weiterhin HTML in `seiten.*` über `PUT /api/config`, kompatibel zur Web-/Quill-Ausgabe.
|
||||||
|
- 2026-05-28: Punkt 14 umgesetzt: Android-Galerie nutzt den strukturierten `/api/galerie/list`-Response, lädt Bilder über Coil aus `/api/media/galerie/:id`, und Admin/Vorstand kann Bilder nativ auswählen, lokal auf JPEG/2000px/85% komprimieren und per Multipart an `/api/galerie/upload` senden.
|
||||||
|
- 2026-05-28: Punkt 18 umgesetzt: `strings.xml` für Deutsch und Englisch ergänzt, App-Label und neue Galerie-Upload-UI auf Ressourcen umgestellt; i18n-Check weist die bestehenden älteren Compose-Harttexte als spätere Nachmigration aus.
|
||||||
|
- 2026-05-28: Punkt 11 abgeschlossen: Android sendet Bearer-Tokens zentral per OkHttp-Interceptor; die portierten geschützten Backend-Endpunkte akzeptieren Cookie- oder Bearer-Authentifizierung. Die Web-UI bleibt absichtlich bei HttpOnly-Cookie-Sessions und muss nicht auf Bearer umgestellt werden.
|
||||||
|
- 2026-05-28: Caching-Teil von Punkt 17 und MVP-D umgesetzt: OkHttp cached öffentliche GET-Antworten und nutzt gecachte Antworten offline, Coil nutzt denselben authentifizierten Client plus Memory-/Diskcache. Geschützte Daten werden bewusst nicht unverschlüsselt im HTTP-Diskcache persistiert.
|
||||||
|
- 2026-05-28: Testbasis für Punkt 20 begonnen: JVM-Unit-Tests für E-Mail- und ISO-Datum-Validierung ergänzt; `:app:testLocalDebugUnitTest` läuft mit `compileSdk 35` grün.
|
||||||
|
- 2026-05-28: Punkte 17, 19, 21 und 23 weiter umgesetzt: geschützte Mitglieder-/CMS-Daten werden verschlüsselt in Keystore-gestützten Preferences gecacht und bei Ladefehlern genutzt; Galerie-Accessibility und Thumbnail-Decoding verbessert; Sentry-Android 8.42.0 über optionalen `SENTRY_DSN`-Gradle-Parameter integriert.
|
||||||
|
- 2026-05-29: Play-Store-Listing-Vorbereitung ergänzt: eigenständige Web-Datenschutzseite (`/datenschutz`) sowie Asset-/Anonymisierungs-Skripte und Anleitung in `android-app/PLAYSTORE_ASSETS.md` hinzugefügt.
|
||||||
|
|
||||||
|
8) Android-Testumgebungen
|
||||||
|
- Lokal im Emulator: `./gradlew :app:installLocalDebug` verwendet `http://10.0.2.2:3100/` und die App-ID `de.harheimertc.local`.
|
||||||
|
- Lokal, wenn `10.0.2.2` nicht erreichbar ist: `./gradlew :app:installLocalDebug -PLOCAL_API_BASE_URL=http://<NUXT-NETWORK-HOST>:3100/`; die passende URL steht in der `npm run dev`-Ausgabe (hier `http://torstens:3100/`).
|
||||||
|
- Test-Instanz: `./gradlew :app:installInstantTestDebug` verwendet `https://harheimertc.tsschulz.de/` und die App-ID `de.harheimertc.test`.
|
||||||
|
- Produktion: `./gradlew :app:installProductionDebug` verwendet `https://harheimertc.de/` und die App-ID `de.harheimertc`.
|
||||||
|
- Nur APKs erzeugen: `./gradlew :app:assembleLocalDebug :app:assembleInstantTestDebug :app:assembleProductionDebug`.
|
||||||
|
|
||||||
|
8a) Aktueller Teststatus & Troubleshooting (Stand: 2026-05-28)
|
||||||
|
|
||||||
|
- **Status:** `:app:assembleAndroidTest` läuft durch; `:app:connectedAndroidTest` ist derzeit instabil und schlägt bei Instrumentation-Läufen fehl.
|
||||||
|
- **Beobachtete Probleme:**
|
||||||
|
- Kompilationsfehler in `LoginScreenTest.kt` wegen `HiltTestActivity` (Unresolved reference). Workaround: `createAndroidComposeRule<ComponentActivity>()` + `setContent{}` verwenden, damit `assembleAndroidTest` durchläuft.
|
||||||
|
- Laufzeit-/Device-Probleme bei `connectedAndroidTest`: `com.android.ddmlib.SyncException: Remote object doesn't exist!` und `DELETE_FAILED_INTERNAL_ERROR` beim Deinstallieren von Test-APKs.
|
||||||
|
- `AndroidTestLogcatPlugin` wirft `FileNotFoundException` für erwartete Log-/Crash-Dateien, weil Gradle/UTP manche Device-Artefakte nicht zuverlässig pulled.
|
||||||
|
- Einzelne Instrumentation-Tests (z. B. `CmsActivateResendTest`, `GalleryScreenTest`) zeigen Assertion-Fehlschläge — diese sollten isoliert reproduziert werden.
|
||||||
|
- **Kurzfristige Empfehlungen (nicht ausführen):**
|
||||||
|
- Emulator neu starten und sicherstellen, dass keine veralteten Test-APKs installiert sind.
|
||||||
|
- Manuell: `adb uninstall` der Test-Pakete, dann frisches `adb install -r` des Test-APKs und gezielter Einzeltest via:
|
||||||
|
|
||||||
|
`adb shell am instrument -w -e class <test-class>#<testMethod> de.harheimertc.test/androidx.test.runner.AndroidJUnitRunner`
|
||||||
|
|
||||||
|
parallel `adb logcat -v time > /tmp/harheimertc_live_logcat.txt` laufen lassen, um vollständige Logs zu speichern.
|
||||||
|
- Falls UTP/ddmlib `SyncException` weiter auftritt: Gradle-Parallelität reduzieren, Test-Plugins (z. B. `AndroidTestLogcatPlugin`) temporär deaktivieren oder Tests in kleinere Gruppen splitten.
|
||||||
|
- **Offene Test‑To‑Dos:**
|
||||||
|
- Reproduzierbaren Einzeltest-Run mit vollständigem `logcat` erfassen (derzeit vom Nutzer pausiert).
|
||||||
|
- Flaky Tests isolieren und Hilt/KSP-Setup prüfen, damit `HiltTestActivity`-Importe nicht mehr fehlschlagen.
|
||||||
|
- Langfristig: Tests aufteilen, flaky tests markieren und CI-Job für androidTests gegen UTP-Transient-Fehler härten.
|
||||||
|
|
||||||
|
9) Dauerhaftes Android-Login: Architektur und Umsetzung
|
||||||
|
- Stand der Umsetzung: Android-Logins erhalten ein ca. 15 Minuten gültiges JWT und eine serverseitig prüfbare Refresh-Sitzung; Access-Tokens mit Sitzungs-ID werden bei widerrufener Gerätesitzung abgelehnt. Web-Logins verwenden weiterhin das bisherige Cookie-JWT, bis ein browserseitiger Refresh-Flow ergänzt ist.
|
||||||
|
- Ziel: Ein Benutzer bleibt auf einem bekannten Gerät angemeldet, ohne dass ein langfristig gültiges Bearer-JWT oder ein extrahierbares App-Secret verwendet wird.
|
||||||
|
- Token-Modell:
|
||||||
|
- Access-Token: JWT mit kurzer Laufzeit, Zielwert ca. 15 Minuten; wird für normale API-Requests als Bearer-Token verwendet.
|
||||||
|
- Refresh-Token: kryptografisch zufälliges, undurchsichtiges Token mit längerer Laufzeit, Zielwert z. B. 90 Tage mit Erneuerung bei aktiver Nutzung.
|
||||||
|
- Server speichert ausschließlich den Hash des Refresh-Tokens zusammen mit `sessionId`, `userId`, `createdAt`, `lastUsedAt`, `expiresAt`, `revokedAt` und optional Gerätebezeichnung.
|
||||||
|
- Backend-Arbeitspaket:
|
||||||
|
- Login-Antwort um `accessToken`, `refreshToken`, `sessionId` und Ablaufmetadaten erweitern; bestehendes `token` nur befristet kompatibel halten.
|
||||||
|
- `POST /api/auth/refresh` implementieren: gültiges Refresh-Token konsumieren, rotieren und ein neues Token-Paar zurückgeben.
|
||||||
|
- Token-Wiederverwendung erkennen: Wird ein rotiertes Refresh-Token erneut präsentiert, die betroffene Token-Familie bzw. Gerätesitzung widerrufen.
|
||||||
|
- `POST /api/auth/logout` auf Widerruf der Gerätesitzung erweitern; optional Endpunkte zum Anzeigen und Widerrufen eigener Geräte-Sitzungen vorsehen.
|
||||||
|
- Kontodeaktivierung und Passwortänderung müssen sämtliche Refresh-Sitzungen des Benutzers widerrufen.
|
||||||
|
- Rate-Limits und Audit-Events für Login, Refresh-Erfolg/-Fehlschlag, Wiederverwendung und Widerruf ergänzen.
|
||||||
|
- Android-Arbeitspaket:
|
||||||
|
- `AuthRepository` auf Access-Token, Refresh-Token und Session-ID erweitern; Speicherung weiter über Keystore-geschützte Preferences.
|
||||||
|
- `ApiService`/DTOs um Refresh-Request und Token-Paar-Antwort ergänzen.
|
||||||
|
- Einen OkHttp-`Authenticator` einsetzen, der auf `401` einmalig ein Access-Token erneuert, parallele Refreshes synchronisiert und den ursprünglichen Request wiederholt.
|
||||||
|
- Beim App-Start zunächst Access-Token prüfen und bei Ablauf transparent mit dem Refresh-Token erneuern; nur bei fehlgeschlagenem Refresh zum Login zurückkehren.
|
||||||
|
- Beim Logout lokale Tokens auch bei Netzwerkfehler entfernen; serverseitiger Widerruf erfolgt best effort bzw. bei nächster Konnektivität.
|
||||||
|
- Sicherheitsregeln:
|
||||||
|
- Kein gemeinsamer App-Key und kein statisches Client-Secret in Sourcecode, `BuildConfig` oder APK.
|
||||||
|
- Refresh-Tokens nie im Klartext serverseitig speichern oder protokollieren.
|
||||||
|
- Nur HTTPS für Test-/Produktionsumgebungen; Token-Werte nicht in Logging-Interceptors ausgeben.
|
||||||
|
- Optional nach MVP: App erzeugt pro Installation ein Keystore-Schlüsselpaar; Backend bindet Refresh-Sitzungen an den öffentlichen Schlüssel und prüft signierte Refresh-Anfragen.
|
||||||
|
|
||||||
|
---
|
||||||
|
Datei: [ANDROID_KOTLIN_PLAN.md](ANDROID_KOTLIN_PLAN.md)
|
||||||
|
|
||||||
|
**CMS-Verbesserungsplan (Analyse → Umsetzung)**
|
||||||
|
|
||||||
|
Ziel: Alle `cms/*`-Screens von rudimentärem Status zu vollständigen, getesteten Admin-Tools weiterentwickeln. Fokus: Datenintegrität, Berechtigungen, bessere UI/UX, Offline-Verhalten und Tests.
|
||||||
|
|
||||||
|
Kurzüberblick (3 Phasen):
|
||||||
|
- Phase A — Analyse (1-2 Tage): Inventar aller CMS-Endpunkte, fehlende CRUD-Workflows identifizieren, Prioritäten setzen (News, Benutzer, Kontaktanfragen, Newsletter, Config). Ergebnis: Aufgabenliste mit Aufwandsschätzung.
|
||||||
|
- Phase B — Implementierung MVP (1-2 Wochen): Kernfunktionen pro Bereich implementieren (News CRUD mit RichText-Vorschau, Benutzerliste + Rollen-Edit, Kontaktanfragen Detail & Antwort-Workflow, Newsletter-Gruppen-Management, Config-Editor inklusive Satzung-PDF-Feld). Unit- / Integrationstests für ViewModels.
|
||||||
|
- Phase C — Harden, UX & Tests (1 Woche): Validierung, Fehlermeldungen, Offline-Caching (verschlüsselt für geschützte Daten), Compose-UI-Tests, Accessibility-, Performance-Feinschliff.
|
||||||
|
|
||||||
|
Detaillierte Aufgaben (priorisiert):
|
||||||
|
- A1: Audit `CmsViewModel`-State vs. Backend-Responses — fehlen Felder/Fehlerfälle? (bereits teilweise umgesetzt)
|
||||||
|
- A2: Prüfen, ob API-Fehler (4xx/5xx) sauber an `FormMessages`/UI gemeldet werden — Standardisiere Fehlermeldungen.
|
||||||
|
- A3: Prüfen, ob `NativeRichTextEditor` HTML speichert, das Web-Editor-kompatibel bleibt (Quill/HTML). Schreibe Roundtrip-Tests.
|
||||||
|
- B1: News-Management
|
||||||
|
- B1.1: News-CRUD: Create/Update/Delete mit Vorschau (RichText-Preview) und Validierung (Titel Pflicht, Inhalt Mindestlänge)
|
||||||
|
- B1.2: Bulk-Aktionen: Sichtbar/Unsichtbar/ExpiresAt setzen
|
||||||
|
- B1.3: Unit-Tests für `NewsViewModel` + `CmsViewModel`-Integrationspfad
|
||||||
|
- B2: Benutzer-Management
|
||||||
|
- B2.1: Rollen-Edit (admin/vorstand/trainer/newsletter) in `CmsBenutzerScreen` (Inline-Action oder Detail-Dialog)
|
||||||
|
- B2.2: Aktiv/Inaktiv Toggle + Resend-Invite (falls API unterstützt)
|
||||||
|
- B2.3: Tests: `CmsViewModel.users()` Verhalten bei Pagination/Leeren Listen
|
||||||
|
- B3: Kontaktanfragen
|
||||||
|
- B3.1: Detailansicht mit Antwort-Option (falls Backend Mail-Sende-Endpunkt vorhanden)
|
||||||
|
- B3.2: Status-Filter (offen/beantwortet) und Bulk-Archiv
|
||||||
|
- B4: Newsletter
|
||||||
|
- B4.1: Entwurf -> Senden Flow mit Preview (falls Backend zulässt)
|
||||||
|
- B4.2: Gruppenverwaltung (CRUD) + Subscribe/Unsubscribe-Preview
|
||||||
|
- B5: Config / Seiten (Inhalte)
|
||||||
|
- B5.1: Sichern/Zurücksetzen von Seiteninhalten mit Undo-Hinweis
|
||||||
|
- B5.2: Satzung: PDF-Upload-Feld und native PDF-Viewer-Integration (falls serverseitig gespeichert)
|
||||||
|
- B5.6: Android-Startseite weiter ausbauen: Nutzer sollen Elemente und Reihenfolge der Startseite selbst zusammenstellen koennen; Detailkonzept und Feinschliff folgen spaeter
|
||||||
|
- B6: Diagnostics / Passwort-Reset-Diagnose
|
||||||
|
- B6.1: Detail-View mit exportierbaren Logs (bei Bedarf)
|
||||||
|
- C1: Offline-/Caching-Strategie
|
||||||
|
- C1.1: Verschlüsseltes lokales Caching für CMS-Daten (EncryptedSharedPreferences/Room)
|
||||||
|
- C1.2: Sync-Strategie: lokale Änderungen buffernd senden, Konflikt-UI
|
||||||
|
- C2: Tests & CI
|
||||||
|
- C2.1: ViewModel-Unit-Tests für alle CMS-Flows
|
||||||
|
- C2.2: Compose-UI-Tests für kritische Pfade (News erstellen, Benutzerrolle ändern, Config speichern)
|
||||||
|
- C2.3: androidTest Hilt-Stubs erweitern (falls nötig)
|
||||||
|
|
||||||
|
Minor UX-Verbesserungen (parallel möglich):
|
||||||
|
- konsistente Buttons/Labels (`Speichern` vs `Inhalt speichern`), Ladezustand-UI, einzeilige Success-/Error-Banner, Inline-Validierungen.
|
||||||
|
|
||||||
|
Deliverables & Milestones:
|
||||||
|
- M1 (nach Analyse): Priorisierte Aufgabenliste + Schätzung (mehrere PRs)
|
||||||
|
- M2 (nach MVP-Implementierung): News + Benutzer + ContactRequests + Config Editor + Tests (smoke)
|
||||||
|
- M3 (Final): Offline, UI-Tests, Accessibility, Performance
|
||||||
|
|
||||||
|
Zeitplanung (empfohlen):
|
||||||
|
- Analyse: 2 Arbeitstage
|
||||||
|
- MVP-Implementierung: 7–10 Arbeitstage
|
||||||
|
- Hardening + Tests: 3–5 Arbeitstage
|
||||||
|
|
||||||
|
Wenn du willst, trage ich die einzelnen Subtickets in unserem lokalen Issue-Tracker (oder als separate TODOs) ein und beginne mit A1/A2.
|
||||||
|
|
||||||
|
**TODO (zum Abhaken) — CMS-Implementierung**
|
||||||
|
|
||||||
|
- [x] A1: Audit `CmsViewModel` vs Backend-Responses (Fehleraggregation implementiert)
|
||||||
|
- [x] A2: Standardisiere API-Fehlerdarstellung in UI (`FormMessages` / globale Errors)
|
||||||
|
- [x] A3: Roundtrip-Tests `NativeRichTextEditor` ↔ Backend-HTML (Kompatibilität / Quill)
|
||||||
|
- [x] B1: News-Management
|
||||||
|
- [x] B1.1: News-CRUD (Create/Update/Delete) mit RichText-Vorschau
|
||||||
|
- [x] B1.2: Bulk-Aktionen (sichtbar/unsichtbar, expiresAt)
|
||||||
|
- [x] B1.3: Unit-Tests für `NewsViewModel`
|
||||||
|
|
||||||
|
- [x] B2: Benutzer-Management
|
||||||
|
- [x] B2.1: Rollen-Edit (Inline oder Detail-Dialog)
|
||||||
|
- [x] B2.2: Aktiv/Inaktiv Toggle, Resend-Invite
|
||||||
|
- [x] B2.3: Tests für Pagination/Leere Listen
|
||||||
|
|
||||||
|
- [x] B3: Kontaktanfragen
|
||||||
|
- [x] B3.1: Detailansicht + Antwort-Option
|
||||||
|
- [x] B3.2: Status-Filter + Archiv
|
||||||
|
|
||||||
|
- [ ] B4: Newsletter
|
||||||
|
- [x] B4.1: Entwurf → Senden Flow mit Preview
|
||||||
|
- [x] B4.2: Gruppenverwaltung (CRUD)
|
||||||
|
|
||||||
|
- [x] B5: Config / Seiten
|
||||||
|
- Web‑Status: Die Web‑UI bietet bereits umfassende CMS‑UIs für `cms/startseite`, `cms/vereinsmeisterschaften`, `cms/sportbetrieb` und `cms/einstellungen` (Drag&Drop, CSV‑Import/Export, Tabbed‑UIs, ImageUpload, native‑like Modals). `cms/startseite` speichert `homepage.sections` via `PUT /api/config`, `vereinsmeisterschaften` arbeitet mit CSV‑Export/Import, `sportbetrieb` kapselt Termine/Mannschaften/Spielpläne in Tabs, `einstellungen` ist ein umfangreicher Config‑Editor.
|
||||||
|
- Android‑Status: Implementiert — die Android‑App enthält native CMS‑Screens (`CmsStartseiteScreen`, `CmsVereinsmeisterschaftenScreen`, `CmsSportbetriebScreen`, `CmsEinstellungenScreen`) mit Save/Load‑Flows via `CmsViewModel`.
|
||||||
|
- Umsetzung (B5.x):
|
||||||
|
- [x] B5.1: `cms/startseite` (Startseiten‑Layout) — Reorderable/Visibility + Save → `PUT /api/config` (via `CmsViewModel`).
|
||||||
|
- [x] B5.2: `cms/vereinsmeisterschaften` — CSV‑Parser/CSV‑Save integration and modal CRUD (native UI present).
|
||||||
|
- [x] B5.3: `cms/sportbetrieb` — Tabbed UI reusing `Termine`, `Mannschaften`, `Spielplan` components.
|
||||||
|
- [x] B5.4: `cms/einstellungen` — Tabbed config editor with Vereinsdaten/Training/Trainer/Mitgliedschaft and save.
|
||||||
|
- [x] B5.5: Roundtrip & Tests — basic ViewModel unit tests and roundtrip checks exist; Compose UI smoke tests remain for hardening.
|
||||||
|
- [x] B5.6: Startseite weiter ausgebaut — zusaetzliche Elemente (`training`, `links`, `vereinsmeisterschaften`) sind konfigurierbar; Android kann Reihenfolge/Sichtbarkeit lokal speichern und Web nutzt Marker (`cookie`, `eingeloggt`) mit marker-spezifischer Persistenz: `eingeloggt` wird als individuelles User-Setting serverseitig gespeichert, `cookie` wird ausschliesslich im Browser-Cookie gehalten. Neu umgesetzt: konfigurierbare Startseiten-Widgets vom Typ `spielplan_team` (Saison + Mannschaft beim Hinzufuegen waehlbar, spaeter jederzeit aenderbar, mehrfach pro Startseite moeglich, persistiert ueber `key` + `config`).
|
||||||
|
|
||||||
|
- [x] B6: Diagnostics / Passwort-Reset-Diagnose (Export/Detail)
|
||||||
|
- Web‑Status: `cms/passwort-reset-diagnose` zeigt vollständige Diagnose‑UI mit Suche, Maskierung, Filter (nur Auffälligkeiten) und listbaren Reset‑Versuchen; Backend: `/api/cms/password-reset-diagnostics` liefert `matchingUsers`, `attempts`, `retentionHours`.
|
||||||
|
- Android‑Status: umgesetzt — native Diagnose‑UI mit Suche (`email`/Name), Filter `Nur Auffälligkeiten`, `matchingUsers`‑Liste mit Schnellfilter, detaillierter Schrittansicht je Versuch (Zeit/Schritt/Status/Grund), Refresh und Share‑Export der maskierten Logs.
|
||||||
|
- Konkrete Android‑ToDos (B6.x):
|
||||||
|
- [x] B6.1: Implementieren Suche + Filter UI, Rendering der `attempts` mit Zeitstempeln, Status‑Badges und Details.
|
||||||
|
- [x] B6.2: Logs exportieren / share (falls API Export unterstützt) und Datenschutz: E‑Mail Maskierung beibehalten.
|
||||||
|
|
||||||
|
- [x] C1: Offline-/Caching-Strategie (verschlüsselt für geschützte CMS-Daten)
|
||||||
|
- Umgesetzt: EncryptedSharedPreferences-basierter Offline-Cache mit Zeitstempel/TTL pro Cache-Key (CMS standard 24h, Reset-Diagnose 6h).
|
||||||
|
- Umgesetzt: Fallback auf verschlüsselte Cache-Daten bei Ladefehlern nur innerhalb der TTL, um veraltete geschützte CMS-Daten zu begrenzen.
|
||||||
|
- Umgesetzt: Gezielte Cache-Invalidierung bei schreibenden CMS-Operationen (Konfiguration, Benutzerverwaltung, Kontaktanfragen, Newsletter, interne News), damit Offline-Daten nach Änderungen konsistent bleiben.
|
||||||
|
- Umgesetzt: Passwort-Reset-Diagnose-Cache wird nur für den Standardfilter (ohne Suchbegriff) verwendet, um falsche Treffer bei gefilterten Diagnosen zu vermeiden.
|
||||||
|
- [x] C2: Tests & CI
|
||||||
|
- [x] C2.1: ViewModel-Unit-Tests für CMS-Flows (`CmsViewModel.load()` / `saveConfig()`)
|
||||||
|
- Status: `:app:testLocalDebugUnitTest` läuft grün; `CmsViewModelTest` wurde auf aktuelle Repository-Signaturen und vollständige `load()`-Abhängigkeiten (inkl. `vereinsmeisterschaften`) aktualisiert.
|
||||||
|
- [x] C2.2: Compose-UI-Tests für kritische Flows
|
||||||
|
- Status: neuer Instrumentation-Test für `CmsPasswordResetDiagnosticsScreen` ergänzt (`diagnosticsScreen_showsFilterAndAttemptDetails`) und gezielt per `connectedLocalDebugAndroidTest` erfolgreich ausgeführt.
|
||||||
|
- [x] C2.3: androidTest Hilt-Stubs erweitern (falls nötig)
|
||||||
|
- Status: androidTest-ApiService-Stubs und Hilt-Testmodul auf neue `passwordResetDiagnostics(email, failedOnly)`-Signatur erweitert; `:app:assembleLocalDebugAndroidTest` läuft grün.
|
||||||
|
|
||||||
|
Markiere die Items, wenn erledigt — ich kann die einzelnen Punkte jetzt in Branches/PRs umsetzen.
|
||||||
|
|
||||||
84
ANDROID_PORT_TODO.md
Executable file
84
ANDROID_PORT_TODO.md
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
ANDROID App - 1:1 Portierung der Web-UI (TODO)
|
||||||
|
|
||||||
|
Ziel: Die Web-UI des Projekts 1:1 in eine native (oder cross-platform) Android-App überführen, inklusive Farben, Designsystem und aller Funktionalitäten.
|
||||||
|
|
||||||
|
1) Analyse Codebasis & Assets
|
||||||
|
- Analysiere `package.json`, `nuxt.config.js`, `tailwind.config.js` und zentrale Server-/API-Endpunkte.
|
||||||
|
- Liste alle verwendeten Farben, CSS-Variablen, Tailwind-Konfigurationen.
|
||||||
|
- Sammle alle statischen Assets: Bilder, Icons, SVGs, Fonts, PDF-Dokumente.
|
||||||
|
- Identifiziere dynamische Komponenten: Formulare, Rich-Text-Editor, Uploads, Kalender, Navigation.
|
||||||
|
|
||||||
|
2) Projektziele und Scope
|
||||||
|
- Entscheide: Native Android (Kotlin/Jetpack Compose) oder Cross-Platform (React Native, Flutter, Kotlin Multiplatform).
|
||||||
|
- Priorisiere Features für MVP vs. Post-Launch.
|
||||||
|
|
||||||
|
3) Designsystem und Farben extrahieren
|
||||||
|
- Extrahiere Farbpalette, Typografie, Abstände, Buttons, Karten, Form-Controls.
|
||||||
|
- Erstelle eine Design-Token-Liste (Hex/RGBA, Namen, Einsatzbereiche).
|
||||||
|
|
||||||
|
4) Technologie-Stack wählen
|
||||||
|
- Empfohlene Optionen: Kotlin + Jetpack Compose (native), Flutter (UI-First), React Native (Wiederverwendung von JS/nuxt-Logik).
|
||||||
|
- Bibliotheken: Navigation, HTTP-Client, Bild-Handling, Auth (WebAuthn falls nötig), Local DB.
|
||||||
|
|
||||||
|
5) Android-Projekt aufsetzen
|
||||||
|
- Erstelle Projekt-Scaffold, CI-Build, Signing-Config.
|
||||||
|
|
||||||
|
6) Theme & Farben implementieren
|
||||||
|
- Implementiere App-Theme mit Farben/Typografie-Token.
|
||||||
|
|
||||||
|
7) Navigation-Struktur implementieren
|
||||||
|
- Bottom/Navigations-Drawer/Stack wie Web-Navigation abbilden.
|
||||||
|
|
||||||
|
8) Screens für Seiten anlegen
|
||||||
|
- Erstelle Screens für: Startseite, Termine, Spielplan, Galerie, Kontakt, News, Mitgliedschaft, Login, CMS-Bereiche.
|
||||||
|
|
||||||
|
9) UI-Komponenten portieren
|
||||||
|
- Navbar, Footer, Cards, Image-Grid, Modal/Dialog, Rich-Text-Viewer/Editor, Date-Picker, Tabellen.
|
||||||
|
|
||||||
|
10) Formulare & Validierung implementieren
|
||||||
|
- Registrieren, Login, Passwort vergessen, Mitgliedschaftsformulare mit Client- und Server-Validierung.
|
||||||
|
|
||||||
|
11) Authentifizierungs-Flow implementieren
|
||||||
|
- JWT / Session, OAuth oder WebAuthn falls benötigt; Token-Handling sicher speichern.
|
||||||
|
|
||||||
|
12) API-Client implementieren
|
||||||
|
- Einheitlicher HTTP-Client, Error-Handling, Retry-Strategien, Pagination.
|
||||||
|
|
||||||
|
13) Bilderupload & Storage einrichten
|
||||||
|
- Multi-part Upload, Progress, Bildkompression, lokale Cache-Strategie.
|
||||||
|
|
||||||
|
14) Offline-Support und Caching
|
||||||
|
- Caching von API-Responses, Bild-Caching, Sync-Strategie für Formulare.
|
||||||
|
|
||||||
|
15) Lokalisierung und Texte prüfen
|
||||||
|
- Alle statischen Texte extrahieren, deutsche Strings prüfen und in Resource-Files ablegen.
|
||||||
|
|
||||||
|
16) Accessibility-Prüfung und Anpassungen
|
||||||
|
- Farbkontrast, Touch-Targets, Screenreader-Labels.
|
||||||
|
|
||||||
|
17) Unit- und UI-Tests schreiben
|
||||||
|
- Komponenten- und Integrations-Tests, E2E (falls möglich).
|
||||||
|
|
||||||
|
18) Performance-Optimierung durchführen
|
||||||
|
- Bilder, Netzwerk, Render-Perf.
|
||||||
|
|
||||||
|
19) CI/CD für Builds einrichten
|
||||||
|
- GitHub Actions / GitLab CI: Build, Test, Lint, Release.
|
||||||
|
|
||||||
|
20) Play Store Release vorbereiten
|
||||||
|
- App-Icons, Screenshots, Privacy-Policy, Datensparsamkeit.
|
||||||
|
|
||||||
|
21) Monitoring & Crash-Reporting einrichten
|
||||||
|
- Sentry / Firebase Crashlytics, Analytics.
|
||||||
|
|
||||||
|
22) Dokumentation: Setup & Architektur
|
||||||
|
- README, Architekturdiagramm, API-Spec, Onboarding-Guide.
|
||||||
|
|
||||||
|
23) Design Review und Abnahme
|
||||||
|
- UX/Design-Review mit Stakeholdern.
|
||||||
|
|
||||||
|
24) Launch und Feedbackrunde durchführen
|
||||||
|
- Release-Notes, Feedback-Formular, Bug-Fixing-Plan.
|
||||||
|
|
||||||
|
|
||||||
|
Datei erstellt: Bitte bestätige, wenn ich mit der in-depth Analyse der Codebasis und Assets beginnen soll (Suche nach Farben, verwendeten Komponenten, Images, Fonts, relevanten Scripts).
|
||||||
58
ANDROID_REPO_ENDPOINTS.md
Executable file
58
ANDROID_REPO_ENDPOINTS.md
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
Repo API Endpoints — Übersicht
|
||||||
|
|
||||||
|
Hinweis: Viele Frontend-Requests verwenden relative Pfade (`/api/...`) und Nuxt's `NUXT_PUBLIC_BASE_URL`.
|
||||||
|
|
||||||
|
Öffentliche/Frontend-Endpunkte (häufig genutzt):
|
||||||
|
- GET /api/config
|
||||||
|
- GET /api/news-public
|
||||||
|
- GET /api/news
|
||||||
|
- GET /api/termine
|
||||||
|
- GET /api/spielplaene
|
||||||
|
- GET /api/spielplan
|
||||||
|
- GET /api/mannschaften
|
||||||
|
- GET /api/galerie
|
||||||
|
- GET /api/media/galerie/{id}
|
||||||
|
- GET /api/personen/{filename}?width=...&height=...
|
||||||
|
- POST /api/contact
|
||||||
|
- POST /api/news (CMS)
|
||||||
|
|
||||||
|
Galerie / Media:
|
||||||
|
- POST /api/galerie/upload
|
||||||
|
- GET /api/galerie/list
|
||||||
|
- GET /api/galerie/[id]
|
||||||
|
- DELETE /api/galerie/[id]
|
||||||
|
|
||||||
|
Authentifizierung:
|
||||||
|
- POST /api/auth/login
|
||||||
|
- POST /api/auth/logout
|
||||||
|
- POST /api/auth/register
|
||||||
|
- POST /api/auth/reset-password
|
||||||
|
- GET /api/auth/status
|
||||||
|
- POST /api/auth/passkeys/authentication-options (Passkeys start: server returns WebAuthn options)
|
||||||
|
- POST /api/auth/passkeys/login (Passkeys finish: credential verification)
|
||||||
|
|
||||||
|
CMS / geschützte Endpunkte (erfordern Auth):
|
||||||
|
- GET /api/cms/* (z.B. /api/cms/users/list, /api/cms/contact-requests)
|
||||||
|
- POST /api/cms/save-csv
|
||||||
|
- POST /api/cms/upload-spielplan-pdf
|
||||||
|
- POST /api/cms/satzung-upload
|
||||||
|
- POST /api/members, DELETE /api/members, POST /api/members/bulk
|
||||||
|
- POST /api/membership/update-status
|
||||||
|
- POST /api/termine-manage, DELETE /api/termine-manage, GET /api/termine-manage
|
||||||
|
|
||||||
|
Weitere (Datei-Uploads, Personen):
|
||||||
|
- POST /api/personen/upload
|
||||||
|
- GET /api/app/version
|
||||||
|
- Various CMS-specific routes under /api/cms
|
||||||
|
|
||||||
|
Auth-Anforderungen & Hinweise:
|
||||||
|
- Frontend nutzt `$fetch('/api/...')` (Nuxt) — serverseitig vermutlich Session-Cookie oder JWT.
|
||||||
|
- `stores/auth.js` verwendet `/api/auth/status` to check login state and `passkeyLogin()` which calls `/api/auth/passkeys/*`.
|
||||||
|
- Passkeys-Flow verwendet `@simplewebauthn/browser` on web; Android port should support FIDO2 / Passkeys (Google Passkeys API) or provide password fallback.
|
||||||
|
- CMS- und Manage-Endpunkte require authentication and role checks (admin/vorstand etc.).
|
||||||
|
|
||||||
|
Empfehlung für Android-Client:
|
||||||
|
- Nutze Retrofit/OkHttp mit anpassbarem Auth-Interceptor (Cookie-jar or token storage). Prüfe, ob Server bevorzugt Cookies (then use CookieJar) or JWT Authorization header.
|
||||||
|
- Implementiere Passkeys via Android FIDO2 / Passkeys APIs as optional fast-login path; for servers expecting WebAuthn payloads adapt encoding accordingly.
|
||||||
|
|
||||||
|
Datei automatisch erzeugt — wenn du möchtest, kann ich nun alle Dateien in `public/` und `assets/` auflisten und exportieren (Bilder, Fonts, PDFs).
|
||||||
0
AUTH_README.md
Normal file → Executable file
0
AUTH_README.md
Normal file → Executable file
0
DATENSCHUTZ.md
Normal file → Executable file
0
DATENSCHUTZ.md
Normal file → Executable file
0
DATENSCHUTZ_UEBERSICHT.md
Normal file → Executable file
0
DATENSCHUTZ_UEBERSICHT.md
Normal file → Executable file
0
DEPLOYMENT.md
Normal file → Executable file
0
DEPLOYMENT.md
Normal file → Executable file
92
android-app/PLAYSTORE_ASSETS.md
Executable file
92
android-app/PLAYSTORE_ASSETS.md
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
# Play Store Assets - Harheimer TC Android
|
||||||
|
|
||||||
|
## 1) Datenschutzerklaerung (Web-URL)
|
||||||
|
|
||||||
|
Empfohlene URL fuer Play Console:
|
||||||
|
- https://harheimertc.de/datenschutz
|
||||||
|
|
||||||
|
Die Seite ist in der Web-App als eigene Route vorhanden.
|
||||||
|
|
||||||
|
## 1b) Konto-Loeschung (Web-URL)
|
||||||
|
|
||||||
|
Empfohlene URL fuer Play Console:
|
||||||
|
- https://harheimertc.de/konto-loeschen
|
||||||
|
|
||||||
|
Die Seite beschreibt den Loeschprozess und Kontaktweg fuer App- und Webkonto.
|
||||||
|
|
||||||
|
## 2) Logo / Grafiken
|
||||||
|
|
||||||
|
### Pflicht
|
||||||
|
- App-Icon (Play): 512 x 512 PNG
|
||||||
|
|
||||||
|
### Optional, aber empfohlen
|
||||||
|
- Feature Graphic: 1024 x 500 PNG
|
||||||
|
|
||||||
|
### Generierung
|
||||||
|
|
||||||
|
Im Repo ist ein Script vorhanden, das aus dem Vereinslogo fertige Dateien erzeugt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/playstore-assets.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Ausgabe in:
|
||||||
|
- android-app/playstore-assets/generated/playstore-icon-512.png
|
||||||
|
- android-app/playstore-assets/generated/playstore-feature-graphic-1024x500.png
|
||||||
|
|
||||||
|
## 3) Screenshots (anonymisiert)
|
||||||
|
|
||||||
|
### Zielgroessen fuer Store-Upload
|
||||||
|
- Telefon (Portrait): 1080 x 1920
|
||||||
|
- Medium 7" Tablet (Portrait): 1200 x 1920
|
||||||
|
- 10" Tablet (Portrait): 1600 x 2560
|
||||||
|
|
||||||
|
Alle Dateien als PNG oder JPEG.
|
||||||
|
|
||||||
|
### Anonymisierung
|
||||||
|
|
||||||
|
Script fuer schwarze halbtransparente Balken ueber sensible Bereiche:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/anonymize-playstore-screenshot.sh <input.png> <output.png> 'x,y,w,h;x,y,w,h'
|
||||||
|
```
|
||||||
|
|
||||||
|
Beispiel:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/anonymize-playstore-screenshot.sh \
|
||||||
|
android-app/playstore-assets/raw/screen1.png \
|
||||||
|
android-app/playstore-assets/anon/screen1-anon.png \
|
||||||
|
'68,118,520,72;70,706,560,98'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Zielprofile erzeugen (Telefon, 7", 10")
|
||||||
|
|
||||||
|
Aus allen Dateien in `android-app/playstore-assets/anon` werden die drei Profile erzeugt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/playstore-screenshot-sizes.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional mit eigenen Ordnern:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/playstore-screenshot-sizes.sh \
|
||||||
|
--input-dir android-app/playstore-assets/anon \
|
||||||
|
--output-dir android-app/playstore-assets/final
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
- android-app/playstore-assets/final/phone
|
||||||
|
- android-app/playstore-assets/final/tablet-7
|
||||||
|
- android-app/playstore-assets/final/tablet-10
|
||||||
|
|
||||||
|
## 4) Upload in Play Console
|
||||||
|
|
||||||
|
- Datenschutzerklaerung: URL eintragen
|
||||||
|
- Konto-Loeschung: URL eintragen
|
||||||
|
- App-Icon: playstore-icon-512.png
|
||||||
|
- Feature Graphic: playstore-feature-graphic-1024x500.png
|
||||||
|
- Screenshots Telefon: Dateien aus `.../final/phone`
|
||||||
|
- Screenshots 7" Tablet: Dateien aus `.../final/tablet-7`
|
||||||
|
- Screenshots 10" Tablet: Dateien aus `.../final/tablet-10`
|
||||||
296
android-app/app/build.gradle.kts
Executable file
296
android-app/app/build.gradle.kts
Executable file
@@ -0,0 +1,296 @@
|
|||||||
|
import java.util.Properties
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
id("com.google.devtools.ksp")
|
||||||
|
id("org.jetbrains.kotlin.plugin.compose")
|
||||||
|
id("com.google.dagger.hilt.android")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file("google-services.json").exists()) {
|
||||||
|
apply(plugin = "com.google.gms.google-services")
|
||||||
|
}
|
||||||
|
|
||||||
|
val localApiBaseUrl = providers.gradleProperty("LOCAL_API_BASE_URL")
|
||||||
|
.orElse("https://harheimertc.tsschulz.de/")
|
||||||
|
.get()
|
||||||
|
val productionApiBaseUrl = providers.gradleProperty("PRODUCTION_API_BASE_URL")
|
||||||
|
.orElse("https://harheimertc.de/")
|
||||||
|
.get()
|
||||||
|
val expectedProductionApiBaseUrl = "https://harheimertc.de/"
|
||||||
|
val sentryDsn = providers.gradleProperty("SENTRY_DSN")
|
||||||
|
.orElse("")
|
||||||
|
.get()
|
||||||
|
val androidVersionCode = providers.gradleProperty("ANDROID_VERSION_CODE")
|
||||||
|
.orElse("2")
|
||||||
|
.get()
|
||||||
|
.toInt()
|
||||||
|
val androidVersionName = providers.gradleProperty("ANDROID_VERSION_NAME")
|
||||||
|
.orElse("1.0.0")
|
||||||
|
.get()
|
||||||
|
val releaseMinifyEnabled = providers.gradleProperty("RELEASE_MINIFY_ENABLED")
|
||||||
|
.orElse("true")
|
||||||
|
.get()
|
||||||
|
.toBoolean()
|
||||||
|
|
||||||
|
val localSigningProperties = Properties().apply {
|
||||||
|
val localSigningFile = rootProject.file("gradle-local.properties")
|
||||||
|
if (localSigningFile.exists()) {
|
||||||
|
localSigningFile.inputStream().use { load(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun signingProperty(name: String): String? =
|
||||||
|
providers.gradleProperty(name).orNull
|
||||||
|
?: providers.environmentVariable(name).orNull
|
||||||
|
?: localSigningProperties.getProperty(name)
|
||||||
|
|
||||||
|
val releaseStoreFile = signingProperty("RELEASE_STORE_FILE")
|
||||||
|
val releaseStorePassword = signingProperty("RELEASE_STORE_PASSWORD")
|
||||||
|
val releaseKeyAlias = signingProperty("RELEASE_KEY_ALIAS")
|
||||||
|
val releaseKeyPassword = signingProperty("RELEASE_KEY_PASSWORD")
|
||||||
|
val hasReleaseSigning = !releaseStoreFile.isNullOrBlank() &&
|
||||||
|
!releaseStorePassword.isNullOrBlank() &&
|
||||||
|
!releaseKeyAlias.isNullOrBlank() &&
|
||||||
|
!releaseKeyPassword.isNullOrBlank()
|
||||||
|
|
||||||
|
val ensureReleaseSigning = tasks.register("ensureReleaseSigning") {
|
||||||
|
doFirst {
|
||||||
|
if (!hasReleaseSigning) {
|
||||||
|
throw GradleException(
|
||||||
|
"Production release signing is not configured. " +
|
||||||
|
"Set RELEASE_STORE_FILE, RELEASE_STORE_PASSWORD, RELEASE_KEY_ALIAS and RELEASE_KEY_PASSWORD " +
|
||||||
|
"(e.g. via ~/.gradle/gradle.properties, environment variables, or android-app/gradle-local.properties)."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val ensureProductionApiBaseUrl = tasks.register("ensureProductionApiBaseUrl") {
|
||||||
|
doFirst {
|
||||||
|
if (productionApiBaseUrl != expectedProductionApiBaseUrl) {
|
||||||
|
throw GradleException(
|
||||||
|
"Production Play Store builds must use $expectedProductionApiBaseUrl, but PRODUCTION_API_BASE_URL is $productionApiBaseUrl."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "de.harheimertc"
|
||||||
|
compileSdk = 36
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "de.harheimertc"
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 36
|
||||||
|
versionCode = androidVersionCode
|
||||||
|
versionName = androidVersionName
|
||||||
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
disable += setOf(
|
||||||
|
"AutoboxingStateCreation",
|
||||||
|
"MutableCollectionMutableState",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
if (hasReleaseSigning) {
|
||||||
|
storeFile = file(releaseStoreFile!!)
|
||||||
|
storePassword = releaseStorePassword
|
||||||
|
keyAlias = releaseKeyAlias
|
||||||
|
keyPassword = releaseKeyPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
getByName("release") {
|
||||||
|
isMinifyEnabled = releaseMinifyEnabled
|
||||||
|
isShrinkResources = false
|
||||||
|
ndk {
|
||||||
|
// Generate a native debug symbols archive for Play Console uploads.
|
||||||
|
debugSymbolLevel = "SYMBOL_TABLE"
|
||||||
|
}
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro",
|
||||||
|
)
|
||||||
|
if (hasReleaseSigning) {
|
||||||
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flavorDimensions += "environment"
|
||||||
|
productFlavors {
|
||||||
|
create("local") {
|
||||||
|
dimension = "environment"
|
||||||
|
applicationIdSuffix = ".local"
|
||||||
|
versionNameSuffix = "-local"
|
||||||
|
buildConfigField("String", "API_BASE_URL", "\"$localApiBaseUrl\"")
|
||||||
|
buildConfigField("String", "SENTRY_DSN", "\"\"")
|
||||||
|
buildConfigField("String", "ENVIRONMENT_NAME", "\"LOCAL\"")
|
||||||
|
manifestPlaceholders["usesCleartextTraffic"] = "true"
|
||||||
|
}
|
||||||
|
create("instantTest") {
|
||||||
|
dimension = "environment"
|
||||||
|
applicationIdSuffix = ".test"
|
||||||
|
versionNameSuffix = "-test"
|
||||||
|
buildConfigField("String", "API_BASE_URL", "\"https://harheimertc.tsschulz.de/\"")
|
||||||
|
buildConfigField("String", "SENTRY_DSN", "\"$sentryDsn\"")
|
||||||
|
buildConfigField("String", "ENVIRONMENT_NAME", "\"TEST\"")
|
||||||
|
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||||
|
}
|
||||||
|
create("production") {
|
||||||
|
dimension = "environment"
|
||||||
|
buildConfigField("String", "API_BASE_URL", "\"$productionApiBaseUrl\"")
|
||||||
|
buildConfigField("String", "SENTRY_DSN", "\"$sentryDsn\"")
|
||||||
|
buildConfigField("String", "ENVIRONMENT_NAME", "\"\"")
|
||||||
|
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests.all {
|
||||||
|
// allow Byte Buddy experimental features for newer JVMs
|
||||||
|
it.jvmArgs = listOf("-Dnet.bytebuddy.experimental=true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val packageNativeDebugSymbolsForProductionRelease = tasks.register<Zip>("packageNativeDebugSymbolsForProductionRelease") {
|
||||||
|
group = "distribution"
|
||||||
|
description = "Packages production release native libraries into a Play Console debug symbols zip."
|
||||||
|
dependsOn(":app:mergeProductionReleaseNativeLibs")
|
||||||
|
from(layout.buildDirectory.dir("intermediates/merged_native_libs/productionRelease/mergeProductionReleaseNativeLibs/out/lib"))
|
||||||
|
destinationDirectory.set(layout.buildDirectory.dir("outputs/native-debug-symbols/productionRelease"))
|
||||||
|
archiveFileName.set("native-debug-symbols.zip")
|
||||||
|
}
|
||||||
|
|
||||||
|
val collectPlayStoreArtifacts = tasks.register("collectPlayStoreArtifacts") {
|
||||||
|
group = "distribution"
|
||||||
|
description = "Builds production release artifacts and collects AAB, mapping, and native symbols for Play Console upload."
|
||||||
|
dependsOn(ensureReleaseSigning)
|
||||||
|
dependsOn(ensureProductionApiBaseUrl)
|
||||||
|
dependsOn(":app:bundleProductionRelease")
|
||||||
|
dependsOn(packageNativeDebugSymbolsForProductionRelease)
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
val outputDir = layout.buildDirectory.dir("outputs/playstore/productionRelease").get().asFile
|
||||||
|
outputDir.mkdirs()
|
||||||
|
|
||||||
|
val bundleFile = layout.buildDirectory.file("outputs/bundle/productionRelease/app-production-release.aab").get().asFile
|
||||||
|
if (bundleFile.exists()) {
|
||||||
|
bundleFile.copyTo(outputDir.resolve(bundleFile.name), overwrite = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mappingFile = layout.buildDirectory.file("outputs/mapping/productionRelease/mapping.txt").get().asFile
|
||||||
|
if (mappingFile.exists()) {
|
||||||
|
mappingFile.copyTo(outputDir.resolve("mapping.txt"), overwrite = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val nativeSymbolsZip = layout.buildDirectory.file("outputs/native-debug-symbols/productionRelease/native-debug-symbols.zip").get().asFile
|
||||||
|
if (nativeSymbolsZip.exists()) {
|
||||||
|
nativeSymbolsZip.copyTo(outputDir.resolve("native-debug-symbols.zip"), overwrite = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Play Store artifacts prepared in: ${outputDir.absolutePath}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.matching {
|
||||||
|
it.name in setOf("bundleProductionRelease", "assembleProductionRelease")
|
||||||
|
}.configureEach {
|
||||||
|
dependsOn(ensureReleaseSigning)
|
||||||
|
dependsOn(ensureProductionApiBaseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||||
|
implementation("androidx.core:core-ktx:1.10.1")
|
||||||
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
|
|
||||||
|
// Compose
|
||||||
|
implementation("androidx.compose.ui:ui:1.5.0")
|
||||||
|
implementation("androidx.compose.ui:ui-tooling-preview:1.5.0")
|
||||||
|
debugImplementation("androidx.compose.ui:ui-tooling:1.5.0")
|
||||||
|
implementation("androidx.compose.material3:material3:1.1.0")
|
||||||
|
implementation("androidx.navigation:navigation-compose:2.6.0")
|
||||||
|
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||||
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
|
||||||
|
|
||||||
|
// Hilt
|
||||||