Files
singlechat/ANDROID-APP-KONZEPT.md
Torsten Schulz (notebook) 84f57facba android version
2026-05-12 10:21:24 +02:00

397 lines
15 KiB
Markdown

# Android-App-Konzept fuer SingleChat
## Zielbild
SingleChat soll als echte Android-App verfuegbar werden, nicht nur als WebView-Wrapper. Die Android-App nutzt die bestehenden Backend-Endpunkte und spricht mit dem vorhandenen Socket.IO-Server dasselbe Ereignisprotokoll wie das Vue-Web-Frontend.
Das Ziel fuer den ersten Release ist Funktionsgleichheit mit dem Kern-Chat:
- Login mit Benutzername, Geschlecht, Alter und Land
- Anzeige aktiver Benutzer
- Suche nach Benutzern
- Einzelchat mit Textnachrichten
- Bildversand ueber bestehenden Upload-Endpunkt
- Inbox mit ungelesenen Chats
- Verlauf geoeffneter Konversationen
- Blockieren und Entblockieren von Benutzern
- Session-Wiederherstellung, Logout und 30-Minuten-Inaktivitaetslogik
- Feedback- und Partnerseiten optional im MVP, aber technisch ueber vorhandene REST-Endpunkte moeglich
## Bestand Im Repo
Das aktuelle System besteht aus:
- Backend: Node.js/Express in `server/index.js`
- REST-Endpunkte in `server/routes.js`
- Socket.IO-Chat-Protokoll in `server/broadcast.js`
- Vue/Pinia-Webclient mit Socket.IO-Client in `client/src/stores/chat.js`
- Bild-Upload im Webclient ueber `client/src/components/ChatInput.vue`
Der Server erlaubt Socket.IO mit `websocket` und `polling`. Das Web-Frontend nutzt aktuell absichtlich nur `polling`, vermutlich wegen Proxy-/WebSocket-Problemen. Fuer Android sollte WebSocket als bevorzugter Transport genutzt werden, mit Polling als Fallback.
## Technische Empfehlung
### App-Technologie
Empfohlen: native Android-App mit Kotlin und Jetpack Compose.
Gruende:
- echte App-Erfahrung statt WebView
- robuste Hintergrund-/Reconnect-Logik
- gute Kontrolle ueber Cookies, Sessions und Uploads
- moderne UI mit Compose schneller wartbar
- bessere Basis fuer spaetere Push Notifications
Alternative: React Native oder Flutter waeren moeglich, bringen aber fuer diese App keinen klaren Vorteil, weil das Protokoll einfach ist und Android explizit das Ziel ist.
### Zielarchitektur
```text
Android App
UI: Jetpack Compose
State: ViewModel + StateFlow
REST: Retrofit/OkHttp
Socket: Socket.IO Android Client
Session: OkHttp CookieJar + EncryptedSharedPreferences/DataStore
Images: Android Photo Picker + Multipart Upload
Existing Backend
Express REST API
Socket.IO Chat Events
express-session Cookie connect.sid
```
## Backend-Anbindung
### Basis-URL
Production:
```text
https://www.ypchat.net
```
Development:
```text
http://10.0.2.2:3300
```
`10.0.2.2` ist im Android Emulator der Host-Rechner. Auf echtem Geraet braucht es die lokale LAN-IP oder einen Dev-Tunnel.
### REST-Endpunkte
Die Android-App kann folgende vorhandene Endpunkte direkt verwenden:
| Zweck | Methode | Pfad | Android-Nutzung |
|---|---:|---|---|
| Health Check | GET | `/api/health` | Diagnose/Verbindungscheck |
| Session holen | GET | `/api/session` | Cookie initialisieren, Session wiederherstellen, Express-Session-ID erhalten |
| Logout | POST | `/api/logout` | serverseitige Session beenden und Socket trennen |
| Laenderliste | GET | `/api/countries` | Login-Land-Auswahl und Flag-Code |
| Bild hochladen | POST | `/api/upload-image` | Multipart-Upload mit Session-Cookie |
| Bild abrufen | GET | `/api/image/:code` | Anzeige empfangener Bilder |
| Partner | GET | `/api/partners` | optionaler App-Screen |
| Feedback laden | GET | `/api/feedback` | optionaler Feedback-Screen |
| Feedback senden | POST | `/api/feedback` | optionaler Feedback-Screen |
| Feedback Admin Login | POST | `/api/feedback/admin-login` | optional, eher nicht MVP |
| Feedback Admin Logout | POST | `/api/feedback/admin-logout` | optional, eher nicht MVP |
| Feedback loeschen | DELETE | `/api/feedback/:id` | optional, eher nicht MVP |
Wichtig: REST und Socket muessen dieselbe Cookie-Session verwenden. Android braucht daher einen gemeinsamen `OkHttpClient` mit persistenter Cookie-Verwaltung.
## Socket.IO-Protokoll
### Verbindungsaufbau
Ablauf analog zum Web-Frontend:
1. `GET /api/session` aufrufen, damit das Backend ein `connect.sid`-Cookie setzt und die `sessionId` zurueckgibt.
2. Socket.IO zu derselben Basis-URL verbinden.
3. Nach `connect` das Event `setSessionId` senden:
```json
{
"expressSessionId": "<sessionId aus /api/session>"
}
```
4. Server sendet `connected`.
5. Wenn `connected.loggedIn === true`, App-State wiederherstellen.
6. Sonst Login-Screen anzeigen.
### Client sendet
| Event | Payload | Zweck |
|---|---|---|
| `setSessionId` | `{ "expressSessionId": string }` | Socket mit Express-Session verknuepfen |
| `login` | `{ "userName": string, "gender": string, "age": number, "country": string, "expressSessionId": string }` | Chat-Login |
| `message` | `{ "toUserName": string, "message": string, "messageId": string }` | Textnachricht |
| `message` | `{ "toUserName": string, "message": imageCode, "messageId": string, "isImage": true, "imageUrl": string }` | Bildnachricht nach REST-Upload |
| `requestConversation` | `{ "withUserName": string }` | Konversation laden und als gelesen markieren |
| `userSearch` | `{ "nameIncludes"?, "minAge"?, "maxAge"?, "countries"?, "genders"? }` | Benutzer suchen |
| `requestHistory` | kein Payload | Chatverlauf-Liste laden |
| `requestOpenConversations` | kein Payload | Inbox/ungelesene Chats laden |
| `blockUser` | `{ "userName": string }` | Benutzer blockieren |
| `unblockUser` | `{ "userName": string }` | Benutzer entblockieren |
### Server sendet
| Event | Payload | Android-State |
|---|---|---|
| `connected` | `{ "sessionId": string, "loggedIn"?: boolean, "user"?: User }` | Session setzen, ggf. Login wiederherstellen |
| `loginSuccess` | `{ "sessionId": string, "user": User }` | Login-State setzen |
| `userList` | `{ "users": User[] }` | Online-Liste aktualisieren |
| `message` | `{ "from": string, "message": string, "messageId": string, "timestamp": string, "isImage"?, "imageUrl"?, "imageCode"? }` | Nachricht empfangen |
| `messageSent` | `{ "messageId": string, "to": string }` | lokale Nachricht bestaetigen |
| `conversation` | `{ "with": string, "messages": Message[] }` | Konversation anzeigen |
| `searchResults` | `{ "results": User[] }` | Suchergebnisse anzeigen |
| `historyResults` | `{ "results": HistoryItem[] }` | Verlauf anzeigen |
| `inboxResults` | `{ "results": InboxItem[] }` | Inbox anzeigen |
| `unreadChats` | `{ "count": number }` | Badge aktualisieren |
| `commandResult` | `{ "lines": string[], "kind": string }` | Admin-/Slash-Command-Hinweise anzeigen |
| `commandTable` | `{ "title": string, "columns": string[], "rows": unknown[][] }` | Admin-/Statistik-Tabelle anzeigen |
| `userBlocked` | `{ "userName": string }` | Blockierstatus setzen |
| `userUnblocked` | `{ "userName": string }` | Blockierstatus entfernen |
| `error` | `{ "message": string }` | Snackbar/Dialog anzeigen |
## Android-Modulstruktur
Vorschlag fuer ein neues Modul oder separates Repo:
```text
android/
app/
src/main/
java/net/ypchat/app/
MainActivity.kt
YpChatApp.kt
core/
Config.kt
SessionCookieJar.kt
NetworkModule.kt
data/
api/
RestApi.kt
SocketClient.kt
model/
UserDto.kt
MessageDto.kt
ConversationDto.kt
repository/
ChatRepository.kt
FeedbackRepository.kt
ui/
login/
chat/
users/
search/
inbox/
history/
feedback/
common/
```
### Verantwortlichkeiten
- `RestApi`: Retrofit-Definitionen fuer `/api/*`
- `SocketClient`: kapselt Socket.IO-Verbindung, Event-Handler und Emits
- `ChatRepository`: verbindet REST, Socket und lokalen App-State
- `ChatViewModel`: bietet `StateFlow<ChatUiState>` fuer Compose
- `SessionCookieJar`: persistiert `connect.sid`, damit REST und Socket dieselbe Session nutzen
- `ImageUploader`: Photo Picker, Komprimierung falls noetig, Multipart Upload
## UI-Konzept
### Navigation
MVP-Screens:
- Login
- Online-Benutzer
- Suche
- Chat
- Inbox
- Verlauf
- Profil/Logout
Optional:
- Feedback
- Partner
- Regeln/Sicherheit/FAQ als native Info-Screens oder WebContent aus statischen Texten
### Mobile UX
Der Webclient ist desktop-/browsernah. Die App sollte mobiler denken:
- Startet in Login oder zuletzt aktiver Chat-Ansicht
- Bottom Navigation fuer `Online`, `Suche`, `Inbox`, `Verlauf`
- Chat-Screen mit fester Eingabezeile unten
- Online-Status und Flagge direkt in User-Zeilen
- Unread-Badge auf Inbox-Tab
- Bildauswahl ueber Android Photo Picker
- Fehlermeldungen als Snackbar, kritische Session-Fehler als Dialog
## Session- und Reconnect-Konzept
### Persistenz
Gespeichert werden lokal:
- `connect.sid` Cookie
- letzter bekannter Login-State fuer UI-Skeleton
- Logout-Marker, analog `singlechat_logged_out` im Web
- keine Chatnachrichten dauerhaft im MVP, weil Backend diese aktuell nur im Arbeitsspeicher haelt
### Reconnect
Empfohlener Ablauf:
1. App startet.
2. Wenn kein Logout-Marker vorhanden: `GET /api/session`.
3. Wenn Session `loggedIn`: Socket verbinden und `setSessionId` senden.
4. Wenn Socket reconnectet: erneut `GET /api/session`, dann `setSessionId`.
5. Bei `connect_error`: exponentielles Retry mit sichtbarem Offline-Banner.
6. Bei Logout: `POST /api/logout`, Socket disconnect, Cookie loeschen, Logout-Marker setzen.
### Inaktivitaet
Backend und Webclient verwenden 30 Minuten. Android sollte dieselbe Regel im UI spiegeln:
- Timer startet nach `loginSuccess` oder Session-Restore.
- Timer wird bei Senden, Empfangen, Suche und Conversation Requests zurueckgesetzt.
- Bei Ablauf: lokaler Logout und optional `POST /api/logout`.
## Bildversand
Ablauf:
1. Benutzer waehlt Bild per Photo Picker.
2. App prueft MIME-Type und Groesse.
3. Optional: Bild auf sinnvolle Chat-Groesse komprimieren, z.B. max. 1600 px Kantenlaenge.
4. `POST /api/upload-image` als Multipart `image`.
5. Backend antwortet mit:
```json
{
"success": true,
"code": "<code>",
"url": "/api/image/<code>"
}
```
6. App sendet Socket-Event `message` mit `isImage: true`, `message: code`, `imageUrl: url`.
7. Anzeige erfolgt ueber volle URL `https://www.ypchat.net/api/image/<code>`.
Hinweis: Der Backend-Endpunkt erlaubt 5 MB Upload und haelt Bilder temporaer fuer 6 Stunden.
## Backend-Anpassungen Vor Android-Release
Die App kann grundsaetzlich mit dem aktuellen Backend starten. Sinnvolle kleine Anpassungen wuerden die Mobile-Integration aber robuster machen:
- CORS ist fuer mobile Apps weniger kritisch, aber Socket.IO-Origin-Handling sollte getestet werden, weil native Clients oft keinen Browser-Origin senden.
- Session-Handling sollte fuer Android explizit dokumentiert werden: `GET /api/session` vor Socket-Verbindung.
- WebSocket ueber Apache sollte sauber funktionieren. Android kann Polling fallbacken, aber echte WebSocket-Verbindung ist fuer Akku und Latenz besser.
- `/api/upload-image` hat aktuell einen Fallback auf den zuletzt aktiven Client. Fuer Mobile waere sauberer: eindeutig ueber `req.sessionID` validieren und keine Aktivitaets-Heuristik verwenden.
- Nachrichten und Konversationen liegen aktuell im Arbeitsspeicher. Fuer eine App mit Reconnect/Background-Nutzung sollte mittelfristig Persistenz ergaenzt werden.
- Optional: API-Versionierung einfuehren, z.B. `/api/v1/session`, bevor App-Versionen langfristig im Umlauf sind.
## Datenschutz Und Store-Themen
Vor Veroeffentlichung im Play Store beachten:
- Datenschutzerklaerung direkt in App verlinken
- klare Alters-/Community-Regeln im Onboarding
- Hinweis, dass Chatnachrichten temporaer serverseitig verarbeitet werden
- Bild-Upload transparent erklaeren
- Melde-/Blockierfunktion prominent erreichbar machen
- Android `INTERNET` Permission erforderlich
- Keine Speicherpermission noetig, wenn Android Photo Picker genutzt wird
Wenn die App spaeter Push Notifications bekommt, braucht es FCM, Datenschutz-Ergaenzung und serverseitige Device-Token-Verwaltung.
## MVP-Backlog
### Phase 1: Android-Projekt
- Gradle/Kotlin/Compose-Projekt anlegen
- App-Theme und Basisnavigation erstellen
- `Config` fuer Dev/Prod-Basis-URL
- OkHttp, Retrofit und Socket.IO-Client einrichten
- Persistente CookieJar implementieren
### Phase 2: Session Und Login
- `GET /api/session`
- Socket-Verbindung mit `setSessionId`
- Login-Screen
- `login` Event
- `connected`, `loginSuccess`, `error`
- Logout mit `/api/logout`
### Phase 3: Chat-Kern
- Online-Userliste via `userList`
- Chat-Screen
- `requestConversation`
- Textnachrichten senden/empfangen
- `messageSent`, `unreadChats`
- Reconnect und Offline-Banner
### Phase 4: Suche, Inbox, Verlauf
- Suchformular und `userSearch`
- Suchergebnisse
- Inbox mit `requestOpenConversations`
- Verlauf mit `requestHistory`
- Blockieren/Entblockieren
### Phase 5: Bilder
- Android Photo Picker
- Multipart Upload zu `/api/upload-image`
- Bildnachricht via Socket senden
- Bildanzeige ueber `/api/image/:code`
- Fehlerbehandlung fuer abgelaufene Bilder
### Phase 6: Release-Haertung
- ProGuard/R8-Regeln fuer Socket.IO/OkHttp testen
- Crash-/Error-Logging entscheiden
- Play Store Icons, Screenshots, App Name
- Datenschutz-/Impressum-/Regeln-Screens
- Test auf Emulator, echtem Android-Geraet, schlechtem Netz und App-Background
## Risiken
| Risiko | Auswirkung | Gegenmassnahme |
|---|---|---|
| Socket.IO Android Client Version passt nicht zum Server | Verbindungsfehler | Version gegen Socket.IO Server 4.x testen, `allowEIO3` ist serverseitig aktiv |
| Session-Cookie wird nicht zwischen REST und Socket geteilt | Login/Upload funktionieren inkonsistent | gemeinsame OkHttp CookieJar und expliziter `setSessionId` Flow |
| Apache WebSocket-Proxy ist instabil | Reconnects/Latenz | WebSocket-Regeln pruefen, Polling als Fallback |
| Backend speichert Chat nur im RAM | Nachrichten nach Server-Restart weg | fuer MVP akzeptieren, spaeter DB-Persistenz |
| App im Hintergrund verliert Socket | Nachrichten kommen nur bei geoeffneter App | fuer MVP akzeptieren, spaeter Push Notifications |
| Bild-Upload-Session-Fallback ist unscharf | falsche Zuordnung theoretisch moeglich | Backend vor Release eindeutiger machen |
## Offene Entscheidungen
- Soll Android zuerst als separates Repo oder als `android/` Ordner in diesem Repo entstehen?
- Soll der MVP nur Chat enthalten oder auch Feedback/Partner/FAQ?
- Soll das bestehende Design exakt nachgebaut oder fuer Mobile bewusst neu interpretiert werden?
- Soll die erste Version ohne Push Notifications starten?
## Empfehlung Fuer Den Naechsten Schritt
Ich wuerde als naechstes ein Android-Projekt im Ordner `android/` scaffolden und zuerst nur den technischen Durchstich bauen:
1. `GET /api/session`
2. Socket.IO connect
3. `setSessionId`
4. Login
5. Empfang von `userList`
6. Senden und Empfangen einer Textnachricht
Wenn dieser Durchstich steht, ist der groesste technische Unsicherheitsblock geloest. Danach ist der Rest vor allem UI- und Zustandsarbeit.