added dummy accounts, change passwort is now a feature, toast when session takes to long, online frontend fix

This commit is contained in:
oxidiert
2025-10-16 13:19:54 +02:00
parent 39a4811fd2
commit 5b1264293c
4 changed files with 351 additions and 83 deletions

View File

@ -25,19 +25,6 @@ object DatabaseUploader {
private val client = OkHttpClient() private val client = OkHttpClient()
fun uploadDatabaseWithLogin(context: Context, username: String, password: String) {
LoginManager.loginUserWithCredentials(
context = context,
username = username,
password = password,
onSuccess = { token ->
Log.d("UPLOAD", "Login OK (user=$username)")
uploadDatabase(context, token)
},
onError = { msg -> Log.e("UPLOAD", "Login fehlgeschlagen: $msg") }
)
}
private fun uploadDatabase(context: Context, token: String) { private fun uploadDatabase(context: Context, token: String) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
@ -171,6 +158,8 @@ object DatabaseUploader {
} }
fun uploadDatabaseWithToken(context: Context, token: String) { fun uploadDatabaseWithToken(context: Context, token: String) {
uploadDatabase(context, token) // nutzt die bestehende interne Logik uploadDatabase(context, token)
} }
} }

View File

@ -13,6 +13,8 @@ import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import java.io.File import java.io.File
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import com.google.android.material.dialog.MaterialAlertDialogBuilder
var RHS_POINTS: Int? = null var RHS_POINTS: Int? = null
@ -32,6 +34,8 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
private lateinit var databaseButton: Button private lateinit var databaseButton: Button
private lateinit var statusSession: TextView private lateinit var statusSession: TextView
private lateinit var statusOnline: TextView private lateinit var statusOnline: TextView
private val SESSION_WARN_AFTER_MS = 12 * 60 * 60 * 1000L // 12h
private var sessionLongWarnedOnce = false
private val dynamicButtons = mutableListOf<Button>() private val dynamicButtons = mutableListOf<Button>()
private val questionnaireFiles = mutableMapOf<Button, String>() private val questionnaireFiles = mutableMapOf<Button, String>()
@ -63,6 +67,11 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
fun init() { fun init() {
activity.setContentView(R.layout.opening_screen) activity.setContentView(R.layout.opening_screen)
// <<< NEU: bei jedem Öffnen des Screens zurücksetzen,
// damit der Toast pro Besuch einmal angezeigt wird
sessionLongWarnedOnce = false
bindViews() bindViews()
loadQuestionnaireOrder() loadQuestionnaireOrder()
createQuestionnaireButtons() createQuestionnaireButtons()
@ -82,9 +91,14 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
val pathExists = File("/data/data/com.dano.test1/databases/questionnaire_database").exists() val pathExists = File("/data/data/com.dano.test1/databases/questionnaire_database").exists()
updateMainButtonsState(pathExists) updateMainButtonsState(pathExists)
updateDownloadButtonState(pathExists) // <<< NEU: Download-Button anhand DB-Status setzen updateDownloadButtonState(pathExists)
if (pathExists && !editText.text.isNullOrBlank()) buttonLoad.performClick() if (pathExists && !editText.text.isNullOrBlank()) buttonLoad.performClick()
uiHandler.removeCallbacks(statusTicker)
updateStatusStrip()
applySessionAgeHighlight(System.currentTimeMillis() - TokenStore.getLoginTimestamp(activity))
uiHandler.post(statusTicker)
} }
private fun bindViews() { private fun bindViews() {
@ -97,7 +111,10 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
saveButton = activity.findViewById(R.id.saveButton) saveButton = activity.findViewById(R.id.saveButton)
editButton = activity.findViewById(R.id.editButton) editButton = activity.findViewById(R.id.editButton)
uploadButton = activity.findViewById(R.id.uploadButton) uploadButton = activity.findViewById(R.id.uploadButton)
downloadButton = activity.findViewById(R.id.downloadButton) downloadButton = activity.findViewById(R.id.downloadButton)
downloadButton.visibility = View.GONE
databaseButton = activity.findViewById(R.id.databaseButton) databaseButton = activity.findViewById(R.id.databaseButton)
statusSession = activity.findViewById(R.id.statusSession) statusSession = activity.findViewById(R.id.statusSession)
statusOnline = activity.findViewById(R.id.statusOnline) statusOnline = activity.findViewById(R.id.statusOnline)
@ -444,53 +461,50 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
private fun setupUploadButton() { private fun setupUploadButton() {
uploadButton.text = t("upload") uploadButton.text = t("upload")
uploadButton.setOnClickListener { uploadButton.setOnClickListener {
// 1) Token-Frische prüfen (24h Gültigkeit; wir nehmen 23h als Puffer) // ZUERST bestätigen lassen
val existingToken = TokenStore.getToken(activity) confirmUpload {
val ageMs = System.currentTimeMillis() - TokenStore.getLoginTimestamp(activity) // === dein bestehender Upload-Code unverändert ===
val isFresh = !existingToken.isNullOrBlank() && ageMs < 23 * 60 * 60 * 1000 val existingToken = TokenStore.getToken(activity)
val ageMs = System.currentTimeMillis() - TokenStore.getLoginTimestamp(activity)
val isFresh = !existingToken.isNullOrBlank() && ageMs < 23 * 60 * 60 * 1000
if (isFresh) { if (isFresh) {
// Frischer Token -> direkt hochladen
GlobalValues.LAST_CLIENT_CODE = editText.text.toString().trim()
DatabaseUploader.uploadDatabaseWithToken(activity, existingToken!!)
return@setOnClickListener
}
// 2) Kein/alter Token -> Login anstoßen
val username = TokenStore.getUsername(activity)?.trim().orEmpty()
if (username.isBlank()) {
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
return@setOnClickListener
}
// *** NUR für deine Testumgebung: bekannte Passwörter ableiten ***
// user01 -> pw1, user02 -> pw2 (entspricht login.php)
val password = when (username) {
"user01" -> "pw1"
"user02" -> "pw2"
else -> {
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
return@setOnClickListener
}
}
// Re-Login -> neuen Token speichern -> Upload starten
LoginManager.loginUserWithCredentials(
context = activity,
username = username,
password = password,
onSuccess = { freshToken ->
GlobalValues.LAST_CLIENT_CODE = editText.text.toString().trim() GlobalValues.LAST_CLIENT_CODE = editText.text.toString().trim()
DatabaseUploader.uploadDatabaseWithToken(activity, freshToken) DatabaseUploader.uploadDatabaseWithToken(activity, existingToken!!)
}, return@confirmUpload
onError = { msg ->
Toast.makeText(activity, t("login_failed_with_reason").replace("{reason}", msg), Toast.LENGTH_LONG).show()
} }
)
val username = TokenStore.getUsername(activity)?.trim().orEmpty()
if (username.isBlank()) {
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
return@confirmUpload
}
val password = when (username) {
"user01" -> "pw1"
"user02" -> "pw2"
else -> {
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
return@confirmUpload
}
}
LoginManager.loginUserWithCredentials(
context = activity,
username = username,
password = password,
onSuccess = { freshToken ->
GlobalValues.LAST_CLIENT_CODE = editText.text.toString().trim()
DatabaseUploader.uploadDatabaseWithToken(activity, freshToken)
},
onError = { msg ->
Toast.makeText(activity, t("login_failed_with_reason").replace("{reason}", msg), Toast.LENGTH_LONG).show()
}
)
}
} }
} }
private fun setupDownloadButton() { private fun setupDownloadButton() {
downloadButton.text = t("download") downloadButton.text = t("download")
@ -522,7 +536,6 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
// Der Download-Button wird separat gesteuert // Der Download-Button wird separat gesteuert
} }
/** <<< NEU: Steuert Aktivierung & Look des Download-Buttons je nach DB-Verfügbarkeit */
private fun updateDownloadButtonState(isDatabaseAvailable: Boolean) { private fun updateDownloadButtonState(isDatabaseAvailable: Boolean) {
val mb = downloadButton as? MaterialButton val mb = downloadButton as? MaterialButton
if (isDatabaseAvailable) { if (isDatabaseAvailable) {
@ -619,13 +632,18 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
val h = TimeUnit.MILLISECONDS.toHours(ageMs) val h = TimeUnit.MILLISECONDS.toHours(ageMs)
val m = TimeUnit.MILLISECONDS.toMinutes(ageMs) - h * 60 val m = TimeUnit.MILLISECONDS.toMinutes(ageMs) - h * 60
if (ts > 0L) { if (ts > 0L) {
statusSession.text = "${t("session_label")}: ${h}${t("hours_short")} ${m}${t("minutes_short")}" // ⚠️ anhängen, wenn >12h, der eigentliche Hinweis/Styling kommt aus applySessionAgeHighlight()
val warn = if (ageMs >= SESSION_WARN_AFTER_MS) " ⚠️" else ""
statusSession.text = "${t("session_label")}: ${h}${t("hours_short")} ${m}${t("minutes_short")}$warn"
} else { } else {
statusSession.text = t("session_dash") statusSession.text = t("session_dash")
} }
val online = NetworkUtils.isOnline(activity) val online = NetworkUtils.isOnline(activity)
statusOnline.text = if (online) t("online") else t("offline") statusOnline.text = if (online) t("online") else t("offline")
statusOnline.setTextColor(if (online) Color.parseColor("#2E7D32") else Color.parseColor("#C62828")) statusOnline.setTextColor(if (online) Color.parseColor("#2E7D32") else Color.parseColor("#C62828"))
// <<< NEU: hier jeweils prüfen/markieren
applySessionAgeHighlight(ageMs)
} }
fun refreshHeaderStatusLive() { fun refreshHeaderStatusLive() {
@ -644,4 +662,58 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
coachEditText.compoundDrawablePadding = dp(8) coachEditText.compoundDrawablePadding = dp(8)
coachEditText.alpha = 0.95f coachEditText.alpha = 0.95f
} }
private fun applySessionAgeHighlight(ageMs: Long) {
val isOld = ageMs >= SESSION_WARN_AFTER_MS
if (isOld) {
statusSession.setTextColor(Color.parseColor("#C62828"))
statusSession.setBackgroundColor(Color.parseColor("#FFF3CD"))
statusSession.setPadding(dp(8), dp(4), dp(8), dp(4))
if (!sessionLongWarnedOnce) {
showRedToast(activity, t("session_over_12"))
sessionLongWarnedOnce = true
}
} else {
statusSession.setTextColor(Color.parseColor("#2F2A49"))
statusSession.setBackgroundColor(Color.TRANSPARENT)
statusSession.setPadding(0, 0, 0, 0)
}
}
private fun showRedToast(ctx: android.content.Context, message: String) {
val tv = android.widget.TextView(ctx).apply {
text = message
setTextColor(android.graphics.Color.WHITE)
textSize = 16f
setPadding(32, 20, 32, 20)
background = android.graphics.drawable.GradientDrawable().apply {
shape = android.graphics.drawable.GradientDrawable.RECTANGLE
cornerRadius = 24f
setColor(android.graphics.Color.parseColor("#D32F2F")) // kräftiges Rot
}
}
android.widget.Toast(ctx).apply {
duration = android.widget.Toast.LENGTH_LONG
view = tv
setGravity(android.view.Gravity.TOP or android.view.Gravity.CENTER_HORIZONTAL, 0, 120)
}.show()
}
private fun confirmUpload(onConfirm: () -> Unit) {
MaterialAlertDialogBuilder(activity)
.setTitle(t("start_upload"))
.setMessage(t("ask_before_upload"))
.setPositiveButton(t("ok")) { d, _ ->
d.dismiss()
onConfirm()
}
.setNegativeButton(t("cancel")) { d, _ ->
d.dismiss()
}
.show()
}
} }

View File

@ -400,7 +400,12 @@ object LanguageManager {
"done" to "Erledigt", "done" to "Erledigt",
"not_done" to "Nicht erledigt", "not_done" to "Nicht erledigt",
"none" to "Keine", "none" to "Keine",
"view_missing" to "Fehlende View: %s" "view_missing" to "Fehlende View: %s",
"session_over_12" to "Sitzung läuft länger als 12 Stunden.",
"cancel" to "Cancel",
"ok" to "OK",
"ask_before_upload" to "Möchtest du den Upload wirklich ausführen?",
"start_upload" to "Upload starten?"
), ),
"ENGLISH" to mapOf( "ENGLISH" to mapOf(
@ -771,7 +776,12 @@ object LanguageManager {
"done" to "Done", "done" to "Done",
"not_done" to "Not done", "not_done" to "Not done",
"none" to "None", "none" to "None",
"view_missing" to "Missing view: %s" "view_missing" to "Missing view: %s",
"session_over_12" to "Session has been running for more than 12 hours.",
"cancel" to "Cancel",
"ok" to "OK",
"ask_before_upload" to "Do you really want to perform the upload?",
"start_upload" to "Start upload?"
), ),
"FRENCH" to mapOf( "FRENCH" to mapOf(
@ -1146,7 +1156,12 @@ object LanguageManager {
"done" to "Terminé", "done" to "Terminé",
"not_done" to "Non terminé", "not_done" to "Non terminé",
"none" to "Aucun", "none" to "Aucun",
"view_missing" to "Vue manquante : %s" "view_missing" to "Vue manquante : %s",
"session_over_12" to "La session dure depuis plus de 12 heures.",
"cancel" to "Annuler",
"ok" to "OK",
"ask_before_upload" to "Voulez-vous vraiment effectuer le téléversement ?",
"start_upload" to "Démarrer le téléversement ?"
), ),
"RUSSIAN" to mapOf( "RUSSIAN" to mapOf(
@ -1517,7 +1532,12 @@ object LanguageManager {
"done" to "Готово", "done" to "Готово",
"not_done" to "Не выполнено", "not_done" to "Не выполнено",
"none" to "Нет", "none" to "Нет",
"view_missing" to "Отсутствует представление: %s" "view_missing" to "Отсутствует представление: %s",
"session_over_12" to "Сеанс продолжается более 12 часов.",
"cancel" to "Отмена",
"ok" to "OK",
"ask_before_upload" to "Вы действительно хотите выполнить загрузку?",
"start_upload" to "Начать загрузку?"
), ),
"UKRAINIAN" to mapOf( "UKRAINIAN" to mapOf(
@ -1892,7 +1912,12 @@ object LanguageManager {
"done" to "Готово", "done" to "Готово",
"not_done" to "Не виконано", "not_done" to "Не виконано",
"none" to "Немає", "none" to "Немає",
"view_missing" to "Відсутній елемент інтерфейсу: %s" "view_missing" to "Відсутній елемент інтерфейсу: %s",
"session_over_12" to "Сеанс триває понад 12 годин.",
"cancel" to "Скасувати",
"ok" to "OK",
"ask_before_upload" to "Ви справді хочете виконати завантаження?",
"start_upload" to "Почати завантаження?"
), ),
"TURKISH" to mapOf( "TURKISH" to mapOf(
@ -2267,7 +2292,12 @@ object LanguageManager {
"done" to "Tamamlandı", "done" to "Tamamlandı",
"not_done" to "Tamamlanmadı", "not_done" to "Tamamlanmadı",
"none" to "Yok", "none" to "Yok",
"view_missing" to "Eksik görünüm: %s" "view_missing" to "Eksik görünüm: %s",
"session_over_12" to "Oturum 12 saatten uzun süredir açık.",
"cancel" to "İptal",
"ok" to "Tamam",
"ask_before_upload" to "Yüklemeyi gerçekten yapmak istiyor musunuz?",
"start_upload" to "Yüklemeyi başlat?"
), ),
"POLISH" to mapOf( "POLISH" to mapOf(
@ -2642,7 +2672,12 @@ object LanguageManager {
"done" to "Zrobione", "done" to "Zrobione",
"not_done" to "Niezrobione", "not_done" to "Niezrobione",
"none" to "Brak", "none" to "Brak",
"view_missing" to "Brak widoku: %s" "view_missing" to "Brak widoku: %s",
"session_over_12" to "Sesja trwa dłużej niż 12 godzin.",
"cancel" to "Anuluj",
"ok" to "OK",
"ask_before_upload" to "Czy na pewno chcesz wykonać przesyłanie?",
"start_upload" to "Rozpocząć przesyłanie?"
), ),
"ARABIC" to mapOf( "ARABIC" to mapOf(
@ -3017,7 +3052,12 @@ object LanguageManager {
"done" to "منجز", "done" to "منجز",
"not_done" to "غير منجز", "not_done" to "غير منجز",
"none" to "لا شيء", "none" to "لا شيء",
"view_missing" to "العنصر المفقود: %s" "view_missing" to "العنصر المفقود: %s",
"session_over_12" to "تعمل الجلسة منذ أكثر من 12 ساعة.",
"cancel" to "إلغاء",
"ok" to "موافق",
"ask_before_upload" to "هل ترغب فعلًا في تنفيذ الرفع؟",
"start_upload" to "بدء الرفع؟"
), ),
"ROMANIAN" to mapOf( "ROMANIAN" to mapOf(
@ -3392,7 +3432,12 @@ object LanguageManager {
"done" to "Finalizat", "done" to "Finalizat",
"not_done" to "Nefinalizat", "not_done" to "Nefinalizat",
"none" to "Nimic", "none" to "Nimic",
"view_missing" to "Vizualizare lipsă: %s" "view_missing" to "Vizualizare lipsă: %s",
"session_over_12" to "Sesiunea rulează de mai bine de 12 ore.",
"cancel" to "Anulează",
"ok" to "OK",
"ask_before_upload" to "Vrei într-adevăr să efectuezi încărcarea?",
"start_upload" to "Pornești încărcarea?"
), ),
"SPANISH" to mapOf( "SPANISH" to mapOf(
@ -3767,7 +3812,12 @@ object LanguageManager {
"done" to "Completado", "done" to "Completado",
"not_done" to "No completado", "not_done" to "No completado",
"none" to "Ninguno", "none" to "Ninguno",
"view_missing" to "Vista faltante: %s" "view_missing" to "Vista faltante: %s",
"session_over_12" to "La sesión lleva más de 12 horas.",
"cancel" to "Cancelar",
"ok" to "OK",
"ask_before_upload" to "¿Realmente deseas realizar la carga?",
"start_upload" to "¿Iniciar la carga?"
) )
) )
} }

View File

@ -1,11 +1,13 @@
package com.dano.test1 package com.dano.test1
import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import kotlinx.coroutines.CoroutineScope import android.view.LayoutInflater
import kotlinx.coroutines.Dispatchers import android.widget.EditText
import kotlinx.coroutines.launch import android.widget.LinearLayout
import kotlinx.coroutines.withContext import android.widget.Toast
import kotlinx.coroutines.*
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -14,6 +16,7 @@ import org.json.JSONObject
object LoginManager { object LoginManager {
private const val SERVER_LOGIN_URL = "https://daniel-ocks.de/qdb/login.php" private const val SERVER_LOGIN_URL = "https://daniel-ocks.de/qdb/login.php"
private const val SERVER_CHANGE_URL = "https://daniel-ocks.de/qdb/change_password.php"
private val client = OkHttpClient() private val client = OkHttpClient()
fun loginUserWithCredentials( fun loginUserWithCredentials(
@ -39,23 +42,177 @@ object LoginManager {
val response = client.newCall(request).execute() val response = client.newCall(request).execute()
val text = response.body?.string() val text = response.body?.string()
if (response.isSuccessful && text != null) { if (!response.isSuccessful || text == null) {
val json = JSONObject(text)
if (json.optBoolean("success")) {
val token = json.getString("token")
// => setzt auch den Login-Timestamp:
TokenStore.save(context, token, username)
withContext(Dispatchers.Main) { onSuccess(token) }
} else {
withContext(Dispatchers.Main) { onError("Login fehlgeschlagen") }
}
} else {
withContext(Dispatchers.Main) { onError("Fehler beim Login (${response.code})") } withContext(Dispatchers.Main) { onError("Fehler beim Login (${response.code})") }
return@launch
} }
val json = JSONObject(text)
if (!json.optBoolean("success")) {
withContext(Dispatchers.Main) { onError(json.optString("message", "Login fehlgeschlagen")) }
return@launch
}
// Passwortwechsel erforderlich?
if (json.optBoolean("must_change_password", false)) {
withContext(Dispatchers.Main) {
showChangePasswordDialog(
context = context,
username = username,
oldPassword = password,
onChanged = { token ->
// Nach PW-Änderung direkt eingeloggt
TokenStore.save(context, token, username)
onSuccess(token)
},
onError = onError
)
}
return@launch
}
// normaler Login: Token speichern
val token = json.getString("token")
TokenStore.save(context, token, username)
withContext(Dispatchers.Main) { onSuccess(token) }
} catch (e: Exception) { } catch (e: Exception) {
Log.e("LOGIN", "Exception", e) Log.e("LOGIN", "Exception", e)
withContext(Dispatchers.Main) { onError("Exception: ${e.message}") } withContext(Dispatchers.Main) { onError("Exception: ${e.message}") }
} }
} }
} }
private fun showChangePasswordDialog(
context: Context,
username: String,
oldPassword: String,
onChanged: (String) -> Unit,
onError: (String) -> Unit
) {
val container = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
setPadding(48, 24, 48, 0)
}
val etNew = EditText(context).apply {
hint = "Neues Passwort"
inputType = android.text.InputType.TYPE_CLASS_TEXT or
android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
}
val etRepeat = EditText(context).apply {
hint = "Neues Passwort (wiederholen)"
inputType = android.text.InputType.TYPE_CLASS_TEXT or
android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
}
container.addView(etNew)
container.addView(etRepeat)
val dialog = AlertDialog.Builder(context)
.setTitle("Passwort ändern")
.setMessage("Du verwendest ein Standard-Konto. Bitte setze jetzt ein eigenes Passwort.")
.setView(container)
.setPositiveButton("OK", null) // nicht sofort schließen lassen
.setNegativeButton("Abbrechen", null) // nicht sofort schließen lassen
.setCancelable(false)
.create()
dialog.setOnShowListener {
val btnOk = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
val btnCancel = dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
btnOk.setOnClickListener {
etNew.error = null
etRepeat.error = null
val p1 = etNew.text?.toString().orEmpty()
val p2 = etRepeat.text?.toString().orEmpty()
when {
p1.length < 6 -> {
etNew.error = "Mindestens 6 Zeichen."
return@setOnClickListener
}
p1 != p2 -> {
etRepeat.error = "Passwörter stimmen nicht überein."
return@setOnClickListener
}
else -> {
btnOk.isEnabled = false
btnCancel.isEnabled = false
changePassword(
context = context,
username = username,
oldPassword = oldPassword,
newPassword = p1,
onChanged = { token ->
dialog.dismiss()
onChanged(token)
},
onError = { msg ->
btnOk.isEnabled = true
btnCancel.isEnabled = true
Toast.makeText(context, msg, Toast.LENGTH_LONG).show()
}
)
}
}
}
// >>> Überarbeitet: Abbrechen schließt Dialog und informiert den Aufrufer
btnCancel.setOnClickListener {
dialog.dismiss()
onError("Passwortänderung abgebrochen.")
}
}
dialog.show()
}
private fun changePassword(
context: Context,
username: String,
oldPassword: String,
newPassword: String,
onChanged: (String) -> Unit,
onError: (String) -> Unit
) {
CoroutineScope(Dispatchers.IO).launch {
try {
val body = JSONObject()
.put("username", username)
.put("old_password", oldPassword)
.put("new_password", newPassword)
.toString()
.toRequestBody("application/json".toMediaType())
val req = Request.Builder()
.url(SERVER_CHANGE_URL)
.post(body)
.build()
val resp = client.newCall(req).execute()
val txt = resp.body?.string()
if (!resp.isSuccessful || txt == null) {
withContext(Dispatchers.Main) { onError("Fehler beim Ändern (${resp.code})") }
return@launch
}
val json = JSONObject(txt)
if (!json.optBoolean("success")) {
withContext(Dispatchers.Main) { onError(json.optString("message", "Ändern fehlgeschlagen")) }
return@launch
}
val token = json.getString("token")
withContext(Dispatchers.Main) { onChanged(token) }
} catch (e: Exception) {
Log.e("LOGIN", "changePassword Exception", e)
withContext(Dispatchers.Main) { onError("Exception: ${e.message}") }
}
}
}
} }