Compare commits
75 Commits
803481ca8e
...
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 |
25
.gitea/workflows/code-analysis.yml
Normal file → Executable file
25
.gitea/workflows/code-analysis.yml
Normal file → Executable file
@@ -6,13 +6,23 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||||
with:
|
with:
|
||||||
clean: true
|
clean: true
|
||||||
fetch-depth: 0
|
# 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
|
- name: Ensure clean workspace
|
||||||
run: |
|
run: |
|
||||||
@@ -28,7 +38,7 @@ jobs:
|
|||||||
echo "eslint entries in package.json:" && rg '"eslint"' package.json || true
|
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
|
||||||
@@ -115,12 +125,11 @@ 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:
|
||||||
needs: analyze
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare SSH
|
- name: Prepare SSH
|
||||||
run: |
|
run: |
|
||||||
@@ -146,7 +155,7 @@ 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
|
needs: analyze
|
||||||
@@ -177,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'"
|
||||||
|
|||||||
0
.gitea/workflows/version-gate.yml
Normal file → Executable file
0
.gitea/workflows/version-gate.yml
Normal file → Executable file
6
.github/workflows/android-ci.yml
vendored
Normal file → Executable file
6
.github/workflows/android-ci.yml
vendored
Normal file → Executable file
@@ -10,14 +10,14 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
- name: Cache Gradle
|
- name: Cache Gradle
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
|||||||
3
.gitignore
vendored
Normal file → Executable file
3
.gitignore
vendored
Normal file → Executable file
@@ -93,6 +93,9 @@ dist
|
|||||||
/android-app/.kotlin/
|
/android-app/.kotlin/
|
||||||
/android-app/**/build/
|
/android-app/**/build/
|
||||||
/android-app/local.properties
|
/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
|
||||||
|
|||||||
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
0
ANDROID_ARCHITECTURE.md
Normal file → Executable file
0
ANDROID_ARCHITECTURE.md
Normal file → Executable file
0
ANDROID_KOTLIN_PLAN.md
Normal file → Executable file
0
ANDROID_KOTLIN_PLAN.md
Normal file → Executable file
0
ANDROID_PORT_TODO.md
Normal file → Executable file
0
ANDROID_PORT_TODO.md
Normal file → Executable file
0
ANDROID_REPO_ENDPOINTS.md
Normal file → Executable file
0
ANDROID_REPO_ENDPOINTS.md
Normal file → Executable file
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
36
android-app/PLAYSTORE_ASSETS.md
Normal file → Executable file
36
android-app/PLAYSTORE_ASSETS.md
Normal file → Executable file
@@ -36,13 +36,12 @@ Ausgabe in:
|
|||||||
|
|
||||||
## 3) Screenshots (anonymisiert)
|
## 3) Screenshots (anonymisiert)
|
||||||
|
|
||||||
### Grobe Anforderungen (Telefon)
|
### Zielgroessen fuer Store-Upload
|
||||||
- Mindestens 2 Screenshots
|
- Telefon (Portrait): 1080 x 1920
|
||||||
- PNG oder JPEG
|
- Medium 7" Tablet (Portrait): 1200 x 1920
|
||||||
- Seitenlaenge je Seite zwischen 320 px und 3840 px
|
- 10" Tablet (Portrait): 1600 x 2560
|
||||||
|
|
||||||
Empfehlung fuer Android-Phone:
|
Alle Dateien als PNG oder JPEG.
|
||||||
- 1080 x 1920 (Portrait)
|
|
||||||
|
|
||||||
### Anonymisierung
|
### Anonymisierung
|
||||||
|
|
||||||
@@ -61,10 +60,33 @@ Beispiel:
|
|||||||
'68,118,520,72;70,706,560,98'
|
'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
|
## 4) Upload in Play Console
|
||||||
|
|
||||||
- Datenschutzerklaerung: URL eintragen
|
- Datenschutzerklaerung: URL eintragen
|
||||||
- Konto-Loeschung: URL eintragen
|
- Konto-Loeschung: URL eintragen
|
||||||
- App-Icon: playstore-icon-512.png
|
- App-Icon: playstore-icon-512.png
|
||||||
- Feature Graphic: playstore-feature-graphic-1024x500.png
|
- Feature Graphic: playstore-feature-graphic-1024x500.png
|
||||||
- Screenshots: anonymisierte PNG/JPEG hochladen
|
- Screenshots Telefon: Dateien aus `.../final/phone`
|
||||||
|
- Screenshots 7" Tablet: Dateien aus `.../final/tablet-7`
|
||||||
|
- Screenshots 10" Tablet: Dateien aus `.../final/tablet-10`
|
||||||
|
|||||||
74
android-app/app/build.gradle.kts
Normal file → Executable file
74
android-app/app/build.gradle.kts
Normal file → Executable file
@@ -1,3 +1,5 @@
|
|||||||
|
import java.util.Properties
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("com.google.devtools.ksp")
|
id("com.google.devtools.ksp")
|
||||||
@@ -5,12 +7,17 @@ plugins {
|
|||||||
id("com.google.dagger.hilt.android")
|
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")
|
val localApiBaseUrl = providers.gradleProperty("LOCAL_API_BASE_URL")
|
||||||
.orElse("https://harheimertc.tsschulz.de/")
|
.orElse("https://harheimertc.tsschulz.de/")
|
||||||
.get()
|
.get()
|
||||||
val productionApiBaseUrl = providers.gradleProperty("PRODUCTION_API_BASE_URL")
|
val productionApiBaseUrl = providers.gradleProperty("PRODUCTION_API_BASE_URL")
|
||||||
.orElse("https://harheimertc.de/")
|
.orElse("https://harheimertc.de/")
|
||||||
.get()
|
.get()
|
||||||
|
val expectedProductionApiBaseUrl = "https://harheimertc.de/"
|
||||||
val sentryDsn = providers.gradleProperty("SENTRY_DSN")
|
val sentryDsn = providers.gradleProperty("SENTRY_DSN")
|
||||||
.orElse("")
|
.orElse("")
|
||||||
.get()
|
.get()
|
||||||
@@ -25,27 +32,69 @@ val releaseMinifyEnabled = providers.gradleProperty("RELEASE_MINIFY_ENABLED")
|
|||||||
.orElse("true")
|
.orElse("true")
|
||||||
.get()
|
.get()
|
||||||
.toBoolean()
|
.toBoolean()
|
||||||
val releaseStoreFile = providers.gradleProperty("RELEASE_STORE_FILE").orNull
|
|
||||||
val releaseStorePassword = providers.gradleProperty("RELEASE_STORE_PASSWORD").orNull
|
val localSigningProperties = Properties().apply {
|
||||||
val releaseKeyAlias = providers.gradleProperty("RELEASE_KEY_ALIAS").orNull
|
val localSigningFile = rootProject.file("gradle-local.properties")
|
||||||
val releaseKeyPassword = providers.gradleProperty("RELEASE_KEY_PASSWORD").orNull
|
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() &&
|
val hasReleaseSigning = !releaseStoreFile.isNullOrBlank() &&
|
||||||
!releaseStorePassword.isNullOrBlank() &&
|
!releaseStorePassword.isNullOrBlank() &&
|
||||||
!releaseKeyAlias.isNullOrBlank() &&
|
!releaseKeyAlias.isNullOrBlank() &&
|
||||||
!releaseKeyPassword.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 {
|
android {
|
||||||
namespace = "de.harheimertc"
|
namespace = "de.harheimertc"
|
||||||
compileSdk = 35
|
compileSdk = 36
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "de.harheimertc"
|
applicationId = "de.harheimertc"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 35
|
targetSdk = 36
|
||||||
versionCode = androidVersionCode
|
versionCode = androidVersionCode
|
||||||
versionName = androidVersionName
|
versionName = androidVersionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
disable += setOf(
|
||||||
|
"AutoboxingStateCreation",
|
||||||
|
"MutableCollectionMutableState",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
create("release") {
|
create("release") {
|
||||||
if (hasReleaseSigning) {
|
if (hasReleaseSigning) {
|
||||||
@@ -135,6 +184,8 @@ val packageNativeDebugSymbolsForProductionRelease = tasks.register<Zip>("package
|
|||||||
val collectPlayStoreArtifacts = tasks.register("collectPlayStoreArtifacts") {
|
val collectPlayStoreArtifacts = tasks.register("collectPlayStoreArtifacts") {
|
||||||
group = "distribution"
|
group = "distribution"
|
||||||
description = "Builds production release artifacts and collects AAB, mapping, and native symbols for Play Console upload."
|
description = "Builds production release artifacts and collects AAB, mapping, and native symbols for Play Console upload."
|
||||||
|
dependsOn(ensureReleaseSigning)
|
||||||
|
dependsOn(ensureProductionApiBaseUrl)
|
||||||
dependsOn(":app:bundleProductionRelease")
|
dependsOn(":app:bundleProductionRelease")
|
||||||
dependsOn(packageNativeDebugSymbolsForProductionRelease)
|
dependsOn(packageNativeDebugSymbolsForProductionRelease)
|
||||||
|
|
||||||
@@ -161,6 +212,13 @@ val collectPlayStoreArtifacts = tasks.register("collectPlayStoreArtifacts") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.matching {
|
||||||
|
it.name in setOf("bundleProductionRelease", "assembleProductionRelease")
|
||||||
|
}.configureEach {
|
||||||
|
dependsOn(ensureReleaseSigning)
|
||||||
|
dependsOn(ensureProductionApiBaseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||||
@@ -206,6 +264,9 @@ dependencies {
|
|||||||
// Crash reporting
|
// Crash reporting
|
||||||
implementation("io.sentry:sentry-android:8.42.0")
|
implementation("io.sentry:sentry-android:8.42.0")
|
||||||
|
|
||||||
|
// Push notifications
|
||||||
|
implementation("com.google.firebase:firebase-messaging:25.0.2")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
implementation("androidx.room:room-runtime:2.6.1")
|
implementation("androidx.room:room-runtime:2.6.1")
|
||||||
ksp("androidx.room:room-compiler:2.6.1")
|
ksp("androidx.room:room-compiler:2.6.1")
|
||||||
@@ -215,6 +276,7 @@ dependencies {
|
|||||||
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
||||||
implementation("androidx.datastore:datastore-preferences:1.0.0")
|
implementation("androidx.datastore:datastore-preferences:1.0.0")
|
||||||
implementation("androidx.security:security-crypto:1.1.0-alpha06")
|
implementation("androidx.security:security-crypto:1.1.0-alpha06")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3")
|
||||||
|
|
||||||
// Testing (skeleton)
|
// Testing (skeleton)
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
|||||||
1
android-app/app/google-services.json
Symbolic link
1
android-app/app/google-services.json
Symbolic link
@@ -0,0 +1 @@
|
|||||||