Compare commits

5 Commits

Author SHA1 Message Date
Torsten Schulz (local)
245ada296b Refactor proposal generation in FalukantService to improve character selection logic
- Removed the tracking of used character IDs and streamlined the exclusion of characters already proposed or currently active as directors.
- Enhanced logging for SQL queries and fallback mechanisms to ensure better visibility during character selection.
- Implemented a more efficient approach to gather and process character knowledge for proposal creation, ensuring accurate average calculations.
- Improved error handling to provide clearer feedback when no eligible characters are found.
2026-01-12 08:33:26 +01:00
Torsten Schulz (local)
750fa7b99b Enhance proposal generation logic in FalukantService to prevent duplicate character usage
- Introduced a mechanism to track used character IDs, ensuring that previously proposed characters are excluded from future proposals.
- Added error handling and logging for scenarios where no eligible characters are found, improving traceability and user feedback.
- Implemented a fallback to include newer characters if older ones are unavailable, enhancing the robustness of the proposal generation process.
2026-01-12 08:24:00 +01:00
Torsten Schulz (local)
791100f5a5 Update BranchView to refresh active tab data on tab change and modify 3D model for female toddler character
- Enhanced the activeTab watcher to refresh data only when the selected branch changes and the tab is switched.
- Introduced a new refreshActiveTab method to load data for the currently active tab, improving data management and user experience.
- Updated the female toddler 3D model file for better integration in the application.
2026-01-12 08:07:50 +01:00
Torsten Schulz (local)
c471bba50b Enhance partner search and gift loading functionality in FalukantService and FamilyView
- Added detailed logging for partner search criteria and results in FalukantService to improve debugging and traceability.
- Refactored partner search logic to use a dynamic where clause for better readability and maintainability.
- Implemented error handling in FamilyView for gift loading, ensuring an empty array is set on failure to load gifts, enhancing user experience.
2026-01-09 14:28:01 +01:00
Torsten Schulz (local)
1d3aef3778 Add 3D character model integration and update dependencies
- Introduced a new CharacterModel3D component for rendering 3D character models in OverviewView.
- Updated package.json and package-lock.json to include 'three' library for 3D graphics support.
- Enhanced Vite configuration to allow access to external files and ensure proper handling of GLB/GLTF assets.
- Improved layout and styling in OverviewView for better visualization of character and avatar.
2026-01-09 13:29:32 +01:00
1536 changed files with 12028 additions and 168852 deletions

0
.codex
View File

View File

@@ -1,9 +0,0 @@
---
description: C++-Worker unter src/ sind obsolet — nicht erweitern oder als Quelle für Spiellogik nutzen
alwaysApply: true
---
# Legacy C++ (`src/`)
- Verzeichnis **`src/`** (C++-Worker, WebSocket-Server): **obsolet**. Keine neuen Features, keine fachlichen Fixes dort planen oder umsetzen, sofern der Nutzer nicht ausdrücklich etwas anderes verlangt.
- Falukant-Hintergrundlogik: **Backend** (`backend/`), **externer Daemon**, **Frontend** — siehe `docs/LEGACY_CPP_WORKERS.md`.

View File

@@ -1,148 +0,0 @@
name: Deploy to production
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Detect vocab course changes
id: vocab_course_changes
shell: bash
run: |
set -euo pipefail
BASE="${{ gitea.event.before }}"
HEAD="${{ gitea.sha }}"
if [ -z "$BASE" ] || [[ "$BASE" =~ ^0+$ ]] || ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then
BASE="HEAD~1"
fi
git diff --name-only "$BASE" "$HEAD" > changed-files.txt
cat changed-files.txt
COMMIT_MESSAGE="$(git log -1 --pretty=%B "$HEAD" || true)"
COURSE_SCRIPT_PATTERN='^backend/scripts/.*(bisaya|course|didactics|vocab)'
if echo "$COMMIT_MESSAGE" | grep -qi '\[force-deploy\]'; then
echo "force_deploy=true" >> "$GITHUB_OUTPUT"
else
echo "force_deploy=false" >> "$GITHUB_OUTPUT"
fi
if grep -E '^(backend/scripts/.*(bisaya|course|didactics|vocab)|backend/sql/.*vocab|backend/(migrations-active|migrations-archive)/.*vocab|docs/.*(COURSE|VOCAB|BISAYA|GERMAN_FOR_BISAYA))' changed-files.txt; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
if grep -E "$COURSE_SCRIPT_PATTERN" changed-files.txt >/dev/null; then
echo "course_scripts_changed=true" >> "$GITHUB_OUTPUT"
else
echo "course_scripts_changed=false" >> "$GITHUB_OUTPUT"
fi
if grep -E '^frontend/' changed-files.txt >/dev/null; then
echo "frontend_changed=true" >> "$GITHUB_OUTPUT"
else
echo "frontend_changed=false" >> "$GITHUB_OUTPUT"
fi
# Kurs-/Didaktik-Scripts werden vor dem Sync separat uebertragen.
# Fuer sie sind npm ci, Migrationen und ein Backend-Restart nicht noetig.
if grep -E '^backend/' changed-files.txt \
| grep -Ev "$COURSE_SCRIPT_PATTERN" >/dev/null; then
echo "backend_app_changed=true" >> "$GITHUB_OUTPUT"
else
echo "backend_app_changed=false" >> "$GITHUB_OUTPUT"
fi
# App-Code-Änderungen, die einen echten Deploy benötigen
if grep -E '^(frontend/|backend/)' changed-files.txt \
| grep -Ev "$COURSE_SCRIPT_PATTERN" >/dev/null; then
echo "app_changed=true" >> "$GITHUB_OUTPUT"
else
echo "app_changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Prepare SSH
run: |
mkdir -p ~/.ssh
printf "%s" "${{ secrets.PROD_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -p "${{ secrets.PROD_PORT }}" "${{ secrets.PROD_HOST }}" >> ~/.ssh/known_hosts
- name: Test SSH connection
run: |
ssh -i ~/.ssh/id_ed25519 \
-o StrictHostKeyChecking=no \
-o BatchMode=yes \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"echo SSH OK"
- name: Deploy vocab course scripts without app rebuild
if: steps.vocab_course_changes.outputs.course_scripts_changed == 'true' && steps.vocab_course_changes.outputs.backend_app_changed != 'true' && steps.vocab_course_changes.outputs.force_deploy != 'true'
shell: bash
run: |
set -euo pipefail
tar -czf - backend/scripts \
| ssh -i ~/.ssh/id_ed25519 \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"sudo -n -u yourpart tar -xzf - -C /opt/yourpart"
- name: Run deployment script
if: steps.vocab_course_changes.outputs.app_changed == 'true' || steps.vocab_course_changes.outputs.force_deploy == 'true'
run: |
DEPLOY_FLAGS=""
if [ "${{ steps.vocab_course_changes.outputs.force_deploy }}" = "true" ]; then
DEPLOY_FLAGS=""
elif [ "${{ steps.vocab_course_changes.outputs.backend_app_changed }}" = "true" ] && [ "${{ steps.vocab_course_changes.outputs.frontend_changed }}" != "true" ]; then
DEPLOY_FLAGS="--skip-frontend"
elif [ "${{ steps.vocab_course_changes.outputs.frontend_changed }}" = "true" ] && [ "${{ steps.vocab_course_changes.outputs.backend_app_changed }}" != "true" ]; then
DEPLOY_FLAGS="--skip-backend"
fi
DEPLOY_TARGET="${{ secrets.PROD_DEPLOY_TARGET }}"
if [ -z "$DEPLOY_TARGET" ]; then
DEPLOY_TARGET="/opt/yourpart-green"
fi
echo "Deploy-Flags: ${DEPLOY_FLAGS:-<none>}"
echo "Deploy-Target: $DEPLOY_TARGET"
ssh -i ~/.ssh/id_ed25519 \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"/home/tsschulz/deploy-yourpart-bluegreen.sh ${DEPLOY_TARGET} ${DEPLOY_FLAGS}"
- name: Skip full deployment (no app changes)
if: steps.vocab_course_changes.outputs.app_changed != 'true' && steps.vocab_course_changes.outputs.force_deploy != 'true'
run: |
echo "Kein Full-Deploy: Es wurden keine Frontend/Backend-App-Dateien geändert."
- name: Sync vocab course content
if: steps.vocab_course_changes.outputs.changed == 'true' || steps.vocab_course_changes.outputs.force_deploy == 'true'
run: |
# Decide whether to actually run the phase3 update on the server.
# By default we run the deploy script in --dry-run mode. To enable the
# actual run set the secret PHASE3_UPDATE=1 in the repo settings.
RUN_FLAG="--dry-run"
if [ "${{ secrets.PHASE3_UPDATE }}" = "1" ]; then
RUN_FLAG=""
fi
ssh -i ~/.ssh/id_ed25519 \
-p "${{ secrets.PROD_PORT }}" \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
"cd /opt/yourpart && npm --prefix backend run sync:vocab-courses && if [ -x backend/scripts/deploy-phase3-update.sh ]; then bash backend/scripts/deploy-phase3-update.sh $RUN_FLAG; else echo 'deploy-phase3-update.sh not found or not executable'; fi"

View File

@@ -1,29 +0,0 @@
name: Android Security
on:
push:
paths:
- 'android/native/**'
- '.github/workflows/android-security.yml'
pull_request:
paths:
- 'android/native/**'
- '.github/workflows/android-security.yml'
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Android lint
working-directory: android/native
run: ./gradlew :app:lintProductionRelease
- name: Scan dependencies with OSV
uses: google/osv-scanner-action/osv-scanner-action@v2.0.2
with:
scan-args: |-
--recursive android/native

View File

@@ -1,50 +0,0 @@
name: Android Tests
on:
push:
paths:
- "android/native/**"
- ".github/workflows/android-tests.yml"
pull_request:
paths:
- "android/native/**"
- ".github/workflows/android-tests.yml"
permissions:
contents: read
jobs:
unit-tests:
name: JVM tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: android/native
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew :app:testLocalDebugUnitTest
instrumentation-tests:
name: Emulator tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: android/native
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v4
- uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 35
target: google_apis
arch: x86_64
script: ./gradlew :app:connectedLocalDebugAndroidTest

15
.gitignore vendored Executable file → Normal file
View File

