Compare commits
4 Commits
05e14147d9
...
cbe363066c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbe363066c | ||
|
|
974c529830 | ||
|
|
b45d4ab473 | ||
|
|
2f64d509d0 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,3 +10,5 @@
|
|||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
keystore.properties
|
||||||
|
*.jks
|
||||||
|
|||||||
@ -9,7 +9,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
JAVA_HOME=/path/to/jdk-17 ./gradlew assembleDebug
|
JAVA_HOME=/path/to/jdk-17 ./gradlew assembleDebug
|
||||||
|
|
||||||
# Build release APK
|
# Build release APK
|
||||||
JAVA_HOME=/path/to/jdk-17 ./gradlew assembleRelease
|
JAVA_HOME=/home/pavelb/Stažené/jdk-17.0.15+6 ./gradlew assembleRelease
|
||||||
|
|
||||||
# Run unit tests
|
# Run unit tests
|
||||||
./gradlew test
|
./gradlew test
|
||||||
|
|||||||
@ -7,16 +7,33 @@ plugins {
|
|||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import java.util.Properties
|
||||||
|
|
||||||
|
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||||
|
val keystoreProperties = Properties()
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(keystorePropertiesFile.inputStream())
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "cz.bugsy.karemote"
|
namespace = "cz.bugsy.karemote"
|
||||||
compileSdk = 36
|
compileSdk = 36
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
storeFile = rootProject.file(keystoreProperties["storeFile"] as String)
|
||||||
|
storePassword = keystoreProperties["storePassword"] as String?
|
||||||
|
keyAlias = keystoreProperties["keyAlias"] as String?
|
||||||
|
keyPassword = keystoreProperties["keyPassword"] as String?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "cz.bugsy.karemote"
|
applicationId = "cz.bugsy.karemote"
|
||||||
minSdk = 33
|
minSdk = 33
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 1
|
versionCode = 2
|
||||||
versionName = "1.0"
|
versionName = "1.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
@ -31,6 +48,7 @@ android {
|
|||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
)
|
)
|
||||||
|
signingConfig = signingConfigs.getByName("release")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@ -42,6 +60,7 @@ android {
|
|||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
|
buildConfig = true
|
||||||
}
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
|
|||||||
@ -65,6 +65,7 @@ class MainViewModel @Inject constructor(
|
|||||||
private var pollingJob: Job? = null
|
private var pollingJob: Job? = null
|
||||||
private var lastStationName: String? = null
|
private var lastStationName: String? = null
|
||||||
private var connectionFailedSince: Long? = null
|
private var connectionFailedSince: Long? = null
|
||||||
|
private var consecutiveFailures: Int = 0
|
||||||
private var volumeJob: Job? = null
|
private var volumeJob: Job? = null
|
||||||
private var lastVolumeSentTime: Long = 0
|
private var lastVolumeSentTime: Long = 0
|
||||||
private var pendingVolume: Int? = null
|
private var pendingVolume: Int? = null
|
||||||
@ -72,6 +73,7 @@ class MainViewModel @Inject constructor(
|
|||||||
companion object {
|
companion object {
|
||||||
private const val ERROR_DISPLAY_DELAY_MS = 10_000L // 10 seconds
|
private const val ERROR_DISPLAY_DELAY_MS = 10_000L // 10 seconds
|
||||||
private const val VOLUME_THROTTLE_MS = 300L // Throttle volume commands
|
private const val VOLUME_THROTTLE_MS = 300L // Throttle volume commands
|
||||||
|
private const val DISCONNECT_THRESHOLD = 3 // Consecutive failures before marking disconnected
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -81,6 +83,7 @@ class MainViewModel @Inject constructor(
|
|||||||
if (settings.serverAddress.isNotBlank()) {
|
if (settings.serverAddress.isNotBlank()) {
|
||||||
// Reset failure tracking when server address changes
|
// Reset failure tracking when server address changes
|
||||||
connectionFailedSince = null
|
connectionFailedSince = null
|
||||||
|
consecutiveFailures = 0
|
||||||
_uiState.update { it.copy(isConnecting = true, errorMessage = null) }
|
_uiState.update { it.copy(isConnecting = true, errorMessage = null) }
|
||||||
startPolling()
|
startPolling()
|
||||||
} else {
|
} else {
|
||||||
@ -115,6 +118,7 @@ class MainViewModel @Inject constructor(
|
|||||||
.onSuccess { status ->
|
.onSuccess { status ->
|
||||||
// Connection successful - reset failure tracking
|
// Connection successful - reset failure tracking
|
||||||
connectionFailedSince = null
|
connectionFailedSince = null
|
||||||
|
consecutiveFailures = 0
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isConnected = true,
|
isConnected = true,
|
||||||
@ -131,6 +135,7 @@ class MainViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onFailure { error ->
|
.onFailure { error ->
|
||||||
|
consecutiveFailures++
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
// Start tracking failure time if this is the first failure
|
// Start tracking failure time if this is the first failure
|
||||||
@ -141,14 +146,18 @@ class MainViewModel @Inject constructor(
|
|||||||
val failureDuration = now - (connectionFailedSince ?: now)
|
val failureDuration = now - (connectionFailedSince ?: now)
|
||||||
val shouldShowError = failureDuration >= ERROR_DISPLAY_DELAY_MS
|
val shouldShowError = failureDuration >= ERROR_DISPLAY_DELAY_MS
|
||||||
|
|
||||||
_uiState.update {
|
// Only mark as disconnected after multiple consecutive failures
|
||||||
it.copy(
|
// to avoid flapping when ESP32 has intermittent errors
|
||||||
isConnected = false,
|
if (consecutiveFailures >= DISCONNECT_THRESHOLD) {
|
||||||
isConnecting = !shouldShowError,
|
_uiState.update {
|
||||||
errorMessage = if (shouldShowError) {
|
it.copy(
|
||||||
"Connection failed: ${error.localizedMessage}"
|
isConnected = false,
|
||||||
} else null
|
isConnecting = !shouldShowError,
|
||||||
)
|
errorMessage = if (shouldShowError) {
|
||||||
|
"Connection failed: ${error.localizedMessage}"
|
||||||
|
} else null
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -206,7 +206,7 @@ fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Version 1.0",
|
text = "Version ${cz.bugsy.karemote.BuildConfig.VERSION_NAME}",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user