Skip to content

Commit 7b9ec8e

Browse files
committed
release: bump version to 0.51 (versionCode 15)
1 parent ea62452 commit 7b9ec8e

21 files changed

Lines changed: 191 additions & 86 deletions

File tree

.github/workflows/sync-translations.yml

Lines changed: 0 additions & 55 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
All notable changes to the BoxViewer project will be documented in this file.
44

5+
## [0.51] - 2026-07-22
6+
7+
### Fixed
8+
- **Widget Inflation Fix**: Fixed RemoteViews inflation failure ("Can't load widget") on Android 12–15 devices by removing non-remotable progress spinner calls.
9+
- **Temperature Adaptive Coloring**: Fixed temperature value color resolver to support locale-formatted numbers with decimal commas (e.g., German `"15,2"`), preventing improper fallback to orange.
10+
- **API Logger Header Sync**: API log header now updates dynamically to reflect the current app version when the app is updated.
11+
12+
### Added
13+
- **Expanded System Diagnostics**: System Diagnostics copy action now includes active home screen widget counts, system locale, app preferences, database state, and saved crash stack traces.
14+
- **6 New Locales Supported**: Prepared language picker, resource infrastructure, and POEditor sync for Czech (`cs`), French (`fr`), Hungarian (`hu`), Italian (`it`), Dutch (`nl`), and Polish (`pl`).
15+
516
## [0.50] - 2026-07-20
617

718
### Added

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ android {
1414
applicationId = "de.nichu42.boxviewer"
1515
minSdk = 24
1616
targetSdk = 37
17-
versionCode = 14
18-
versionName = "0.50"
17+
versionCode = 15
18+
versionName = "0.51"
1919
}
2020

