added dummy accounts, change passwort is now a feature, toast when session takes to long, online frontend fix
This commit is contained in:
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,37 +461,34 @@ 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
|
||||||
|
confirmUpload {
|
||||||
|
// === dein bestehender Upload-Code unverändert ===
|
||||||
val existingToken = TokenStore.getToken(activity)
|
val existingToken = TokenStore.getToken(activity)
|
||||||
val ageMs = System.currentTimeMillis() - TokenStore.getLoginTimestamp(activity)
|
val ageMs = System.currentTimeMillis() - TokenStore.getLoginTimestamp(activity)
|
||||||
val isFresh = !existingToken.isNullOrBlank() && ageMs < 23 * 60 * 60 * 1000
|
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()
|
GlobalValues.LAST_CLIENT_CODE = editText.text.toString().trim()
|
||||||
DatabaseUploader.uploadDatabaseWithToken(activity, existingToken!!)
|
DatabaseUploader.uploadDatabaseWithToken(activity, existingToken!!)
|
||||||
return@setOnClickListener
|
return@confirmUpload
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Kein/alter Token -> Login anstoßen
|
|
||||||
val username = TokenStore.getUsername(activity)?.trim().orEmpty()
|
val username = TokenStore.getUsername(activity)?.trim().orEmpty()
|
||||||
if (username.isBlank()) {
|
if (username.isBlank()) {
|
||||||
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
|
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
|
||||||
return@setOnClickListener
|
return@confirmUpload
|
||||||
}
|
}
|
||||||
|
|
||||||
// *** NUR für deine Testumgebung: bekannte Passwörter ableiten ***
|
|
||||||
// user01 -> pw1, user02 -> pw2 (entspricht login.php)
|
|
||||||
val password = when (username) {
|
val password = when (username) {
|
||||||
"user01" -> "pw1"
|
"user01" -> "pw1"
|
||||||
"user02" -> "pw2"
|
"user02" -> "pw2"
|
||||||
else -> {
|
else -> {
|
||||||
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
|
Toast.makeText(activity, t("login_required"), Toast.LENGTH_LONG).show()
|
||||||
return@setOnClickListener
|
return@confirmUpload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-Login -> neuen Token speichern -> Upload starten
|
|
||||||
LoginManager.loginUserWithCredentials(
|
LoginManager.loginUserWithCredentials(
|
||||||
context = activity,
|
context = activity,
|
||||||
username = username,
|
username = username,
|
||||||
@ -489,7 +503,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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?"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
withContext(Dispatchers.Main) { onError("Fehler beim Login (${response.code})") }
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
val json = JSONObject(text)
|
val json = JSONObject(text)
|
||||||
if (json.optBoolean("success")) {
|
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")
|
val token = json.getString("token")
|
||||||
// => setzt auch den Login-Timestamp:
|
|
||||||
TokenStore.save(context, token, username)
|
TokenStore.save(context, token, username)
|
||||||
withContext(Dispatchers.Main) { onSuccess(token) }
|
withContext(Dispatchers.Main) { onSuccess(token) }
|
||||||
} else {
|
|
||||||
withContext(Dispatchers.Main) { onError("Login fehlgeschlagen") }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
withContext(Dispatchers.Main) { onError("Fehler beim Login (${response.code})") }
|
|
||||||
}
|
|
||||||
} 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}") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user