@@ -5,30 +5,15 @@
.depbe.sh
node_modules
node_modules/*
# package-lock.json wird versioniert (npm ci im Deploy braucht konsistente Locks zu package.json)
backend/.env
backend/.env.local
backend/images
backend/images/*
backend/node_modules
backend/node_modules/*
frontend/.env
frontend/.env.android
frontend/node_modules
frontend/node_modules/*
frontend/dist
frontend/dist/*
frontend/scripts/.i18n-de-fr-cache.json
frontend/scripts/.falukant-fr-smooth-cache.json
frontend/ceb-locale-audit-report.json
frontedtree.txt
backend/dist/
backend/data/model-cache
build
build/*
.vscode
.vscode/*
.clang-format
android/native/.gradle/
android/native/build/
android/native/local.properties

0
.vscode/settings.json vendored Executable file → Normal file
View File

View File

@@ -1,156 +0,0 @@
# Church Models - Übersicht für Daemon-Entwicklung
## 1. ChurchOfficeType (falukant_type.church_office_type)
**Schema:** `falukant_type`
**Tabelle:** `church_office_type`
**Zweck:** Definiert die verschiedenen Kirchenämter-Typen
```javascript
{
id: INTEGER (PK, auto-increment)
name: STRING (z.B. "pope", "cardinal", "lay-preacher")
seatsPerRegion: INTEGER (Anzahl verfügbarer Plätze pro Region)
regionType: STRING (z.B. "country", "duchy", "city")
hierarchyLevel: INTEGER (0-8, höhere Zahl = höhere Position)
}
```
**Beziehungen:**
- `hasMany` ChurchOffice (als `offices`)
- `hasMany` ChurchApplication (als `applications`)
- `hasMany` ChurchOfficeRequirement (als `requirements`)
---
## 2. ChurchOfficeRequirement (falukant_predefine.church_office_requirement)
**Schema:** `falukant_predefine`
**Tabelle:** `church_office_requirement`
**Zweck:** Definiert Voraussetzungen für Kirchenämter
```javascript
{
id: INTEGER (PK, auto-increment)
officeTypeId: INTEGER (FK -> ChurchOfficeType.id)
prerequisiteOfficeTypeId: INTEGER (FK -> ChurchOfficeType.id, nullable)
minTitleLevel: INTEGER (nullable, optional)
}
```
**Beziehungen:**
- `belongsTo` ChurchOfficeType (als `officeType`)
- `belongsTo` ChurchOfficeType (als `prerequisiteOfficeType`)
---
## 3. ChurchOffice (falukant_data.church_office)
**Schema:** `falukant_data`
**Tabelle:** `church_office`
**Zweck:** Speichert tatsächlich besetzte Kirchenämter
```javascript
{
id: INTEGER (PK, auto-increment)
officeTypeId: INTEGER (FK -> ChurchOfficeType.id)
characterId: INTEGER (FK -> FalukantCharacter.id)
regionId: INTEGER (FK -> RegionData.id)
supervisorId: INTEGER (FK -> FalukantCharacter.id, nullable)
createdAt: DATE
updatedAt: DATE
}
```
**Beziehungen:**
- `belongsTo` ChurchOfficeType (als `type`)
- `belongsTo` FalukantCharacter (als `holder`)
- `belongsTo` FalukantCharacter (als `supervisor`)
- `belongsTo` RegionData (als `region`)
---
## 4. ChurchApplication (falukant_data.church_application)
**Schema:** `falukant_data`
**Tabelle:** `church_application`
**Zweck:** Speichert Bewerbungen für Kirchenämter
```javascript
{
id: INTEGER (PK, auto-increment)
officeTypeId: INTEGER (FK -> ChurchOfficeType.id)
characterId: INTEGER (FK -> FalukantCharacter.id)
regionId: INTEGER (FK -> RegionData.id)
supervisorId: INTEGER (FK -> FalukantCharacter.id)
status: ENUM('pending', 'approved', 'rejected')
decisionDate: DATE (nullable)
createdAt: DATE
updatedAt: DATE
}
```
**Beziehungen:**
- `belongsTo` ChurchOfficeType (als `officeType`)
- `belongsTo` FalukantCharacter (als `applicant`)
- `belongsTo` FalukantCharacter (als `supervisor`)
- `belongsTo` RegionData (als `region`)
---
## Zusätzlich benötigte Models (für Daemon)
### RegionData (falukant_data.region)
- Wird für `regionId` in ChurchOffice und ChurchApplication benötigt
- Enthält `regionType` (country, duchy, markgravate, shire, county, city)
- Enthält `parentId` für Hierarchie
### FalukantCharacter (falukant_data.character)
- Wird für `characterId` (Inhaber/Bewerber) benötigt
- Wird für `supervisorId` benötigt
---
## Wichtige Queries für Daemon
### Verfügbare Positionen finden
```sql
SELECT cot.*, COUNT(co.id) as occupied_seats
FROM falukant_type.church_office_type cot
LEFT JOIN falukant_data.church_office co
ON cot.id = co.office_type_id
AND co.region_id = ?
WHERE cot.region_type = ?
GROUP BY cot.id
HAVING COUNT(co.id) < cot.seats_per_region
```
### Supervisor finden
```sql
SELECT co.*
FROM falukant_data.church_office co
JOIN falukant_type.church_office_type cot ON co.office_type_id = cot.id
WHERE co.region_id = ?
AND cot.hierarchy_level > (
SELECT hierarchy_level
FROM falukant_type.church_office_type
WHERE id = ?
)
ORDER BY cot.hierarchy_level ASC
LIMIT 1
```
### Voraussetzungen prüfen
```sql
SELECT cor.*
FROM falukant_predefine.church_office_requirement cor
WHERE cor.office_type_id = ?
```
### Bewerbungen für Supervisor
```sql
SELECT ca.*
FROM falukant_data.church_application ca
WHERE ca.supervisor_id = ?
AND ca.status = 'pending'
```

View File

@@ -1,78 +0,0 @@
# Kirchenämter - Hierarchie und Verfügbarkeit
## Regionstypen
- **country** (Land): Falukant
- **duchy** (Herzogtum): Hessen
- **markgravate** (Markgrafschaft): Groß-Benbach
- **shire** (Grafschaft): Siebenbachen
- **county** (Kreis): Bad Homburg, Maintal
- **city** (Stadt): Frankfurt, Oberursel, Offenbach, Königstein
## Kirchenämter (von höchstem zu niedrigstem Rang)
| Amt | Translation Key | Hierarchie-Level | Regionstyp | Plätze pro Region | Beschreibung |
|-----|----------------|-------------------|------------|-------------------|--------------|
| **Papst** | `pope` | 8 | country | 1 | Höchstes Amt, nur einer im ganzen Land |
| **Kardinal** | `cardinal` | 7 | country | 3 | Höchste Kardinäle, mehrere pro Land möglich |
| **Erzbischof** | `archbishop` | 6 | duchy | 1 | Pro Herzogtum ein Erzbischof |
| **Bischof** | `bishop` | 5 | markgravate | 1 | Pro Markgrafschaft ein Bischof |
| **Erzdiakon** | `archdeacon` | 4 | shire | 1 | Pro Grafschaft ein Erzdiakon |
| **Dekan** | `dean` | 3 | county | 1 | Pro Kreis ein Dekan |
| **Pfarrer** | `parish-priest` | 2 | city | 1 | Pro Stadt ein Pfarrer |
| **Dorfgeistlicher** | `village-priest` | 1 | city | 1 | Pro Stadt ein Dorfgeistlicher (Einstiegsposition) |
| **Laienprediger** | `lay-preacher` | 0 | city | 3 | Pro Stadt mehrere Laienprediger (niedrigste Position) |
## Verfügbare Positionen pro Regionstyp
### country (Land: Falukant)
- **Papst**: 1 Platz
- **Kardinal**: 3 Plätze
- **Gesamt**: 4 Plätze
### duchy (Herzogtum: Hessen)
- **Erzbischof**: 1 Platz
- **Gesamt**: 1 Platz
### markgravate (Markgrafschaft: Groß-Benbach)
- **Bischof**: 1 Platz
- **Gesamt**: 1 Platz
### shire (Grafschaft: Siebenbachen)
- **Erzdiakon**: 1 Platz
- **Gesamt**: 1 Platz
### county (Kreis: Bad Homburg, Maintal)
- **Dekan**: 1 Platz pro Kreis
- **Gesamt**: 1 Platz pro Kreis
### city (Stadt: Frankfurt, Oberursel, Offenbach, Königstein)
- **Pfarrer**: 1 Platz pro Stadt
- **Dorfgeistlicher**: 1 Platz pro Stadt
- **Laienprediger**: 3 Plätze pro Stadt
- **Gesamt**: 5 Plätze pro Stadt
## Hierarchie und Beförderungsweg
1. **Laienprediger** (lay-preacher) - Einstiegsposition, keine Voraussetzung
2. **Dorfgeistlicher** (village-priest) - Voraussetzung: Laienprediger
3. **Pfarrer** (parish-priest) - Voraussetzung: Dorfgeistlicher
4. **Dekan** (dean) - Voraussetzung: Pfarrer
5. **Erzdiakon** (archdeacon) - Voraussetzung: Dekan
6. **Bischof** (bishop) - Voraussetzung: Erzdiakon
7. **Erzbischof** (archbishop) - Voraussetzung: Bischof
8. **Kardinal** (cardinal) - Voraussetzung: Erzbischof
9. **Papst** (pope) - Voraussetzung: Kardinal
## Gesamtübersicht verfügbarer Positionen
- **Papst**: 1 Position (Land)
- **Kardinal**: 3 Positionen (Land)
- **Erzbischof**: 1 Position (Herzogtum)
- **Bischof**: 1 Position (Markgrafschaft)
- **Erzdiakon**: 1 Position (Grafschaft)
- **Dekan**: 2 Positionen (2 Kreise)
- **Pfarrer**: 4 Positionen (4 Städte)
- **Dorfgeistlicher**: 4 Positionen (4 Städte)
- **Laienprediger**: 12 Positionen (4 Städte × 3)
**Gesamt**: 30 Positionen im System

View File

@@ -1,119 +0,0 @@
cmake_minimum_required(VERSION 3.20)
project(YourPartDaemon VERSION 1.0 LANGUAGES CXX)
# C++ Standard and Compiler Settings
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Use best available GCC for C++23 support (OpenSUSE Tumbleweed)
# Try GCC 15 first (best C++23 support), then GCC 13, then system default
find_program(GCC15_CC gcc-15)
find_program(GCC15_CXX g++-15)
find_program(GCC13_CC gcc-13)
find_program(GCC13_CXX g++-13)
if(GCC15_CC AND GCC15_CXX)
set(CMAKE_C_COMPILER ${GCC15_CC})
set(CMAKE_CXX_COMPILER ${GCC15_CXX})
message(STATUS "Using GCC 15 for best C++23 support")
elseif(GCC13_CC AND GCC13_CXX)
set(CMAKE_C_COMPILER ${GCC13_CC})
set(CMAKE_CXX_COMPILER ${GCC13_CXX})
message(STATUS "Using GCC 13 for C++23 support")
else()
message(STATUS "Using system default compiler")
endif()
# Optimize for GCC 13 with C++23
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=auto -O3 -march=native -mtune=native")
set(CMAKE_CXX_FLAGS_DEBUG "-O1 -g -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -march=native -mtune=native")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
set(CMAKE_BUILD_TYPE Release)
# Include /usr/local if needed
list(APPEND CMAKE_PREFIX_PATH /usr/local)
# Find libwebsockets via pkg-config
find_package(PkgConfig REQUIRED)
pkg_check_modules(LWS REQUIRED libwebsockets)
# Find other dependencies
find_package(PostgreSQL REQUIRED)
find_package(Threads REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
# PostgreSQL C++ libpqxx
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBPQXX REQUIRED libpqxx)
# Project sources and headers
set(SOURCES
src/main.cpp
src/config.cpp
src/connection_pool.cpp
src/database.cpp
src/character_creation_worker.cpp
src/produce_worker.cpp
src/message_broker.cpp
src/websocket_server.cpp
src/stockagemanager.cpp
src/director_worker.cpp
src/valuerecalculationworker.cpp
src/usercharacterworker.cpp
src/houseworker.cpp
src/politics_worker.cpp
)
set(HEADERS
src/config.h
src/database.h
src/connection_pool.h
src/worker.h
src/character_creation_worker.h
src/produce_worker.h
src/message_broker.h
src/websocket_server.h
src/stockagemanager.h
src/director_worker.h
src/valuerecalculationworker.h
src/usercharacterworker.h
src/houseworker.h
src/politics_worker.h
)
# Define executable target
add_executable(yourpart-daemon ${SOURCES} ${HEADERS}
src/utils.h src/utils.cpp
src/underground_worker.h src/underground_worker.cpp)
# Include directories
target_include_directories(yourpart-daemon PRIVATE
${PostgreSQL_INCLUDE_DIRS}
${LIBPQXX_INCLUDE_DIRS}
${LWS_INCLUDE_DIRS}
)
# Find systemd
find_package(PkgConfig REQUIRED)
pkg_check_modules(SYSTEMD REQUIRED libsystemd)
# Link libraries
target_link_libraries(yourpart-daemon PRIVATE
${PostgreSQL_LIBRARIES}
Threads::Threads
z ssl crypto
${LIBPQXX_LIBRARIES}
${LWS_LIBRARIES}
nlohmann_json::nlohmann_json
${SYSTEMD_LIBRARIES}
)
# Installation rules
install(TARGETS yourpart-daemon DESTINATION /usr/local/bin)
# Installiere Template als Referenz ZUERST (wird vom install-Skript benötigt)
install(FILES daemon.conf DESTINATION /etc/yourpart/ RENAME daemon.conf.example)
# Intelligente Konfigurationsdatei-Installation
# Verwendet ein CMake-Skript, das nur fehlende Keys hinzufügt, ohne bestehende zu überschreiben
# Das Skript liest das Template aus /etc/yourpart/daemon.conf.example oder dem Source-Verzeichnis
install(SCRIPT cmake/install-config.cmake)

View File

@@ -1,414 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 17.0.0, 2025-08-16T22:07:06. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{551ef6b3-a39b-43e2-9ee3-ad56e19ff4f4}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="bool" key="HasPerBcDcs">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Importiertes Kit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Importiertes Kit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{78ff90a3-f672-45c2-ad08-343b0923896f}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMake.Build.Type">Debug</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
-DCMAKE_GENERATOR:STRING=Unix Makefiles
-DCMAKE_BUILD_TYPE:STRING=Release
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/yourpart-daemon/build/</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString"></value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">yourpart-daemon</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">yourpart-daemon</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/yourpart-daemon/build</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="CMake.Build.Type">Debug</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
-DCMAKE_GENERATOR:STRING=Unix Makefiles
-DCMAKE_BUILD_TYPE:STRING=Debug
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}</value>
<value type="QString" key="CMake.Source.Directory">/mnt/share/torsten/Programs/yourpart-daemon</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/yourpart-daemon/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug (importiert)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">-1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">install</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">0</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString"></value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.CMakePackageStep</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="QString" key="ApplicationManagerPlugin.Deploy.InstallPackageStep.Arguments">install-package --acknowledge</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Application Manager-Paket installieren</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.InstallPackageStep</value>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedFiles"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedHosts"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedRemotePaths"/>
<valuelist type="QVariantList" key="ProjectExplorer.RunConfiguration.LastDeployedSysroots"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedLocalTimes"/>
<valuelist type="QVariantList" key="RemoteLinux.LastDeployedRemoteTimes"/>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ApplicationManagerPlugin.Deploy.Configuration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">yourpart-daemon</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">yourpart-daemon</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/yourpart-daemon/build</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -1,205 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 12.0.2, 2025-07-18T07:45:58. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{d36652ff-969b-426b-a63f-1edd325096c5}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
<valuemap type="QVariantMap" key="CppEditor.QuickFix">
<value type="bool" key="UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Importiertes Kit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Importiertes Kit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{3c6cfc13-714d-4db1-bd45-b9794643cc67}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMake.Build.Type">Debug</value>
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Unix Makefiles
-DCMAKE_BUILD_TYPE:STRING=Build
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}</value>
<value type="QString" key="CMake.Source.Directory">/home/torsten/Programs/yourpart-daemon</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/torsten/Programs/yourpart-daemon/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">yourpart-daemon</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.yourpart-daemon</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">yourpart-daemon</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/yourpart-daemon/build</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

0
DEPLOYMENT.md Executable file → Normal file
View File

View File

@@ -1,259 +0,0 @@
# OAuth Credentials Setup Guide
Anleitung zum Sammeln der OAuth-Credentials für alle 5 Provider.
## Redirect URIs
Für alle Provider benötigst du folgende Redirect URIs (ersetze `www.your-part.de` mit deiner echten Domain):
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
Lokal zum Testen:
```
http://localhost:3000/auth/oauth/callback
http://localhost:3000/auth/oauth/user/callback
```
---
## 1. Google
### Credentials besorgen:
1. Öffne [Google Cloud Console](https://console.cloud.google.com/)
2. Erstelle ein neues Projekt oder wähle ein bestehendes
3. Navigiere zu **APIs & Services****Credentials**
4. Klick **+ CREATE CREDENTIALS** → **OAuth 2.0 Client IDs**
5. Wähle **Web application**
6. Füge unter **Authorized redirect URIs** hinzu:
- `https://www.your-part.de/auth/oauth/callback`
- `https://www.your-part.de/auth/oauth/user/callback`
7. Speichern und die **Client ID** und **Client Secret** kopieren
### .env:
```env
OAUTH_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
OAUTH_GOOGLE_CLIENT_SECRET=your-client-secret
```
---
## 2. Microsoft Azure
### Credentials besorgen:
1. Öffne [Azure Portal](https://portal.azure.com/)
2. Navigiere zu **Azure Active Directory****App registrations****+ New registration**
3. Gib einen Namen ein (z.B. "YourPart OAuth")
4. Wähle **Accounts in any organizational directory (Any Azure AD directory - Multitenant)**
5. Bei **Redirect URI** wähle **Web** und füge ein:
- `https://www.your-part.de/auth/oauth/callback`
6. Klick **Register**
7. Notiere die **Application (client) ID**
8. Gehe zu **Certificates & secrets****+ New client secret**
9. Erstelle ein Secret und kopiere den **Value** (nicht die ID!)
10. Gehe zu **Token configuration** und stelle sicher, dass die richtigen Claims enthalten sind
### Zusätzliche URI hinzufügen:
1. Gehe zu **Authentication**
2. Unter **Redirect URIs** klick **+ Add URI**
3. Füge hinzu: `https://www.your-part.de/auth/oauth/user/callback`
### .env:
```env
OAUTH_MICROSOFT_CLIENT_ID=your-application-id
OAUTH_MICROSOFT_CLIENT_SECRET=your-client-secret-value
```
---
## 3. Keycloak
Keycloak ist ein Open-Source OIDC Provider. Du kannst ihn selbst hosten oder eine gehostete Lösung nutzen.
### Option A: Selbst gehostet mit Docker
```bash
docker run -d \
-p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:latest \
start-dev
```
Dann:
1. Öffne http://localhost:8080
2. Login mit `admin` / `admin`
3. **Realm erstellen**: Oben links Dropdown → **Create realm**
- Name: `yourpart` (oder beliebig)
4. Im neuen Realm: **Clients****Create client**
- Client ID: `yourpart`
- Client Protocol: `openid-connect`
- Access Type: `confidential`
5. Im **Settings** Tab:
- Valid Redirect URIs:
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
6. Speichern
7. Gehe zu **Credentials** Tab
- **Client Secret** kopieren
### Option B: Gehosteter Service
- [Keycloak.cloud](https://www.keycloak.cloud/) oder
- [Red Hat Managed Keycloak](https://www.keycloak.org/cloud/)
### .env:
```env
OAUTH_KEYCLOAK_ISSUER=https://your-keycloak-domain/realms/yourpart
OAUTH_KEYCLOAK_CLIENT_ID=yourpart
OAUTH_KEYCLOAK_CLIENT_SECRET=your-client-secret
```
---
## 4. ORY Hydra / ORY Cloud
ORY ist ein moderner OIDC Provider. Am einfachsten ist ORY Cloud.
### Option A: ORY Cloud (empfohlen)
1. Öffne [ORY Cloud Console](https://console.ory.sh/)
2. Registriere dich oder logge dich ein
3. Erstelle ein neues **Project**
4. Gehe zu **Applications**
5. Klick **Create New Application**
6. Gib einen Namen ein (z.B. "YourPart")
7. Unter **Redirect URLs** füge ein:
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
8. Speichern
9. Die **Client ID** und **Client Secret** werden angezeigt
10. Finde deine **Issuer URL** in den Project Settings (meist `https://your-project-slug.eu.hydra.cloud`)
### Option B: Selbst gehostet (komplex)
Siehe [ORY Hydra Dokumentation](https://www.ory.sh/hydra/docs/)
### .env:
```env
OAUTH_ORY_ISSUER=https://your-project-slug.eu.hydra.cloud
OAUTH_ORY_CLIENT_ID=your-client-id
OAUTH_ORY_CLIENT_SECRET=your-client-secret
```
---
## 5. ZITADEL
ZITADEL ist ein Zero-Trust Identity Platform als SaaS.
### Credentials besorgen:
1. Öffne [ZITADEL Console](https://zitadel.cloud/)
2. Registriere dich oder logge dich ein
3. Erstelle eine neue **Organization** (oder verwende die existierende)
4. Gehe zu **Projects** → **+ New Project**
5. Gib einen Namen ein (z.B. "YourPart")
6. Gehe zum Projekt → **Applications** → **+ New Application**
7. Wähle **Type: Web**
8. Gib einen Namen ein
9. Bei **Redirect URIs** füge ein:
```
https://www.your-part.de/auth/oauth/callback
https://www.your-part.de/auth/oauth/user/callback
```
10. Speichern
11. Unter **Client Information** kopiere:
- **Client ID**
- **Client Secret** (falls sichtbar, sonst im "CREDENTIALS" Tab generieren)
12. Finde deine **Issuer URL** in den Organization Settings (meist `https://your-instance.zitadel.cloud`)
### .env:
```env
OAUTH_ZITADEL_ISSUER=https://your-instance.zitadel.cloud
OAUTH_ZITADEL_CLIENT_ID=your-client-id
OAUTH_ZITADEL_CLIENT_SECRET=your-client-secret
```
---
## Komplette .env für alle 5 Provider
```env
# Google
OAUTH_GOOGLE_CLIENT_ID=...
OAUTH_GOOGLE_CLIENT_SECRET=...
# Microsoft
OAUTH_MICROSOFT_CLIENT_ID=...
OAUTH_MICROSOFT_CLIENT_SECRET=...
# Keycloak
OAUTH_KEYCLOAK_ISSUER=...
OAUTH_KEYCLOAK_CLIENT_ID=...
OAUTH_KEYCLOAK_CLIENT_SECRET=...
# ORY
OAUTH_ORY_ISSUER=...
OAUTH_ORY_CLIENT_ID=...
OAUTH_ORY_CLIENT_SECRET=...
# ZITADEL
OAUTH_ZITADEL_ISSUER=...
OAUTH_ZITADEL_CLIENT_ID=...
OAUTH_ZITADEL_CLIENT_SECRET=...
```
---
## Schnell-Checkliste
- [ ] Google: Client ID & Secret
- [ ] Microsoft: Application ID & Client Secret
- [ ] Keycloak: Issuer, Client ID, Secret
- [ ] ORY: Issuer, Client ID, Secret
- [ ] ZITADEL: Issuer, Client ID, Secret
- [ ] Alle Redirect URIs in den Providern konfiguriert
- [ ] .env datei aktualisiert
- [ ] Server neu gestartet (`npm restart`)
---
## Testing
Nach der Konfiguration:
1. Frontend öffnen: http://www.your-part.de
2. Auf "Login" oder Provider-Button klicken
3. Jeder verfügbare Provider sollte als Button angezeigt werden
4. Test: Mit jedem Provider einloggen
5. Test: Existender Nutzer → Einstellungen → Authentifizierung hinzufügen
---
## Troubleshooting
### "Invalid redirect URI"
- Stelle sicher, dass die Redirect URIs **exakt** übereinstimmen (inkl. `https://` vs `http://`)
- Beachte Trailing Slashes
### "Invalid client secret"
- Kopiere das Secret neu (nicht die ID)
- Manche Provider verstecken das Secret nach einmaliger Anzeige
### "Discovery endpoint not found"
- Überprüfe die Issuer URL (mit/ohne Trailing Slash)
- Für Keycloak: URL muss auf `/realms/xxx` enden
- Für ORY: URL darf nicht auf `/` enden
### Port-Konflikt lokal
- Keycloak benutzt `8080` → ändere auf: `docker run -p 8081:8080 ...`
- Stelle sicher, dass 3000 (Frontend) und 5000 (Backend) frei sind

0
PERFORMANCE_ANALYSIS.md Executable file → Normal file
View File

View File

@@ -1,3 +0,0 @@
## zum testen des push
Hinweis: Das Verzeichnis **`src/`** (C++-Worker) ist veraltet; siehe [`docs/LEGACY_CPP_WORKERS.md`](docs/LEGACY_CPP_WORKERS.md).

0
README_MATCH3_CAMPAIGN.md Executable file → Normal file
View File

0
SELL_OVERVIEW.md Executable file → Normal file
View File

View File

@@ -1,168 +0,0 @@
# SSL/TLS Setup für YourPart Daemon
Dieses Dokument beschreibt, wie Sie SSL/TLS-Zertifikate für den YourPart Daemon einrichten können.
## 🚀 Schnellstart
### 1. Self-Signed Certificate (Entwicklung/Testing)
```bash
./setup-ssl.sh
# Wählen Sie Option 1
```
### 2. Let's Encrypt Certificate (Produktion)
```bash
./setup-ssl.sh
# Wählen Sie Option 2
```
### 3. Apache2-Zertifikate verwenden (empfohlen für Ubuntu)
```bash
./setup-ssl.sh
# Wählen Sie Option 4
# Verwendet bereits vorhandene Apache2-Zertifikate
# ⚠️ Warnung bei Snakeoil-Zertifikaten (nur für localhost)
```
### 4. DNS-01 Challenge (für komplexe Setups)
```bash
./setup-ssl-dns.sh
# Für Cloudflare, Route53, etc.
```
## 📋 Voraussetzungen
### Für Apache2-Zertifikate:
- Apache2 installiert oder Zertifikate in Standard-Pfaden
- Unterstützte Pfade (priorisiert nach Qualität):
- `/etc/letsencrypt/live/your-part.de/fullchain.pem` (Let's Encrypt - empfohlen)
- `/etc/letsencrypt/live/$(hostname)/fullchain.pem` (Let's Encrypt)
- `/etc/apache2/ssl/apache.crt` (Custom Apache2)
- `/etc/ssl/certs/ssl-cert-snakeoil.pem` (Ubuntu Standard - nur localhost)
### Für Let's Encrypt (HTTP-01 Challenge):
- Port 80 muss verfügbar sein
- Domain `your-part.de` muss auf den Server zeigen
- Kein anderer Service auf Port 80
### Für DNS-01 Challenge:
- DNS-Provider Account (Cloudflare, Route53, etc.)
- API-Credentials für DNS-Management
## 🔧 Konfiguration
Nach der Zertifikats-Erstellung:
1. **SSL in der Konfiguration aktivieren:**
```ini
# /etc/yourpart/daemon.conf
WEBSOCKET_SSL_ENABLED=true
WEBSOCKET_SSL_CERT_PATH=/etc/yourpart/server.crt
WEBSOCKET_SSL_KEY_PATH=/etc/yourpart/server.key
```
2. **Daemon neu starten:**
```bash
sudo systemctl restart yourpart-daemon
```
3. **Verbindung testen:**
```bash
# WebSocket Secure
wss://your-part.de:4551
# Oder ohne SSL
ws://your-part.de:4551
```
## 🔄 Automatische Erneuerung
### Let's Encrypt-Zertifikate:
- **Cron Job:** Täglich um 2:30 Uhr
- **Script:** `/etc/yourpart/renew-ssl.sh`
- **Log:** `/var/log/yourpart/ssl-renewal.log`
### Apache2-Zertifikate:
- **Ubuntu Snakeoil:** Automatisch von Apache2 verwaltet
- **Let's Encrypt:** Automatische Erneuerung wenn erkannt
- **Custom:** Manuelle Verwaltung erforderlich
## 📁 Dateistruktur
```
/etc/yourpart/
├── server.crt # Zertifikat (Symlink zu Let's Encrypt)
├── server.key # Private Key (Symlink zu Let's Encrypt)
├── renew-ssl.sh # Auto-Renewal Script
└── cloudflare.ini # Cloudflare Credentials (falls verwendet)
/etc/letsencrypt/live/your-part.de/
├── fullchain.pem # Vollständige Zertifikatskette
├── privkey.pem # Private Key
├── cert.pem # Zertifikat
└── chain.pem # Intermediate Certificate
```
## 🛠️ Troubleshooting
### Zertifikat wird nicht akzeptiert
```bash
# Prüfe Zertifikats-Gültigkeit
openssl x509 -in /etc/yourpart/server.crt -text -noout
# Prüfe Berechtigungen
ls -la /etc/yourpart/server.*
```
### Let's Encrypt Challenge fehlgeschlagen
```bash
# Prüfe Port 80
sudo netstat -tlnp | grep :80
# Prüfe DNS
nslookup your-part.de
# Prüfe Firewall
sudo ufw status
```
### Auto-Renewal funktioniert nicht
```bash
# Prüfe Cron Jobs
sudo crontab -l
# Teste Renewal Script
sudo /etc/yourpart/renew-ssl.sh
# Prüfe Logs
tail -f /var/log/yourpart/ssl-renewal.log
```
## 🔒 Sicherheit
### Berechtigungen
- **Zertifikat:** `644` (readable by all, writable by owner)
- **Private Key:** `600` (readable/writable by owner only)
- **Owner:** `yourpart:yourpart`
### Firewall
```bash
# Öffne Port 80 für Let's Encrypt Challenge
sudo ufw allow 80/tcp
# Öffne Port 4551 für WebSocket
sudo ufw allow 4551/tcp
```
## 📚 Weitere Informationen
- [Let's Encrypt Dokumentation](https://letsencrypt.org/docs/)
- [Certbot Dokumentation](https://certbot.eff.org/docs/)
- [libwebsockets SSL](https://libwebsockets.org/lws-api-doc-master/html/group__ssl.html)
## 🆘 Support
Bei Problemen:
1. Prüfen Sie die Logs: `sudo journalctl -u yourpart-daemon -f`
2. Testen Sie die Zertifikate: `openssl s_client -connect your-part.de:4551`
3. Prüfen Sie die Firewall: `sudo ufw status`

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownSettings">
<option name="previewPanelProviderInfo">
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
</option>
</component>
</project>

View File

@@ -1,5 +0,0 @@
<project version="4">
<component name="ProjectRootManager" version="2">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/android.iml" filepath="$PROJECT_DIR$/.idea/android.iml" />
</modules>
</component>
</project>

6
android/.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@@ -1,605 +0,0 @@
# Android-App-Plan fuer YourPart3
Stand: 2026-07-08
## Ziel
Dieses Dokument plant eine Android-App fuer das bestehende YourPart3-Projekt unter `/android`.
Das Projekt besteht aktuell aus:
- `frontend`: Vue 3, Vite, Vuetify, Vue Router, Vuex, Axios, Socket.IO Client, Three.js
- `backend`: Node.js/Express 5, Sequelize, Redis-Sessiondaten, OAuth/OIDC, Socket.IO, REST-APIs
- Authentifizierung: Login liefert einen User mit `id`/`authCode`; API-Requests senden Header `userid` und `authcode`
- Realtime: Socket.IO fuer Backend-Events und separater Daemon-WebSocket fuer Falukant-Updates
- Deployment: Backend serviert die gebaute SPA aus `frontend/dist`
Backend-Implementierung ist nicht Teil dieses Android-Starts. Das vorhandene Backend wird als gegeben betrachtet; Android-seitig werden nur Kompatibilitaet, URLs, Auth-Header, OAuth-Weiterleitung und Realtime-Verbindungen getestet.
## Empfehlung
Die Android-App sollte in Phase 1 als Capacitor-App umgesetzt werden, nicht als nativer Rewrite.
Begruendung:
- Die bestehende App ist bereits eine grosse SPA mit vielen Views: Social Network, Falukant, Vokabeltrainer, Minigames, Kalender, Admin, Settings.
- Der Auth-Mechanismus und die vorhandenen API-Clients sind Web-orientiert und koennen in einer WebView nahezu unveraendert weiterlaufen.
- OAuth-Callback-Routen existieren bereits im Frontend (`/auth/oauth/callback`, `/auth/oauth/user/callback`).
- Socket.IO und WebSocket-Verbindungen funktionieren in Capacitor deutlich schneller als in einem nativen Rewrite.
- Ein nativer Rewrite wuerde zuerst API-Vertraege, Sessionmodell, Navigation, Offline-Strategie und UI-Komponenten neu definieren muessen.
Zielarchitektur Phase 1:
```text
/android
capacitor.config.ts
package.json
android/
Android-native Projektdateien, durch Capacitor generiert
/frontend
bestehende Vue/Vite-App
/backend
bestehende Express/API/Socket.IO-App
```
Capacitor verwendet den bestehenden `frontend`-Build als Web-Bundle und erzeugt daraus eine installierbare Android-App.
## Nicht-Ziele fuer Phase 1
- Kein kompletter nativer Kotlin/Jetpack-Compose-Rewrite.
- Keine Offline-First-Synchronisation fuer Falukant/Social/Vocab.
- Keine komplette Neugestaltung aller mobilen Screens.
- Keine API-Versionierung fuer mobile Clients, solange die App nur das bestehende Web-Frontend verpackt.
- Keine Play-Store-Verteilung vor Datenschutz-, Content- und OAuth-Pruefung.
- Keine Backend-Neuentwicklung und keine Backend-API-Neuplanung.
## Getroffene Entscheidungen
### App-Technologie
- Entscheidung: Capacitor mit lokal gebuendelter Vue/Vite-App.
- Kein nativer Kotlin-Rewrite in Phase 1.
- Kein reiner Remote-WebView-Wrapper auf `https://www.your-part.de`.
- Begruendung: Der vorhandene Funktionsumfang ist breit, Web-Auth und Socket.IO existieren bereits, und Capacitor ermoeglicht spaetere native Erweiterungen ohne sofortigen Rewrite.
### App-ID und Name
- App-ID: `de.yourpart.app`
- Launcher-Name: `YourPart`
- Android-Projektpfad: `/android`
- Capacitor `webDir`: `../frontend/dist`
### Ziel-Distribution
- Phase 1: interne Debug-/Test-APK.
- Phase 2: signiertes internes AAB/APK fuer Testgeraete.
- Play Store erst nach separatem Compliance-Check fuer Datenschutz, UGC, Moderation und Adult Content.
### Backend-Abgrenzung
- Backend bleibt unveraendert.
- Android nutzt die bestehenden REST-Endpunkte, Auth-Header, OAuth-Routen, Socket.IO-Events und Daemon-WebSocket-Events.
- Backendbezogene TODOs sind nur Test- und Konfigurationschecks. Falls ein Test scheitert, wird der konkrete Anpassungsbedarf danach separat entschieden.
### Admin-Bereich
- Admin-Routen bleiben in Phase 1 nicht priorisiert.
- Wenn Admin-Menues durch bestehende Berechtigungen sichtbar sind, werden sie nicht aktiv entfernt.
- Abnahmekriterien fuer Phase 1 gelten nur fuer normale Nutzerfunktionen.
### Adult-/Erotikbereiche
- Phase 1: vorhandenes Web-Gating bleibt bestehen, keine neue native Adult-Content-Funktion.
- Play-Store-Ziel ist blockiert, bis Altersfreigabe, UGC-Moderation, Melden/Blockieren, Datenschutz und Store-Policy separat geprueft sind.
- Fuer interne APK-Tests darf der Bereich technisch erreichbar bleiben, wenn der bestehende Account-/Altersstatus ihn erlaubt.
### OAuth
- Phase 1 startet mit Username/Passwort-Login als Pflichtfunktion.
- OAuth ist Phase-1-Testumfang, aber kein Blocker fuer das erste Debug-APK.
- Zielrichtung fuer OAuth: externer Browser bzw. System-Browser plus App Links, nicht OAuth in einer versteckten WebView erzwingen.
### Push Notifications
- Push ist nicht Teil des ersten Android-Scaffolds.
- Push wird nach stabiler App-Shell geplant, weil dafuer Device Tokens, Opt-in, Settings und Backend-Zustellung noetig sind.
### 3D / WebGL
- 3D-Charaktere werden im Android-Debug-Build initial per `VITE_DISABLE_3D=true` deaktiviert.
- Begruendung: Die Login-Seite rendert mehrere `Character3D`-Instanzen sofort. In Kombination mit CORS-Fehlern und vielen GLB-Kandidaten kann der Emulator-WebView-Renderer per OOM abstuerzen.
- Reaktivierung erfolgt erst nach erfolgreichem CORS-Test und separatem 3D-Performance-Test.
### Navigation
- Vue `createWebHistory` bleibt initial unveraendert.
- Hash-Routing wird nur eingefuehrt, wenn Capacitor-Tests echte Routing-Probleme zeigen.
- Android Back Button wird als native App-Anforderung in Phase 1 umgesetzt.
- Mobile Navigation bleibt Teil der Web-App, wird aber fuer Android als kompakte Hybrid-Navigation gehaertet: Header-Leiste, aufklappbares scrollbares Menue, keine dauerhaft sichtbare Desktop-Menueflaeche auf kleinen Displays.
### Sichere Speicherung
- Phase 1 darf bestehendes `localStorage`/`sessionStorage` weiterverwenden.
- Vor Play-Store-Release wird Auth-Persistenz auf Secure Storage/Android Keystore umgestellt.
## Technische Ausgangslage
### Frontend
- Zentrale API-Konfiguration: `frontend/src/utils/axios.js`
- API-Basis-URL: `frontend/src/utils/appConfig.js` ueber `VITE_API_BASE_URL`
- Auth-Header: `userid` und `authcode`
- Persistenz: `localStorage` oder `sessionStorage` fuer `isLoggedIn`, `user`, `userid`
- Router: `createWebHistory`, viele Clean-URL-Routen
- Realtime: `socket.io-client` ueber `VITE_SOCKET_IO_URL`
- Daemon-WebSocket: ueber `VITE_DAEMON_SOCKET`
- 3D/Assets: Three.js und Modelle ueber `/api/models` bzw. statische Assets
### Backend
- CORS erlaubt aktuell konfigurierte Origins und lokale Web-Origins.
- Auth-Middleware prueft `userid` und `authcode`.
- OAuth startet serverseitig per Redirect.
- Socket.IO erlaubt aktuell `origin: '*'`.
- Backend serviert SPA-Fallback fuer Nicht-API-Routen.
## Android-Strategie
### Phase 1: Capacitor Shell
Die Android-App laedt nicht die Produktionswebsite remote, sondern nutzt das lokal gebundelte Vite-Build-Artefakt.
Vorteile:
- App startet auch dann, wenn die Website selbst nicht als Web-Seite geladen werden muss.
- Google Play bewertet sie eher als App statt als reinen Browser-Shortcut.
- Versionierbare Builds mit reproduzierbarer Web-Bundle-Version.
- Zugriff auf native Funktionen bleibt moeglich: Splash Screen, Deep Links, Push Notifications, sichere Speicherung.
Wichtige Anpassung:
- `VITE_API_BASE_URL`, `VITE_SOCKET_IO_URL`, `VITE_DAEMON_SOCKET` muessen fuer Android-Builds explizit auf die produktiven HTTPS/WSS-Endpunkte gesetzt werden.
- Relative API-URLs sind in einer gebuendelten Android-WebView riskant, weil `window.location.origin` nicht der Server-Origin ist.
### Phase 2: Mobile Web-Haertung
Die bestehende UI muss fuer kleine Viewports und Touch-Nutzung stabilisiert werden.
Prioritaeten:
- Login/Register/OAuth
- Hauptnavigation
- Falukant-Overview, Branch, Family, Bank
- Vokabeltrainer und Lessons
- Chat/Friends/Forum-Basics
- Minigames nur nach separater Touch-Performance-Pruefung
### Phase 3: Native Integrationen
Nach stabiler Shell koennen echte App-Features ergaenzt werden:
- Push Notifications fuer Chat, Freund-Login, Falukant-Events
- Deep Links fuer OAuth-Callbacks und geteilte Inhalte
- Android Back Button Integration
- Splash Screen und App Icons
- Secure Storage fuer Authdaten
- App Update / Version Check
### Phase 4: Selektiv native Screens
Wenn einzelne Bereiche Performance- oder UX-Probleme haben, koennen sie spaeter nativ ersetzt werden. Kandidaten:
- Login/Onboarding
- Push Notification Center
- Vokabeltrainer Session
- Falukant Status/Quick Actions
## Repository-Layout
Vorgeschlagenes Layout:
```text
android/
ANDROID_APP_PLAN.md
README.md
package.json
capacitor.config.ts
android/ # Capacitor Android-Projekt
scripts/
build-android-web.sh
sync-android.sh
```
Hinweis: In Phase 1 wird `/android/android` generiert, weil `/android` die Capacitor-Projektwurzel ist. Die generierten Android-Dateien werden versioniert, weil reproduzierbare Builds und native Anpassungen wichtig sind.
## Build-Konzept
### Android Web-Build
Ein eigener Build-Modus verhindert Vermischung mit Web-Production:
```bash
cd frontend
npm run build -- --mode android
```
Dafuer wird eine Datei `frontend/.env.android` benoetigt:
```env
VITE_API_BASE_URL=https://www.your-part.de
VITE_SOCKET_IO_URL=https://www.your-part.de
VITE_DAEMON_SOCKET=wss://www.your-part.de
VITE_PUBLIC_BASE_URL=https://www.your-part.de
```
Wichtig: `VITE_API_BASE_URL` ist bewusst nur die Origin ohne `/api`, weil die bestehenden Frontend-Aufrufe bereits Pfade wie `/api/auth/login` enthalten. Mit `/api` in der Base-URL entstehen Android-seitig falsche Requests wie `/api/api/...`.
Die exakten WebSocket-Pfade muessen gegen die produktive Apache-/Backend-Konfiguration verifiziert werden.
### Lokaler Emulator-Stand
Android Studio und mehrere AVDs sind lokal vorhanden. Fuer die ersten Stabilitaetstests ist `trainingstagebuchApi35` die bevorzugte VM, weil sie im bisherigen Test stabiler lief als das Play-Store-Image `Medium_Phone`.
```bash
ANDROID_AVD_HOME=/home/torsten/.config/.android/avd /home/torsten/Android/Sdk/emulator/emulator -avd trainingstagebuchApi35 -no-snapshot-save
adb install -r android/android/app/build/outputs/apk/debug/app-debug.apk
adb shell am start -W -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n de.yourpart.app/.MainActivity
```
Ergebnis bisher:
- Debug-APK wurde erfolgreich gebaut.
- APK wurde auf einem Emulator installiert.
- App startet als `de.yourpart.app/.MainActivity`.
- Login-Screen rendert mobil.
- API-Base-URL wurde von `/api` auf die Origin `https://www.your-part.de` korrigiert, damit keine `/api/api`-Requests entstehen.
- Capacitor `server.hostname` darf nicht auf `www.your-part.de` gesetzt werden, weil sonst echte Backend- und Model-URLs als lokale App-Assets behandelt werden.
- Nach Entfernung der Hostname-Kollision ist die Android-App-Origin `https://localhost`. Das Backend muss diese Origin in `CORS_ORIGINS` erlauben, sonst blockiert die WebView REST- und GLB-Requests.
### Capacitor Sync
Nach dem Frontend-Build:
```bash
cd android
npx cap sync android
```
Danach:
```bash
cd android
npx cap open android
```
Oder per CLI:
```bash
cd android
./gradlew assembleDebug
```
Lokaler Build-Hinweis:
- Auf diesem System liegt das Android SDK unter `/home/torsten/Android/Sdk`.
- Die lokale Datei `/android/android/local.properties` setzt `sdk.dir` darauf und ist absichtlich nicht versioniert.
- Erstes Debug-APK wurde erfolgreich gebaut: `/android/android/app/build/outputs/apk/debug/app-debug.apk`.
## Authentifizierung
### Bestehender Login
Der bestehende Login kann initial unveraendert bleiben:
- `POST /api/auth/login`
- Antwort enthaelt User inkl. `authCode`
- Axios haengt `userid` und `authcode` an Folgerequests
- Storage bleibt vorerst `localStorage`/`sessionStorage`
### Sicherheitsverbesserung
Phase 1 kann noch mit Web Storage starten. Vor Play-Store-Release sollte Auth aber in native sichere Speicherung verschoben werden:
- Capacitor Preferences nur fuer unkritische Werte
- Fuer sensible Werte besser Android Keystore ueber ein Secure-Storage-Plugin
- Migration: Store liest zuerst Secure Storage, fallback auf lokalen Web Storage, migriert dann
### OAuth
OAuth ist der groesste Integrationspunkt.
Kurzfristige Option:
- OAuth im In-App Browser oder externen Browser starten
- Callback bleibt auf `https://www.your-part.de/auth/oauth/callback`
- Die Callback-Seite tauscht `code/state` wie bisher gegen einen App-User
Bessere App-Option:
- Android App Links fuer `https://www.your-part.de/auth/oauth/callback`
- `assetlinks.json` auf der Domain bereitstellen
- Capacitor App Plugin verarbeitet den Deep Link und routet intern weiter
Risiko:
- OAuth Provider koennen eingebettete WebViews einschraenken. Deshalb sollte OAuth nicht in einer versteckten WebView erzwungen werden, sondern ueber Browser/App Links laufen.
## API und CORS
Fuer Capacitor-Bundles ist die Origin nicht immer identisch mit der Website-Origin.
Zu pruefen:
- Welche Origin sendet Android WebView bei Requests an `https://www.your-part.de/api`?
- Muss `CORS_ORIGINS` um `https://localhost` erweitert werden?
- Funktionieren Custom Header `userid` und `authcode` in der WebView?
- Funktionieren Preflight-Requests mit den bestehenden erlaubten Headers?
Noetige Backend-Konfiguration fuer Debug-Builds:
```env
CORS_ORIGINS=https://www.your-part.de,https://localhost,http://localhost:5173,http://127.0.0.1:5173
```
Nur nach Test setzen; nicht blind `CORS_ALLOW_ALL=1` fuer Produktion verwenden.
## Navigation und Deep Links
Die Vue-App nutzt `createWebHistory`. In Capacitor kann das funktionieren, aber folgende Punkte muessen getestet werden:
- Direktstart auf `/`
- Interne Navigation zu `/falukant/home`, `/friends`, `/socialnetwork/vocab/...`
- Android Back Button
- OAuth Callback URL
- App-Resume nach Browser-OAuth
Falls History-Mode Probleme in der WebView macht:
- Option A: Capacitor-spezifisch auf Hash-History wechseln
- Option B: History beibehalten und Deep-Link-Routing sauber behandeln
Hash-History waere technisch einfacher, aber wegen bestehender SEO-/Web-Routen nicht global fuer das Web-Frontend umstellen.
## Realtime
### Socket.IO
Bestehender Ablauf:
- Nach Login `initializeSocket`
- Socket verbindet zu `VITE_SOCKET_IO_URL`
- Client sendet `setUserId` mit `hashedId` oder `id`
Android-Testfaelle:
- Login erzeugt Socket-Verbindung
- App im Hintergrund trennt/reconnectet sauber
- Friend-Login-Events kommen an
- Schlechte Verbindung erzeugt keine Endlos-Fehlerdialoge
### Daemon-WebSocket
Falukant nutzt zusaetzliche Daemon-Events. Android muss mindestens diese Events empfangen koennen:
- `falukantUpdateFamily`
- `falukantUpdateStatus`
- `falukantUpdateProductionCertificate`
- `children_update`
- `falukantUpdateChurch`
- `falukantUpdateDebt`
Test:
- WSS ueber produktiven Proxy
- Reconnect nach App-Resume
- Filterung nach `user_id`
## Mobile UX Prioritaeten
Die App sollte nicht nur ein Desktop-Layout in einer WebView zeigen. Phase 1 braucht mindestens diese UX-Haertung:
- Header/Navigation auf kleinen Viewports pruefen
- Dialoge auf 360px Breite testen
- Tabellen und breite Falukant-Views horizontal oder responsiv absichern
- Touch-Ziele mindestens ca. 44px
- Keyboard-Verhalten im Login/Register testen
- Safe Area Insets fuer Statusbar/Navigationbar beachten
- Android Back Button: Dialog schliessen, sonst Router zurueck, sonst App minimieren
Aktueller Stand 2026-07-08:
- Hauptnavigation ist auf kleinen Viewports zu einer kompakten Menueleiste mit aufklappbarem, scrollbarem Menue umgebaut.
- Header ist auf Smartphone-Breite kompakter, Statusanzeigen laufen zweispaltig statt als lange Desktop-Leiste.
- Footer blendet leere System-/Fensterbereiche auf Smartphone-Breite aus und reserviert Safe-Area-Abstand nach unten.
- App-Shell nutzt `100dvh` als Android-WebView-freundlichere Viewport-Hoehe.
## Datenschutz, Content und Store-Risiken
Das Projekt enthaelt Social-, Chat-, Galerie-, Erotik-/Adult- und Moderationsbereiche. Fuer Play Store sind diese Punkte kritisch:
- Altersfreigabe und Adult-Content-Gating
- UGC-Moderation, Meldefunktion, Blockieren
- Datenschutzrichtlinie in der App und im Store Listing
- Account-Loeschung oder klare Anleitung
- Sichere Uebertragung nur per HTTPS/WSS
- Keine unsicheren Debug-Endpunkte im Release-Build
- Keine Secrets im Android-Bundle
Vor Play-Store-Release muss ein eigener Compliance-Check erfolgen.
## Teststrategie
### Lokale Tests
- Android Emulator mit Debug-Build
- Echtes Android-Geraet im gleichen Netz
- Produktions-API mit Testnutzer
- Offline/Online-Wechsel
- App Kill/Restart/Resume
### Kern-Testmatrix
- Login mit Username/Passwort
- Logout
- Registrierung
- Passwort vergessen
- OAuth Login je Provider, soweit konfiguriert
- Menu-Load nach Login
- Falukant Overview laden
- Falukant Realtime-Update empfangen
- Vokabeltrainer Lesson starten und abschliessen
- Chat verbinden und Nachricht empfangen
- Galerie/Bild-Upload, falls mobil zunaechst erlaubt
- Minigames Touch-Steuerung
- Admin-Bereiche entweder nutzbar oder bewusst ausgeblendet
### Build-Checks
- `npm run build` im Frontend
- Android Web-Build mit `.env.android`
- `npx cap sync android`
- `./gradlew assembleDebug`
- `./gradlew lint`
- Release-Build mit Signing-Konfiguration
## Verbleibende offene Punkte
- Produktivdomain final technisch testen: voraussichtlich `https://www.your-part.de`.
- Daemon-WebSocket-URL und Pfad final gegen Deploy-/Proxy-Konfiguration testen.
- Mindest-Android-Version aus Capacitor-Default uebernehmen und nach erstem Scaffold im Gradle-Projekt dokumentieren.
- Play-Store-Entscheidung bleibt nachgelagert bis Compliance-Pruefung abgeschlossen ist.
## TODO
### 1. Grundsatzentscheidungen
- [x] App-ID festlegen: `de.yourpart.app`.
- [x] App-Name und Launcher-Label festlegen: `YourPart`.
- [x] Ziel-Distribution festlegen: zuerst interne Debug-/Test-APK.
- [x] Entscheiden, ob Admin-Routen in der App sichtbar bleiben: nicht priorisiert, nicht aktiv entfernt.
- [x] Entscheiden, wie Adult-/Erotikbereiche in Android behandelt werden: bestehendes Web-Gating, kein Play Store ohne Compliance-Check.
- [x] Entscheiden, ob Backend Teil des Android-Starts ist: nein, nur Kompatibilitaetstests.
- [x] Entscheiden, ob Push Teil des ersten Scaffolds ist: nein.
- [x] Entscheiden, ob OAuth erster Blocker ist: nein, Username/Passwort-Login zuerst.
### 2. Android-Projekt scaffolden
- [x] In `/android` eigenes `package.json` anlegen.
- [x] Capacitor installieren: `@capacitor/core`, `@capacitor/cli`, `@capacitor/android`.
- [x] `capacitor.config.ts` mit App-ID `de.yourpart.app`, App-Name `YourPart` und `webDir` auf `../frontend/dist` konfigurieren.
- [x] Android-Plattform generieren: `npx cap add android`.
- [x] `/android/README.md` mit Build-Kommandos anlegen.
- [x] Entscheiden, ob generiertes `/android/android` versioniert wird: ja.
### 3. Frontend Android-Build
- [x] `frontend/.env.android.example` anlegen.
- [x] `VITE_API_BASE_URL` fuer Android explizit setzen.
- [x] `VITE_SOCKET_IO_URL` fuer Android explizit setzen.
- [x] `VITE_DAEMON_SOCKET` fuer Android explizit setzen.
- [x] Root- oder Android-Script fuer `build:android:web` ergaenzen.
- [x] Android-Web-Build mit produktiver Origin statt lokaler Dev-URL bauen.
- [ ] Release-Build-Gate ergaenzen, das lokale Dev-URLs automatisiert verhindert.
### 4. Backend-Kompatibilitaet
- [ ] Android-Origin im CORS-Verhalten messen.
- [x] Fehlerhafte Android-Request-Basis `/api/api` identifizieren und durch Origin-only-Config beheben.
- [x] Capacitor-Hostname-Kollision mit Backend-Host vermeiden; `server.hostname` bleibt Default.
- [x] `CORS_ORIGINS` fuer Capacitor als bestehenden Backend-Konfigurationspunkt notieren: `https://localhost`.
- [ ] Produktiv-/Testbackend mit `CORS_ORIGINS` inklusive `https://localhost` neu starten und Android-Requests erneut pruefen.
- [ ] Custom Header `userid`/`authcode` auf Android testen.
- [ ] Socket.IO-Verbindung von Android testen.
- [ ] Daemon-WebSocket ueber Android testen.
- [ ] Produktionsproxy fuer HTTPS/WSS pruefen.
- [ ] Keine Backend-Aenderung ohne konkreten fehlgeschlagenen Android-Test einplanen.
### 5. Auth und OAuth
- [ ] Username/Passwort-Login in Android testen.
- [ ] Persistenz nach App-Neustart testen.
- [ ] Logout inklusive Socket-Cleanup testen.
- [ ] OAuth-Login-Flow je Provider testen.
- [ ] Entscheiden: OAuth per externem Browser plus App Links oder innerhalb bestehender WebView.
- [ ] Android App Links einrichten, falls OAuth nativ zurueck in die App fuehren soll.
- [ ] Authdaten spaeter in Secure Storage migrieren.
### 6. Native App-Verhalten
- [ ] Android Back Button behandeln.
- [ ] Splash Screen konfigurieren.
- [ ] App Icons erzeugen.
- [ ] Statusbar/Safe-Area pruefen; aktueller Test zeigt nicht-blockierende Safe-Area-CSS-Console-Fehler.
- [x] WebView-Textfeld-Eingabe fuer Emulator reparieren: `android.captureInput` nicht aktivieren.
- [x] Android-Studio-Projektpfad dokumentieren: `/android/android`.
- [x] Android-Studio-Run-Configuration `YourPart Debug` anlegen.
- [ ] Deep-Link-Handling vorbereiten.
- [ ] App Resume/Pause Events fuer Socket-Reconnect nutzen.
### 7. Mobile UI-Haertung
- [ ] Login/Register auf 360px Breite testen.
- [x] Login-Screen im Emulator visuell pruefen.
- [x] Parameterlisten-Handling gegen Nicht-Array-Antworten haerten, damit Backend-Fehler keine UI-Exception ausloesen.
- [x] 3D-Modelle fuer Android-Debug per `VITE_DISABLE_3D=true` deaktivieren, damit Login/Onboarding stabil bleibt.
- [x] Hauptnavigation fuer kleine Viewports umbauen: kompakte Menueleiste plus scrollbares Menue statt voller Desktop-Navigation.
- [ ] Hauptnavigation mobil mit mehreren Rollen/Berechtigungssets testen.
- [ ] Dialoge auf kleinen Screens pruefen.
- [ ] Falukant-Views mit breiten Tabellen pruefen.
- [ ] Vokabeltrainer Touch- und Keyboard-Verhalten testen.
- [ ] Minigames separat auf Touch-Performance testen.
- [ ] Bild-/Dateiupload auf Android pruefen.
### 8. Push Notifications
- [ ] Entscheiden, ob Push in Phase 1 oder spaeter kommt.
- [ ] Event-Kandidaten definieren: Chat, Friend Login, Falukant, Vocab Reminder.
- [ ] Backend-Device-Token-Modell planen.
- [ ] FCM-Projekt konfigurieren.
- [ ] Opt-in und Settings-UI planen.
### 9. Store/Compliance
- [ ] Datenschutzseite in App erreichbar machen.
- [ ] Impressum in App erreichbar machen.
- [ ] Account-Loeschung/Anfrageprozess klaeren.
- [ ] Adult Content Policy pruefen.
- [ ] UGC-Moderation fuer Store Review dokumentieren.
- [ ] Release-Build ohne Debug-Konfiguration pruefen.
### 10. CI/CD
- [x] Android-Build-Script anlegen.
- [x] Debug-Build lokal reproduzierbar machen.
- [ ] Release-Signing-Konzept festlegen.
- [ ] Keystore sicher ausserhalb des Repos verwalten.
- [ ] Optional CI-Job fuer `frontend build` + `cap sync` + Gradle Build einrichten.
## Empfohlene erste Umsetzungsschritte
1. `/android` als Capacitor-Projekt initialisieren.
2. `frontend/.env.android.example` und Android-Build-Script anlegen.
3. Debug-APK mit produktiver Test-API bauen.
4. Login, Menu, Falukant Overview und Socket.IO testen.
5. Erst danach OAuth, Deep Links und Push angehen.
## Abnahmekriterien fuer Phase 1
- App installiert und startet auf Emulator und echtem Android-Geraet.
- Login/Logout funktionieren.
- Persistierter Login funktioniert nach App-Neustart.
- API-Requests senden `userid` und `authcode` korrekt.
- Socket.IO verbindet nach Login und reconnectet nach Resume.
- Mindestens Falukant Overview, Vokabeltrainer-Liste und Social/Friends laden.
- Android Back Button fuehrt nicht zu kaputten Zustanden.
- Build ist reproduzierbar dokumentiert.
Aktueller Stand:
- Android/Capacitor-Projekt ist erzeugt.
- Android-Web-Build ist erfolgreich.
- Capacitor Sync ist erfolgreich.
- Debug-APK-Build ist erfolgreich.
- Installation und Laufzeittests auf Emulator/Geraet stehen noch aus.

View File

@@ -1,58 +0,0 @@
# Android App Links und OAuth
## Verbindliche Domain
Die Produktions-App verwendet ausschließlich `https://www.your-part.de`.
Staging und lokale Builds öffnen diese URLs weiterhin im Browser und beanspruchen keine
Domain-Verknüpfung.
## Unterstützte Pfade
| Web-Pfad | Native Route | Anmeldung |
| --- | --- | --- |
| `/` | `home` | ja |
| `/socialnetwork/*` | Community, Suche, Galerie, Forum oder Vokabeln | ja |
| `/falukant/*` | `falukant` | ja |
| `/settings/*` | `settings` | ja |
| `/blogs/*` | `blogs` | nein |
| `/guides/*` | `guides` | nein |
| `/android/oauth/callback` | OAuth-Callback | nein |
Geschützte Links bleiben im Speicher erhalten. Nach erfolgreichem Login navigiert die App
automatisch zur ursprünglich angeforderten Route.
## OAuth
Die native App startet `/api/auth/oauth/{provider}/start?client=android` in einer Android
Custom Tab. Der Server erzeugt PKCE und `state`, speichert beides in Redis und nutzt als feste
Redirect-URI `https://www.your-part.de/android/oauth/callback`. Die App sendet nur `code`,
`state` und optional `iss` an `/api/auth/oauth/exchange`; Tokens von OAuth-Providern gelangen
nicht in die App.
In jedem aktivierten Provider muss diese URI als Redirect URI hinterlegt sein:
```text
https://www.your-part.de/android/oauth/callback
```
Optional kann das Backend die URL mittels `OAUTH_ANDROID_CALLBACK_URL` überschreiben. Der Wert
muss HTTPS verwenden und exakt den Pfad `/android/oauth/callback` haben.
## assetlinks.json
Nach Erzeugung des Release-Keystores den SHA-256-Fingerprint ermitteln:
```bash
keytool -list -v -keystore release.jks -alias <alias>
```
`frontend/public/.well-known/assetlinks.json.example` nach
`frontend/public/.well-known/assetlinks.json` kopieren, den Platzhalter durch den Fingerprint
ersetzen und mit dem Frontend ausliefern. Die finale Datei muss unter dieser URL ohne Redirect
und mit `Content-Type: application/json` abrufbar sein:
```text
https://www.your-part.de/.well-known/assetlinks.json
```
Erst dann kann Android `android:autoVerify` erfolgreich abschließen.

View File

@@ -1,869 +0,0 @@
# Native Android App Plan fuer YourPart3
Stand: 2026-07-08
## Zielbild
Dieses Dokument plant eine komplette native Android-App fuer YourPart3 als langfristige Ablösung bzw. Ergänzung der aktuellen Capacitor-Hybrid-App.
Ziel ist eine echte Android-App mit:
- Kotlin
- Jetpack Compose
- MVVM bzw. unidirektionalem UI-State
- Retrofit/OkHttp fuer REST
- Kotlinx Serialization oder Moshi fuer JSON
- Room/DataStore fuer lokale Persistenz
- Android Keystore fuer sensible Authdaten
- Socket.IO/WebSocket-Clients fuer Realtime
- Coil fuer Bilder
- WorkManager fuer Hintergrundjobs
- FCM fuer Push Notifications
Das vorhandene Backend bleibt die maßgebliche Datenquelle. Eine native Komplettumsetzung bedeutet deshalb nicht, das Backend neu zu schreiben, sondern die bestehende Web-App-Funktionalitaet systematisch in native Screens, native Navigation und robuste mobile Datenmodelle zu ueberfuehren.
## Grundentscheidung
- Die bestehende Hybrid-App bleibt als lauffaehige Zwischenloesung erhalten.
- Die native App wird parallel aufgebaut.
- Nicht als Big Bang migrieren. Stattdessen werden Modulgruppen nacheinander nativ umgesetzt und gegen produktionsnahe APIs getestet.
- Admin- und Adult-Bereiche werden nicht im ersten nativen MVP umgesetzt.
- Backend-Aenderungen sind nur erlaubt, wenn sie fuer stabile mobile API-Vertraege, Sicherheit, Push oder Datei-Uploads notwendig sind.
## Native Zielarchitektur
```text
android-native/
app/
core/
network/
auth/
database/
realtime/
design/
common/
feature-auth/
feature-home/
feature-social/
feature-chat/
feature-falukant/
feature-vocab/
feature-settings/
feature-media/
feature-minigames/
feature-admin/ # spaeter
```
Empfohlener Pfad im Repository:
- Entweder `/android/native` fuer die neue native App neben der Capacitor-App.
- Oder spaeter Migration von `/android/android` zu einem rein nativen Gradle-Projekt.
Empfehlung: `/android/native`, damit die Hybrid-App weiter testbar bleibt.
## Technische Zielentscheidungen
- Sprache: Kotlin.
- UI: Jetpack Compose, Material 3, eigene YourPart-Design-Tokens.
- Min SDK: aus Capacitor-Projekt/Play-Store-Ziel final ableiten, voraussichtlich Android 8+ oder Android 9+.
- Navigation: Compose Navigation mit typed Routes.
- Dependency Injection: Hilt.
- REST: Retrofit + OkHttp Interceptors.
- JSON: Kotlinx Serialization, falls Backend-Antworten stabil typisiert werden; sonst Moshi als toleranterer Start.
- Lokale Einstellungen: DataStore Preferences.
- Lokale Daten: Room fuer Cache, Entitaeten, Outbox.
- Auth-Geheimnisse: EncryptedSharedPreferences oder direkter Keystore-basierter Token Store.
- Bilder: Coil.
- Datei-Uploads: OkHttp Multipart, Android Photo Picker.
- Realtime: Socket.IO Android Client plus separater OkHttp WebSocket fuer Daemon.
- Push: Firebase Cloud Messaging.
- Tests: JUnit, Turbine, MockWebServer, Compose UI Tests.
## API-Strategie
Die Web-App nutzt aktuell viele direkte REST-Aufrufe aus Komponenten. Fuer native Android muss daraus ein klarer API-Client entstehen.
Native API-Schichten:
- `AuthApi`
- `SettingsApi`
- `MenuApi`
- `SocialApi`
- `GalleryApi`
- `ForumApi`
- `ChatApi`
- `FalukantApi`
- `VocabApi`
- `CalendarApi`
- `BlogGuideApi`
- `MinigamesApi`
- `AdminApi` spaeter
Wichtige Backend-Vertraege:
- Login liefert User inklusive `id` und `authCode`.
- REST-Requests brauchen Header `userid` und `authcode`.
- Socket.IO registriert User per `setUserId`.
- Daemon-WebSocket nutzt eigene Events und User-Kontext.
- Datei-/Bild-Endpunkte muessen Android Multipart und Android Content-URIs sauber unterstuetzen.
## Migrationsprinzip
Jedes Modul wird in vier Schritten umgesetzt:
1. API-Vertrag erfassen: Endpunkte, Payloads, Fehler, Rechte.
2. Domain-Modelle definieren: Kotlin DTOs, Mapping, UI-State.
3. Native Compose-UI bauen: kleine Screens, klare Loading/Error/Empty States.
4. Gegen Backend testen: Emulator, echtes Geraet, Offline/Resume, Realtime.
## MVP-Schnitt
Ein sinnvoller nativer MVP ist nicht "alles", sondern:
- Login/Logout
- Session-Persistenz
- Home/Dashboard
- native Navigation
- Settings: Sprache und Account-Basis
- Friends/Search/Profile light
- Chat light
- Falukant Overview + Status + Branch-Liste light
- Vokabeltrainer Course/Lesson light
- Push-Grundlage optional
Alles Weitere wird danach iterativ migriert.
## Nicht-Ziele fuer den nativen MVP
- Kein vollstaendiger Admin-Bereich.
- Keine vollstaendige Falukant-Wirtschaftssimulation in Version 1.
- Keine nativen Minigames in Version 1, ausser als separate Spike-/Proof-of-Concepts.
- Keine 3D-Charakterdarstellung in Version 1.
- Keine Offline-First-Synchronisation fuer alle Module.
- Keine Store-Verteilung vor Datenschutz-/UGC-/Adult-Compliance.
## Risiken
- Sehr breite Web-App-Funktionalitaet: ein nativer Rewrite ist ein Mehrmonatsthema, nicht ein Scaffold-Thema.
- API-Vertraege sind aktuell komponentennah, nicht als Mobile API versioniert.
- Auth nutzt `userid`/`authcode` statt standardisiertem Bearer Token.
- Viele Screens erwarten Web-Layout, Dialoge und dynamische Menues.
- Falukant ist daten- und realtime-intensiv.
- Chat, Galerie, Adult Content und UGC erfordern Store-Compliance.
- Native 3D/Minigames brauchen eigene Performance-Entscheidungen.
## Abhakbare Roadmap
### 0. Projektentscheidung und Scope
Entscheidungen:
- Native App wird unter `/android/native` aufgebaut.
- Die bestehende Capacitor-App bleibt parallel erhalten, bis die native App mindestens den MVP stabil abdeckt.
- Der native MVP ist kein vollstaendiger Rewrite, sondern ein lauffaehiger nativer Kern: Auth, App-Shell, Home, Settings light, Social light, Chat light, Falukant light, Vokabeltrainer light.
- Admin wird aus dem nativen MVP ausgeschlossen.
- Adult-/Erotikbereiche werden aus dem nativen MVP ausgeschlossen und nur spaeter mit separater Compliance-Entscheidung umgesetzt.
- Minigames werden aus dem nativen MVP ausgeschlossen und spaeter als eigener Performance-/Touch-Spike bewertet.
- 3D-Charaktere werden aus dem nativen MVP ausgeschlossen und spaeter als eigener Rendering-Spike bewertet.
- Zielgeraete fuer MVP: kleine Phones ab 360dp Breite, normale Phones, spaeter Tablet-Layouts ab 600dp.
- Mindest-Android fuer MVP: Android 8.0 / API 26, sofern Dependencies und Testgeraete keine hoehere Grenze erzwingen.
- Distribution fuer MVP: interne Debug-/Test-APK. Play Store bleibt nachgelagert bis Compliance, Signing, Datenschutz und Store-Review vorbereitet sind.
Todo:
- [x] Entscheiden, ob die native App unter `/android/native` angelegt wird.
- [x] Entscheiden, ob die Capacitor-App langfristig parallel gepflegt bleibt.
- [x] Native MVP-Funktionsumfang final freigeben.
- [x] Admin-Bereich aus MVP ausschliessen oder explizit aufnehmen.
- [x] Adult-/Erotikbereiche aus MVP ausschliessen oder explizit mit Compliance-Aufwand aufnehmen.
- [x] Minigames aus MVP ausschliessen oder als separaten Spike aufnehmen.
- [x] 3D-Charaktere aus MVP ausschliessen oder als separaten Spike aufnehmen.
- [x] Zielgeraete festlegen: kleine Phones, Tablets, Mindest-Android-Version.
- [x] Play-Store-Zieltermin oder interne Distribution als Ziel definieren.
### 1. Native Projektbasis
Stand 2026-07-09:
- Native Projektbasis ist unter `/android/native` angelegt.
- Gradle Kotlin DSL ist aktiv.
- Separate Native-App-ID ist bewusst gewaehlt: `de.yourpart.nativeapp` mit Flavor-/Build-Type-Suffixen fuer parallele Installation.
- Flavors `local`, `staging`, `production` sind angelegt.
- Build Types `debug`, `release` sind angelegt.
- Versionierung ist initial definiert: `versionCode`, `versionName`, `BuildConfig.GIT_SHA`.
- Compose, Material 3, Hilt, Retrofit/OkHttp, Kotlinx Serialization JSON, Room, DataStore, Coil, WorkManager und FCM-Dependency sind im Projekt hinterlegt.
- Ein reproduzierbarer lokaler Build laeuft erfolgreich: `:app:assembleLocalDebug`.
- [x] Neues natives Gradle-Projekt unter `/android/native` erstellen.
- [x] Kotlin DSL fuer Gradle verwenden.
- [x] App-ID `de.yourpart.app` oder separate Dev-ID `de.yourpart.native.dev` entscheiden.
- [x] Produktflavors anlegen: `local`, `staging`, `production`.
- [x] Build Types anlegen: `debug`, `release`.
- [x] Versionierung definieren: `versionCode`, `versionName`, Git-Hash im Build.
- [x] Compose aktivieren.
- [x] Material 3 aktivieren.
- [x] Hilt einrichten.
- [x] Retrofit/OkHttp einrichten.
- [x] JSON-Library festlegen und einrichten.
- [x] Room einrichten.
- [x] DataStore einrichten.
- [x] Coil einrichten.
- [x] WorkManager einrichten.
- [x] FCM Dependency vorbereiten, aber noch nicht aktiv schalten.
- [x] Lint, Detekt oder Ktlint einrichten.
- [x] CI-Build-Script fuer native App definieren.
### 2. Design System
Stand 2026-07-09:
- Die Web-Farbpalette ist in native Tokens uebertragen.
- Typography, Spacing und Shapes sind als Theme-Tokens angelegt.
- Wiederverwendbare Compose-Bausteine existieren fuer Buttons, Textfelder, Dialoge, Info-/Loading-/Empty-States, Avatar und Status-Chips.
- Das Light Theme ist aktiv und die Demo-Shell nutzt die neuen Komponenten bereits.
- Ein minimales Dark-Theme-Grundgeruest existiert technisch, die Produktentscheidung dafuer bleibt aber offen.
- [x] YourPart-Farbpalette aus Web-App ableiten.
- [x] Typography fuer Android definieren.
- [x] Spacing-Skala definieren.
- [x] Shape-/Radius-System definieren.
- [x] Button-Komponenten definieren.
- [x] TextField-Komponenten definieren.
- [x] Dialog-Komponenten definieren.
- [x] Error-/Info-/Success-Komponenten definieren.
- [x] Loading/Empty-State-Komponenten definieren.
- [x] Avatar-/Image-Komponenten definieren.
- [x] Status-Chips fuer Backend/Daemon definieren.
- [x] Light Theme implementieren.
- [x] Dark Theme bewusst entscheiden: nein, nicht Teil des MVP.
- [x] Kleine Displaybreiten 360dp und 393dp als Design-Baseline testen: nein, nicht Teil des MVP.
### 3. App Shell und Navigation
Stand 2026-07-09:
- Die Demo-App ist zu einer nativen Shell mit Navigation ausgebaut.
- Top App Bar, Bottom Navigation und Drawer existieren.
- Primaere und sekundaere Bereiche sind als Route-Objekte modelliert.
- Eine erste Menue-Policy auf Basis von Session/Rollen ist vorhanden.
- Drawer-Back-Handling und Session-expired Rueckfuehrung zur Auth-Route sind umgesetzt.
- Offline-Banner sowie kompakte Backend-/Daemon-Status-Chips sind sichtbar.
- [x] Root Compose App mit Theme erstellen.
- [x] Top App Bar definieren.
- [x] Bottom Navigation fuer Hauptbereiche definieren.
- [x] Navigation Drawer fuer Sekundaerbereiche definieren.
- [x] Typed Routes fuer Auth, Home, Social, Falukant, Vocab, Settings anlegen.
- [x] Rollen-/Rechte-basierte Menueeintraege modellieren.
- [x] Backend-Menue-Response analysieren und native Menue-Policy definieren.
- [x] Android Back Button Verhalten definieren.
- [x] Dialog-Back-Handling implementieren.
- [x] Session-expired Navigation implementieren.
- [x] Offline-Banner implementieren.
- [x] Backend-/Daemon-Status sichtbar, aber kompakt darstellen.
### 4. Konfiguration und Environments
Stand 2026-07-09:
- Flavor-spezifische URLs fuer `local`, `staging`, `production` sind im Build hinterlegt.
- `local` nutzt bewusst `10.0.2.2` fuer Emulator-Zugriff.
- Release-Builds validieren automatisch, dass `staging` und `production` keine lokalen oder unverschluesselten URLs verwenden.
- Debug und Release nutzen getrennte Network-Security-Configs.
- Release verbietet Cleartext komplett.
- Feature Flags fuer `Admin`, `Adult`, `3D`, `Minigames` und `Push` sind als BuildConfig-Felder angelegt.
- Eine zentrale `AppConfig` liest die Flavor-/Build-Konfiguration aus `BuildConfig`.
- [x] API Base URLs fuer `local`, `staging`, `production` definieren.
- [x] Socket.IO URLs je Flavor definieren.
- [x] Daemon WebSocket URLs je Flavor definieren.
- [x] Lokale Emulator-Regel dokumentieren: Host-Rechner ist `10.0.2.2`.
- [x] Release-Build gegen lokale URLs blockieren.
- [x] Network Security Config fuer Debug und Release definieren.
- [x] TLS-only fuer Release sicherstellen.
- [x] Secrets aus APK fernhalten.
- [x] Feature Flags definieren: Admin, Adult, 3D, Minigames, Push.
### 5. Auth und Session
Stand 2026-07-09:
- Login, Logout und Session-Persistenz sind nativ angebunden.
- REST-Requests setzen automatisch `userid` und `authcode`.
- 401-Antworten fuehren zu Session-Loeschung und Session-expired-Hinweis.
- Registrierung, Passwort-Reset und OAuth-Provider-Liste sind als native Auth-Bausteine vorhanden.
- [x] Login-Endpunkt dokumentieren.
- [x] Login DTOs erstellen.
- [x] Login Repository implementieren.
- [x] Auth Interceptor fuer `userid` und `authcode` implementieren.
- [x] Session Store mit sicherer Speicherung implementieren.
- [x] Auto-Login beim App-Start implementieren.
- [x] Logout implementieren.
- [x] Session-expired Handling implementieren.
- [x] User-Aktivstatus pruefen.
- [x] Account gesperrt Handling implementieren.
- [x] Registrierung API-Vertrag erfassen.
- [x] Registrierung Screen implementieren.
- [ ] Account-Aktivierung Flow bewerten.
- [x] Passwort-Reset Flow implementieren.
- [x] OAuth-Provider-Liste laden.
- [ ] OAuth per Custom Tabs planen.
- [ ] App Links fuer OAuth Callback planen.
- [ ] OAuth erst nach Username/Passwort stabil aktivieren.
### 6. Netzwerk-Grundlage
Stand 2026-07-09:
- Eine zentrale `ApiResult`/`NetworkError`-Basis ist angelegt.
- Backend-Fehler werden zentral geparst und in lesbare Meldungen ueberfuehrt.
- Timeout- und Retry-Policy sind als feste Netzwerk-Defaults definiert.
- Debug-Logging laeuft nur ausserhalb von Release.
- Multipart- und Download-Helfer sind vorhanden.
- Ein MockWebServer-Test prueft den Auth-Header-Interceptor.
- Ein zentraler `NetworkRequestExecutor` fasst OkHttp-Fehlerbehandlung fuer Repository-Aufrufe zusammen.
- [x] Zentrale `ApiResult`/`NetworkError` Struktur definieren.
- [x] Fehlercodes und Backend-Fehlerformate erfassen.
- [x] Retry-Policy definieren.
- [x] Timeout-Policy definieren.
- [x] Request Logging nur fuer Debug aktivieren.
- [x] Auth Header Tests mit MockWebServer schreiben.
- [x] CORS ist nativ irrelevant, aber Backend-Origin-Checks gegen mobile Clients pruefen.
- [x] Multipart Upload Helper bauen.
- [x] Download Helper fuer Bilder/Dateien bauen.
- [x] Pagination Pattern definieren.
- [x] Refresh Pattern definieren.
### 7. Lokale Persistenz und Cache
Stand 2026-07-09:
- Ein zentraler Preferences-Store fuer UI-Sprache und Feature-Flags ist angelegt.
- Eine Room-Cache-Basis mit ersten Tabellen fuer Profil, Friends-Search, Falukant-Status und Vokabeldaten ist definiert.
- Cache-TTLs und Invalidierungsregeln sind als feste Policy hinterlegt.
- Die MVP-Entscheidung lautet: Offline-Anzeige statt Offline-First.
- Outbox/Offline-Write-Queues werden im MVP nur geplant, nicht erzwungen.
- [x] DataStore fuer UI-Sprache verwenden.
- [x] DataStore fuer Feature Flags verwenden.
- [x] Room Schema fuer User/Profile Cache definieren.
- [x] Room Schema fuer Friends/Search Cache definieren.
- [x] Room Schema fuer Falukant Status light definieren.
- [x] Room Schema fuer Vocab Course/Lesson Cache definieren.
- [x] Cache-Invalidation Regeln definieren.
- [x] Offline-Anzeige statt Offline-First fuer MVP festlegen.
- [x] Outbox fuer spaetere Offline-Aktionen nur planen, nicht im MVP erzwingen.
### 8. Realtime
Stand 2026-07-09:
- Socket.IO ist als Backend-Realtime-Transport in die native App eingebunden.
- Der Client sendet nach Login automatisch `setUserId` an Backend und Daemon.
- App-Foreground/Background koppelt die Verbindung an den Lifecycle.
- Backend- und Daemon-Status sind in der Shell sichtbar.
- Der Daemon-WebSocket-Client ist technisch angebunden.
- Realtime-Event-Basis und Daemon-Message-Parser sind vorhanden.
- Realtime-Events invalidieren nun gezielt Cache-/UI-Zustaende und erscheinen im internen Debug-Screen.
- [x] Socket.IO Android Client evaluieren.
- [x] Verbindung nach Login aufbauen.
- [x] `setUserId` nach Verbindungsaufbau senden.
- [x] Events erfassen: `forumschanged`, `friendloginchanged`, `reloadmenu`, `adultVerificationChanged`, `moderationReportChanged`, `userAccessChanged`.
- [x] Falukant-Events erfassen: `falukantUpdateStatus`, `falukantUpdateFamily`, `falukantUpdateChurch`, `falukantUpdateDebt`, `children_update`, `falukantUpdateProductionCertificate`, `falukantBranchUpdate`, `stock_change`, `familychanged`.
- [x] Socket Lifecycle an App Foreground/Background koppeln.
- [x] Reconnect Policy definieren.
- [x] Daemon-WebSocket Client implementieren.
- [x] Daemon-Message Parsing robust gegen unbekannte Events machen.
- [x] Realtime Events in Repositories einspeisen.
- [x] UI-State bei Events gezielt invalidieren.
- [x] Realtime Debug Screen fuer interne Builds planen.
### 9. Home und Dashboard
Stand 2026-07-09:
- Die nicht eingeloggte Startseite ist als native Landing/Auth-Kombination umgesetzt.
- Die eingeloggte Startseite laedt Dashboard-Daten aus dem Backend und zeigt Karten fuer Termine, Geburtstage, Falukant und Vokabeln.
- Dashboard-Widget- und Kalender-Widget-Vertraege sind nativ angebunden.
- Backend-/Daemon-Status bleibt als kompakte Info sichtbar.
- [x] Home API-Vertraege erfassen.
- [x] Eingeloggt/Nicht-eingeloggt Home getrennt modellieren.
- [x] Native Startseite fuer nicht eingeloggte Nutzer bauen.
- [x] Native Startseite fuer eingeloggte Nutzer bauen.
- [x] Dashboard Widget API-Vertraege erfassen.
- [x] Termine/Upcoming Events als native Cards umsetzen.
- [x] Falukant Kurzstatus als native Card umsetzen.
- [x] Backend-/Daemon-Status light anzeigen.
### 10. Settings
- [x] Settings API-Vertraege erfassen: `/api/settings/filter`, `/api/settings/update`, `/api/settings/account`, `/api/settings/set-account`, `/api/settings/visibilities`.
- [x] Spracheinstellung nativ implementieren.
- [x] Account-Basisdaten nativ implementieren.
- [x] Sichtbarkeitseinstellungen modellieren.
- [x] Personal/View/Sexuality/Flirt Settings priorisieren.
- [x] Interessen-Settings implementieren.
- [x] Language Assistant Settings bewerten.
- [x] Account-Loeschung oder Anfrageprozess fuer Store-Compliance klaeren.
### 11. Social Basis
- Stand 2026-07-09:
- Friends, Benutzersuche, Profil-Light und Gästebuch-Light sind nativ angebunden.
- Freundschaftsaktionen laufen in der nativen Friends-Ansicht gegen die vorhandenen Backend-APIs.
- Profilfelder werden inklusive Backend-Visibility-Regeln angezeigt.
- Moderationsmeldungen fuer Profile und Gästebucheintraege sind in der nativen UX integriert.
- [x] Friends API-Vertraege erfassen.
- [x] Friends Screen implementieren: bestehend, angefragt, offen, abgelehnt.
- [x] User Search API-Vertraege erfassen.
- [x] User Search Screen implementieren.
- [x] User Profile API-Vertraege erfassen.
- [x] Profile Light Screen implementieren.
- [x] Guestbook API-Vertraege erfassen.
- [x] Guestbook light implementieren.
- [x] Friend Request Aktionen implementieren.
- [x] Blockieren/Melden UX fuer Store-Compliance planen.
- [x] Privacy/Visibility Regeln aus Backend nativ abbilden.
### 12. Chat
Stand 2026-07-09:
- Der nativen MVP-Schnitt fuer Chat ist begonnen.
- Öffentliche Räume, eigene Räume, ein einfacher 1:1-Verlauf und Random Chat sind nativ angebunden.
- Direktnachrichten, Random Chat und die Raumlisten werden per Polling aktualisiert, bis die Socket.IO-Paritaet fuer den Chat folgt.
- Die MultiChat-Raumübersicht ist nativ verfuegbar; die historischen Socket.IO-Kommandos fuer den alten MultiChat bleiben als separate Paritaetsaufgabe offen.
- Direktnachrichten koennen gesendet und gemeldet werden.
- Push-Kandidaten fuer Chat sind identifiziert: Direktchat, Random-Chat, Raumbeitritt, Moderationsmeldungen.
- Socket.IO-Paritaet fuer Chat ist ein Pflichtpunkt und muss vor dem nativen Chat-Full-Release abgeschlossen werden.
- [x] Aktuellen Chat-Mechanismus erfassen: Dialoge, Raeume, Random Chat, MultiChat.
- [x] Chat Backend-/WebSocket-Vertraege dokumentieren.
- [x] Chat Room Liste implementieren.
- [x] 1:1 Chat MVP implementieren.
- [x] MultiChat MVP implementieren oder bewusst verschieben.
- [x] RandomChat bewusst verschieben oder implementieren.
- [x] Message Input mit Keyboard-Verhalten testen.
- [x] Neue Nachrichten per Realtime anzeigen.
- [x] Push-Kandidaten fuer Chat definieren.
- [x] Melden/Blockieren im Chat implementieren oder als Release-Blocker markieren.
- [ ] Socket.IO-Paritaet fuer Chat vollstaendig nativ umsetzen: Room-Join, Room-Events, Direktchat-Events, Random-Events und User-Registrierung ohne Polling.
### 13. Galerie und Medien
Stand 2026-07-10:
- Die Galerie wird als nativer Social-Unterbereich aufgebaut.
- Folder-Struktur, Bildliste, Sichtbarkeiten und Upload sind im nativen MVP bereits angelegt.
- Adult-Galerie und Video-Unterstuetzung bleiben bewusst aus dem ersten nativen Galerie-MVP heraus.
- [x] Galerie API-Vertraege erfassen.
- [x] Folder-Struktur nativ modellieren.
- [x] Bildliste mit Coil implementieren.
- [x] Bilddetail mit Vorschau sowie nativer Metadatenpflege fuer Titel und Sichtbarkeit implementieren.
- [x] Android Photo Picker fuer Upload implementieren.
- [x] Multipart Upload gegen den Backend-Vertrag automatisiert testen.
- [x] Bildbearbeitung aus Web-App bewerten: MVP nein. Die Web-Galerie bearbeitet nur Metadaten; Pixelbearbeitung, Crop und Filter bleiben aus dem nativen MVP ausgeschlossen.
- [x] Sichtbarkeiten laden und setzen.
- [x] Adult-Galerie aus MVP ausschliessen oder Compliance-Aufwand planen.
- [x] Video-Unterstuetzung separat planen.
### 14. Forum
Stand 2026-07-10:
- Forum, Themenliste und Themenansicht sind nativ als Social-Unterbereich umgesetzt.
- Beiträge werden nativ als Klartext gerendert; die bestehende HTML-Eingabe wird nicht ausgeführt.
- `forumschanged`, `topicschanged` und `messageschanged` aktualisieren alle offenen Forum-Ansichten gezielt.
- [x] Forum-Liste API-Vertrag erfassen.
- [x] Topic-Liste API-Vertrag erfassen.
- [x] Topic-Detail API-Vertrag erfassen.
- [x] Forum-Liste native umsetzen.
- [x] Topic-Liste native umsetzen.
- [x] Topic-Detail native umsetzen.
- [x] Antwort erstellen implementieren.
- [x] Moderation Report fuer Forum implementieren.
- [x] Realtime `forumschanged` integrieren.
### 15. Falukant MVP
Stand 2026-07-10:
- Der native Falukant-MVP ist eine Read-only-Spielstandszentrale: Übersicht, Status, Filialen, Bank, Familie und Nachrichten.
- Die Datenmodelle sind auf die bestehenden, je Spielstand unterschiedlich umfangreichen Backend-Antworten ausgelegt und ignorieren zusätzliche Felder.
- Create wird bewusst verschoben: Der bestehende Flow hängt an Charakter-, Namen- und 3D-Entscheidungen, die nicht Teil des MVP sind.
- [x] Falukant API-Endpunkte aus Web-App inventarisieren.
- [x] `StatusBar` Datenmodell nativ definieren.
- [x] Overview Screen implementieren.
- [x] Character/Familien-Basisdaten implementieren.
- [x] Branch-Liste implementieren.
- [x] Branch-Detail light implementieren.
- [x] Bank light implementieren.
- [x] Messages/Notifications light implementieren.
- [x] Realtime Falukant Events integrieren.
- [x] Create Falukant Flow bewusst verschieben.
- [x] Family View light implementieren.
- [x] Church/Reputation/Health/Nobility als Falukant-Vollausbau markieren.
- [x] Production/Storage/Sale/Director als Falukant-Vollausbau markieren.
- [x] Karten-/Regionen-Features als Falukant-Vollausbau markieren.
### 16. Falukant Vollausbau
Stand 2026-07-10:
- Der Wirtschaftsblock ist begonnen: Filialdetails laden Produktion, Lager, Inventar, Director, Fahrzeuge und laufende Transporte.
- Bereits native Kernaktionen: Produktion starten, Lager kaufen, Einzel- und Komplettverkauf, Fahrzeuge gesammelt reparieren, Director-Einkommen speichern, Transport starten und Kredit aufnehmen.
- Die verbliebenen Punkte werden erst abgehakt, sobald ihre jeweiligen Detail- und Aktionsflows nativ abgeschlossen sind.
#### 16.1 Filiale und Wirtschaft
- [x] Filialdetail-Basis laden: Filiale, Produktion, Lager, Inventar, Director, Fahrzeuge und Transporte.
- [x] Produktion starten implementieren.
- [x] Lagerkapazität kaufen implementieren.
- [x] Einzelverkauf implementieren.
- [x] Gesamtes Inventar verkaufen implementieren.
- [x] Fahrzeuge gesammelt reparieren implementieren.
- [x] Director-Einkommen speichern implementieren.
- [x] Transport starten implementieren.
- [x] Branch Detail vollstaendig umsetzen: Upgrade, Steuerübersicht, Preisvergleich und Detaildarstellung vervollständigen.
- [x] Production Section vervollständigen: laufende Produktionen, Restzeit, Qualitäts-/Wetterdaten und Abbruchregeln.
- [x] Storage Section vervollständigen: Kapazität je Typ verkaufen und verfügbare Lagertypen auswählen.
- [x] Sale Section vervollständigen: Inventarpositionen auswählen, regionale Preise vergleichen und Verkauf bestätigen.
- [x] Director Info vervollständigen: Proposal, Einstellung, Wissens-/Lehrfluss und alle Einstellungen.
- [x] Transport Routes vervollständigen: Routen-Vorschau, Kapazitätsprüfung, Wachkosten und Transportstatus.
#### 16.2 Bank und Historie
- [x] Bankübersicht und aktive Kredite laden.
- [x] Kreditaufnahme implementieren.
- [x] Bank vollstaendig umsetzen: Tilgung, Gebührenvorschau, Sperren und Schuldgefängnis-Aktionen.
- [x] Money History umsetzen: Filter, Pagination und Graphdaten.
#### 16.3 Familie und Person
- [x] Family vollstaendig umsetzen: Partnerschaft, Geschenke, Erben, Kinder und Liebesbeziehungen.
- [x] Health umsetzen: Gesundheitsstatus und Aktivitäten mit Cooldown.
- [x] Reputation umsetzen: Aktionen, Partys und Fortschritt.
- [x] Church umsetzen: Taufe, Ämter, Bewerbungen und Entscheidungen.
- [x] Nobility umsetzen: Stand, Voraussetzungen und Aufstieg.
- [x] House umsetzen: Hauskauf, Renovierung, Personal und Haushaltsordnung.
- [x] Education umsetzen: Lernende, Inhalte und Schulaktionen.
#### 16.4 Gesellschaft und Konflikt
- [x] Politics umsetzen: Übersicht, Ämter, Steuern, Ernennungen, Wahlen und Kandidaturen.
- [x] Underground umsetzen: Aktivitäten, Ziele, Angriffe und Raid-Regionen.
#### 16.5 Qualität
- [x] Performance fuer grosse Falukant-Datenmengen testen.
### 17. Vokabeltrainer MVP
- [x] Vocab Languages API-Vertrag erfassen.
- [x] Course List API-Vertrag erfassen.
- [x] Course Detail API-Vertrag erfassen.
- [x] Lesson API-Vertrag erfassen.
- [x] Review API-Vertrag erfassen.
- [x] Vocab Landing native umsetzen.
- [x] Course List native umsetzen.
- [x] Course Detail native umsetzen.
- [x] Lesson Player MVP implementieren.
- [x] Lesson Review implementieren.
- [x] Dictionary light implementieren.
- [x] Progress/Completion speichern.
- [x] Keyboard-/Audio-/Touch-Verhalten testen.
- [x] Offline Cache fuer aktive Lektion planen: aktive Lektion und Antworten werden erst in Abschnitt 18 per Room synchronisiert.
### 18. Vokabeltrainer Vollausbau
- [x] Neue Sprache anlegen implementieren.
- [x] Subscribe Flow implementieren.
- [x] Chapter View implementieren.
- [x] Dictionary vollstaendig implementieren.
- [x] Practice Dialog nativ ersetzen.
- [x] SRS-/Review-Logik gegen Backend validieren.
- [x] Offline Lesson Cache implementieren.
- [x] Sync-Konflikte definieren.
Konfliktregel: Der vom Server geladene Lektionsstand ist verbindlich. Nicht bestaetigte
SRS-Reviews bleiben mit einer lokalen Request-ID in der Outbox und werden in Reihenfolge
erneut gesendet; nach einer Server-Antwort wird der Eintrag entfernt.
### 19. Kalender und Persoenliches
- [x] Calendar API-Vertraege erfassen.
- [x] Monats-/Wochen-/Listenansicht entscheiden.
- [x] Termine laden.
- [x] Termin erstellen.
- [x] Termin bearbeiten.
- [x] Termin loeschen.
- [x] Date/Time Picker nativ einsetzen.
- [x] Reminder/Push spaeter planen.
- [x] Diary API-Vertrag erfassen.
- [x] Diary light umsetzen oder verschieben.
Entscheidung: Die mobile Umsetzung verwendet eine nach Datum sortierte Listenansicht mit
Monatsbereich. Termine nutzen `/api/calendar/events` (GET mit Datumsbereich, POST, PUT,
DELETE). Das Diary nutzt `/api/socialnetwork/diary` mit paginierter Liste und CRUD. Lokale
Reminder und Push-Benachrichtigungen werden erst nach der Push-Grundlage aus Abschnitt 12
als WorkManager-Aufgabe mit Android-Notification-Kanal umgesetzt.
### 20. Public Content
- [x] Blog List API-Vertrag erfassen.
- [x] Blog Detail API-Vertrag erfassen.
- [x] Guide List API-Vertrag erfassen.
- [x] Guide Detail API-Vertrag erfassen.
- [x] Public Landing Screens nativ priorisieren oder aus App entfernen.
- [x] Rich Text Rendering nativ loesen.
- [x] Blog Editor aus MVP ausschliessen.
- [x] Guide/Marketing Content als WebView-Fallback pruefen oder nativ rendern.
- [x] News/Blog/Guide Kurzlisten bewerten.
Entscheidung: Öffentliche Blogs nutzen `/api/blog/blogs` und `/api/blog/blogs/:id/posts`.
Ratgeber haben keinen Backend-Vertrag, sondern stammen aus dem versionierten Frontend-Katalog;
die mobile App führt dafür einen kuratierten, nativ gerenderten Lesekatalog. HTML aus
Blogbeiträgen wird als sicherer Text ohne WebView gerendert. Der Blog-Editor bleibt im Web.
News verwendet einen authentifizierungspflichtigen Drittanbieter-Proxy und wird nicht als
öffentliche Kurzliste dupliziert; Blogs und Ratgeber bleiben Drawer-Bereiche.
### 21. Minigames
#### 21.1 Match3
- [x] Match3 Game als native Compose/Canvas Machbarkeit pruefen.
- [x] Spielbrett, Tile-Modell und Zuglogik definieren.
- [x] Touch-Steuerung fuer Auswahl und Tausch definieren.
- [x] Animationen und Aufloesung von Matches definieren.
- [x] Score-/Leaderboard-API-Vertraege fuer Match3 erfassen.
- [ ] Performance auf Emulator und echtem Geraet testen. In eine spaetere gemeinsame QA-Runde verschoben.
Entscheidung: Match3 wird als natives Compose-Canvas-Spiel mit einem 8x8-Board umgesetzt.
Ein Zug besteht aus zwei Tap-Eingaben auf benachbarte Steine. Matches werden gesammelt,
aufgeloest, von oben aufgefuellt und mit 10 Punkten pro Stein bewertet. Der Fortschritt nutzt
`/api/match3/campaigns`, das erste aktive Level und den vorhandenen Progress-Endpunkt samt
Hash-Format. Admin-Routen bleiben ausgeschlossen.
#### 21.2 Taxi
- [x] Taxi Game als native Canvas/OpenGL Machbarkeit pruefen.
- [x] Spielfeld, Fahrzeugzustand und Fahrphysik definieren.
- [x] Touch-Steuerung fuer Lenken, Beschleunigen und Bremsen definieren.
- [x] Auftrags-, Ziel- und Kollisionslogik definieren.
- [x] Score-/Leaderboard-API-Vertraege fuer Taxi erfassen.
- [ ] Performance auf Emulator und echtem Geraet testen. In eine spaetere gemeinsame QA-Runde verschoben.
Entscheidung: Taxi wird als natives Compose-Canvas-Spiel umgesetzt. Vier Touch-Schaltflächen
steuern Lenken, Beschleunigen und Bremsen. Ein gelber Abholpunkt und ein grünes Ziel bilden
einen Auftrag; Verkehr erzeugt Kollisionen, drei Kollisionen oder leerer Tank beenden die
Fahrt. Highscores nutzen `/api/taxi/highscores` mit Benutzer-ID, Punkten, Fahrgästen,
Spielzeit und Kartenkennung. OpenGL ist für diese 2D-Strecke nicht erforderlich.
#### 21.3 Gemeinsame Integration
- [x] WebView-Fallback fuer Minigames als Zwischenloesung bewerten.
- [x] Gemeinsame Minigame-Navigation und Lifecycle-Verhalten definieren.
- [x] Gemeinsames Persistenz- und Abbruchverhalten definieren.
- [x] Admin-Tools fuer Minigames aus nativer App ausschliessen.
Entscheidung: Es gibt keinen WebView-Fallback. Match3 und Taxi bleiben native Canvas-
Implementierungen; der vorhandene Webbereich wird nicht in die native App eingebettet. Der
Drawer zeigt bei aktiviertem `FEATURE_MINIGAMES` nur den geschuetzten Hub `minigames`; von dort
sind die beiden geschuetzten Spielrouten erreichbar. Spielschleifen sind an die Composition
gebunden und werden beim Verlassen automatisch beendet. Match3 speichert nur abgeschlossene
Level ueber den vorhandenen Progress-Endpunkt. Taxi speichert beim Verlassen einer laufenden
Fahrt einen Zwischenstand ueber `/api/taxi/game-state`; abgeschlossene Fahrten werden als
Highscore gespeichert. Admin- und Verwaltungsrouten fuer Minigames werden nicht registriert und
bleiben ausschliesslich im Web-Adminbereich.
### 22. Admin spaeter
- [x] Entscheiden, ob Admin ueberhaupt in native App gehoert. - Ja, Admin gehört in die native App
- [x] Admin Users API-Vertraege erfassen.
- [x] Admin Rights API-Vertraege erfassen.
- [x] Moderation Reports API-Vertraege erfassen.
- [x] Adult Verification API-Vertraege erfassen.
- [x] Erotic Moderation API-Vertraege erfassen.
- [x] Forum Admin API-Vertraege erfassen.
- [x] Falukant Admin API-Vertraege erfassen.
- [x] Services Status Screen fuer interne Builds planen.
- [x] Admin nur per Feature Flag und Rollencheck sichtbar machen.
Entscheidung: Die native Administration wird nur eingeblendet, wenn sowohl
`FEATURE_ADMIN` aktiviert ist als auch die authentifizierte Anfrage an
`GET /api/navigation/:userid` einen Bereich `administration` liefert. Die vom Backend
gefilterte Navigation ist die Rechtequelle; die serverseitige Berechtigungsprüfung jeder
Admin-Route bleibt verbindlich. Direkte Navigation ohne beide Bedingungen zeigt keinen Inhalt.
API-Vertraege:
- Benutzer: `GET /api/admin/users/search?q=`, `GET /api/admin/users/:id`, `PUT /api/admin/users/:id`, `GET /api/admin/users/batch?ids=`, `GET /api/admin/users/statistics`; alle erfordern die vom Service gepruefte Berechtigung `useradministration` bzw. `mainadmin`.
- Rechte: `GET /api/admin/rights/types`, `GET /api/admin/rights/:id`, `POST /api/admin/rights/:id` und `DELETE /api/admin/rights/:id` mit `{ rightTypeId }`; Berechtigung `rights` bzw. `mainadmin`.
- Moderationsmeldungen: `GET /api/admin/moderation/reports?status=&limit=` sowie `POST /api/admin/moderation/reports/:reportId/status` mit Status und Review-Notiz; Berechtigung `forum` bzw. `mainadmin`.
- Altersverifikation: `GET /api/admin/users/adult-verification?status=`, `PUT /api/admin/users/:id/adult-verification` mit `approved`, `rejected` oder `pending`, sowie der geschuetzte Dokument-Download `GET /api/admin/users/:id/adult-verification/document`.
- Erotikmoderation: `GET /api/admin/users/erotic-moderation?status=`, geschuetzte Vorschau unter `/preview/:type/:targetId` und `PUT /api/admin/users/erotic-moderation/:id` mit erlaubter Aktion und optionaler Notiz.
- Forum: `GET` und `POST /api/forum/`, `DELETE /api/forum/:forumId`; Erstellen erwartet `{ name, permissions }`, die Service-Schicht prueft `forum` bzw. `mainadmin`. Allgemeine Meldungen laufen ueber den Moderationsvertrag.
- Falukant: Die geschuetzten Werkzeuge liegen unter `/api/admin/falukant/*`: Benutzersuche/-bearbeitung, Familien- und Schwangerschaftsaktionen, Bestands-/Region-/Distanzpflege, NPC-Auftraege und Titel. Die native Umsetzung beschraenkt sich auf klar abgegrenzte Fach-Screens mit serverseitiger `falukant`-/`mainadmin`-Pruefung; keine generische Datenbankbearbeitung.
Service-Status: Nur interne Debug-Builds erhalten einen kompakten, rein lesenden Statusscreen
auf Basis der bereits vorhandenen Backend-/Daemon-Verbindungssignale. Keine Prozessdaten,
Tokens, Konfigurationen oder Diagnose-Endpunkte werden in Release-Builds angezeigt. Die
Match3-/Taxi-Adminwerkzeuge bleiben gemaess Abschnitt 21 ausserhalb der nativen App.
### 23. Adult Content und Store-Compliance
- [x] Adult Content aus MVP entfernen oder per Feature Flag deaktivieren.
- [x] Altersverifikation nativ modellieren.
- [x] UGC-Melden nativ in Chat, Galerie, Forum, Profil implementieren.
- [x] Blockieren nativ implementieren.
- [x] Moderationserreichbarkeit dokumentieren.
- [x] Datenschutzseite nativ erreichbar machen.
- [x] Impressum nativ erreichbar machen.
- [x] Kontakt nativ erreichbar machen.
- [x] Account-Loeschung oder klare Anleitung nativ erreichbar machen.
- [x] Play Store Content Rating vorbereiten.
- [x] Store-Review-Risiko fuer Adult Content separat entscheiden.
Stand 2026-07-16: Der geschuetzte Bereich ist im lokalen Debug-Build per `FEATURE_ADULT`
aktiviert; Staging und Production bleiben bis zur Store-Entscheidung deaktiviert. Die native App
laedt den Status ueber `/api/settings/account` und laedt erst nach lokaler Pruefung von
Volljaehrigkeit und `adultAccessEnabled` die serverseitig ebenfalls geschuetzten Endpunkte unter
`/api/socialnetwork/erotic/*`. Ein Verifikationsnachweis (JPEG, PNG, WebP oder PDF) wird per
Android-Dateiauswahl an `/api/settings/adult-verification/request` gesendet. Die API prueft
zusaetzlich Alter und Verifikationsstatus. Die Account-Endpunkte weisen nun ausserdem Anfragen
ab, deren Body-`userId` nicht der authentifizierten `userid` entspricht.
Blockieren verwendet den neuen serverseitigen Vertrag `POST`/`DELETE
/api/socialnetwork/blocked-users/:userId` und die Migration
`20260716000000-create-user-block.sql`; der Profil-Screen bietet die Blockaktion nativ an.
Die Store-Entscheidung und Rating-Vorbereitung sind in `android/STORE_COMPLIANCE.md`
dokumentiert: Der Bereich bleibt ausserhalb lokaler Debug-Builds deaktiviert, bis eine formelle
Store- und Altersfreigabe vorliegt.
### 24. Push Notifications
- [x] Firebase Projekt klaeren.
- [x] FCM in native App integrieren.
- [x] Device Token Backend-Modell planen.
- [x] Device Token Registration API definieren.
- [x] Opt-in UI implementieren.
- [x] Notification Settings implementieren.
- [x] Chat Push definieren.
- [x] Friend Login Push definieren.
- [x] Falukant Event Push definieren.
- [x] Vocab Reminder Push definieren.
- [x] Deep Links aus Notifications implementieren.
- [x] Token Refresh Handling implementieren.
- [ ] Firebase-Service-Account im Backend-Deployment hinterlegen (`FIREBASE_SERVICE_ACCOUNT_PATH` oder `FIREBASE_SERVICE_ACCOUNT_JSON`).
### 25. Deep Links und OAuth
- [x] App Links Domain festlegen.
- [x] `assetlinks.json` planen.
- [x] Deep-Link-Struktur fuer Auth, Home, Social, Falukant, Vocab, Settings und Public Content definieren.
- [x] OAuth Redirect URIs fuer Android planen.
- [x] Custom Tabs Flow implementieren.
- [x] OAuth Callback Handling implementieren.
- [x] Deep Links fuer Profile, Forum, Falukant, Vocab, Blog definieren.
- [x] Deep Link Auth Guard implementieren.
- [x] Nicht eingeloggte Deep Links nach Login fortsetzen.
### 26. Sicherheit
- [x] Authdaten nicht im Klartext speichern.
- [x] Release Logging sensibler Daten verhindern.
- [x] Certificate Pinning bewerten, nicht vorschnell erzwingen.
- [x] Network Security Config fuer Release restriktiv halten.
- [x] Root/Jailbreak Detection bewusst entscheiden.
- [x] Screenshot-Schutz fuer Adult/Private Bereiche bewerten.
- [x] Datei-Uploads auf MIME/Größe pruefen.
- [x] WebView-Fallbacks minimieren.
- [x] Dependency-Scanning fuer Android einrichten.
### 27. Testing
- [x] Unit Tests fuer Auth Repository.
- [x] Unit Tests fuer Settings Repository.
- [x] Unit Tests fuer Error Mapping.
- [x] MockWebServer Tests fuer Auth Header.
- [x] MockWebServer Tests fuer API-Fehler.
- [x] Room Migration Tests.
- [x] Compose Tests fuer Login.
- [x] Compose Tests fuer Navigation.
- [x] Compose Tests fuer Home.
- [x] Compose Tests fuer Vocab Lesson.
- [x] Realtime Tests mit Testserver planen.
- [x] Emulator-Testmatrix definieren: kleines Phone, grosses Phone, Tablet.
- [x] Echtes Android-Geraet in Testmatrix aufnehmen.
- [x] Offline/Online-Wechsel testen.
- [x] App Kill/Restart/Resume testen.
- [ ] Instrumentation-Suite auf einem verbundenen Geraet oder KVM-faehigen Host ausfuehren (lokal blockiert: kein ADB-Geraet, keine x86_64-Hardwarebeschleunigung).
### 28. Build, Release und Betrieb
- [ ] Debug APK Build einrichten.
- [ ] Release AAB Build einrichten.
- [ ] Signing-Konzept definieren.
- [ ] Keystore sicher ablegen.
- [ ] Internal App Sharing oder interne Testspur planen.
- [ ] Crashlytics oder alternatives Crash Reporting entscheiden.
- [ ] Analytics bewusst entscheiden: ja/nein, Datenschutz.
- [ ] App Version Check implementieren oder planen.
- [ ] Rollback-Strategie definieren.
- [ ] Hybrid-App und Native-App Parallelbetrieb dokumentieren.
### 29. Backend-Vorbereitung fuer Native
- [ ] Release-Signaturfingerprint in die ausgelieferte `/.well-known/assetlinks.json` eintragen.
- [ ] Native OAuth-Redirect-URI bei jedem aktivierten Provider hinterlegen.
- [ ] Mobile API-Inventar aus allen Web-Komponenten erstellen.
- [ ] API-Versionierung bewerten: `/api/mobile/v1` ja/nein.
- [ ] Einheitliches Fehlerformat definieren.
- [ ] Einheitliches Pagination-Format definieren.
- [ ] Auth auf Bearer Token/JWT oder bestehendes `userid`/`authcode` final entscheiden.
- [ ] Refresh Token Konzept bewerten.
- [ ] Device Token API fuer Push planen.
- [ ] Datei-Upload-Limits dokumentieren.
- [ ] Image Thumbnail Endpunkte fuer mobile Listen planen.
- [ ] Falukant Summary Endpunkte fuer mobile Screens planen.
- [ ] Vocab Lesson Endpunkte fuer mobile Offline-Caches planen.
### 30. Abnahmekriterien MVP
- [ ] App startet kalt unter 2 Sekunden auf Testgeraet oder Zielwert begruendet anpassen.
- [ ] Login funktioniert gegen production/staging.
- [ ] Session ueberlebt App-Neustart.
- [ ] Logout entfernt lokale Session und trennt Realtime.
- [ ] Home laedt ohne WebView.
- [ ] Navigation ist vollstaendig nativ.
- [ ] Mindestens ein Social-Basisflow funktioniert.
- [ ] Mindestens ein Falukant-Basisflow funktioniert.
- [ ] Mindestens ein Vocab-Lesson-Flow funktioniert.
- [ ] Fehler werden nativ und verstaendlich angezeigt.
- [ ] Offline-Zustand wird erkannt und blockiert keine App.
- [ ] Keine lokalen Dev-URLs im Release-Build.
- [ ] Keine Secrets im APK/AAB.
- [ ] Datenschutz, Impressum und Kontakt sind erreichbar.
## Empfohlene erste Umsetzungsschritte
1. `/android/native` als neues Kotlin/Compose-Projekt anlegen.
2. Core Module fuer Network, Auth, Design und Navigation erstellen.
3. Login komplett nativ implementieren.
4. Session Store und Auth Interceptor stabilisieren.
5. Native App Shell mit Home und Navigation bauen.
6. Danach Social light, Falukant light und Vocab light nacheinander umsetzen.
## Realistische Einordnung
Die native Komplettumsetzung ist erheblich groesser als die Hybrid-App. Die Hybrid-App ist sinnvoll als lauffaehige Zwischenloesung und Referenzimplementierung. Die native App sollte als paralleles Produkt mit klarem MVP gestartet werden, sonst entsteht ein langer Rewrite ohne nutzbaren Zwischenstand.

View File

@@ -1,101 +0,0 @@
# YourPart Android
Android-App-Shell fuer YourPart auf Basis der bestehenden Vue/Vite-App und Capacitor.
## Entscheidungen
- App-ID: `de.yourpart.app`
- App-Name: `YourPart`
- Technologie: Capacitor mit lokal gebuendeltem `frontend/dist`
- Backend: bestehendes Backend wird verwendet, keine Backend-Implementierung in diesem Android-Start
- Erstes Ziel: interne Debug-/Test-APK
## Vorbereitung
1. Frontend-Env-Vorlage kopieren und Werte pruefen:
```bash
cp ../frontend/.env.android.example ../frontend/.env.android
```
2. Android-Dependencies installieren:
```bash
npm install
```
3. Web-Bundle fuer Android bauen:
```bash
npm run build:web
```
4. Android-Projekt erzeugen:
```bash
npm run add:android
```
5. Danach synchronisieren:
```bash
npm run sync
```
## Debug-Build
Nach `npm run add:android`:
```bash
npm run build:debug
```
Das erzeugte APK liegt danach unter `android/app/build/outputs/apk/debug/`.
Innerhalb dieses Repositorys ist das der Pfad `/android/android/app/build/outputs/apk/debug/`.
## Android Studio
In Android Studio muss das native Gradle-Projekt geoeffnet werden:
```text
/home/torsten/Programs/YourPart3/android/android
```
Nicht `/home/torsten/Programs/YourPart3/android` oeffnen. Dieser Ordner ist nur die Capacitor-Projektwurzel mit `package.json` und `capacitor.config.ts`; die Android-Studio-App liegt eine Ebene tiefer in `/android/android`.
Nach dem Oeffnen:
1. Gradle Sync abwarten.
2. Run Configuration `YourPart Debug` auswaehlen.
3. Emulator auswaehlen.
4. Starten.
Wenn Android Studio die Konfiguration nicht sofort anzeigt, `File > Sync Project with Gradle Files` ausfuehren oder das Projektfenster neu laden.
## Emulator-Eingabe
Die App verwendet die normale Android-WebView-Eingabe. `android.captureInput` ist bewusst nicht aktiviert, weil diese Capacitor-Option die WebView-InputConnection ersetzt und im Emulator verhindern kann, dass Textfelder normal beschrieben werden.
## Backend-CORS fuer Android-Debug
Capacitor laedt die lokale Android-App standardmaessig unter:
```text
https://localhost
```
Die API-Requests gehen weiterhin an `https://www.your-part.de/api/...`. Deshalb muss das Backend `https://localhost` in `CORS_ORIGINS` erlauben:
```env
CORS_ORIGINS=https://www.your-part.de,https://localhost,http://localhost:5173,http://127.0.0.1:5173
```
`server.hostname` in `capacitor.config.ts` darf nicht auf `www.your-part.de` gesetzt werden. Sonst interpretiert Capacitor Backend- und Model-URLs wie `/api/models/...glb` als lokale App-Assets.
## Wichtige Hinweise
- `frontend/.env.android` darf echte produktive URLs enthalten, aber keine Secrets.
- `VITE_DISABLE_3D=true` ist fuer Android-Debug absichtlich gesetzt. Das verhindert WebGL/GLB-Last auf der Login-Seite, bis CORS und 3D-Performance separat freigegeben sind.
- OAuth ist im ersten Debug-APK nicht der Blocker; Username/Passwort-Login ist die Pflichtfunktion.
- Push Notifications werden erst nach stabiler Shell geplant.
- Play Store ist vorerst kein Ziel, bis Datenschutz, UGC, Moderation und Adult-Content separat geprueft sind.

View File

@@ -1,37 +0,0 @@
# Native Android Security
## Umgesetzte Schutzmaßnahmen
- Die Sitzung liegt ausschließlich in `EncryptedSharedPreferences` mit Android-Master-Key.
- Release-Builds enthalten keinen HTTP-Body-Logger. Der detaillierte OkHttp-Logger ist auf
Nicht-Release-Builds begrenzt.
- Release-Netzwerkverkehr erlaubt kein Cleartext. Nur der lokale Debug-Flavor darf
`10.0.2.2` und `localhost` per HTTP ansprechen.
- Chat, Galerie, Persönliches und der Adult-Bereich setzen `FLAG_SECURE`. Damit verhindert
Android Screenshots, Bildschirmaufnahmen und die Anzeige im App-Switcher.
- Uploads werden vor dem Service auf einen erlaubten MIME-Typ und eine Größe begrenzt:
Bilder/Verifikationsnachweise 10 MiB, Videos 100 MiB. Bilder werden zusätzlich durch
Sharp dekodiert, damit ein behaupteter MIME-Typ nicht genügt.
## Bewusste Entscheidungen
### Certificate Pinning
Certificate Pinning wird aktuell **nicht** erzwungen. Die API verwendet HTTPS und die
plattformseitige Trust-Store-Prüfung. Pinning würde bei Zertifikats- oder CDN-Wechseln ohne
App-Update zu vollständigen Ausfällen führen. Es wird erst eingeführt, wenn es mindestens zwei
parallel gültige Pins, ein dokumentiertes Rotation-Verfahren und Monitoring für Pin-Fehler gibt.
### Root- und Bootloader-Erkennung
Die App blockiert gerootete Geräte nicht. Eine lokale Erkennung ist umgehbar und würde legitime
Nutzer sowie Emulator-Tests ausschließen. Sensible Daten bleiben verschlüsselt, Screenshots in
sensiblen Bereichen sind gesperrt und Berechtigungen werden auf dem Server durchgesetzt.
Play Integrity wird bei späteren Hochrisiko-Aktionen als serverseitig prüfbares Signal bewertet,
nicht als pauschale Startblockade.
## Dependency Scanning
`.github/workflows/android-security.yml` führt bei Pull Requests und Pushes einen
OSV-Abhängigkeitsscan sowie Android-Lint für den Produktions-Release aus. Kritische Findings
werden vor einem Release bewertet.

View File

@@ -1,17 +0,0 @@
# Store-Compliance
## Adult Content
- `FEATURE_ADULT` ist nur im lokalen Debug-Build aktiv.
- Staging und Production liefern keinen nativen Adult-Bereich aus.
- Vor einer Aktivierung sind Play-Content-Rating, Altersklassifizierung, Moderationsprozess,
Meldewege und die Store-Richtlinien erneut zu pruefen und freizugeben.
- Die API bleibt auch bei aktivem Client-Feature die Autoritaet: Volljaehrigkeit und der Status
`approved` der Altersverifikation sind fuer jeden geschuetzten Endpunkt erforderlich.
## UGC und Moderation
- Native Meldungen existieren fuer Chat, Forum, Profile, Gaestebuch und Galerie.
- Meldungen werden serverseitig gespeichert und sind fuer berechtigte Moderatoren sichtbar.
- Nutzerblockaden werden serverseitig in `community.user_block` gespeichert; die SQL-Migration
`backend/migrations/20260716000000-create-user-block.sql` ist vor dem Rollout auszufuehren.

View File

@@ -1,45 +0,0 @@
# Native Android Testmatrix
## Automatisiert
| Bereich | Testart | Ausführung |
| --- | --- | --- |
| Auth, Settings, HTTP-Fehler, Header, Daemon-Parser | JVM + MockWebServer | `./gradlew :app:testLocalDebugUnitTest` |
| Room-Migration und verschlüsselte Session nach Store-Neuerzeugung | Instrumentation | `./gradlew :app:connectedLocalDebugAndroidTest` |
| Login, Navigation, Home und Vokabel-Lektion | Compose Instrumentation | `./gradlew :app:connectedLocalDebugAndroidTest` |
| Realtime | JVM-Parser plus manueller Socket.IO-Testserver-Plan | siehe unten |
### Lokaler Ausfuehrungsstatus
- Die JVM-Suite (`:app:testLocalDebugUnitTest`) ist erfolgreich ausgefuehrt.
- Die Instrumentation-Suite inklusive Room- und Compose-Tests kompiliert erfolgreich.
- Die Ausfuehrung auf diesem Host ist blockiert: Es ist kein ADB-Geraet verbunden und die
x86_64-Emulatoren benoetigen KVM-Hardwarebeschleunigung, die hier nicht verfuegbar ist.
Das vorhandene ARM64-System-Image kann auf einem x86_64-Host nicht emuliert werden.
- Auf einem Host mit KVM oder einem verbundenen Geraet wird die Suite mit
`./gradlew :app:connectedLocalDebugAndroidTest` ausgefuehrt. Der CI-Workflow
`.github/workflows/android-tests.yml` fuehrt dieselbe Suite auf einem GitHub-Emulator aus.
## Emulator- und Geräte-Matrix
| Ziel | Pflichtfälle |
| --- | --- |
| Kleines Phone, API 34+ | Login, Navigation, Formular, Tastatur, Adult-Screenshotschutz |
| Großes Phone, API 34+ | Galerie, Falukant und Vokabel-Lektion |
| Tablet, API 34+ | Drawer, Landscape und Listenbreiten |
| Reales Android-Gerät, aktuelle API | Push-Berechtigung, OAuth Custom Tab, App Links, Kamera/Dateiauswahl |
Die Gradle Managed Devices `mediumPhone` und `mediumTablet` decken die CI-Basis ab. Das lokale
Team ergänzt für jeden Release-Lauf die installierten kleinen/großen Emulatoren und mindestens
ein reales Gerät.
## Netzwerk, Realtime und Lifecycle
1. Backend und Daemon starten, mit einem Testkonto anmelden und Socket.IO-Verbindung prüfen.
2. Backend stoppen: Offline-Banner, verständlicher Fehler und keine blockierte Navigation prüfen.
3. Backend wieder starten: Screen aktualisieren und Realtime-Verbindung erneut prüfen.
4. App im Hintergrund beenden und über Launcher oder Push erneut öffnen: verschlüsselte Sitzung,
Deep-Link-Fortsetzung und Realtime-Reconnect prüfen.
5. Für Socket.IO-Regressionen wird ein isolierter Node-Testserver mit den Ereignissen
`friendloginchanged`, `falukantUpdateStatus`, `familychanged` und `reloadmenu` verwendet.
Der Android-Test verbindet sich mit dessen URL statt mit Production.

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="YourPart Debug">
<option name="selectionMode" value="DROPDOWN" />
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@@ -1,68 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="YourPart Debug" type="AndroidRunConfigurationType" factoryName="Android App">
<option name="ANDROID_RUN_CONFIGURATION_SCHEMA_VERSION" value="1" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
<option name="ARTIFACT_NAME" value="" />
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
<option name="ALLOW_ASSUME_VERIFIED" value="false" />
<option name="CLEAR_APP_STORAGE" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="default_activity" />
<option name="RESTORE_ENABLED" value="false" />
<option name="RESTORE_FILE" value="" />
<option name="RESTORE_FRESH_INSTALL_ONLY" value="false" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="DEBUGGER_TYPE" value="Auto" />
<module name="android.app.main" />
<Auto>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
</Auto>
<Hybrid>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
</Hybrid>
<Java>
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
</Java>
<Native>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY" value="" />
<option name="ACTIVITY_CLASS" value="" />
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,54 +0,0 @@
apply plugin: 'com.android.application'
android {
namespace = "de.yourpart.app"
compileSdk = rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "de.yourpart.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}
apply from: 'capacitor.build.gradle'
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}

View File

@@ -1,19 +0,0 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
}
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-app')
}
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}

View File

@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -1,26 +0,0 @@
package de.yourpart.app;
import static org.junit.Assert.*;
import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("de.yourpart.app", appContext.getPackageName());
}
}

View File

@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
android:name=".MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@@ -1,5 +0,0 @@
package de.yourpart.app;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,34 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View File

@@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">YourPart</string>
<string name="title_activity_main">YourPart</string>
<string name="package_name">de.yourpart.app</string>
<string name="custom_url_scheme">de.yourpart.app</string>
</resources>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash</item>
</style>
</resources>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
<cache-path name="my_cache_images" path="." />
</paths>

View File

@@ -1,18 +0,0 @@
package de.yourpart.app;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

View File

@@ -1,30 +0,0 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
// Trigger sync
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.13.0'
classpath 'com.google.gms:google-services:4.4.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
apply from: "variables.gradle"
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -1,6 +0,0 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')

View File

@@ -1,22 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true

Binary file not shown.

View File

@@ -1,7 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,251 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -1,94 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,5 +0,0 @@
include ':app'
include ':capacitor-cordova-android-plugins'
project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')
apply from: 'capacitor.settings.gradle'

View File

@@ -1,16 +0,0 @@
ext {
minSdkVersion = 24
compileSdkVersion = 36
targetSdkVersion = 36
androidxActivityVersion = '1.11.0'
androidxAppCompatVersion = '1.7.1'
androidxCoordinatorLayoutVersion = '1.3.0'
androidxCoreVersion = '1.17.0'
androidxFragmentVersion = '1.8.9'
coreSplashScreenVersion = '1.2.0'
androidxWebkitVersion = '1.14.0'
junitVersion = '4.13.2'
androidxJunitVersion = '1.3.0'
androidxEspressoCoreVersion = '3.7.0'
cordovaAndroidVersion = '14.0.1'
}

View File

@@ -1,22 +0,0 @@
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'de.yourpart.app',
appName: 'YourPart',
webDir: '../frontend/dist',
bundledWebRuntime: false,
server: {
androidScheme: 'https'
},
plugins: {
SplashScreen: {
launchAutoHide: true
}
},
android: {
allowMixedContent: false,
webContentsDebuggingEnabled: true
}
};
export default config;

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="YourPartNative.app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-07-10T13:18:31.238846181Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/torsten/.config/.android/avd/Medium_Tablet.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="/mnt/share/torsten/Programs/YourPart3/android/native" />
<option value="/mnt/share/torsten/Programs/YourPart3/android/native/app" />
</set>
</option>
</GradleProjectSettings>
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="/mnt/share/torsten/Programs/YourPart3/android/native" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="/mnt/share/torsten/Programs/YourPart3/android/native" />
<option value="/mnt/share/torsten/Programs/YourPart3/android/native/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownSettings">
<option name="previewPanelProviderInfo">
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
</option>
</component>
</project>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="/mnt/share/torsten/Programs/YourPart3/android/native" />
</set>
</option>
</component>
</project>

View File

@@ -1,9 +0,0 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

Some files were not shown because too many files have changed in this diff Show More