2121
signingConfigs {

app/src/main/java/de/nichu42/boxviewer/util/ApiLogger.kt

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,8 @@ object ApiLogger {
100100
val context = appContext ?: return@launch
101101
val file = File(context.filesDir, FILE_NAME)
102102

103-
// 1. Create file and write diagnostics header if it doesn't exist
104-
val isNewFile = !file.exists() || file.length() == 0L
105-
if (isNewFile) {
106-
writeDiagnosticsHeader(file)
107-
}
103+
// 1. Ensure file exists and header matches current app version
104+
updateOrEnsureHeader(file)
108105

109106
// 2. Format request entry as a single JSON line
110107
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
@@ -136,7 +133,7 @@ object ApiLogger {
136133
}
137134
}
138135

139-
private fun writeDiagnosticsHeader(file: File) {
136+
private fun updateOrEnsureHeader(file: File) {
140137
try {
141138
val context = appContext ?: return
142139
val pm = context.packageManager
@@ -154,24 +151,50 @@ object ApiLogger {
154151
val versionCode = pi?.let {
155152
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) it.longVersionCode else @Suppress("DEPRECATION") it.versionCode
156153
} ?: 0
157-
154+
val currentAppVersion = "$versionName ($versionCode)"
158155
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
159-
160-
// Format system diagnostics as the first JSON line
161-
val header = mapOf(
156+
157+
val headerMap = mapOf(
162158
"type" to "diagnostics",
163159
"date" to sdf.format(Date()),
164-
"appVersion" to "$versionName ($versionCode)",
160+
"appVersion" to currentAppVersion,
165161
"androidSdk" to Build.VERSION.SDK_INT,
166162
"androidOs" to Build.VERSION.RELEASE,
167163
"device" to "${Build.MANUFACTURER} ${Build.MODEL}",
168164
"brand" to Build.BRAND,
169165
"hardware" to Build.HARDWARE,
170166
"status" to "Running"
171167
)
172-
173-
val headerJson = moshi.adapter(Map::class.java).toJson(header)
174-
file.writeText(headerJson + "\n")
168+
val newHeaderJson = moshi.adapter(Map::class.java).toJson(headerMap)
169+
170+
if (!file.exists() || file.length() == 0L) {
171+
file.writeText(newHeaderJson + "\n")
172+
return
173+
}
174+
175+
val lines = file.readLines()
176+
if (lines.isEmpty()) {
177+
file.writeText(newHeaderJson + "\n")
178+
return
179+
}
180+
181+
val firstLine = lines.first()
182+
if (firstLine.contains("\"type\":\"diagnostics\"") || firstLine.contains("\"type\" : \"diagnostics\"")) {
183+
if (!firstLine.contains("\"appVersion\":\"$currentAppVersion\"") && !firstLine.contains("\"appVersion\" : \"$currentAppVersion\"")) {
184+
// App was updated since log was initialized! Rewrite line 1 header with current version.
185+
val remainingLines = lines.drop(1)
186+
file.writeText(newHeaderJson + "\n")
187+
for (line in remainingLines) {
188+
file.appendText(line + "\n")
189+
}
190+
}
191+
} else {
192+
// Prepend missing header
193+
file.writeText(newHeaderJson + "\n")
194+
for (line in lines) {
195+
file.appendText(line + "\n")
196+
}
197+
}
175198
} catch (e: Exception) {
176199
e.printStackTrace()
177200
}
@@ -240,6 +263,8 @@ object ApiLogger {
240263
val file = File(context.filesDir, FILE_NAME)
241264
if (!file.exists()) return@withLock Pair(null, emptyList())
242265

266+
updateOrEnsureHeader(file)
267+
243268
val diagnostics = mutableMapOf<String, Any>()
244269
val entries = mutableListOf<ApiLogEntry>()
245270

app/src/main/java/de/nichu42/boxviewer/util/CrashHandler.kt

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
package de.nichu42.boxviewer.util
22

3+
import android.appwidget.AppWidgetManager
4+
import android.content.ComponentName
35
import android.content.Context
46
import android.os.Build
7+
import de.nichu42.boxviewer.data.db.DB_VERSION
8+
import de.nichu42.boxviewer.widget.SenseBoxWidgetProvider
9+
import de.nichu42.boxviewer.widget.SenseBoxWidgetProviderLarge
10+
import de.nichu42.boxviewer.widget.SenseBoxWidgetProviderSmall
511
import java.io.File
612
import java.io.PrintWriter
713
import java.io.StringWriter
@@ -102,6 +108,32 @@ object CrashHandler {
102108
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) it.longVersionCode else @Suppress("DEPRECATION") it.versionCode
103109
} ?: 0
104110

111+
val crashLog = getCrashLog(context)
112+
val statusText = if (crashLog != null) "Crash Recorded (See log below)" else "Running Normally"
113+
114+
val prefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
115+
val savedLanguage = prefs.getString("app_language", "system_default")
116+
val activeLocale = Locale.getDefault().toLanguageTag()
117+
val tempUnit = prefs.getString("temperature_unit", "°C")
118+
val pressUnit = prefs.getString("pressure_unit", "hPa")
119+
val formatPressure = prefs.getBoolean("format_pressure", true)
120+
val windUnit = prefs.getString("wind_unit", "m/s")
121+
val aqiSystem = prefs.getString("aqi_system", AqiSystem.US_EPA.name) ?: AqiSystem.US_EPA.name
122+
val textScale = prefs.getFloat("app_text_scale", 1.0f)
123+
val appTheme = prefs.getString("app_theme", "SYSTEM") ?: "SYSTEM"
124+
125+
val appWidgetManager = try { AppWidgetManager.getInstance(context) } catch (e: Exception) { null }
126+
val mediumWidgets = appWidgetManager?.getAppWidgetIds(ComponentName(context, SenseBoxWidgetProvider::class.java))?.size ?: 0
127+
val smallWidgets = appWidgetManager?.getAppWidgetIds(ComponentName(context, SenseBoxWidgetProviderSmall::class.java))?.size ?: 0
128+
val largeWidgets = appWidgetManager?.getAppWidgetIds(ComponentName(context, SenseBoxWidgetProviderLarge::class.java))?.size ?: 0
129+
val totalWidgets = mediumWidgets + smallWidgets + largeWidgets
130+
131+
val dbFile = context.getDatabasePath("sensebox_database")
132+
val dbExist = dbFile.exists()
133+
val dbSizeKb = if (dbExist) dbFile.length() / 1024 else 0L
134+
135+
val apiLogEnabled = ApiLogger.isLoggingEnabled()
136+
105137
return StringBuilder().apply {
106138
append("=== BOXVIEWER SYSTEM DIAGNOSTICS ===\n")
107139
append("Date: ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())}\n")
@@ -111,7 +143,25 @@ object CrashHandler {
111143
append("Device: ${Build.MANUFACTURER} ${Build.MODEL}\n")
112144
append("Brand: ${Build.BRAND}\n")
113145
append("Hardware: ${Build.HARDWARE}\n")
114-
append("Status: Running Normally\n")
146+
append("Status: $statusText\n")
147+
append("\n--- Home Widgets ---\n")
148+
append("Active Widgets: $totalWidgets (Medium: $mediumWidgets, Small: $smallWidgets, Large: $largeWidgets)\n")
149+
append("\n--- Preferences & State ---\n")
150+
append("App Language: $savedLanguage (System Locale: $activeLocale)\n")
151+
append("Temperature Unit: $tempUnit\n")
152+
append("Pressure Unit: $pressUnit (Format: $formatPressure)\n")
153+
append("Wind Unit: $windUnit\n")
154+
append("AQI Standard: $aqiSystem\n")
155+
append("Text Scale: ${String.format(Locale.US, "%.1fx", textScale)}\n")
156+
append("Theme: $appTheme\n")
157+
append("\n--- Database & Diagnostics ---\n")
158+
append("Database: ${if (dbExist) "Present (${dbSizeKb} KB, Schema v$DB_VERSION)" else "Missing"}\n")
159+
append("API Logging Enabled: $apiLogEnabled\n")
160+
161+
if (crashLog != null) {
162+
append("\n--- Saved Crash Report ---\n")
163+
append(crashLog)
164+
}
115165
}.toString()
116166
}
117167
}

app/src/main/java/de/nichu42/boxviewer/util/LocaleHelper.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ object LocaleHelper {
2626
val SUPPORTED_LOCALES = listOf(
2727
SupportedLocale(tag = SYSTEM_DEFAULT, displayNameRes = R.string.language_system_default),
2828
SupportedLocale(tag = "en", displayNameRes = R.string.language_english),
29-
SupportedLocale(tag = "de", displayNameRes = R.string.language_german)
29+
SupportedLocale(tag = "de", displayNameRes = R.string.language_german),
30+
SupportedLocale(tag = "cs", displayNameRes = R.string.language_czech),
31+
SupportedLocale(tag = "fr", displayNameRes = R.string.language_french),
32+
SupportedLocale(tag = "hu", displayNameRes = R.string.language_hungarian),
33+
SupportedLocale(tag = "it", displayNameRes = R.string.language_italian),
34+
SupportedLocale(tag = "nl", displayNameRes = R.string.language_dutch),
35+
SupportedLocale(tag = "pl", displayNameRes = R.string.language_polish)
3036
)
3137

3238
data class SupportedLocale(

app/src/main/java/de/nichu42/boxviewer/util/PressureConverter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ object PressureConverter {
2929
if (!isPa && !isHpa && !isMbar && !isInHg && !isMmHg) return null
3030

3131
return try {
32-
val value = valueStr.toDouble()
32+
val cleanVal = valueStr.replace(',', '.').trim()
33+
val value = cleanVal.toDouble()
3334

3435
// First normalize to hPa
3536
val hpaValue = when {

app/src/main/java/de/nichu42/boxviewer/util/SensorValueColorResolver.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ object SensorValueColorResolver {
2424
val lower = title.lowercase()
2525

2626
val value = valueString?.let { raw ->
27+
val cleanRaw = raw.replace(',', '.').trim()
2728
when {
28-
lower.contains("temp") -> TemperatureConverter.convertToDouble(raw, unit, "°C")
29+
lower.contains("temp") -> TemperatureConverter.convertToDouble(cleanRaw, unit, "°C")
2930
lower.contains("druck") || lower.contains("press") ->
30-
PressureConverter.convertToDouble(raw, unit, "hPa")
31-
else -> raw.toDoubleOrNull()
31+
PressureConverter.convertToDouble(cleanRaw, unit, "hPa")
32+
else -> cleanRaw.toDoubleOrNull()
3233
}
3334
}
3435

app/src/main/java/de/nichu42/boxviewer/util/TemperatureConverter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ object TemperatureConverter {
2323
}
2424

2525
return try {
26-
var celsius = valueStr.toDouble()
26+
val cleanVal = valueStr.replace(',', '.').trim()
27+
var celsius = cleanVal.toDouble()
2728
if (isFromF) {
2829
celsius = (celsius - 32.0) / 1.8
2930
} else if (isFromK) {

app/src/main/java/de/nichu42/boxviewer/util/WindConverter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ object WindConverter {
2020
if (!isMs && !isKmh && !isMph && !isKn) return null
2121

2222
return try {
23-
val value = valueStr.toDouble()
23+
val cleanVal = valueStr.replace(',', '.').trim()
24+
val value = cleanVal.toDouble()
2425

2526
// First normalize to m/s
2627
val msValue = when {

0 commit comments

Comments
 (0)