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 {
namespace = "com.dano.test1"
namespace = "com.tomhempel.rbarometer"
compileSdk = 35
defaultConfig {
applicationId = "com.dano.test1"
applicationId = "com.tomhempel.rbarometer"
minSdk = 29
targetSdk = 35
versionCode = 1

View File

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

View File

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

View File

@ -1,12 +1,12 @@
package com.dano.test1
package com.tomhempel.rbarometer
import android.app.Application
import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import androidx.room.Room
import androidx.room.RoomDatabase
import com.dano.test1.data.AppDatabase
import com.dano.test1.security.SensitiveDataCrypto
import com.tomhempel.rbarometer.data.AppDatabase
import com.tomhempel.rbarometer.security.SensitiveDataCrypto
/*
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.dano.test1.security.SensitiveDataCrypto
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.security.SensitiveDataCrypto
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.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

View File

@ -1,7 +1,7 @@
package com.dano.test1.data
package com.tomhempel.rbarometer.data
import com.dano.test1.MyApp
import com.dano.test1.security.SensitiveDataCrypto
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.security.SensitiveDataCrypto
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.dano.test1.security.SensitiveDataCrypto
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.security.SensitiveDataCrypto
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.dano.test1.security.SensitiveDataCrypto
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.security.SensitiveDataCrypto
object CompletedQuestionnaireRepository {
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.*

View File

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

View File

@ -1,4 +1,4 @@
package com.dano.test1.data
package com.tomhempel.rbarometer.data
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.

View File

@ -1,4 +1,4 @@
package com.dano.test1.network
package com.tomhempel.rbarometer.network
data class ApiError(
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 com.dano.test1.security.SensitiveDataCrypto
import com.tomhempel.rbarometer.security.SensitiveDataCrypto
/** Persists per-client answer sync timestamps (survives process restarts). */
internal object ClientAnswerSyncStore {

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package com.dano.test1.network
package com.tomhempel.rbarometer.network
import android.content.Context
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.net.ConnectivityManager

View File

@ -1,14 +1,14 @@
package com.dano.test1.network
package com.tomhempel.rbarometer.network
import android.content.Context
import com.dano.test1.MyApp
import com.dano.test1.data.AnswerEntity
import com.dano.test1.data.AnswerRepository
import com.dano.test1.data.CompletedQuestionnaire
import com.dano.test1.data.CompletedQuestionnaireRepository
import com.dano.test1.data.Question
import com.dano.test1.data.Questionnaire
import com.dano.test1.questionnaire.AnswerKeyUtils
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.data.AnswerEntity
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.tomhempel.rbarometer.data.Question
import com.tomhempel.rbarometer.data.Questionnaire
import com.tomhempel.rbarometer.questionnaire.AnswerKeyUtils
import com.google.gson.JsonArray
import com.google.gson.JsonObject
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 sumPoints = block.sumPoints
val clientKey = com.dano.test1.security.SensitiveDataCrypto.clientCodeKey(clientCode)
val clientKey = com.tomhempel.rbarometer.security.SensitiveDataCrypto.clientCodeKey(clientCode)
return PreparedBlockImport(
questionnaireId = questionnaireId,
@ -382,7 +382,7 @@ object QuestionnaireAnswerSync {
AnswerEntity(
clientCodeKey = clientKey,
questionId = AnswerKeyUtils.roomQuestionId(questionnaireId, key),
answerValueEnc = com.dano.test1.security.SensitiveDataCrypto.encrypt(value),
answerValueEnc = com.tomhempel.rbarometer.security.SensitiveDataCrypto.encrypt(value),
)
},
completion = CompletedQuestionnaire(
@ -461,8 +461,8 @@ object QuestionnaireAnswerSync {
}
private fun CompletedQuestionnaire.toEntity(clientCode: String) =
com.dano.test1.data.CompletedQuestionnaireEntity(
clientCodeKey = com.dano.test1.security.SensitiveDataCrypto.clientCodeKey(clientCode),
com.tomhempel.rbarometer.data.CompletedQuestionnaireEntity(
clientCodeKey = com.tomhempel.rbarometer.security.SensitiveDataCrypto.clientCodeKey(clientCode),
questionnaireId = questionnaireId,
timestamp = timestamp,
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.JsonArray
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 com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.data.ClientRepository
import com.dano.test1.security.SensitiveDataCrypto
import com.dano.test1.questionnaire.GlobalValues
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.data.ClientRepository
import com.tomhempel.rbarometer.security.SensitiveDataCrypto
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
@ -102,9 +102,9 @@ object QuestionnaireCache {
*/
suspend fun wipeEntireLocalStore(context: Context) {
clearAssignedClients(context)
com.dano.test1.MyApp.database.clearAllTables()
com.tomhempel.rbarometer.MyApp.database.clearAllTables()
cacheDir(context).deleteRecursively()
com.dano.test1.notification.UploadReminderScheduler.cancel(context)
com.tomhempel.rbarometer.notification.UploadReminderScheduler.cancel(context)
LanguageManager.setApiTranslations(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 com.dano.test1.data.AnswerRepository
import com.dano.test1.data.CompletedQuestionnaireRepository
import com.dano.test1.questionnaire.AnswerKeyUtils
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.tomhempel.rbarometer.questionnaire.AnswerKeyUtils
import com.google.gson.JsonArray
import com.google.gson.JsonObject
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 com.dano.test1.MyApp
import com.dano.test1.data.CompletedQuestionnaire
import com.dano.test1.data.Questionnaire
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.tomhempel.rbarometer.data.Questionnaire
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -203,7 +203,7 @@ object QuestionnaireSyncService {
private suspend fun seedServerCompletions(clientInfosFromApi: List<AssignedClientInfo>) {
val toInsert = mutableListOf<CompletedQuestionnaire>()
for (clientInfo in clientInfosFromApi) {
val existingByQn = com.dano.test1.data.CompletedQuestionnaireRepository
val existingByQn = com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
.getAllForClient(clientInfo.clientCode)
.associateBy { it.questionnaireId }
for (serverCompletion in clientInfo.completedQuestionnaires) {
@ -225,7 +225,7 @@ object QuestionnaireSyncService {
}
if (toInsert.isNotEmpty()) {
runCatching {
com.dano.test1.data.CompletedQuestionnaireRepository.insertAll(toInsert)
com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository.insertAll(toInsert)
}.onSuccess {
SyncLog.d(TAG, "seeded ${toInsert.size} server completion(s)")
}.onFailure { e ->

View File

@ -1,8 +1,8 @@
package com.dano.test1.network
package com.tomhempel.rbarometer.network
import android.content.Context
import android.widget.Toast
import com.dano.test1.LanguageManager
import com.tomhempel.rbarometer.LanguageManager
sealed class 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 com.dano.test1.BuildConfig
import com.tomhempel.rbarometer.BuildConfig
import java.security.MessageDigest
/** 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. */
enum class SyncPhase {

View File

@ -1,4 +1,4 @@
package com.dano.test1.network
package com.tomhempel.rbarometer.network
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.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.NotificationManager
@ -8,9 +8,9 @@ import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.dano.test1.MainActivity
import com.dano.test1.MyApp
import com.dano.test1.R
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
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.PendingIntent
@ -51,7 +51,7 @@ object UploadReminderScheduler {
*/
suspend fun hasPendingUploads(): Boolean {
return try {
com.dano.test1.data.PendingUploadRepository.hasAny()
com.tomhempel.rbarometer.data.PendingUploadRepository.hasAny()
} catch (e: Exception) {
Log.w(TAG, "hasPendingUploads check failed: ${e.message}")
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.

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.

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.Dispatchers
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.

View File

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

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire
package com.tomhempel.rbarometer.questionnaire
import android.util.Log
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 {

View File

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

View File

@ -1,16 +1,16 @@
package com.dano.test1.questionnaire
package com.tomhempel.rbarometer.questionnaire
import android.content.Context
import com.dano.test1.questionnaire.handlers.HandlerClientCoachCode
import com.dano.test1.questionnaire.handlers.HandlerDateSpinner
import com.dano.test1.questionnaire.handlers.HandlerFreeText
import com.dano.test1.questionnaire.handlers.HandlerGlassScaleQuestion
import com.dano.test1.questionnaire.handlers.HandlerLastPage
import com.dano.test1.questionnaire.handlers.HandlerMultiCheckboxQuestion
import com.dano.test1.questionnaire.handlers.HandlerRadioQuestion
import com.dano.test1.questionnaire.handlers.HandlerSliderQuestion
import com.dano.test1.questionnaire.handlers.HandlerStringSpinner
import com.dano.test1.questionnaire.handlers.HandlerValueSpinner
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerClientCoachCode
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerDateSpinner
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerFreeText
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerGlassScaleQuestion
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerLastPage
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerMultiCheckboxQuestion
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerRadioQuestion
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerSliderQuestion
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerStringSpinner
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerValueSpinner
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.widget.TextView
import com.dano.test1.LanguageManager
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
/** Renders optional noteBeforeKey / noteAfterKey above and below the main question text. */
object QuestionNotesHelper {

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire
package com.tomhempel.rbarometer.questionnaire
import android.graphics.Color
import android.text.Editable
@ -9,19 +9,19 @@ import android.view.ViewGroup
import android.widget.*
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.R
import com.dano.test1.network.TokenStore
import com.dano.test1.questionnaire.handlers.HandlerStringSpinner
import com.dano.test1.questionnaire.handlers.HandlerValueSpinner
import com.dano.test1.ui.AppLanguageStore
import com.dano.test1.ui.ReviewScoresHandler
import com.dano.test1.utils.DateAnswerFormat
import com.dano.test1.utils.DatePrecision
import com.dano.test1.utils.ScreenLoading
import com.dano.test1.utils.TextInputSanitizer
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.network.TokenStore
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerStringSpinner
import com.tomhempel.rbarometer.questionnaire.handlers.HandlerValueSpinner
import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.tomhempel.rbarometer.ui.ReviewScoresHandler
import com.tomhempel.rbarometer.utils.DateAnswerFormat
import com.tomhempel.rbarometer.utils.DatePrecision
import com.tomhempel.rbarometer.utils.ScreenLoading
import com.tomhempel.rbarometer.utils.TextInputSanitizer
import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import kotlinx.coroutines.*
@ -296,7 +296,7 @@ class QuestionnaireAllInOne(private val questionnaireFileName: String) : Questio
}
}
languageID = newLang
com.dano.test1.ui.AppLanguageStore.set(context, newLang)
com.tomhempel.rbarometer.ui.AppLanguageStore.set(context, newLang)
persistedBaselineAnswers = snapshotAnswerKeys(answers)
uiChangedSinceBaseline = false
buildAllInOneUi()
@ -927,7 +927,7 @@ class QuestionnaireAllInOne(private val questionnaireFileName: String) : Questio
private fun selectedMonthNumber(view: View): Int? {
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)
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.app.Activity
import android.util.Log
import android.view.View
import android.widget.*
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MainActivity
import kotlinx.coroutines.*
import java.io.File
import java.util.Calendar
@ -77,7 +77,7 @@ abstract class QuestionnaireBase<T> {
protected fun showEmptyScreen() {
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 com.dano.test1.MainActivity
import com.tomhempel.rbarometer.MainActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
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.View
@ -6,8 +6,8 @@ import android.widget.AdapterView
import android.widget.Button
import android.widget.FrameLayout
import android.widget.Spinner
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
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]
if (newLang == languageID) return
languageID = newLang
com.dano.test1.ui.AppLanguageStore.set(context, newLang)
com.tomhempel.rbarometer.ui.AppLanguageStore.set(context, newLang)
spinner.post { showCurrentQuestion() }
}
override fun onNothingSelected(parent: AdapterView<*>?) {}

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire
package com.tomhempel.rbarometer.questionnaire
data class Option(
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 com.dano.test1.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.google.gson.Gson
import com.google.gson.JsonObject
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 com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.data.Answer
import com.dano.test1.data.AnswerRepository
import com.dano.test1.data.ClientAnswersCache
import com.dano.test1.data.ClientRepository
import com.dano.test1.data.CompletedQuestionnaireRepository
import com.dano.test1.data.CompletedQuestionnaire
import com.dano.test1.data.Question
import com.dano.test1.data.Questionnaire
import com.dano.test1.network.SyncLog
import com.dano.test1.notification.UploadReminderScheduler
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.data.Answer
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.data.ClientAnswersCache
import com.tomhempel.rbarometer.data.ClientRepository
import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.tomhempel.rbarometer.data.Question
import com.tomhempel.rbarometer.data.Questionnaire
import com.tomhempel.rbarometer.network.SyncLog
import com.tomhempel.rbarometer.notification.UploadReminderScheduler
object QuestionnairePersistence {

View File

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

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire
package com.tomhempel.rbarometer.questionnaire
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. */
object QuestionnaireTranslationsLoader {

View File

@ -1,10 +1,10 @@
package com.dano.test1.questionnaire
package com.tomhempel.rbarometer.questionnaire
import android.app.Activity
import android.util.Log
import android.widget.Toast
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MainActivity
/**
* 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,

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 {

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire
package com.tomhempel.rbarometer.questionnaire
import com.dano.test1.LanguageManager
import com.dano.test1.ui.localization.UiStrings
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.ui.localization.UiStrings
/**
* 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.widget.*
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.data.ClientRepository
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.network.TokenStore
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.data.ClientRepository
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.TokenStore
import com.tomhempel.rbarometer.utils.ViewUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
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.view.View
import android.widget.Button
import android.widget.Spinner
import android.widget.TextView
import com.dano.test1.LanguageManager
import com.dano.test1.data.AnswerRepository
import com.dano.test1.R
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.questionnaire.MAX_VALUE_YEAR
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper
import com.dano.test1.ui.Month
import com.dano.test1.ui.Months
import com.dano.test1.utils.DateAnswerFormat
import com.dano.test1.utils.DatePrecision
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.questionnaire.MAX_VALUE_YEAR
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.tomhempel.rbarometer.ui.Month
import com.tomhempel.rbarometer.ui.Months
import com.tomhempel.rbarometer.utils.DateAnswerFormat
import com.tomhempel.rbarometer.utils.DatePrecision
import com.tomhempel.rbarometer.utils.ViewUtils
import java.util.Calendar
import kotlinx.coroutines.CoroutineScope
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.text.InputFilter
@ -6,16 +6,16 @@ import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import com.dano.test1.LanguageManager
import com.dano.test1.data.AnswerRepository
import com.dano.test1.R
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper
import com.dano.test1.utils.TextInputSanitizer
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.tomhempel.rbarometer.utils.TextInputSanitizer
import com.tomhempel.rbarometer.utils.ViewUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
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.view.Gravity
import android.view.View
import android.widget.*
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.data.AnswerRepository
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
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.view.View
@ -6,16 +6,16 @@ import android.widget.*
import android.text.Html
import androidx.core.widget.TextViewCompat
import kotlinx.coroutines.*
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.network.TokenStore
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.ui.AppLanguageStore
import com.dano.test1.ui.ReviewScoresHandler
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.network.TokenStore
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.tomhempel.rbarometer.ui.ReviewScoresHandler
import com.tomhempel.rbarometer.utils.ViewUtils
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.util.TypedValue
@ -6,14 +6,14 @@ import android.view.View
import android.widget.*
import androidx.core.widget.TextViewCompat
import kotlinx.coroutines.*
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.data.AnswerRepository
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
/*
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.view.View
import android.text.Html
import android.widget.*
import kotlinx.coroutines.*
import com.dano.test1.questionnaire.AnswerRestoreHelper
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.questionnaire.AnswerRestoreHelper
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
/*
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.view.View
import android.widget.Button
import android.widget.SeekBar
import android.widget.TextView
import com.dano.test1.LanguageManager
import com.dano.test1.R
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerSliderQuestion(
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.view.View
import android.widget.*
import kotlinx.coroutines.*
import com.dano.test1.questionnaire.AnswerLookup
import com.dano.test1.questionnaire.AnswerRestoreHelper
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.LanguageManager
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.questionnaire.SpinnerAnswerRules
import com.dano.test1.questionnaire.SpinnerSelectionHelper
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.questionnaire.AnswerLookup
import com.tomhempel.rbarometer.questionnaire.AnswerRestoreHelper
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.questionnaire.SpinnerAnswerRules
import com.tomhempel.rbarometer.questionnaire.SpinnerSelectionHelper
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerStringSpinner(
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.view.View
import android.widget.*
import kotlinx.coroutines.*
import com.dano.test1.questionnaire.AnswerLookup
import com.dano.test1.questionnaire.AnswerRestoreHelper
import com.dano.test1.questionnaire.QuestionFlowResolver
import com.dano.test1.questionnaire.SpinnerAnswerRules
import com.dano.test1.questionnaire.SpinnerSelectionHelper
import com.dano.test1.LanguageManager
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionNotesHelper
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.questionnaire.AnswerLookup
import com.tomhempel.rbarometer.questionnaire.AnswerRestoreHelper
import com.tomhempel.rbarometer.questionnaire.QuestionFlowResolver
import com.tomhempel.rbarometer.questionnaire.SpinnerAnswerRules
import com.tomhempel.rbarometer.questionnaire.SpinnerSelectionHelper
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.questionnaire.QuestionHandler
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.questionnaire.QuestionNotesHelper
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerValueSpinner(
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 com.dano.test1.data.AnswerRepository
import com.dano.test1.data.CompletedQuestionnaireRepository
import com.dano.test1.network.ClientScoringReview
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.network.ScoringProfileDefinition
import com.dano.test1.network.ScoringReviewProfile
import com.dano.test1.questionnaire.AnswerKeyUtils
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.tomhempel.rbarometer.network.ClientScoringReview
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.ScoringProfileDefinition
import com.tomhempel.rbarometer.network.ScoringReviewProfile
import com.tomhempel.rbarometer.questionnaire.AnswerKeyUtils
import com.google.gson.JsonParser
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
/**

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.JsonObject

View File

@ -1,4 +1,4 @@
package com.dano.test1.security
package com.tomhempel.rbarometer.security
import android.content.Context
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 com.google.gson.JsonObject

View File

@ -1,4 +1,4 @@
package com.dano.test1.ui
package com.tomhempel.rbarometer.ui
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 com.dano.test1.questionnaire.AppLanguages
import com.tomhempel.rbarometer.questionnaire.AppLanguages
/**
* 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.os.Bundle
@ -9,17 +9,17 @@ import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.dano.test1.LanguageManager
import com.dano.test1.R
import com.dano.test1.network.LoginManager
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.network.SyncCoordinator
import com.dano.test1.network.TokenStore
import com.dano.test1.questionnaire.AppLanguages
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.network.LoginManager
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.SyncCoordinator
import com.tomhempel.rbarometer.network.TokenStore
import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.google.android.material.button.MaterialButton
import com.google.android.material.textfield.TextInputEditText
import com.dano.test1.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.utils.ScreenLoading
import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.CoroutineScope
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.TextWatcher
@ -10,8 +10,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.textfield.TextInputEditText
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 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 com.dano.test1.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.QuestionnaireCache
object DevSettingsAccess {
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.os.Bundle
@ -12,16 +12,16 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat
import androidx.appcompat.widget.Toolbar
import com.dano.test1.MainActivity
import com.dano.test1.R
import com.dano.test1.network.QuestionnaireSyncService
import com.dano.test1.network.TokenStore
import com.dano.test1.utils.SyncProgressOverlay
import com.dano.test1.utils.SyncProgressTexts
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.network.QuestionnaireSyncService
import com.tomhempel.rbarometer.network.TokenStore
import com.tomhempel.rbarometer.utils.SyncProgressOverlay
import com.tomhempel.rbarometer.utils.SyncProgressTexts
import com.google.android.material.button.MaterialButton
import com.dano.test1.ui.AppLanguageStore
import com.dano.test1.ui.LogoutDialogs
import com.dano.test1.ui.SessionLogout
import com.tomhempel.rbarometer.ui.AppLanguageStore
import com.tomhempel.rbarometer.ui.LogoutDialogs
import com.tomhempel.rbarometer.ui.SessionLogout
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
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.res.ColorStateList
@ -12,36 +12,36 @@ import androidx.appcompat.widget.AppCompatImageButton
import android.widget.*
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.R
import com.dano.test1.data.PendingUploadRepository
import com.dano.test1.data.UnuploadedWorkRepository
import com.dano.test1.network.NetworkUtils
import com.dano.test1.network.QuestionnaireAnswerSync
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.network.SyncCoordinator
import com.dano.test1.network.SyncResult
import com.dano.test1.questionnaire.AppLanguages
import com.dano.test1.network.TokenStore
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.data.PendingUploadRepository
import com.tomhempel.rbarometer.data.UnuploadedWorkRepository
import com.tomhempel.rbarometer.network.NetworkUtils
import com.tomhempel.rbarometer.network.QuestionnaireAnswerSync
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.SyncCoordinator
import com.tomhempel.rbarometer.network.SyncResult
import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.tomhempel.rbarometer.network.TokenStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.dano.test1.questionnaire.ConditionJsonParser
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionnaireBase
import com.dano.test1.questionnaire.QuestionnaireAllInOne
import com.dano.test1.questionnaire.QuestionnaireGeneric
import com.dano.test1.questionnaire.QuestionnaireValidation
import com.tomhempel.rbarometer.questionnaire.ConditionJsonParser
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.questionnaire.QuestionItem
import com.tomhempel.rbarometer.questionnaire.QuestionnaireBase
import com.tomhempel.rbarometer.questionnaire.QuestionnaireAllInOne
import com.tomhempel.rbarometer.questionnaire.QuestionnaireGeneric
import com.tomhempel.rbarometer.questionnaire.QuestionnaireValidation
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.bottomsheet.BottomSheetDialog
import org.json.JSONObject
import com.dano.test1.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.utils.ScreenLoading
import com.tomhempel.rbarometer.utils.ViewUtils
class HandlerOpeningScreen(private val activity: MainActivity) {
@ -60,6 +60,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
private lateinit var spinner: Spinner
private lateinit var textView: 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 nextStepEyebrow: TextView
private lateinit var nextStepTitle: TextView
@ -213,6 +216,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
spinner = activity.findViewById(R.id.string_spinner1)
textView = activity.findViewById(R.id.textView)
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)
nextStepEyebrow = activity.findViewById(R.id.nextStepEyebrow)
nextStepTitle = activity.findViewById(R.id.nextStepTitle)
@ -233,23 +239,40 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
refreshProgress = activity.findViewById(R.id.refreshProgress)
refreshButton.contentDescription = t("refresh")
settingsButton.contentDescription = tOr("settings", hubFallback("Einstellungen", "Settings"))
textView.text = t("questionnaire_title")
textView.text = hubFallback("Sitzungsinstrumente", "Session measures")
applyHubLabels()
updateSessionMetrics()
applyCoachDisplay()
}
private fun applyHubLabels() {
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"))
labelClientCode.text = tOr("client_code", hubFallback("Klientencode", "Client code"))
labelClientCode.text = hubFallback("Aktiver Klient", "Active client")
}
private fun applyCoachDisplay() {
if (!::coachNameDisplay.isInitialized) return
val username = TokenStore.getUsername(activity)?.trim().orEmpty()
coachNameDisplay.text = username.ifBlank {
tOr("coach_code", hubFallback("Coach", "Coach"))
coachNameDisplay.text = if (username.isBlank()) {
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)
}
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 {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
@ -557,6 +612,14 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
}
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)
dialog.setContentView(container)
dialog.show()
@ -688,6 +751,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
refreshClientSpinner()
applyUpdateButtonTexts(force = true)
updateStatusStrip()
updateSessionMetrics()
updateQuestionnaireSectionVisibility()
refreshLoadedClientUi()
}
@ -711,6 +775,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
noClientsAssignedText.text = t("no_clients_assigned")
noClientsAssignedText.visibility = View.VISIBLE
textView.visibility = View.GONE
updateSessionMetrics()
nextStepBannerCard.visibility = View.GONE
questionnaireScroll.visibility = View.GONE
bottomUploadDock.visibility = View.GONE
@ -728,9 +793,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
nextStepBannerCard.visibility = View.VISIBLE
val completed = dynamicButtons.count { isCompleted(it) }
nextStepEyebrow.text = t("next_step_label")
nextStepTitle.text = t("next_step_loading_title")
nextStepBody.text = t("next_step_loading_body")
nextStepEyebrow.text = hubFallback("Nächster Sitzungsschritt", "Next session step")
nextStepTitle.text = hubFallback("Sitzungsstatus wird geprüft", "Checking session status")
nextStepBody.text = hubFallback("Lokale Daten, Reviews und Synchronisation werden ausgewertet.", "Reviewing local measures, scoring work, and sync status.")
nextStepActionButton.visibility = View.GONE
nextStepActionButton.setOnClickListener(null)
@ -769,9 +834,19 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
}
private fun applyNextStepBanner(titleKey: String, bodyKey: String, action: NextStepAction) {
nextStepEyebrow.text = t("next_step_label")
nextStepTitle.text = t(titleKey)
nextStepBody.text = t(bodyKey)
nextStepEyebrow.text = hubFallback("Nächster Sitzungsschritt", "Next session step")
nextStepTitle.text = when (action) {
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) {
NextStepAction.UPLOAD -> {
nextStepActionButton.visibility = View.VISIBLE
@ -793,7 +868,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
}
NextStepAction.QUESTIONNAIRE -> {
nextStepActionButton.visibility = View.VISIBLE
nextStepActionButton.text = t("next_step_show_questionnaire")
nextStepActionButton.text = hubFallback("Messpunkt anzeigen", "Show measure")
nextStepActionButton.setOnClickListener { highlightNextAvailableQuestionnaire() }
}
NextStepAction.NONE -> {
@ -952,6 +1027,9 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
val base = t("upload")
uploadButton.text = if (count > 0) "$base ($count)" else base
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")
applyHubLabels()
applyCoachDisplay()
textView.text = t("questionnaire_title")
textView.text = hubFallback("Sitzungsinstrumente", "Session measures")
updateSessionMetrics()
updateNextStepBanner()
}

View File

@ -1,21 +1,21 @@
package com.dano.test1.ui
package com.tomhempel.rbarometer.ui
import android.content.Intent
import android.widget.Button
import android.widget.Toast
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.data.ClientAnswersCache
import com.dano.test1.data.ClientRepository
import com.dano.test1.data.CompletedQuestionnaireRepository
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.data.ClientAnswersCache
import com.tomhempel.rbarometer.data.ClientRepository
import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import kotlinx.coroutines.*
import com.dano.test1.data.CompletedQuestionnaire
import com.dano.test1.network.NetworkUtils
import com.dano.test1.network.QuestionnaireAnswerSync
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.network.TokenStore
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionItem
import com.tomhempel.rbarometer.data.CompletedQuestionnaire
import com.tomhempel.rbarometer.network.NetworkUtils
import com.tomhempel.rbarometer.network.QuestionnaireAnswerSync
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.TokenStore
import com.tomhempel.rbarometer.questionnaire.GlobalValues
import com.tomhempel.rbarometer.questionnaire.QuestionItem
class LoadButtonHandler(
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.content.res.ColorStateList
@ -13,7 +13,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.widget.NestedScrollView
import com.dano.test1.R
import com.tomhempel.rbarometer.R
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) {
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.app.Activity
@ -7,7 +7,7 @@ import android.os.Build
import android.view.LayoutInflater
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.ContextCompat
import com.dano.test1.R
import com.tomhempel.rbarometer.R
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.graphics.Typeface
@ -8,8 +8,8 @@ import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton
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.graphics.Typeface
@ -15,19 +15,19 @@ import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.dano.test1.MainActivity
import com.dano.test1.R
import com.dano.test1.data.CoachScoringReviewRepository
import com.dano.test1.network.ClientScoringReview
import com.dano.test1.network.QuestionnaireApiClient
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.network.ScoringReviewProfile
import com.dano.test1.notification.UploadReminderScheduler
import com.dano.test1.scoring.LocalScoringReviewBuilder
import com.dano.test1.questionnaire.AppLanguages
import com.dano.test1.ui.localization.UiStrings
import com.dano.test1.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.data.CoachScoringReviewRepository
import com.tomhempel.rbarometer.network.ClientScoringReview
import com.tomhempel.rbarometer.network.QuestionnaireApiClient
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.ScoringReviewProfile
import com.tomhempel.rbarometer.notification.UploadReminderScheduler
import com.tomhempel.rbarometer.scoring.LocalScoringReviewBuilder
import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.tomhempel.rbarometer.ui.localization.UiStrings
import com.tomhempel.rbarometer.utils.ScreenLoading
import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton
import kotlinx.coroutines.CoroutineScope
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 com.dano.test1.LanguageManager
import com.dano.test1.data.UnuploadedWorkRepository
import com.dano.test1.network.ClientAnswerSyncStore
import com.dano.test1.network.NetworkUtils
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.network.TokenStore
import com.tomhempel.rbarometer.LanguageManager
import com.tomhempel.rbarometer.data.UnuploadedWorkRepository
import com.tomhempel.rbarometer.network.ClientAnswerSyncStore
import com.tomhempel.rbarometer.network.NetworkUtils
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.network.TokenStore
import kotlinx.coroutines.Dispatchers
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.view.View
@ -6,7 +6,7 @@ import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import com.dano.test1.R
import com.tomhempel.rbarometer.R
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.util.Log
@ -9,20 +9,20 @@ import android.widget.*
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.dano.test1.MainActivity
import com.dano.test1.MyApp
import com.dano.test1.R
import com.dano.test1.data.AnswerRepository
import com.dano.test1.data.CoachScoringReviewRepository
import com.dano.test1.data.CompletedQuestionnaireRepository
import com.dano.test1.data.Question
import com.dano.test1.network.DatabaseUploader
import com.dano.test1.network.QuestionnaireCache
import com.dano.test1.questionnaire.AppLanguages
import com.dano.test1.questionnaire.QuestionnaireTranslationsLoader
import com.dano.test1.ui.localization.UiStrings
import com.dano.test1.utils.ScreenLoading
import com.dano.test1.utils.ViewUtils
import com.tomhempel.rbarometer.MainActivity
import com.tomhempel.rbarometer.MyApp
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.data.AnswerRepository
import com.tomhempel.rbarometer.data.CoachScoringReviewRepository
import com.tomhempel.rbarometer.data.CompletedQuestionnaireRepository
import com.tomhempel.rbarometer.data.Question
import com.tomhempel.rbarometer.network.DatabaseUploader
import com.tomhempel.rbarometer.network.QuestionnaireCache
import com.tomhempel.rbarometer.questionnaire.AppLanguages
import com.tomhempel.rbarometer.questionnaire.QuestionnaireTranslationsLoader
import com.tomhempel.rbarometer.ui.localization.UiStrings
import com.tomhempel.rbarometer.utils.ScreenLoading
import com.tomhempel.rbarometer.utils.ViewUtils
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
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.

View File

@ -1,4 +1,4 @@
package com.dano.test1.utils
package com.tomhempel.rbarometer.utils
import java.util.Calendar
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).

View File

@ -1,10 +1,10 @@
package com.dano.test1.utils
package com.tomhempel.rbarometer.utils
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
import com.dano.test1.R
import com.tomhempel.rbarometer.R
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 com.dano.test1.R
import com.dano.test1.network.SyncPhase
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.network.SyncPhase
/** Flat Material-style icons per sync phase — no emoji or decorative chrome. */
object SyncPhaseIcons {

View File

@ -1,4 +1,4 @@
package com.dano.test1.utils
package com.tomhempel.rbarometer.utils
import android.app.Activity
import android.graphics.drawable.GradientDrawable
@ -12,9 +12,9 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.ImageViewCompat
import com.dano.test1.R
import com.dano.test1.network.SyncPhase
import com.dano.test1.network.SyncProgress
import com.tomhempel.rbarometer.R
import com.tomhempel.rbarometer.network.SyncPhase
import com.tomhempel.rbarometer.network.SyncProgress
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. */
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.

View File

@ -1,4 +1,4 @@
package com.dano.test1.utils
package com.tomhempel.rbarometer.utils
import android.content.Context
import android.os.Build
@ -14,7 +14,7 @@ import android.widget.Spinner
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.widget.TextViewCompat
import com.dano.test1.R
import com.tomhempel.rbarometer.R
import kotlin.math.roundToInt
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:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:fillColor="#0F766E"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
android:fillColor="#0E7490"
android:pathData="M0,78 C26,62 48,65 73,49 C88,39 99,27 108,15 L108,108 L0,108 Z" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
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" />
android:fillColor="#14B8A6"
android:pathData="M0,0h108v26 C86,36 64,39 44,32 C27,26 14,17 0,14 Z" />
</vector>

View File

@ -1,30 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="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
android:fillColor="#FFFFFF"
android:fillType="nonZero"
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"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
android:fillColor="#FFFFFFFF"
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" />
<path
android:fillColor="#FFFFFFFF"
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" />
<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