prototype changes for demo

This commit is contained in:
2026-06-24 16:51:26 +02:00
parent 653071d4bf
commit 43890d430b
108 changed files with 1445 additions and 668 deletions

View File

@ -5,11 +5,11 @@ plugins {
} }
android { android {
namespace = "com.dano.test1" namespace = "com.tomhempel.rbarometer"
compileSdk = 35 compileSdk = 35
defaultConfig { defaultConfig {
applicationId = "com.dano.test1" applicationId = "com.tomhempel.rbarometer"
minSdk = 29 minSdk = 29
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.dano.test1"> package="com.tomhempel.rbarometer">
<!-- Netzwerkberechtigungen --> <!-- Netzwerkberechtigungen -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -19,7 +19,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Test1" android:theme="@style/Theme.Barometer"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="false" android:usesCleartextTraffic="false"
tools:targetApi="31"> tools:targetApi="31">

View File

@ -1,4 +1,4 @@
package com.dano.test1 package com.tomhempel.rbarometer
import android.Manifest import android.Manifest
import android.content.Context import android.content.Context
@ -16,27 +16,27 @@ import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.ui.AppLanguageStore import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.dano.test1.ui.NotificationPermissionPrompter import com.tomhempel.rbarometer.ui.NotificationPermissionPrompter
import com.dano.test1.ui.AuthActivity import com.tomhempel.rbarometer.ui.AuthActivity
import com.dano.test1.ui.HandlerOpeningScreen import com.tomhempel.rbarometer.ui.HandlerOpeningScreen
import com.dano.test1.ui.LogoutDialogs import com.tomhempel.rbarometer.ui.LogoutDialogs
import com.dano.test1.ui.SessionLogout import com.tomhempel.rbarometer.ui.SessionLogout
import com.dano.test1.ui.SystemBarInsets import com.tomhempel.rbarometer.ui.SystemBarInsets
import com.dano.test1.utils.SyncProgressOverlay import com.tomhempel.rbarometer.utils.SyncProgressOverlay
import com.dano.test1.utils.SyncProgressTexts import com.tomhempel.rbarometer.utils.SyncProgressTexts
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.dano.test1.data.PendingUploadRepository import com.tomhempel.rbarometer.data.PendingUploadRepository
import com.dano.test1.network.NetworkUtils import com.tomhempel.rbarometer.network.NetworkUtils
import com.dano.test1.network.QuestionnaireApiClient import com.tomhempel.rbarometer.network.QuestionnaireApiClient
import com.dano.test1.network.SyncLog import com.tomhempel.rbarometer.network.SyncLog
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.SyncCoordinator import com.tomhempel.rbarometer.network.SyncCoordinator
import com.dano.test1.network.SyncProgress import com.tomhempel.rbarometer.network.SyncProgress
import com.dano.test1.network.SyncResult import com.tomhempel.rbarometer.network.SyncResult
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import com.dano.test1.questionnaire.QuestionnaireBase import com.tomhempel.rbarometer.questionnaire.QuestionnaireBase
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View File

@ -1,12 +1,12 @@
package com.dano.test1 package com.tomhempel.rbarometer
import android.app.Application import android.app.Application
import android.util.Log import android.util.Log
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.dano.test1.data.AppDatabase import com.tomhempel.rbarometer.data.AppDatabase
import com.dano.test1.security.SensitiveDataCrypto import com.tomhempel.rbarometer.security.SensitiveDataCrypto
/* /*
MyApp (Application) MyApp (Application)

View File

@ -1,7 +1,7 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.security.SensitiveDataCrypto import com.tomhempel.rbarometer.security.SensitiveDataCrypto
object AnswerRepository { object AnswerRepository {

View File

@ -1,4 +1,4 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase

View File

@ -1,4 +1,4 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
/** /**
* In-memory cache of answers for the active client to avoid repeated Room reads * In-memory cache of answers for the active client to avoid repeated Room reads

View File

@ -1,7 +1,7 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.security.SensitiveDataCrypto import com.tomhempel.rbarometer.security.SensitiveDataCrypto
object ClientRepository { object ClientRepository {

View File

@ -1,7 +1,7 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.security.SensitiveDataCrypto import com.tomhempel.rbarometer.security.SensitiveDataCrypto
object CoachScoringReviewRepository { object CoachScoringReviewRepository {

View File

@ -1,7 +1,7 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.security.SensitiveDataCrypto import com.tomhempel.rbarometer.security.SensitiveDataCrypto
object CompletedQuestionnaireRepository { object CompletedQuestionnaireRepository {
private val dao get() = MyApp.database.completedQuestionnaireDao() private val dao get() = MyApp.database.completedQuestionnaireDao()

View File

@ -1,4 +1,4 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
import androidx.room.* import androidx.room.*

View File

@ -1,4 +1,4 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
import androidx.room.* import androidx.room.*

View File

@ -1,4 +1,4 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
object PendingUploadRepository { object PendingUploadRepository {

View File

@ -1,4 +1,4 @@
package com.dano.test1.data package com.tomhempel.rbarometer.data
/** /**
* Summarises local interview data that must be uploaded (or finished) before logout. * Summarises local interview data that must be uploaded (or finished) before logout.

View File

@ -1,4 +1,4 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
data class ApiError( data class ApiError(
val code: String? = null, val code: String? = null,

View File

@ -1,7 +1,7 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import com.dano.test1.security.SensitiveDataCrypto import com.tomhempel.rbarometer.security.SensitiveDataCrypto
/** Persists per-client answer sync timestamps (survives process restarts). */ /** Persists per-client answer sync timestamps (survives process restarts). */
internal object ClientAnswerSyncStore { internal object ClientAnswerSyncStore {

View File

@ -1,8 +1,8 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.notification.UploadReminderScheduler import com.tomhempel.rbarometer.notification.UploadReminderScheduler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -48,9 +48,9 @@ object DatabaseUploader {
onResult: (UploadResult) -> Unit, onResult: (UploadResult) -> Unit,
) { ) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val allPending = com.dano.test1.data.CompletedQuestionnaireRepository val allPending = com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
.getAllPendingUploads() .getAllPendingUploads()
val pendingScoring = com.dano.test1.data.CoachScoringReviewRepository val pendingScoring = com.tomhempel.rbarometer.data.CoachScoringReviewRepository
.getAllPendingUploads() .getAllPendingUploads()
if (allPending.isEmpty() && pendingScoring.isEmpty()) { if (allPending.isEmpty() && pendingScoring.isEmpty()) {
val assigned = QuestionnaireCache.getAssignedClients(context) val assigned = QuestionnaireCache.getAssignedClients(context)
@ -113,7 +113,7 @@ object DatabaseUploader {
QuestionnaireApiClient.submitQuestionnaire(token, request) QuestionnaireApiClient.submitQuestionnaire(token, request)
}.onSuccess { response -> }.onSuccess { response ->
submittedQuestionnaires++ submittedQuestionnaires++
com.dano.test1.data.CompletedQuestionnaireRepository com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
.markUploaded(clientCode, entry.questionnaireId, System.currentTimeMillis()) .markUploaded(clientCode, entry.questionnaireId, System.currentTimeMillis())
if (response.legacySubmit) { if (response.legacySubmit) {
val submitRev = response.structureRevision val submitRev = response.structureRevision
@ -162,7 +162,7 @@ object DatabaseUploader {
) )
}.onSuccess { }.onSuccess {
submittedScoringReviews++ submittedScoringReviews++
com.dano.test1.data.CoachScoringReviewRepository.markUploaded( com.tomhempel.rbarometer.data.CoachScoringReviewRepository.markUploaded(
review.clientCode, review.clientCode,
review.profileID, review.profileID,
System.currentTimeMillis(), System.currentTimeMillis(),

View File

@ -1,4 +1,4 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import okhttp3.Dispatcher import okhttp3.Dispatcher
import okhttp3.OkHttpClient import okhttp3.OkHttpClient

View File

@ -1,4 +1,4 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope

View File

@ -1,4 +1,4 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import android.net.ConnectivityManager import android.net.ConnectivityManager

View File

@ -1,14 +1,14 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.data.AnswerEntity import com.tomhempel.rbarometer.data.AnswerEntity
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.data.CompletedQuestionnaire import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.dano.test1.data.CompletedQuestionnaireRepository import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.dano.test1.data.Question import com.tomhempel.rbarometer.data.Question
import com.dano.test1.data.Questionnaire import com.tomhempel.rbarometer.data.Questionnaire
import com.dano.test1.questionnaire.AnswerKeyUtils import com.tomhempel.rbarometer.questionnaire.AnswerKeyUtils
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
@ -367,7 +367,7 @@ object QuestionnaireAnswerSync {
val completedAtMs = (block.completedAt ?: 0L).let { if (it > 0) it * 1000L else System.currentTimeMillis() } val completedAtMs = (block.completedAt ?: 0L).let { if (it > 0) it * 1000L else System.currentTimeMillis() }
val sumPoints = block.sumPoints val sumPoints = block.sumPoints
val clientKey = com.dano.test1.security.SensitiveDataCrypto.clientCodeKey(clientCode) val clientKey = com.tomhempel.rbarometer.security.SensitiveDataCrypto.clientCodeKey(clientCode)
return PreparedBlockImport( return PreparedBlockImport(
questionnaireId = questionnaireId, questionnaireId = questionnaireId,
@ -382,7 +382,7 @@ object QuestionnaireAnswerSync {
AnswerEntity( AnswerEntity(
clientCodeKey = clientKey, clientCodeKey = clientKey,
questionId = AnswerKeyUtils.roomQuestionId(questionnaireId, key), questionId = AnswerKeyUtils.roomQuestionId(questionnaireId, key),
answerValueEnc = com.dano.test1.security.SensitiveDataCrypto.encrypt(value), answerValueEnc = com.tomhempel.rbarometer.security.SensitiveDataCrypto.encrypt(value),
) )
}, },
completion = CompletedQuestionnaire( completion = CompletedQuestionnaire(
@ -461,8 +461,8 @@ object QuestionnaireAnswerSync {
} }
private fun CompletedQuestionnaire.toEntity(clientCode: String) = private fun CompletedQuestionnaire.toEntity(clientCode: String) =
com.dano.test1.data.CompletedQuestionnaireEntity( com.tomhempel.rbarometer.data.CompletedQuestionnaireEntity(
clientCodeKey = com.dano.test1.security.SensitiveDataCrypto.clientCodeKey(clientCode), clientCodeKey = com.tomhempel.rbarometer.security.SensitiveDataCrypto.clientCodeKey(clientCode),
questionnaireId = questionnaireId, questionnaireId = questionnaireId,
timestamp = timestamp, timestamp = timestamp,
isDone = isDone, isDone = isDone,

View File

@ -1,6 +1,6 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import com.dano.test1.security.SessionPayloadCrypto import com.tomhempel.rbarometer.security.SessionPayloadCrypto
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement

View File

@ -1,11 +1,11 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.data.ClientRepository import com.tomhempel.rbarometer.data.ClientRepository
import com.dano.test1.security.SensitiveDataCrypto import com.tomhempel.rbarometer.security.SensitiveDataCrypto
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
@ -102,9 +102,9 @@ object QuestionnaireCache {
*/ */
suspend fun wipeEntireLocalStore(context: Context) { suspend fun wipeEntireLocalStore(context: Context) {
clearAssignedClients(context) clearAssignedClients(context)
com.dano.test1.MyApp.database.clearAllTables() com.tomhempel.rbarometer.MyApp.database.clearAllTables()
cacheDir(context).deleteRecursively() cacheDir(context).deleteRecursively()
com.dano.test1.notification.UploadReminderScheduler.cancel(context) com.tomhempel.rbarometer.notification.UploadReminderScheduler.cancel(context)
LanguageManager.setApiTranslations(emptyMap()) LanguageManager.setApiTranslations(emptyMap())
LanguageManager.setQuestionnaireTranslations(emptyMap()) LanguageManager.setQuestionnaireTranslations(emptyMap())
} }

View File

@ -1,9 +1,9 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.data.CompletedQuestionnaireRepository import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.dano.test1.questionnaire.AnswerKeyUtils import com.tomhempel.rbarometer.questionnaire.AnswerKeyUtils
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser

View File

@ -1,9 +1,9 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.data.CompletedQuestionnaire import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.dano.test1.data.Questionnaire import com.tomhempel.rbarometer.data.Questionnaire
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
@ -203,7 +203,7 @@ object QuestionnaireSyncService {
private suspend fun seedServerCompletions(clientInfosFromApi: List<AssignedClientInfo>) { private suspend fun seedServerCompletions(clientInfosFromApi: List<AssignedClientInfo>) {
val toInsert = mutableListOf<CompletedQuestionnaire>() val toInsert = mutableListOf<CompletedQuestionnaire>()
for (clientInfo in clientInfosFromApi) { for (clientInfo in clientInfosFromApi) {
val existingByQn = com.dano.test1.data.CompletedQuestionnaireRepository val existingByQn = com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
.getAllForClient(clientInfo.clientCode) .getAllForClient(clientInfo.clientCode)
.associateBy { it.questionnaireId } .associateBy { it.questionnaireId }
for (serverCompletion in clientInfo.completedQuestionnaires) { for (serverCompletion in clientInfo.completedQuestionnaires) {
@ -225,7 +225,7 @@ object QuestionnaireSyncService {
} }
if (toInsert.isNotEmpty()) { if (toInsert.isNotEmpty()) {
runCatching { runCatching {
com.dano.test1.data.CompletedQuestionnaireRepository.insertAll(toInsert) com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository.insertAll(toInsert)
}.onSuccess { }.onSuccess {
SyncLog.d(TAG, "seeded ${toInsert.size} server completion(s)") SyncLog.d(TAG, "seeded ${toInsert.size} server completion(s)")
}.onFailure { e -> }.onFailure { e ->

View File

@ -1,8 +1,8 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import android.widget.Toast import android.widget.Toast
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
sealed class SyncResult { sealed class SyncResult {
data object Success : SyncResult() data object Success : SyncResult()

View File

@ -1,7 +1,7 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.util.Log import android.util.Log
import com.dano.test1.BuildConfig import com.tomhempel.rbarometer.BuildConfig
import java.security.MessageDigest import java.security.MessageDigest
/** Debug-gated logging for sync/API paths (avoids logging full JSON bodies in production). */ /** Debug-gated logging for sync/API paths (avoids logging full JSON bodies in production). */

View File

@ -1,4 +1,4 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
/** User-facing sync step — no counts; shown as friendly status text only. */ /** User-facing sync step — no counts; shown as friendly status text only. */
enum class SyncPhase { enum class SyncPhase {

View File

@ -1,4 +1,4 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import kotlinx.coroutines.delay import kotlinx.coroutines.delay

View File

@ -1,4 +1,4 @@
package com.dano.test1.network package com.tomhempel.rbarometer.network
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log

View File

@ -1,4 +1,4 @@
package com.dano.test1.notification package com.tomhempel.rbarometer.notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
@ -8,9 +8,9 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.R import com.tomhempel.rbarometer.R
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -1,4 +1,4 @@
package com.dano.test1.notification package com.tomhempel.rbarometer.notification
import android.app.AlarmManager import android.app.AlarmManager
import android.app.PendingIntent import android.app.PendingIntent
@ -51,7 +51,7 @@ object UploadReminderScheduler {
*/ */
suspend fun hasPendingUploads(): Boolean { suspend fun hasPendingUploads(): Boolean {
return try { return try {
com.dano.test1.data.PendingUploadRepository.hasAny() com.tomhempel.rbarometer.data.PendingUploadRepository.hasAny()
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "hasPendingUploads check failed: ${e.message}") Log.w(TAG, "hasPendingUploads check failed: ${e.message}")
false false

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
/** /**
* Stable keys for Room answer rows and submit mapping. * Stable keys for Room answer rows and submit mapping.

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
/** /**
* Resolves stored answers from the in-memory map using the same keys as persistence/sync. * Resolves stored answers from the in-memory map using the same keys as persistence/sync.

View File

@ -1,6 +1,6 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -1,6 +1,6 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
/** /**
* Single source for coach language selection across opening screen and questionnaires. * Single source for coach language selection across opening screen and questionnaires.

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.content.Context import android.content.Context
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
object ClientAssignmentGuard { object ClientAssignmentGuard {

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.util.Log import android.util.Log
import org.json.JSONArray import org.json.JSONArray

View File

@ -1,6 +1,6 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
object QuestionFlowResolver { object QuestionFlowResolver {

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.view.View import android.view.View
interface QuestionHandler { interface QuestionHandler {

View File

@ -1,16 +1,16 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.content.Context import android.content.Context
import com.dano.test1.questionnaire.handlers.HandlerClientCoachCode import com.tomhempel.rbarometer.questionnaire.handlers.HandlerClientCoachCode
import com.dano.test1.questionnaire.handlers.HandlerDateSpinner import com.tomhempel.rbarometer.questionnaire.handlers.HandlerDateSpinner
import com.dano.test1.questionnaire.handlers.HandlerFreeText import com.tomhempel.rbarometer.questionnaire.handlers.HandlerFreeText
import com.dano.test1.questionnaire.handlers.HandlerGlassScaleQuestion import com.tomhempel.rbarometer.questionnaire.handlers.HandlerGlassScaleQuestion
import com.dano.test1.questionnaire.handlers.HandlerLastPage import com.tomhempel.rbarometer.questionnaire.handlers.HandlerLastPage
import com.dano.test1.questionnaire.handlers.HandlerMultiCheckboxQuestion import com.tomhempel.rbarometer.questionnaire.handlers.HandlerMultiCheckboxQuestion
import com.dano.test1.questionnaire.handlers.HandlerRadioQuestion import com.tomhempel.rbarometer.questionnaire.handlers.HandlerRadioQuestion
import com.dano.test1.questionnaire.handlers.HandlerSliderQuestion import com.tomhempel.rbarometer.questionnaire.handlers.HandlerSliderQuestion
import com.dano.test1.questionnaire.handlers.HandlerStringSpinner import com.tomhempel.rbarometer.questionnaire.handlers.HandlerStringSpinner
import com.dano.test1.questionnaire.handlers.HandlerValueSpinner import com.tomhempel.rbarometer.questionnaire.handlers.HandlerValueSpinner
enum class NavigationMode { Step, Embedded } enum class NavigationMode { Step, Embedded }

View File

@ -1,10 +1,10 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
/** Renders optional noteBeforeKey / noteAfterKey above and below the main question text. */ /** Renders optional noteBeforeKey / noteAfterKey above and below the main question text. */
object QuestionNotesHelper { object QuestionNotesHelper {

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.graphics.Color import android.graphics.Color
import android.text.Editable import android.text.Editable
@ -9,19 +9,19 @@ import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import com.dano.test1.questionnaire.handlers.HandlerStringSpinner import com.tomhempel.rbarometer.questionnaire.handlers.HandlerStringSpinner
import com.dano.test1.questionnaire.handlers.HandlerValueSpinner import com.tomhempel.rbarometer.questionnaire.handlers.HandlerValueSpinner
import com.dano.test1.ui.AppLanguageStore import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.dano.test1.ui.ReviewScoresHandler import com.tomhempel.rbarometer.ui.ReviewScoresHandler
import com.dano.test1.utils.DateAnswerFormat import com.tomhempel.rbarometer.utils.DateAnswerFormat
import com.dano.test1.utils.DatePrecision import com.tomhempel.rbarometer.utils.DatePrecision
import com.dano.test1.utils.ScreenLoading import com.tomhempel.rbarometer.utils.ScreenLoading
import com.dano.test1.utils.TextInputSanitizer import com.tomhempel.rbarometer.utils.TextInputSanitizer
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -296,7 +296,7 @@ class QuestionnaireAllInOne(private val questionnaireFileName: String) : Questio
} }
} }
languageID = newLang languageID = newLang
com.dano.test1.ui.AppLanguageStore.set(context, newLang) com.tomhempel.rbarometer.ui.AppLanguageStore.set(context, newLang)
persistedBaselineAnswers = snapshotAnswerKeys(answers) persistedBaselineAnswers = snapshotAnswerKeys(answers)
uiChangedSinceBaseline = false uiChangedSinceBaseline = false
buildAllInOneUi() buildAllInOneUi()
@ -927,7 +927,7 @@ class QuestionnaireAllInOne(private val questionnaireFileName: String) : Questio
private fun selectedMonthNumber(view: View): Int? { private fun selectedMonthNumber(view: View): Int? {
val month = view.findViewById<Spinner>(R.id.spinner_value_month)?.selectedItem val month = view.findViewById<Spinner>(R.id.spinner_value_month)?.selectedItem
val months = com.dano.test1.ui.Months.getAllMonths(languageID) val months = com.tomhempel.rbarometer.ui.Months.getAllMonths(languageID)
val idx = months.indexOf(month) val idx = months.indexOf(month)
return if (idx >= 0) idx + 1 else null return if (idx >= 0) idx + 1 else null
} }

View File

@ -1,12 +1,12 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.R import android.R
import android.app.Activity import android.app.Activity
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.util.Calendar import java.util.Calendar
@ -77,7 +77,7 @@ abstract class QuestionnaireBase<T> {
protected fun showEmptyScreen() { protected fun showEmptyScreen() {
navigateTo(getLayoutResId("empty")) { navigateTo(getLayoutResId("empty")) {
setupPrevButton(com.dano.test1.R.id.Qprev) { goToPreviousQuestion() } setupPrevButton(com.tomhempel.rbarometer.R.id.Qprev) { goToPreviousQuestion() }
} }
} }

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.content.Context import android.content.Context
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
@ -6,8 +6,8 @@ import android.widget.AdapterView
import android.widget.Button import android.widget.Button
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.Spinner import android.widget.Spinner
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
open class QuestionnaireGeneric(private val questionnaireFileName: String) : QuestionnaireBase<Unit>() { open class QuestionnaireGeneric(private val questionnaireFileName: String) : QuestionnaireBase<Unit>() {
@ -70,7 +70,7 @@ open class QuestionnaireGeneric(private val questionnaireFileName: String) : Que
val newLang = AppLanguages.ids[position] val newLang = AppLanguages.ids[position]
if (newLang == languageID) return if (newLang == languageID) return
languageID = newLang languageID = newLang
com.dano.test1.ui.AppLanguageStore.set(context, newLang) com.tomhempel.rbarometer.ui.AppLanguageStore.set(context, newLang)
spinner.post { showCurrentQuestion() } spinner.post { showCurrentQuestion() }
} }
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
data class Option( data class Option(
val key: String, // Must always be set val key: String, // Must always be set

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.content.Context import android.content.Context
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser

View File

@ -1,18 +1,18 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.content.Context import android.content.Context
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.data.Answer import com.tomhempel.rbarometer.data.Answer
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.data.ClientAnswersCache import com.tomhempel.rbarometer.data.ClientAnswersCache
import com.dano.test1.data.ClientRepository import com.tomhempel.rbarometer.data.ClientRepository
import com.dano.test1.data.CompletedQuestionnaireRepository import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.dano.test1.data.CompletedQuestionnaire import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.dano.test1.data.Question import com.tomhempel.rbarometer.data.Question
import com.dano.test1.data.Questionnaire import com.tomhempel.rbarometer.data.Questionnaire
import com.dano.test1.network.SyncLog import com.tomhempel.rbarometer.network.SyncLog
import com.dano.test1.notification.UploadReminderScheduler import com.tomhempel.rbarometer.notification.UploadReminderScheduler
object QuestionnairePersistence { object QuestionnairePersistence {

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
object QuestionnaireProgressiveCallbacks { object QuestionnaireProgressiveCallbacks {
@JvmStatic @JvmStatic

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.content.Context import android.content.Context
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
/** Applies per-questionnaire translation tables from synced JSON cache. */ /** Applies per-questionnaire translation tables from synced JSON cache. */
object QuestionnaireTranslationsLoader { object QuestionnaireTranslationsLoader {

View File

@ -1,10 +1,10 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import android.app.Activity import android.app.Activity
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
/** /**
* Validates questionnaire JSON before the coach opens a flow. * Validates questionnaire JSON before the coach opens a flow.

View File

@ -1,6 +1,6 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import com.dano.test1.data.ClientAnswersCache import com.tomhempel.rbarometer.data.ClientAnswersCache
/** /**
* Loads prior answers from Room (or session cache) into the in-memory map before UI bind, * Loads prior answers from Room (or session cache) into the in-memory map before UI bind,

View File

@ -1,6 +1,6 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
object SpinnerAnswerRules { object SpinnerAnswerRules {

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire package com.tomhempel.rbarometer.questionnaire
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.ui.localization.UiStrings import com.tomhempel.rbarometer.ui.localization.UiStrings
/** /**
* Resolves stored answer values to spinner indices (keys, localized labels, or free text). * Resolves stored answer values to spinner indices (keys, localized labels, or free text).

View File

@ -1,17 +1,17 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.data.ClientRepository import com.tomhempel.rbarometer.data.ClientRepository
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -1,24 +1,24 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.Spinner import android.widget.Spinner
import android.widget.TextView import android.widget.TextView
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.questionnaire.MAX_VALUE_YEAR import com.tomhempel.rbarometer.questionnaire.MAX_VALUE_YEAR
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.dano.test1.ui.Month import com.tomhempel.rbarometer.ui.Month
import com.dano.test1.ui.Months import com.tomhempel.rbarometer.ui.Months
import com.dano.test1.utils.DateAnswerFormat import com.tomhempel.rbarometer.utils.DateAnswerFormat
import com.dano.test1.utils.DatePrecision import com.tomhempel.rbarometer.utils.DatePrecision
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import java.util.Calendar import java.util.Calendar
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.text.InputFilter import android.text.InputFilter
@ -6,16 +6,16 @@ import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.dano.test1.utils.TextInputSanitizer import com.tomhempel.rbarometer.utils.TextInputSanitizer
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -1,17 +1,17 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import kotlinx.coroutines.* import kotlinx.coroutines.*
/* /*

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
@ -6,16 +6,16 @@ import android.widget.*
import android.text.Html import android.text.Html
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
import kotlinx.coroutines.* import kotlinx.coroutines.*
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.ui.AppLanguageStore import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.dano.test1.ui.ReviewScoresHandler import com.tomhempel.rbarometer.ui.ReviewScoresHandler
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
/* /*

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.util.TypedValue import android.util.TypedValue
@ -6,14 +6,14 @@ import android.view.View
import android.widget.* import android.widget.*
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
import kotlinx.coroutines.* import kotlinx.coroutines.*
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
/* /*
Zweck: Zweck:

View File

@ -1,18 +1,18 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import android.text.Html import android.text.Html
import android.widget.* import android.widget.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import com.dano.test1.questionnaire.AnswerRestoreHelper import com.tomhempel.rbarometer.questionnaire.AnswerRestoreHelper
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
/* /*
Zweck: Zweck:

View File

@ -1,17 +1,17 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView import android.widget.TextView
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerSliderQuestion( class HandlerSliderQuestion(
private val context: Context, private val context: Context,

View File

@ -1,20 +1,20 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import com.dano.test1.questionnaire.AnswerLookup import com.tomhempel.rbarometer.questionnaire.AnswerLookup
import com.dano.test1.questionnaire.AnswerRestoreHelper import com.tomhempel.rbarometer.questionnaire.AnswerRestoreHelper
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.questionnaire.SpinnerAnswerRules import com.tomhempel.rbarometer.questionnaire.SpinnerAnswerRules
import com.dano.test1.questionnaire.SpinnerSelectionHelper import com.tomhempel.rbarometer.questionnaire.SpinnerSelectionHelper
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerStringSpinner( class HandlerStringSpinner(
private val context: Context, private val context: Context,

View File

@ -1,20 +1,20 @@
package com.dano.test1.questionnaire.handlers package com.tomhempel.rbarometer.questionnaire.handlers
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import com.dano.test1.questionnaire.AnswerLookup import com.tomhempel.rbarometer.questionnaire.AnswerLookup
import com.dano.test1.questionnaire.AnswerRestoreHelper import com.tomhempel.rbarometer.questionnaire.AnswerRestoreHelper
import com.dano.test1.questionnaire.QuestionFlowResolver import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.SpinnerAnswerRules import com.tomhempel.rbarometer.questionnaire.SpinnerAnswerRules
import com.dano.test1.questionnaire.SpinnerSelectionHelper import com.tomhempel.rbarometer.questionnaire.SpinnerSelectionHelper
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.questionnaire.QuestionHandler import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerValueSpinner( class HandlerValueSpinner(
private val context: Context, private val context: Context,

View File

@ -1,13 +1,13 @@
package com.dano.test1.scoring package com.tomhempel.rbarometer.scoring
import android.content.Context import android.content.Context
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.data.CompletedQuestionnaireRepository import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.dano.test1.network.ClientScoringReview import com.tomhempel.rbarometer.network.ClientScoringReview
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.ScoringProfileDefinition import com.tomhempel.rbarometer.network.ScoringProfileDefinition
import com.dano.test1.network.ScoringReviewProfile import com.tomhempel.rbarometer.network.ScoringReviewProfile
import com.dano.test1.questionnaire.AnswerKeyUtils import com.tomhempel.rbarometer.questionnaire.AnswerKeyUtils
import com.google.gson.JsonParser import com.google.gson.JsonParser
object LocalScoringReviewBuilder { object LocalScoringReviewBuilder {

View File

@ -1,6 +1,6 @@
package com.dano.test1.scoring package com.tomhempel.rbarometer.scoring
import com.dano.test1.network.ScoringProfileDefinition import com.tomhempel.rbarometer.network.ScoringProfileDefinition
import kotlin.math.round import kotlin.math.round
/** /**

View File

@ -1,4 +1,4 @@
package com.dano.test1.scoring package com.tomhempel.rbarometer.scoring
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject

View File

@ -1,4 +1,4 @@
package com.dano.test1.security package com.tomhempel.rbarometer.security
import android.content.Context import android.content.Context
import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyGenParameterSpec

View File

@ -1,4 +1,4 @@
package com.dano.test1.security package com.tomhempel.rbarometer.security
import android.util.Base64 import android.util.Base64
import com.google.gson.JsonObject import com.google.gson.JsonObject

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Context import android.content.Context

View File

@ -1,7 +1,7 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Context import android.content.Context
import com.dano.test1.questionnaire.AppLanguages import com.tomhempel.rbarometer.questionnaire.AppLanguages
/** /**
* Persists the coach's chosen UI language across app restarts and screens. * Persists the coach's chosen UI language across app restarts and screens.

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
@ -9,17 +9,17 @@ import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.network.LoginManager import com.tomhempel.rbarometer.network.LoginManager
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.SyncCoordinator import com.tomhempel.rbarometer.network.SyncCoordinator
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import com.dano.test1.questionnaire.AppLanguages import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.dano.test1.utils.ScreenLoading import com.tomhempel.rbarometer.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -10,8 +10,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Context import android.content.Context
import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences

View File

@ -0,0 +1,404 @@
package com.tomhempel.rbarometer.ui
import android.text.Editable
import android.text.TextWatcher
import android.content.res.ColorStateList
import android.graphics.Typeface
import android.view.Gravity
import android.view.View
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.PopupMenu
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.R
class DashboardHandler(
private val activity: MainActivity,
private val onClose: () -> Unit,
) {
private data class DashboardClient(
val code: String,
val status: String,
val cycle: Int,
val session: Int,
val totalSessions: Int,
val lastSession: String,
val nextSession: String,
val pendingMeasures: Int,
val syncStatus: String,
val trend: String,
val participation: String,
val cadence: String,
val ownerNote: String,
)
private val clients = listOf(
DashboardClient(
code = "A102",
status = "Due today",
cycle = 3,
session = 2,
totalSessions = 6,
lastSession = "Jun 17",
nextSession = "Today",
pendingMeasures = 2,
syncStatus = "Sync OK",
trend = "Stable",
participation = "Active",
cadence = "Weekly",
ownerNote = "Continue current cycle",
),
DashboardClient(
code = "B447",
status = "Overdue",
cycle = 1,
session = 5,
totalSessions = 6,
lastSession = "Jun 10",
nextSession = "Jun 18",
pendingMeasures = 4,
syncStatus = "3 pending sync",
trend = "Needs review",
participation = "Active",
cadence = "Weekly",
ownerNote = "Needs scheduling contact",
),
DashboardClient(
code = "C083",
status = "Upcoming",
cycle = 2,
session = 1,
totalSessions = 6,
lastSession = "Jun 21",
nextSession = "Jun 28",
pendingMeasures = 0,
syncStatus = "Sync OK",
trend = "Improving",
participation = "Active",
cadence = "Every 2 weeks",
ownerNote = "Ready for next cycle",
),
DashboardClient(
code = "D219",
status = "Paused",
cycle = 4,
session = 6,
totalSessions = 6,
lastSession = "Jun 03",
nextSession = "Not planned",
pendingMeasures = 1,
syncStatus = "1 pending sync",
trend = "Participation paused",
participation = "Inactive",
cadence = "On hold",
ownerNote = "Confirm continuation",
),
)
fun show() {
activity.setContentView(R.layout.dashboard_screen)
activity.setBackPressInterceptor {
close()
true
}
bind()
}
private fun bind() {
activity.findViewById<MaterialButton>(R.id.dashboardBackButton).setOnClickListener {
close()
}
activity.findViewById<TextView>(R.id.dashboardSubtitle).text =
"Prototype view for repeated limited-session cycles"
activity.findViewById<TextView>(R.id.dashboardDueValue).text =
clients.count { it.status == "Due today" || it.status == "Overdue" }.toString()
activity.findViewById<TextView>(R.id.dashboardActiveValue).text =
clients.count { it.status != "Paused" }.toString()
activity.findViewById<TextView>(R.id.dashboardSyncValue).text =
clients.count { it.syncStatus.contains("pending") }.toString()
activity.findViewById<EditText>(R.id.dashboardSearchInput).addTextChangedListener(
object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
renderClients(s?.toString().orEmpty())
}
override fun afterTextChanged(s: Editable?) = Unit
},
)
renderClients("")
}
private fun renderClients(query: String) {
val list = activity.findViewById<LinearLayout>(R.id.dashboardClientList)
list.removeAllViews()
val normalized = query.trim()
val visibleClients = if (normalized.isBlank()) {
clients
} else {
clients.filter { client ->
client.code.contains(normalized, ignoreCase = true) ||
client.status.contains(normalized, ignoreCase = true) ||
client.participation.contains(normalized, ignoreCase = true) ||
client.trend.contains(normalized, ignoreCase = true) ||
client.nextSession.contains(normalized, ignoreCase = true)
}
}
visibleClients.forEach { client ->
list.addView(createClientCard(client))
}
}
private fun createClientCard(client: DashboardClient): View {
val card = MaterialCardView(activity).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT,
).also { it.setMargins(0, dp(10), 0, 0) }
radius = dp(10).toFloat()
strokeWidth = dp(1)
setStrokeColor(ContextCompat.getColor(activity, R.color.brand_header_stroke))
setCardBackgroundColor(ContextCompat.getColor(activity, R.color.brand_header_surface))
cardElevation = dp(1).toFloat()
}
val content = LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL
setPadding(dp(16), dp(14), dp(16), dp(16))
}
val header = LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
}
val titleBlock = LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
}
val title = TextView(activity).apply {
text = client.code
textSize = 20f
setTypeface(typeface, Typeface.BOLD)
setTextColor(ContextCompat.getColor(activity, R.color.brand_text_dark))
}
val subtitle = TextView(activity).apply {
text = "Cycle ${client.cycle} - Session ${client.session} of ${client.totalSessions} - ${client.cadence}"
textSize = 13f
setTextColor(ContextCompat.getColor(activity, R.color.brand_text_muted))
}
titleBlock.addView(title)
titleBlock.addView(subtitle)
header.addView(titleBlock)
header.addView(statusChip(client.status))
val scheduleRow = metricRow(
leftTitle = "Last session",
leftValue = client.lastSession,
rightTitle = "Next planned",
rightValue = client.nextSession,
rightAction = "Edit",
)
val workRow = metricRow(
leftTitle = "Open measures",
leftValue = client.pendingMeasures.toString(),
rightTitle = "Data state",
rightValue = client.syncStatus,
rightAction = null,
)
val trend = TextView(activity).apply {
text = "Trend: ${client.trend} - Participation: ${client.participation}\n${client.ownerNote}"
textSize = 14f
setLineSpacing(dp(2).toFloat(), 1f)
setTextColor(ContextCompat.getColor(activity, R.color.brand_text_dark))
setPadding(0, dp(12), 0, 0)
}
val actionRow = LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
setPadding(0, dp(14), 0, 0)
}
actionRow.addView(primaryAction("Select client", weight = 1.25f))
actionRow.addView(statusDropdown(client.participation, weight = 1.15f))
actionRow.addView(destructiveAction("Remove", weight = 0.8f))
content.addView(header)
content.addView(scheduleRow)
content.addView(workRow)
content.addView(trend)
content.addView(actionRow)
card.addView(content)
return card
}
private fun metricRow(
leftTitle: String,
leftValue: String,
rightTitle: String,
rightValue: String,
rightAction: String?,
): View {
val row = LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
isBaselineAligned = false
setPadding(0, dp(12), 0, 0)
}
row.addView(metricBlock(leftTitle, leftValue, weight = 1f))
row.addView(metricBlock(rightTitle, rightValue, weight = 1f, action = rightAction))
return row
}
private fun metricBlock(
title: String,
value: String,
weight: Float,
action: String? = null,
): View {
val block = LinearLayout(activity).apply {
orientation = LinearLayout.VERTICAL
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, weight)
.also { it.setMargins(0, 0, dp(8), 0) }
background = ContextCompat.getDrawable(activity, R.drawable.bg_session_metric)
setPadding(dp(12), dp(10), dp(12), dp(10))
}
val titleView = TextView(activity).apply {
text = title
textSize = 11f
setTextColor(ContextCompat.getColor(activity, R.color.brand_text_muted))
}
val valueRow = LinearLayout(activity).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
setPadding(0, dp(3), 0, 0)
}
val valueView = TextView(activity).apply {
text = value
textSize = 16f
setTypeface(typeface, Typeface.BOLD)
setTextColor(ContextCompat.getColor(activity, R.color.brand_text_dark))
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
}
valueRow.addView(valueView)
if (action != null) {
valueRow.addView(smallAction(action))
}
block.addView(titleView)
block.addView(valueRow)
return block
}
private fun statusChip(status: String): TextView {
val bg = when (status) {
"Overdue" -> R.drawable.bg_chip_amber
"Paused" -> R.drawable.bg_chip_locked
else -> R.drawable.bg_chip_green
}
return TextView(activity).apply {
text = status
textSize = 12f
setTypeface(typeface, Typeface.BOLD)
setTextColor(
ContextCompat.getColor(
activity,
if (status == "Paused") R.color.brand_chip_locked_text else R.color.brand_text_on_dark,
),
)
setBackgroundResource(bg)
setPadding(dp(10), dp(6), dp(10), dp(6))
}
}
private fun primaryAction(label: String, weight: Float): MaterialButton =
dashboardButton(label, weight).apply {
setTextColor(ContextCompat.getColor(activity, R.color.brand_text_on_dark))
backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_purple))
strokeColor = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_purple))
}
private fun lifecycleAction(label: String, weight: Float): MaterialButton =
dashboardButton(label, weight).apply {
setTextColor(ContextCompat.getColor(activity, R.color.brand_text_dark))
backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_header_surface))
strokeColor = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_header_stroke))
}
private fun statusDropdown(status: String, weight: Float): MaterialButton =
lifecycleAction("Status: $status", weight).apply {
setOnClickListener { anchor ->
PopupMenu(activity, anchor).apply {
menu.add("Active")
menu.add("Inactive")
menu.add("Archived")
setOnMenuItemClickListener { item ->
Toast.makeText(
activity,
"Set status to ${item.title} is prototype-only",
Toast.LENGTH_SHORT,
).show()
true
}
}.show()
}
}
private fun destructiveAction(label: String, weight: Float): MaterialButton =
dashboardButton(label, weight).apply {
setTextColor(ContextCompat.getColor(activity, R.color.brand_destructive_text))
backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_destructive_fill))
strokeColor = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_destructive_fill))
}
private fun dashboardButton(label: String, weight: Float): MaterialButton =
MaterialButton(activity).apply {
layoutParams = LinearLayout.LayoutParams(0, dp(42), weight).also {
it.setMargins(0, 0, dp(6), 0)
}
text = label
textSize = 11f
isAllCaps = false
cornerRadius = dp(18)
strokeWidth = dp(1)
insetTop = 0
insetBottom = 0
setPadding(dp(6), 0, dp(6), 0)
setOnClickListener {
Toast.makeText(activity, "$label is prototype-only", Toast.LENGTH_SHORT).show()
}
}
private fun smallAction(label: String): MaterialButton =
MaterialButton(activity).apply {
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, dp(32))
text = label
textSize = 11f
isAllCaps = false
cornerRadius = dp(16)
strokeWidth = dp(1)
insetTop = 0
insetBottom = 0
minWidth = dp(52)
minimumWidth = dp(52)
setPadding(dp(8), 0, dp(8), 0)
setTextColor(ContextCompat.getColor(activity, R.color.brand_purple))
backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_header_surface))
strokeColor = ColorStateList.valueOf(ContextCompat.getColor(activity, R.color.brand_purple))
setOnClickListener {
Toast.makeText(activity, "$label next session is prototype-only", Toast.LENGTH_SHORT).show()
}
}
private fun close() {
activity.setBackPressInterceptor(null)
onClose()
}
private fun dp(value: Int): Int =
(value * activity.resources.displayMetrics.density).toInt()
}

View File

@ -1,7 +1,7 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Context import android.content.Context
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
object DevSettingsAccess { object DevSettingsAccess {
const val CLIENT_CODE = "DEVELOPER-SETTINGS" const val CLIENT_CODE = "DEVELOPER-SETTINGS"

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
@ -12,16 +12,16 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.network.QuestionnaireSyncService import com.tomhempel.rbarometer.network.QuestionnaireSyncService
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import com.dano.test1.utils.SyncProgressOverlay import com.tomhempel.rbarometer.utils.SyncProgressOverlay
import com.dano.test1.utils.SyncProgressTexts import com.tomhempel.rbarometer.utils.SyncProgressTexts
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.dano.test1.ui.AppLanguageStore import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.dano.test1.ui.LogoutDialogs import com.tomhempel.rbarometer.ui.LogoutDialogs
import com.dano.test1.ui.SessionLogout import com.tomhempel.rbarometer.ui.SessionLogout
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
@ -12,36 +12,36 @@ import androidx.appcompat.widget.AppCompatImageButton
import android.widget.* import android.widget.*
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.data.PendingUploadRepository import com.tomhempel.rbarometer.data.PendingUploadRepository
import com.dano.test1.data.UnuploadedWorkRepository import com.tomhempel.rbarometer.data.UnuploadedWorkRepository
import com.dano.test1.network.NetworkUtils import com.tomhempel.rbarometer.network.NetworkUtils
import com.dano.test1.network.QuestionnaireAnswerSync import com.tomhempel.rbarometer.network.QuestionnaireAnswerSync
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.SyncCoordinator import com.tomhempel.rbarometer.network.SyncCoordinator
import com.dano.test1.network.SyncResult import com.tomhempel.rbarometer.network.SyncResult
import com.dano.test1.questionnaire.AppLanguages import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import com.dano.test1.questionnaire.ConditionJsonParser import com.tomhempel.rbarometer.questionnaire.ConditionJsonParser
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionnaireBase import com.tomhempel.rbarometer.questionnaire.QuestionnaireBase
import com.dano.test1.questionnaire.QuestionnaireAllInOne import com.tomhempel.rbarometer.questionnaire.QuestionnaireAllInOne
import com.dano.test1.questionnaire.QuestionnaireGeneric import com.tomhempel.rbarometer.questionnaire.QuestionnaireGeneric
import com.dano.test1.questionnaire.QuestionnaireValidation import com.tomhempel.rbarometer.questionnaire.QuestionnaireValidation
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import org.json.JSONObject import org.json.JSONObject
import com.dano.test1.utils.ScreenLoading import com.tomhempel.rbarometer.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerOpeningScreen(private val activity: MainActivity) { class HandlerOpeningScreen(private val activity: MainActivity) {
@ -60,6 +60,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
private lateinit var spinner: Spinner private lateinit var spinner: Spinner
private lateinit var textView: TextView private lateinit var textView: TextView
private lateinit var noClientsAssignedText: TextView private lateinit var noClientsAssignedText: TextView
private lateinit var sessionMetricClientsValue: TextView
private lateinit var sessionMetricMeasuresValue: TextView
private lateinit var sessionMetricSyncValue: TextView
private lateinit var nextStepBannerCard: View private lateinit var nextStepBannerCard: View
private lateinit var nextStepEyebrow: TextView private lateinit var nextStepEyebrow: TextView
private lateinit var nextStepTitle: TextView private lateinit var nextStepTitle: TextView
@ -213,6 +216,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
spinner = activity.findViewById(R.id.string_spinner1) spinner = activity.findViewById(R.id.string_spinner1)
textView = activity.findViewById(R.id.textView) textView = activity.findViewById(R.id.textView)
noClientsAssignedText = activity.findViewById(R.id.noClientsAssignedText) noClientsAssignedText = activity.findViewById(R.id.noClientsAssignedText)
sessionMetricClientsValue = activity.findViewById(R.id.sessionMetricClientsValue)
sessionMetricMeasuresValue = activity.findViewById(R.id.sessionMetricMeasuresValue)
sessionMetricSyncValue = activity.findViewById(R.id.sessionMetricSyncValue)
nextStepBannerCard = activity.findViewById(R.id.nextStepBannerCard) nextStepBannerCard = activity.findViewById(R.id.nextStepBannerCard)
nextStepEyebrow = activity.findViewById(R.id.nextStepEyebrow) nextStepEyebrow = activity.findViewById(R.id.nextStepEyebrow)
nextStepTitle = activity.findViewById(R.id.nextStepTitle) nextStepTitle = activity.findViewById(R.id.nextStepTitle)
@ -233,23 +239,40 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
refreshProgress = activity.findViewById(R.id.refreshProgress) refreshProgress = activity.findViewById(R.id.refreshProgress)
refreshButton.contentDescription = t("refresh") refreshButton.contentDescription = t("refresh")
settingsButton.contentDescription = tOr("settings", hubFallback("Einstellungen", "Settings")) settingsButton.contentDescription = tOr("settings", hubFallback("Einstellungen", "Settings"))
textView.text = t("questionnaire_title") textView.text = hubFallback("Sitzungsinstrumente", "Session measures")
applyHubLabels() applyHubLabels()
updateSessionMetrics()
applyCoachDisplay() applyCoachDisplay()
} }
private fun applyHubLabels() { private fun applyHubLabels() {
if (!::labelLanguage.isInitialized) return if (!::labelLanguage.isInitialized) return
headerWelcome.text = tOr("hub_welcome_back", hubFallback("Willkommen zurück", "Welcome back")) headerWelcome.text = "Barometer"
labelLanguage.text = tOr("language", hubFallback("Sprache", "Language")) labelLanguage.text = tOr("language", hubFallback("Sprache", "Language"))
labelClientCode.text = tOr("client_code", hubFallback("Klientencode", "Client code")) labelClientCode.text = hubFallback("Aktiver Klient", "Active client")
} }
private fun applyCoachDisplay() { private fun applyCoachDisplay() {
if (!::coachNameDisplay.isInitialized) return if (!::coachNameDisplay.isInitialized) return
val username = TokenStore.getUsername(activity)?.trim().orEmpty() val username = TokenStore.getUsername(activity)?.trim().orEmpty()
coachNameDisplay.text = username.ifBlank { coachNameDisplay.text = if (username.isBlank()) {
tOr("coach_code", hubFallback("Coach", "Coach")) hubFallback("Sitzungskonsole", "Session console")
} else {
hubFallback("Beraterkonsole: $username", "Counselor console: $username")
}
}
private fun updateSessionMetrics() {
if (!::sessionMetricClientsValue.isInitialized) return
sessionMetricClientsValue.text = assignedClientsForUi().size.toString()
sessionMetricMeasuresValue.text = questionnaireEntries.size.toString()
CoroutineScope(Dispatchers.Main).launch {
val count = withContext(Dispatchers.IO) {
PendingUploadRepository.countAll()
}
if (::sessionMetricSyncValue.isInitialized) {
sessionMetricSyncValue.text = if (count > 0) count.toString() else "OK"
}
} }
} }
@ -529,6 +552,38 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
setTypeface(typeface, android.graphics.Typeface.BOLD) setTypeface(typeface, android.graphics.Typeface.BOLD)
} }
val dashboardButton = MaterialButton(activity).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT,
).also {
it.bottomMargin = dp(10)
}
text = hubFallback("Dashboard", "Dashboard")
isAllCaps = false
icon = ContextCompat.getDrawable(activity, R.drawable.ic_person_24)
iconGravity = MaterialButton.ICON_GRAVITY_TEXT_START
iconPadding = dp(10)
iconTint = ColorStateList.valueOf(color(R.color.brand_purple))
setTextColor(color(R.color.brand_text_dark))
textAlignment = View.TEXT_ALIGNMENT_VIEW_START
gravity = Gravity.START or Gravity.CENTER_VERTICAL
backgroundTintList = ColorStateList.valueOf(color(R.color.brand_header_surface))
strokeColor = ColorStateList.valueOf(color(R.color.brand_header_stroke))
strokeWidth = dp(1)
cornerRadius = dp(20)
insetTop = 0
insetBottom = 0
setPadding(dp(12), dp(14), dp(12), dp(14))
setOnClickListener {
dialog.dismiss()
DashboardHandler(activity) {
activity.setBackPressInterceptor(null)
init()
}.show()
}
}
val logoutButton = MaterialButton(activity).apply { val logoutButton = MaterialButton(activity).apply {
layoutParams = LinearLayout.LayoutParams( layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT,
@ -557,6 +612,14 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
} }
container.addView(handle) container.addView(handle)
container.addView(titleView)
container.addView(View(activity).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
dp(14),
)
})
container.addView(dashboardButton)
container.addView(logoutButton) container.addView(logoutButton)
dialog.setContentView(container) dialog.setContentView(container)
dialog.show() dialog.show()
@ -688,6 +751,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
refreshClientSpinner() refreshClientSpinner()
applyUpdateButtonTexts(force = true) applyUpdateButtonTexts(force = true)
updateStatusStrip() updateStatusStrip()
updateSessionMetrics()
updateQuestionnaireSectionVisibility() updateQuestionnaireSectionVisibility()
refreshLoadedClientUi() refreshLoadedClientUi()
} }
@ -711,6 +775,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
noClientsAssignedText.text = t("no_clients_assigned") noClientsAssignedText.text = t("no_clients_assigned")
noClientsAssignedText.visibility = View.VISIBLE noClientsAssignedText.visibility = View.VISIBLE
textView.visibility = View.GONE textView.visibility = View.GONE
updateSessionMetrics()
nextStepBannerCard.visibility = View.GONE nextStepBannerCard.visibility = View.GONE
questionnaireScroll.visibility = View.GONE questionnaireScroll.visibility = View.GONE
bottomUploadDock.visibility = View.GONE bottomUploadDock.visibility = View.GONE
@ -728,9 +793,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
nextStepBannerCard.visibility = View.VISIBLE nextStepBannerCard.visibility = View.VISIBLE
val completed = dynamicButtons.count { isCompleted(it) } val completed = dynamicButtons.count { isCompleted(it) }
nextStepEyebrow.text = t("next_step_label") nextStepEyebrow.text = hubFallback("Nächster Sitzungsschritt", "Next session step")
nextStepTitle.text = t("next_step_loading_title") nextStepTitle.text = hubFallback("Sitzungsstatus wird geprüft", "Checking session status")
nextStepBody.text = t("next_step_loading_body") nextStepBody.text = hubFallback("Lokale Daten, Reviews und Synchronisation werden ausgewertet.", "Reviewing local measures, scoring work, and sync status.")
nextStepActionButton.visibility = View.GONE nextStepActionButton.visibility = View.GONE
nextStepActionButton.setOnClickListener(null) nextStepActionButton.setOnClickListener(null)
@ -769,9 +834,19 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
} }
private fun applyNextStepBanner(titleKey: String, bodyKey: String, action: NextStepAction) { private fun applyNextStepBanner(titleKey: String, bodyKey: String, action: NextStepAction) {
nextStepEyebrow.text = t("next_step_label") nextStepEyebrow.text = hubFallback("Nächster Sitzungsschritt", "Next session step")
nextStepTitle.text = t(titleKey) nextStepTitle.text = when (action) {
nextStepBody.text = t(bodyKey) NextStepAction.UPLOAD -> hubFallback("Synchronisation offen", "Sync required")
NextStepAction.REVIEW -> hubFallback("Score-Review offen", "Score review pending")
NextStepAction.QUESTIONNAIRE -> hubFallback("Sitzung fortsetzen", "Continue session")
NextStepAction.NONE -> hubFallback("Sitzung vollständig", "Session complete")
}
nextStepBody.text = when (action) {
NextStepAction.UPLOAD -> hubFallback("Es gibt fertige lokale Sitzungsdaten, die hochgeladen werden müssen.", "Completed local session data is waiting to be uploaded.")
NextStepAction.REVIEW -> hubFallback("Mindestens ein Score benötigt noch eine fachliche Prüfung.", "At least one score still needs counselor review.")
NextStepAction.QUESTIONNAIRE -> hubFallback("Der nächste verfügbare Messpunkt ist bereit für diese Sitzung.", "The next available measure is ready for this session.")
NextStepAction.NONE -> hubFallback("Alle verfügbaren Messpunkte für diesen Klienten sind abgeschlossen.", "All available measures for this client are complete.")
}
when (action) { when (action) {
NextStepAction.UPLOAD -> { NextStepAction.UPLOAD -> {
nextStepActionButton.visibility = View.VISIBLE nextStepActionButton.visibility = View.VISIBLE
@ -793,7 +868,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
} }
NextStepAction.QUESTIONNAIRE -> { NextStepAction.QUESTIONNAIRE -> {
nextStepActionButton.visibility = View.VISIBLE nextStepActionButton.visibility = View.VISIBLE
nextStepActionButton.text = t("next_step_show_questionnaire") nextStepActionButton.text = hubFallback("Messpunkt anzeigen", "Show measure")
nextStepActionButton.setOnClickListener { highlightNextAvailableQuestionnaire() } nextStepActionButton.setOnClickListener { highlightNextAvailableQuestionnaire() }
} }
NextStepAction.NONE -> { NextStepAction.NONE -> {
@ -952,6 +1027,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
val base = t("upload") val base = t("upload")
uploadButton.text = if (count > 0) "$base ($count)" else base uploadButton.text = if (count > 0) "$base ($count)" else base
uploadButton.contentDescription = uploadButton.text uploadButton.contentDescription = uploadButton.text
if (::sessionMetricSyncValue.isInitialized) {
sessionMetricSyncValue.text = if (count > 0) count.toString() else "OK"
}
} }
} }
@ -1045,7 +1123,8 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
uploadButton.text = t("upload") uploadButton.text = t("upload")
applyHubLabels() applyHubLabels()
applyCoachDisplay() applyCoachDisplay()
textView.text = t("questionnaire_title") textView.text = hubFallback("Sitzungsinstrumente", "Session measures")
updateSessionMetrics()
updateNextStepBanner() updateNextStepBanner()
} }

View File

@ -1,21 +1,21 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Intent import android.content.Intent
import android.widget.Button import android.widget.Button
import android.widget.Toast import android.widget.Toast
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.data.ClientAnswersCache import com.tomhempel.rbarometer.data.ClientAnswersCache
import com.dano.test1.data.ClientRepository import com.tomhempel.rbarometer.data.ClientRepository
import com.dano.test1.data.CompletedQuestionnaireRepository import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import kotlinx.coroutines.* import kotlinx.coroutines.*
import com.dano.test1.data.CompletedQuestionnaire import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.dano.test1.network.NetworkUtils import com.tomhempel.rbarometer.network.NetworkUtils
import com.dano.test1.network.QuestionnaireAnswerSync import com.tomhempel.rbarometer.network.QuestionnaireAnswerSync
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import com.dano.test1.questionnaire.GlobalValues import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionItem import com.tomhempel.rbarometer.questionnaire.QuestionItem
class LoadButtonHandler( class LoadButtonHandler(
private val activity: MainActivity, private val activity: MainActivity,

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.app.Dialog import android.app.Dialog
import android.content.res.ColorStateList import android.content.res.ColorStateList
@ -13,7 +13,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
/** /**

View File

@ -1,6 +1,6 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
data class Month(val name: String) { data class Month(val name: String) {
override fun toString(): String = name override fun toString(): String = name

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
@ -7,7 +7,7 @@ import android.os.Build
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
/** /**

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Typeface import android.graphics.Typeface
@ -8,8 +8,8 @@ import android.widget.Button
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import kotlin.math.roundToInt import kotlin.math.roundToInt

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Typeface import android.graphics.Typeface
@ -15,19 +15,19 @@ import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.data.CoachScoringReviewRepository import com.tomhempel.rbarometer.data.CoachScoringReviewRepository
import com.dano.test1.network.ClientScoringReview import com.tomhempel.rbarometer.network.ClientScoringReview
import com.dano.test1.network.QuestionnaireApiClient import com.tomhempel.rbarometer.network.QuestionnaireApiClient
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.ScoringReviewProfile import com.tomhempel.rbarometer.network.ScoringReviewProfile
import com.dano.test1.notification.UploadReminderScheduler import com.tomhempel.rbarometer.notification.UploadReminderScheduler
import com.dano.test1.scoring.LocalScoringReviewBuilder import com.tomhempel.rbarometer.scoring.LocalScoringReviewBuilder
import com.dano.test1.questionnaire.AppLanguages import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.dano.test1.ui.localization.UiStrings import com.tomhempel.rbarometer.ui.localization.UiStrings
import com.dano.test1.utils.ScreenLoading import com.tomhempel.rbarometer.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View File

@ -1,12 +1,12 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.content.Context import android.content.Context
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
import com.dano.test1.data.UnuploadedWorkRepository import com.tomhempel.rbarometer.data.UnuploadedWorkRepository
import com.dano.test1.network.ClientAnswerSyncStore import com.tomhempel.rbarometer.network.ClientAnswerSyncStore
import com.dano.test1.network.NetworkUtils import com.tomhempel.rbarometer.network.NetworkUtils
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.network.TokenStore import com.tomhempel.rbarometer.network.TokenStore
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.app.Activity import android.app.Activity
import android.view.View import android.view.View
@ -6,7 +6,7 @@ import android.view.ViewGroup
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import com.dano.test1.R import com.tomhempel.rbarometer.R
import kotlin.math.max import kotlin.math.max
/** /**

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui package com.tomhempel.rbarometer.ui
import android.graphics.Typeface import android.graphics.Typeface
import android.util.Log import android.util.Log
@ -9,20 +9,20 @@ import android.widget.*
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.dano.test1.MainActivity import com.tomhempel.rbarometer.MainActivity
import com.dano.test1.MyApp import com.tomhempel.rbarometer.MyApp
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.data.AnswerRepository import com.tomhempel.rbarometer.data.AnswerRepository
import com.dano.test1.data.CoachScoringReviewRepository import com.tomhempel.rbarometer.data.CoachScoringReviewRepository
import com.dano.test1.data.CompletedQuestionnaireRepository import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.dano.test1.data.Question import com.tomhempel.rbarometer.data.Question
import com.dano.test1.network.DatabaseUploader import com.tomhempel.rbarometer.network.DatabaseUploader
import com.dano.test1.network.QuestionnaireCache import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.dano.test1.questionnaire.AppLanguages import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.dano.test1.questionnaire.QuestionnaireTranslationsLoader import com.tomhempel.rbarometer.questionnaire.QuestionnaireTranslationsLoader
import com.dano.test1.ui.localization.UiStrings import com.tomhempel.rbarometer.ui.localization.UiStrings
import com.dano.test1.utils.ScreenLoading import com.tomhempel.rbarometer.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.gson.JsonParser import com.google.gson.JsonParser

View File

@ -1,6 +1,6 @@
package com.dano.test1.ui.localization package com.tomhempel.rbarometer.ui.localization
import com.dano.test1.LanguageManager import com.tomhempel.rbarometer.LanguageManager
/** /**
* Shared UI string helpers for upload overview and activities. * Shared UI string helpers for upload overview and activities.

View File

@ -1,4 +1,4 @@
package com.dano.test1.utils package com.tomhempel.rbarometer.utils
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale

View File

@ -1,4 +1,4 @@
package com.dano.test1 package com.tomhempel.rbarometer
/** /**
* Resolves UI strings from API translations (synced from the website). * Resolves UI strings from API translations (synced from the website).

View File

@ -1,10 +1,10 @@
package com.dano.test1.utils package com.tomhempel.rbarometer.utils
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.google.android.material.progressindicator.CircularProgressIndicator import com.google.android.material.progressindicator.CircularProgressIndicator
/** /**

View File

@ -1,8 +1,8 @@
package com.dano.test1.utils package com.tomhempel.rbarometer.utils
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.network.SyncPhase import com.tomhempel.rbarometer.network.SyncPhase
/** Flat Material-style icons per sync phase — no emoji or decorative chrome. */ /** Flat Material-style icons per sync phase — no emoji or decorative chrome. */
object SyncPhaseIcons { object SyncPhaseIcons {

View File

@ -1,4 +1,4 @@
package com.dano.test1.utils package com.tomhempel.rbarometer.utils
import android.app.Activity import android.app.Activity
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
@ -12,9 +12,9 @@ import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.ImageViewCompat import androidx.core.widget.ImageViewCompat
import com.dano.test1.R import com.tomhempel.rbarometer.R
import com.dano.test1.network.SyncPhase import com.tomhempel.rbarometer.network.SyncPhase
import com.dano.test1.network.SyncProgress import com.tomhempel.rbarometer.network.SyncProgress
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
/** /**

View File

@ -1,6 +1,6 @@
package com.dano.test1.utils package com.tomhempel.rbarometer.utils
import com.dano.test1.network.SyncPhase import com.tomhempel.rbarometer.network.SyncPhase
/** Professional DE/EN copy for the sync overlay — phase messages only, no counts. */ /** Professional DE/EN copy for the sync overlay — phase messages only, no counts. */
object SyncProgressTexts { object SyncProgressTexts {

View File

@ -1,4 +1,4 @@
package com.dano.test1.utils package com.tomhempel.rbarometer.utils
/** /**
* Strips patterns commonly used in SQL injection attempts from free-text answers. * Strips patterns commonly used in SQL injection attempts from free-text answers.

View File

@ -1,4 +1,4 @@
package com.dano.test1.utils package com.tomhempel.rbarometer.utils
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
@ -14,7 +14,7 @@ import android.widget.Spinner
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
import com.dano.test1.R import com.tomhempel.rbarometer.R
import kotlin.math.roundToInt import kotlin.math.roundToInt
object ViewUtils { object ViewUtils {

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/brand_header_surface" />
<stroke
android:width="1dp"
android:color="@color/brand_header_stroke" />
<corners android:radius="10dp" />
</shape>

View File

@ -5,166 +5,12 @@
android:viewportWidth="108" android:viewportWidth="108"
android:viewportHeight="108"> android:viewportHeight="108">
<path <path
android:fillColor="#3DDC84" android:fillColor="#0F766E"
android:pathData="M0,0h108v108h-108z" /> android:pathData="M0,0h108v108h-108z" />
<path <path
android:fillColor="#00000000" android:fillColor="#0E7490"
android:pathData="M9,0L9,108" android:pathData="M0,78 C26,62 48,65 73,49 C88,39 99,27 108,15 L108,108 L0,108 Z" />
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path <path
android:fillColor="#00000000" android:fillColor="#14B8A6"
android:pathData="M19,0L19,108" android:pathData="M0,0h108v26 C86,36 64,39 44,32 C27,26 14,17 0,14 Z" />
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector> </vector>

View File

@ -1,30 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="108" android:viewportWidth="108"
android:viewportHeight="108"> android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path <path
android:fillColor="#FFFFFF" android:fillColor="#FFFFFFFF"
android:fillType="nonZero" android:pathData="M54,23C36.88,23 23,36.88 23,54C23,71.12 36.88,85 54,85C71.12,85 85,71.12 85,54C85,36.88 71.12,23 54,23ZM54,31C66.7,31 77,41.3 77,54C77,66.7 66.7,77 54,77C41.3,77 31,66.7 31,54C31,41.3 41.3,31 54,31Z" />
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" <path
android:strokeWidth="1" android:fillColor="#FFFFFFFF"
android:strokeColor="#00000000" /> android:pathData="M54,37C51.79,37 50,38.79 50,41C50,43.21 51.79,45 54,45C56.21,45 58,43.21 58,41C58,38.79 56.21,37 54,37ZM54,41C61.38,41 67.67,45.6 70,52H63.5C61.76,48.45 58.16,46 54,46C49.84,46 46.24,48.45 44.5,52H38C40.33,45.6 46.62,41 54,41Z" />
</vector> <path
android:fillColor="#FFFFFFFF"
android:pathData="M52,57 L76,43 L58,64 Z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M31,70h46v7h-46z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M32,54h6v5h-6zM70,54h6v5h-6zM51,30h6v8h-6z" />
</vector>

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