15 KiB
Android-App-Konzept fuer YpChat
Zielbild
YpChat 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
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:
https://www.ypchat.net
Development:
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:
GET /api/sessionaufrufen, damit das Backend einconnect.sid-Cookie setzt und diesessionIdzurueckgibt.- Socket.IO zu derselben Basis-URL verbinden.
- Nach
connectdas EventsetSessionIdsenden:
{
"expressSessionId": "<sessionId aus /api/session>"
}
- Server sendet
connected. - Wenn
connected.loggedIn === true, App-State wiederherstellen. - 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:
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 EmitsChatRepository: verbindet REST, Socket und lokalen App-StateChatViewModel: bietetStateFlow<ChatUiState>fuer ComposeSessionCookieJar: persistiertconnect.sid, damit REST und Socket dieselbe Session nutzenImageUploader: 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.sidCookie- letzter bekannter Login-State fuer UI-Skeleton
- Logout-Marker, analog
singlechat_logged_outim Web - keine Chatnachrichten dauerhaft im MVP, weil Backend diese aktuell nur im Arbeitsspeicher haelt
Reconnect
Empfohlener Ablauf:
- App startet.
- Wenn kein Logout-Marker vorhanden:
GET /api/session. - Wenn Session
loggedIn: Socket verbinden undsetSessionIdsenden. - Wenn Socket reconnectet: erneut
GET /api/session, dannsetSessionId. - Bei
connect_error: exponentielles Retry mit sichtbarem Offline-Banner. - 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
loginSuccessoder Session-Restore. - Timer wird bei Senden, Empfangen, Suche und Conversation Requests zurueckgesetzt.
- Bei Ablauf: lokaler Logout und optional
POST /api/logout.
Bildversand
Ablauf:
- Benutzer waehlt Bild per Photo Picker.
- App prueft MIME-Type und Groesse.
- Optional: Bild auf sinnvolle Chat-Groesse komprimieren, z.B. max. 1600 px Kantenlaenge.
POST /api/upload-imageals Multipartimage.- Backend antwortet mit:
{
"success": true,
"code": "<code>",
"url": "/api/image/<code>"
}
- App sendet Socket-Event
messagemitisImage: true,message: code,imageUrl: url. - 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/sessionvor Socket-Verbindung. - WebSocket ueber Apache sollte sauber funktionieren. Android kann Polling fallbacken, aber echte WebSocket-Verbindung ist fuer Akku und Latenz besser.
/api/upload-imagehat aktuell einen Fallback auf den zuletzt aktiven Client. Fuer Mobile waere sauberer: eindeutig ueberreq.sessionIDvalidieren 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
INTERNETPermission 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
Configfuer 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
loginEventconnected,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:
GET /api/session- Socket.IO connect
setSessionId- Login
- Empfang von
userList - Senden und Empfangen einer Textnachricht
Wenn dieser Durchstich steht, ist der groesste technische Unsicherheitsblock geloest. Danach ist der Rest vor allem UI- und Zustandsarbeit.