From ee0c7e128b51d146b37e724c64b44317c45fa7f1 Mon Sep 17 00:00:00 2001 From: Nico Wiedemann Date: Thu, 23 Jul 2026 01:24:17 +0200 Subject: [PATCH 1/2] fix: address security, correctness, and CI issues from full audit Fixes surfaced by a full codebase audit, covering navigation, security hardening, and formatting correctness, plus a broken release pipeline. - Screens no longer duplicate on the nav stack: LoadingScreen and NoProfilesScreen used a perpetual profilesFlow.collect that kept running (and re-pushing) after being covered by another screen. - Credential DataStore is now excluded from Android backup so plaintext Nightscout API tokens don't leave the device via cloud/adb backup. - HostValidator now restricts to Google's known Android Auto/ Automotive hosts in release builds instead of allowing any host. - Cleartext HTTP is now explicitly permitted via a network security config, since self-hosted Nightscout is often reached over a VPN without a public TLS cert; the profile editor warns when used. - Numeric/time formatting now consistently uses Locale.US so mmol/L values and graph labels don't render with locale-dependent decimal separators or digits. - NightscoutApiFactory's per-host API cache is now a ConcurrentHashMap. - getThresholds defaults all four values instead of failing the whole fetch when target-range fields are missing. - displayDelta rounds instead of truncates for mg/dL, matching displayValue. - release.yml: pass changelog notes through env + printf instead of splicing into a single-quoted echo, which broke on the apostrophe in "what's new" during the v1.2.1 release. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/release.yml | 3 ++- CHANGELOG.md | 18 ++++++++++++++++++ app/src/main/AndroidManifest.xml | 3 +++ .../de/autosugar/car/AutoSugarCarAppService.kt | 9 ++++++++- .../de/autosugar/car/GlucoseGraphRenderer.kt | 6 +++--- .../java/de/autosugar/car/GlucoseScreen.kt | 10 +++++++--- .../java/de/autosugar/car/LoadingScreen.kt | 18 +++++++++--------- .../java/de/autosugar/car/NoProfilesScreen.kt | 12 +++++------- .../de/autosugar/data/model/GlucoseEntry.kt | 7 ++++--- .../data/network/NightscoutApiFactory.kt | 5 +++-- .../data/repository/NightscoutRepository.kt | 4 ++-- .../autosugar/ui/settings/ProfileEditScreen.kt | 10 +++++++--- app/src/main/res/values-ar/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-hi/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-nl/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/backup_rules.xml | 8 ++++++++ app/src/main/res/xml/data_extraction_rules.xml | 9 +++++++++ .../main/res/xml/network_security_config.xml | 9 +++++++++ .../repository/NightscoutRepositoryTest.kt | 9 ++++++--- 27 files changed, 114 insertions(+), 37 deletions(-) create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/main/res/xml/network_security_config.xml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 60ff4d3..bc367ea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,13 +91,14 @@ jobs: if: steps.tag_check.outputs.exists == 'false' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_BODY: ${{ steps.notes.outputs.body }} run: | BASE="${{ steps.version.outputs.artifact_base }}" EXTRA_ASSETS="" if [ -f "app/build/outputs/mapping/release/mapping.txt" ]; then EXTRA_ASSETS="app/build/outputs/mapping/release/mapping.txt" fi - echo '${{ steps.notes.outputs.body }}' > release_notes.txt + printf '%s\n' "$RELEASE_BODY" > release_notes.txt gh release create "${{ steps.version.outputs.tag }}" \ --title "${{ steps.version.outputs.tag }}" \ --notes-file release_notes.txt \ diff --git a/CHANGELOG.md b/CHANGELOG.md index 8012ea7..45af3d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Security + +- Profile credentials (Nightscout API tokens) are now excluded from Android backup so they are no longer included in cloud/adb backups +- Android Auto host validation now restricts to Google's known Auto/Automotive hosts in release builds instead of accepting any host +- Cleartext HTTP is now explicitly permitted via a network security config for self-hosted Nightscout instances reached over a VPN/LAN without TLS; the profile editor warns when an `http://` URL is entered + +### Fixed + +- Duplicate glucose screens no longer get pushed onto the navigation stack when profiles change while a screen is covered by another +- mmol/L values and graph time labels now render consistently regardless of device locale +- Threshold fetch no longer fails entirely when target-range values are missing from the Nightscout status response; sensible defaults are used instead +- mg/dL delta display now rounds instead of truncating, matching the main reading +- Release workflow no longer breaks when changelog notes contain an apostrophe (shell quoting fix) + +### Changed + +- `NightscoutApiFactory`'s per-host API client cache is now thread-safe + ## [1.2.2] - 2026-07-22 ### Fixed diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 512ed33..969f9c6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,9 @@ android:roundIcon="@mipmap/ic_launcher" android:theme="@style/Theme.AutoSugar" android:allowBackup="true" + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupContent="@xml/backup_rules" + android:networkSecurityConfig="@xml/network_security_config" android:supportsRtl="true"> diff --git a/app/src/main/java/de/autosugar/car/AutoSugarCarAppService.kt b/app/src/main/java/de/autosugar/car/AutoSugarCarAppService.kt index c492b90..f45fa67 100644 --- a/app/src/main/java/de/autosugar/car/AutoSugarCarAppService.kt +++ b/app/src/main/java/de/autosugar/car/AutoSugarCarAppService.kt @@ -3,6 +3,7 @@ package de.autosugar.car import androidx.car.app.CarAppService import androidx.car.app.Session import androidx.car.app.validation.HostValidator +import de.autosugar.BuildConfig import de.autosugar.data.repository.NightscoutRepository import de.autosugar.data.storage.AppPreferencesDataStore import dagger.hilt.android.AndroidEntryPoint @@ -15,7 +16,13 @@ class AutoSugarCarAppService : CarAppService() { @Inject lateinit var appPrefs: AppPreferencesDataStore override fun createHostValidator(): HostValidator = - HostValidator.ALLOW_ALL_HOSTS_VALIDATOR + if (BuildConfig.DEBUG) { + HostValidator.ALLOW_ALL_HOSTS_VALIDATOR + } else { + HostValidator.Builder(applicationContext) + .addAllowedHosts(androidx.car.app.R.array.hosts_allowlist_sample) + .build() + } override fun onCreateSession(): Session = AutoSugarSession(repository, appPrefs) } diff --git a/app/src/main/java/de/autosugar/car/GlucoseGraphRenderer.kt b/app/src/main/java/de/autosugar/car/GlucoseGraphRenderer.kt index c095e74..2ec6992 100644 --- a/app/src/main/java/de/autosugar/car/GlucoseGraphRenderer.kt +++ b/app/src/main/java/de/autosugar/car/GlucoseGraphRenderer.kt @@ -128,7 +128,7 @@ private fun drawTimeLabelsAndDropPins( if (i % 2 != 1) return@forEachIndexed val label = when (unit) { GlucoseUnit.MG_DL -> sgv.toInt().toString() - GlucoseUnit.MMOL_L -> "%.0f".format(sgv / 18f) + GlucoseUnit.MMOL_L -> "%.0f".format(Locale.US, sgv / 18f) } canvas.drawText(label, yLabelX, y - 5f, labelPaint) } @@ -142,7 +142,7 @@ private fun drawTimeLabelsAndDropPins( labelPaint.color = Color.argb(160, 200, 200, 200) labelPaint.textAlign = Paint.Align.CENTER canvas.drawText( - "%02d:30".format(cal.get(java.util.Calendar.HOUR_OF_DAY)), + "%02d:30".format(Locale.US, cal.get(java.util.Calendar.HOUR_OF_DAY)), xOf(tHalf), pad + plotH - 4f, labelPaint, ) tHalf += msPerHour @@ -194,7 +194,7 @@ private fun drawDropPinsAndHourLines( labelPaint.color = Color.argb(160, 200, 200, 200) labelPaint.textAlign = Paint.Align.CENTER canvas.drawText( - "%02d:00".format(cal.get(java.util.Calendar.HOUR_OF_DAY)), + "%02d:00".format(Locale.US, cal.get(java.util.Calendar.HOUR_OF_DAY)), x, pad + plotH - 4f, labelPaint, ) } diff --git a/app/src/main/java/de/autosugar/car/GlucoseScreen.kt b/app/src/main/java/de/autosugar/car/GlucoseScreen.kt index 1561c07..5d3b28b 100644 --- a/app/src/main/java/de/autosugar/car/GlucoseScreen.kt +++ b/app/src/main/java/de/autosugar/car/GlucoseScreen.kt @@ -133,7 +133,9 @@ class GlucoseScreen( } val delta = currentEntry.delta ?: return - val projected15 = sgv + delta * 3 // 3 readings × ~5 min = 15 min ahead + // Assumes ~5-min reading cadence: 3 readings × 5 min = 15 min ahead. Sources + // posting at other intervals will skew this projection. + val projected15 = sgv + delta * 3 if (projected15 > thresholds.bgHigh && sgv <= thresholds.bgHigh && now - lastPredictedHighAlertMs > alertCooldownMs @@ -157,7 +159,7 @@ class GlucoseScreen( } } - // region TabTemplate (CarApi >= 6, 2–5 profiles) + // region TabTemplate (CarApi >= 6, 2–4 profiles) @RequiresCarApi(6) private fun buildTabTemplate(): Template { @@ -222,7 +224,9 @@ class GlucoseScreen( .build() ).build() profiles.size in 2..5 -> { - // Numbered icon fallback when TabTemplate is unavailable + // Numbered icon fallback when TabTemplate is unavailable (CarApi < 6). + // The upper bound of 5 here is unreachable in practice since the ">4" + // branch above already matches size 5 — this only ever runs for 2..4. profiles.forEachIndexed { index, profile -> builder.addAction( Action.Builder() diff --git a/app/src/main/java/de/autosugar/car/LoadingScreen.kt b/app/src/main/java/de/autosugar/car/LoadingScreen.kt index c4dc4a7..20f67f2 100644 --- a/app/src/main/java/de/autosugar/car/LoadingScreen.kt +++ b/app/src/main/java/de/autosugar/car/LoadingScreen.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.lifecycleScope import de.autosugar.R import de.autosugar.data.repository.NightscoutRepository import de.autosugar.data.storage.AppPreferencesDataStore +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch class LoadingScreen( @@ -18,17 +19,16 @@ class LoadingScreen( init { lifecycleScope.launch { - repository.profilesFlow.collect { profiles -> - val nextScreen = when { - profiles.isEmpty() -> NoProfilesScreen(carContext, repository, appPrefs) - else -> { - val activeId = repository.activeProfileId.value ?: profiles.first().id - repository.setActiveProfile(activeId) - GlucoseScreen(carContext, repository, appPrefs, activeId) - } + val profiles = repository.profilesFlow.first() + val nextScreen = when { + profiles.isEmpty() -> NoProfilesScreen(carContext, repository, appPrefs) + else -> { + val activeId = repository.activeProfileId.value ?: profiles.first().id + repository.setActiveProfile(activeId) + GlucoseScreen(carContext, repository, appPrefs, activeId) } - screenManager.push(nextScreen) } + screenManager.push(nextScreen) } } diff --git a/app/src/main/java/de/autosugar/car/NoProfilesScreen.kt b/app/src/main/java/de/autosugar/car/NoProfilesScreen.kt index 6dd6e59..25db288 100644 --- a/app/src/main/java/de/autosugar/car/NoProfilesScreen.kt +++ b/app/src/main/java/de/autosugar/car/NoProfilesScreen.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.lifecycleScope import de.autosugar.R import de.autosugar.data.repository.NightscoutRepository import de.autosugar.data.storage.AppPreferencesDataStore +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch class NoProfilesScreen( @@ -18,13 +19,10 @@ class NoProfilesScreen( init { lifecycleScope.launch { - repository.profilesFlow.collect { profiles -> - if (profiles.isNotEmpty()) { - val id = profiles.first().id - repository.setActiveProfile(id) - screenManager.push(GlucoseScreen(carContext, repository, appPrefs, id)) - } - } + val profiles = repository.profilesFlow.first { it.isNotEmpty() } + val id = profiles.first().id + repository.setActiveProfile(id) + screenManager.push(GlucoseScreen(carContext, repository, appPrefs, id)) } } diff --git a/app/src/main/java/de/autosugar/data/model/GlucoseEntry.kt b/app/src/main/java/de/autosugar/data/model/GlucoseEntry.kt index 505c57f..ccb55bb 100644 --- a/app/src/main/java/de/autosugar/data/model/GlucoseEntry.kt +++ b/app/src/main/java/de/autosugar/data/model/GlucoseEntry.kt @@ -1,5 +1,6 @@ package de.autosugar.data.model +import java.util.Locale import kotlin.math.roundToInt data class GlucoseEntry( @@ -17,7 +18,7 @@ data class GlucoseEntry( /** Returns the display value converted to the requested unit. */ fun displayValue(unit: GlucoseUnit): String = when (unit) { GlucoseUnit.MG_DL -> sgv.roundToInt().toString() - GlucoseUnit.MMOL_L -> "%.1f".format(sgv / 18.0) + GlucoseUnit.MMOL_L -> "%.1f".format(Locale.US, sgv / 18.0) } /** Returns the delta converted to the requested unit with sign prefix. */ @@ -29,8 +30,8 @@ data class GlucoseEntry( } val sign = if (converted >= 0) "+" else "" return when (unit) { - GlucoseUnit.MG_DL -> "$sign${converted.toInt()}" - GlucoseUnit.MMOL_L -> "$sign${"%.1f".format(converted)}" + GlucoseUnit.MG_DL -> "$sign${converted.roundToInt()}" + GlucoseUnit.MMOL_L -> "$sign${"%.1f".format(Locale.US, converted)}" } } diff --git a/app/src/main/java/de/autosugar/data/network/NightscoutApiFactory.kt b/app/src/main/java/de/autosugar/data/network/NightscoutApiFactory.kt index ca03b04..30b44c7 100644 --- a/app/src/main/java/de/autosugar/data/network/NightscoutApiFactory.kt +++ b/app/src/main/java/de/autosugar/data/network/NightscoutApiFactory.kt @@ -7,6 +7,7 @@ import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton @@ -19,11 +20,11 @@ class NightscoutApiFactory @Inject constructor() { .build() /** Cache of one Retrofit-backed API instance per normalized base URL. */ - private val cache = mutableMapOf() + private val cache = ConcurrentHashMap() fun get(baseUrl: String): NightscoutApi { val normalized = baseUrl.trimEnd('/') + "/" - return cache.getOrPut(normalized) { buildApi(normalized) } + return cache.computeIfAbsent(normalized) { buildApi(it) } } fun invalidate(baseUrl: String) { diff --git a/app/src/main/java/de/autosugar/data/repository/NightscoutRepository.kt b/app/src/main/java/de/autosugar/data/repository/NightscoutRepository.kt index 5dc6197..0a123f5 100644 --- a/app/src/main/java/de/autosugar/data/repository/NightscoutRepository.kt +++ b/app/src/main/java/de/autosugar/data/repository/NightscoutRepository.kt @@ -79,8 +79,8 @@ class NightscoutRepository @Inject constructor( val t = api.getStatus(token = profile.apiToken.ifBlank { null }).settings?.thresholds GlucoseThresholds( bgLow = t?.bgLow?.roundToInt() ?: 70, - bgTargetBottom = t?.bgTargetBottom?.roundToInt() ?: error("bgTargetBottom not in status response"), - bgTargetTop = t?.bgTargetTop?.roundToInt() ?: error("bgTargetTop not in status response"), + bgTargetBottom = t?.bgTargetBottom?.roundToInt() ?: 70, + bgTargetTop = t?.bgTargetTop?.roundToInt() ?: 180, bgHigh = t?.bgHigh?.roundToInt() ?: 180, ) } diff --git a/app/src/main/java/de/autosugar/ui/settings/ProfileEditScreen.kt b/app/src/main/java/de/autosugar/ui/settings/ProfileEditScreen.kt index 34ceb8e..774a964 100644 --- a/app/src/main/java/de/autosugar/ui/settings/ProfileEditScreen.kt +++ b/app/src/main/java/de/autosugar/ui/settings/ProfileEditScreen.kt @@ -102,12 +102,14 @@ fun ProfileEditScreen( } val isLoading = uiState is ProfileEditUiState.Loading + val urlScheme = runCatching { java.net.URI(baseUrl.trim()).scheme }.getOrNull() val isValidUrl = runCatching { val uri = java.net.URI(baseUrl.trim()) uri.scheme in listOf("http", "https") && !uri.host.isNullOrEmpty() && uri.host.contains('.') }.getOrDefault(false) + val isCleartextUrl = isValidUrl && urlScheme == "http" val canSave = !isLoading && displayName.isNotBlank() && isValidUrl val title = if (profileId == null) { @@ -180,9 +182,11 @@ fun ProfileEditScreen( imeAction = ImeAction.Next, ), isError = baseUrl.isNotBlank() && !isValidUrl, - supportingText = if (baseUrl.isNotBlank() && !isValidUrl) { - { Text(stringResource(R.string.error_invalid_url)) } - } else null, + supportingText = when { + baseUrl.isNotBlank() && !isValidUrl -> { { Text(stringResource(R.string.error_invalid_url)) } } + isCleartextUrl -> { { Text(stringResource(R.string.warning_cleartext_url)) } } + else -> null + }, enabled = !isLoading, ) OutlinedTextField( diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index a3bcd96..f439d0c 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -59,4 +59,5 @@ الإصدار %s يجب أن يبدأ بـ http:// أو https:// يمتلك هذا الرمز المميز صلاحيات الكتابة. يحتاج AutoSugar إلى صلاحيات القراءة فقط — يُنصح باستخدام رمز مميز للقراءة فقط لمزيد من الأمان. + غير مشفر — استخدمه فقط على شبكة موثوقة أو VPN diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index db3c35c..c5c631b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -59,4 +59,5 @@ Version %s Muss mit http:// oder https:// beginnen Dieses Token hat Schreibrechte. AutoSugar benötigt nur Lesezugriff – ein Token mit reinen Leserechten wird für mehr Sicherheit empfohlen. + Unverschlüsselt – nur in einem vertrauenswürdigen Netzwerk oder VPN verwenden diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c0a54f4..abe89b9 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -59,4 +59,5 @@ Versión %s Debe comenzar con http:// o https:// Este token tiene permisos de escritura. AutoSugar solo requiere acceso de lectura — se recomienda un token de solo lectura para mayor seguridad. + Sin cifrar — usar solo en una red de confianza o VPN diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index bfce3fe..d80dfe0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -59,4 +59,5 @@ Version %s Doit commencer par http:// ou https:// Ce jeton dispose de permissions en écriture. AutoSugar ne nécessite qu\'un accès en lecture — un jeton en lecture seule est recommandé pour plus de sécurité. + Non chiffré — à utiliser uniquement sur un réseau de confiance ou VPN diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 1bdf355..702b5da 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -59,4 +59,5 @@ संस्करण %s http:// या https:// से शुरू होना चाहिए इस टोकन में लिखने की अनुमतियाँ हैं। AutoSugar को केवल पढ़ने की पहुँच चाहिए — बेहतर सुरक्षा के लिए रीड-ओनली टोकन की अनुशंसा की जाती है। + असुरक्षित — केवल किसी विश्वसनीय नेटवर्क या VPN पर उपयोग करें diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 4cca855..11718ac 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -59,4 +59,5 @@ Versione %s Deve iniziare con http:// o https:// Questo token ha permessi di scrittura. AutoSugar richiede solo l\'accesso in lettura — si consiglia un token di sola lettura per una maggiore sicurezza. + Non crittografato — da usare solo su una rete affidabile o VPN diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index f086336..781cadc 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -59,4 +59,5 @@ バージョン %s http:// または https:// で始まる必要があります このトークンには書き込み権限があります。AutoSugar が必要とするのは読み取りアクセスのみです — セキュリティ向上のため、読み取り専用トークンの使用を推奨します。 + 暗号化されていません — 信頼できるネットワークまたは VPN 上でのみ使用してください diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 3e57d3a..0ca0cd8 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -59,4 +59,5 @@ Versie %s Moet beginnen met http:// of https:// Dit token heeft schrijfrechten. AutoSugar heeft alleen leestoegang nodig — een alleen-lezen token wordt aanbevolen voor betere beveiliging. + Niet versleuteld — alleen gebruiken op een vertrouwd netwerk of VPN diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 546e701..4706334 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -59,4 +59,5 @@ Versão %s Deve começar com http:// ou https:// Este token tem permissões de escrita. O AutoSugar requer apenas acesso de leitura — um token somente leitura é recomendado para maior segurança. + Não criptografado — usar apenas em uma rede confiável ou VPN diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 9198cf4..968c872 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -59,4 +59,5 @@ 版本 %s 必须以 http:// 或 https:// 开头 此令牌具有写入权限。AutoSugar 仅需要读取访问权限——建议使用只读令牌以提高安全性。 + 未加密——仅可在受信任的网络或 VPN 上使用 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1110470..bd01aab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,4 +60,5 @@ Version %s Must start with http:// or https:// This token has write permissions. AutoSugar only requires read access — a read-only token is recommended for better security. + Unencrypted — only use on a trusted or VPN network diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..9487b55 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..7d5c887 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..a2e4c2b --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/test/java/de/autosugar/data/repository/NightscoutRepositoryTest.kt b/app/src/test/java/de/autosugar/data/repository/NightscoutRepositoryTest.kt index f537069..21c45ce 100644 --- a/app/src/test/java/de/autosugar/data/repository/NightscoutRepositoryTest.kt +++ b/app/src/test/java/de/autosugar/data/repository/NightscoutRepositoryTest.kt @@ -204,13 +204,16 @@ class NightscoutRepositoryTest { } @Test - fun `getThresholds returns failure when bgTargetBottom is missing`() = runTest { + fun `getThresholds falls back to defaults when settings is null`() = runTest { every { mockDataStore.profilesFlow } returns flowOf(listOf(profile)) every { mockFactory.get(any()) } returns mockApi coEvery { mockApi.getStatus(any()) } returns StatusDto(settings = null) - val result = repository.getThresholds("test-id") - assertTrue(result.isFailure) + val thresholds = repository.getThresholds("test-id").getOrThrow() + assertEquals(70, thresholds.bgLow) + assertEquals(70, thresholds.bgTargetBottom) + assertEquals(180, thresholds.bgTargetTop) + assertEquals(180, thresholds.bgHigh) } @Test From 01029343b9ff4fa798454e45bf99bf6e07745b26 Mon Sep 17 00:00:00 2001 From: Nico Wiedemann Date: Thu, 23 Jul 2026 01:29:29 +0200 Subject: [PATCH 2/2] chore(release): bump version to 1.2.3 --- CHANGELOG.md | 2 ++ app/build.gradle.kts | 4 ++-- docs/whatsnew/1.2.3-ar | 4 ++++ docs/whatsnew/1.2.3-de-DE | 4 ++++ docs/whatsnew/1.2.3-en-US | 4 ++++ docs/whatsnew/1.2.3-es-ES | 4 ++++ docs/whatsnew/1.2.3-fr-FR | 4 ++++ docs/whatsnew/1.2.3-hi-IN | 4 ++++ docs/whatsnew/1.2.3-it-IT | 4 ++++ docs/whatsnew/1.2.3-ja-JP | 4 ++++ docs/whatsnew/1.2.3-nl-NL | 4 ++++ docs/whatsnew/1.2.3-pt-PT | 4 ++++ docs/whatsnew/1.2.3-zh-CN | 4 ++++ 13 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 docs/whatsnew/1.2.3-ar create mode 100644 docs/whatsnew/1.2.3-de-DE create mode 100644 docs/whatsnew/1.2.3-en-US create mode 100644 docs/whatsnew/1.2.3-es-ES create mode 100644 docs/whatsnew/1.2.3-fr-FR create mode 100644 docs/whatsnew/1.2.3-hi-IN create mode 100644 docs/whatsnew/1.2.3-it-IT create mode 100644 docs/whatsnew/1.2.3-ja-JP create mode 100644 docs/whatsnew/1.2.3-nl-NL create mode 100644 docs/whatsnew/1.2.3-pt-PT create mode 100644 docs/whatsnew/1.2.3-zh-CN diff --git a/CHANGELOG.md b/CHANGELOG.md index 45af3d2..92681a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +## [1.2.3] - 2026-07-23 + ### Security - Profile credentials (Nightscout API tokens) are now excluded from Android backup so they are no longer included in cloud/adb backups diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c5c2c02..ee16c8a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { applicationId = "de.autosugar" minSdk = 26 targetSdk = 36 - versionCode = 7 - versionName = "1.2.2" + versionCode = 8 + versionName = "1.2.3" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/docs/whatsnew/1.2.3-ar b/docs/whatsnew/1.2.3-ar new file mode 100644 index 0000000..5525b8a --- /dev/null +++ b/docs/whatsnew/1.2.3-ar @@ -0,0 +1,4 @@ +• إصلاح ظهور شاشات مكررة في Android Auto عند تبديل المصادر +• يعمل الآن الاتصال بخوادم Nightscout الذاتية عبر HTTP غير المشفر (مثل VPN) +• إصلاح مشكلات عرض قيم الجلوكوز والرسم البياني في بعض اللغات +• لم يعد رمز Nightscout المميز مضمّنًا في نسخ Android الاحتياطية diff --git a/docs/whatsnew/1.2.3-de-DE b/docs/whatsnew/1.2.3-de-DE new file mode 100644 index 0000000..25f11b1 --- /dev/null +++ b/docs/whatsnew/1.2.3-de-DE @@ -0,0 +1,4 @@ +• Doppelte Bildschirme in Android Auto beim Quellenwechsel behoben +• Selbstgehostetes Nightscout über reines HTTP (z. B. via VPN) verbindet sich jetzt korrekt +• Anzeigefehler bei Glukosewerten und Grafik in einigen Sprachen behoben +• Nightscout-Token wird nicht mehr in Android-Backups gesichert diff --git a/docs/whatsnew/1.2.3-en-US b/docs/whatsnew/1.2.3-en-US new file mode 100644 index 0000000..81ee00b --- /dev/null +++ b/docs/whatsnew/1.2.3-en-US @@ -0,0 +1,4 @@ +• Fixed duplicate screens in Android Auto when switching sources +• Self-hosted Nightscout over plain HTTP (e.g. via VPN) now connects +• Fixed glucose/graph display issues in some languages +• Nightscout token no longer included in Android backups diff --git a/docs/whatsnew/1.2.3-es-ES b/docs/whatsnew/1.2.3-es-ES new file mode 100644 index 0000000..1063931 --- /dev/null +++ b/docs/whatsnew/1.2.3-es-ES @@ -0,0 +1,4 @@ +• Corregidas pantallas duplicadas en Android Auto al cambiar de fuente +• Nightscout autoalojado por HTTP sin cifrar (p. ej. vía VPN) ahora se conecta correctamente +• Corregidos problemas de visualización de glucosa/gráfico en algunos idiomas +• El token de Nightscout ya no se incluye en las copias de seguridad de Android diff --git a/docs/whatsnew/1.2.3-fr-FR b/docs/whatsnew/1.2.3-fr-FR new file mode 100644 index 0000000..07981d5 --- /dev/null +++ b/docs/whatsnew/1.2.3-fr-FR @@ -0,0 +1,4 @@ +• Correction des écrans dupliqués dans Android Auto lors du changement de source +• Nightscout auto-hébergé en HTTP non chiffré (ex. via VPN) se connecte désormais correctement +• Correction de problèmes d'affichage du glucose/graphique dans certaines langues +• Le jeton Nightscout n'est plus inclus dans les sauvegardes Android diff --git a/docs/whatsnew/1.2.3-hi-IN b/docs/whatsnew/1.2.3-hi-IN new file mode 100644 index 0000000..34264d2 --- /dev/null +++ b/docs/whatsnew/1.2.3-hi-IN @@ -0,0 +1,4 @@ +• स्रोत बदलते समय Android Auto में डुप्लिकेट स्क्रीन की समस्या ठीक की गई +• स्व-होस्टेड Nightscout अब सादे HTTP (जैसे VPN के माध्यम से) पर सही ढंग से कनेक्ट होता है +• कुछ भाषाओं में ग्लूकोज/ग्राफ प्रदर्शन की समस्याएं ठीक की गईं +• Nightscout टोकन अब Android बैकअप में शामिल नहीं होता diff --git a/docs/whatsnew/1.2.3-it-IT b/docs/whatsnew/1.2.3-it-IT new file mode 100644 index 0000000..441ae18 --- /dev/null +++ b/docs/whatsnew/1.2.3-it-IT @@ -0,0 +1,4 @@ +• Corrette schermate duplicate in Android Auto durante il cambio di fonte +• Nightscout self-hosted tramite HTTP non crittografato (ad es. via VPN) ora si connette correttamente +• Corretti problemi di visualizzazione di glicemia/grafico in alcune lingue +• Il token Nightscout non viene più incluso nei backup Android diff --git a/docs/whatsnew/1.2.3-ja-JP b/docs/whatsnew/1.2.3-ja-JP new file mode 100644 index 0000000..fe45bc8 --- /dev/null +++ b/docs/whatsnew/1.2.3-ja-JP @@ -0,0 +1,4 @@ +• ソース切り替え時にAndroid Autoで画面が重複する問題を修正 +• セルフホストのNightscoutに平文HTTP(VPN経由など)で正しく接続できるように修正 +• 一部の言語でグルコース値・グラフの表示崩れを修正 +• NightscoutトークンはAndroidバックアップに含まれなくなりました diff --git a/docs/whatsnew/1.2.3-nl-NL b/docs/whatsnew/1.2.3-nl-NL new file mode 100644 index 0000000..34b4e8a --- /dev/null +++ b/docs/whatsnew/1.2.3-nl-NL @@ -0,0 +1,4 @@ +• Dubbele schermen in Android Auto bij het wisselen van bron opgelost +• Zelf gehoste Nightscout via onversleuteld HTTP (bijv. via VPN) maakt nu correct verbinding +• Weergaveproblemen met glucose/grafiek in sommige talen opgelost +• Nightscout-token wordt niet meer opgenomen in Android-back-ups diff --git a/docs/whatsnew/1.2.3-pt-PT b/docs/whatsnew/1.2.3-pt-PT new file mode 100644 index 0000000..37dd303 --- /dev/null +++ b/docs/whatsnew/1.2.3-pt-PT @@ -0,0 +1,4 @@ +• Corrigidos ecrãs duplicados no Android Auto ao mudar de fonte +• Nightscout auto-hospedado via HTTP não encriptado (ex. via VPN) agora liga corretamente +• Corrigidos problemas de exibição de glicose/gráfico em alguns idiomas +• O token do Nightscout deixou de ser incluído nas cópias de segurança do Android diff --git a/docs/whatsnew/1.2.3-zh-CN b/docs/whatsnew/1.2.3-zh-CN new file mode 100644 index 0000000..6d75cca --- /dev/null +++ b/docs/whatsnew/1.2.3-zh-CN @@ -0,0 +1,4 @@ +• 修复切换数据源时 Android Auto 出现重复屏幕的问题 +• 通过明文 HTTP(例如经 VPN)连接自建 Nightscout 现在可以正常工作 +• 修复部分语言下血糖值和图表显示异常的问题 +• Nightscout 令牌不再包含在 Android 备份中