diff --git a/app/src/main/java/ink/trmnl/android/MainActivity.kt b/app/src/main/java/ink/trmnl/android/MainActivity.kt index cc85740..df95c7c 100644 --- a/app/src/main/java/ink/trmnl/android/MainActivity.kt +++ b/app/src/main/java/ink/trmnl/android/MainActivity.kt @@ -118,11 +118,13 @@ class MainActivity( trmnlImageUpdateManager.updateImage(newImageUrl) } } + WorkInfo.State.FAILED -> { val error = workInfo.outputData.getString(TrmnlImageRefreshWorker.KEY_ERROR_MESSAGE) Timber.e("${workInfo.tags} work failed: $error") trmnlImageUpdateManager.updateImage(imageUrl = "", errorMessage = error) } + else -> { Timber.d("${workInfo.tags} work state updated: ${workInfo.state}") } diff --git a/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt b/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt index dd985b6..217d0ca 100644 --- a/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt +++ b/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt @@ -75,6 +75,7 @@ class TrmnlDisplayRepository( is ApiResult.Failure -> { return failedTrmnlDisplayInfo(trmnlDeviceConfig, result) } + is ApiResult.Success -> { // Map the response to the display info val response: TrmnlDisplayResponse = result.value @@ -134,6 +135,7 @@ class TrmnlDisplayRepository( is ApiResult.Failure -> { return failedTrmnlDisplayInfo(trmnlDeviceConfig, result) } + is ApiResult.Success -> { // Map the response to the display info val response = result.value @@ -189,6 +191,7 @@ class TrmnlDisplayRepository( message = "Failed to setup device with ID (${trmnlDeviceConfig.deviceMacId}). Reason: $result", ) } + is ApiResult.Success -> { Timber.i("Device setup successful: ${result.value}") return DeviceSetupInfo( @@ -276,6 +279,7 @@ class TrmnlDisplayRepository( Timber.e("Failed to fetch device models: ${result.exceptionOrNull()}") emptyList() } + is ApiResult.Success -> { Timber.i("Successfully fetched ${result.value.data.size} device models") // Convert API models to simplified SupportedDeviceModel DTOs diff --git a/app/src/main/java/ink/trmnl/android/data/TrmnlUserRepository.kt b/app/src/main/java/ink/trmnl/android/data/TrmnlUserRepository.kt index cbd8cf0..a269d23 100644 --- a/app/src/main/java/ink/trmnl/android/data/TrmnlUserRepository.kt +++ b/app/src/main/java/ink/trmnl/android/data/TrmnlUserRepository.kt @@ -1,3 +1,5 @@ +@file:Suppress("DEPRECATION") + package ink.trmnl.android.data import com.slack.eithernet.ApiResult @@ -62,6 +64,7 @@ class TrmnlUserRepository( Timber.e(exception, "User API token validation failed") Result.failure(exception ?: Exception("Failed to validate user token")) } + is ApiResult.Success -> { Timber.i("User API token validated successfully: ${result.value.data.email}") Result.success(result.value.data) diff --git a/app/src/main/java/ink/trmnl/android/di/NetworkModule.kt b/app/src/main/java/ink/trmnl/android/di/NetworkModule.kt index ada4465..eeb7a0e 100644 --- a/app/src/main/java/ink/trmnl/android/di/NetworkModule.kt +++ b/app/src/main/java/ink/trmnl/android/di/NetworkModule.kt @@ -109,6 +109,7 @@ interface NetworkModule { @SingleIn(AppScope::class) fun provideTrmnlApiService(retrofit: Retrofit): TrmnlApiService = retrofit.create(TrmnlApiService::class.java) + @Suppress("DEPRECATION") @Provides @SingleIn(AppScope::class) fun provideTrmnlUserApiService(retrofit: Retrofit): TrmnlUserApiService = retrofit.create(TrmnlUserApiService::class.java) diff --git a/app/src/main/java/ink/trmnl/android/network/TrmnlUserApiService.kt b/app/src/main/java/ink/trmnl/android/network/TrmnlUserApiService.kt index 34f28ed..57c975c 100644 --- a/app/src/main/java/ink/trmnl/android/network/TrmnlUserApiService.kt +++ b/app/src/main/java/ink/trmnl/android/network/TrmnlUserApiService.kt @@ -107,6 +107,7 @@ interface TrmnlUserApiService { * @param updateRequest The device update request containing the fields to update * @return An [ApiResult] containing [TrmnlDeviceResponse] with the updated device data */ + @Suppress("DEPRECATION") @Headers("Content-Type: application/json") @PATCH suspend fun updateDevice( diff --git a/app/src/main/java/ink/trmnl/android/ui/aboutapp/AppInfoScreen.kt b/app/src/main/java/ink/trmnl/android/ui/aboutapp/AppInfoScreen.kt index 20f4eb9..292d8e4 100644 --- a/app/src/main/java/ink/trmnl/android/ui/aboutapp/AppInfoScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/aboutapp/AppInfoScreen.kt @@ -87,10 +87,14 @@ class AppInfoPresenter( buildType = buildType, eventSink = { event -> when (event) { - Event.BackPressed -> navigator.pop() + Event.BackPressed -> { + navigator.pop() + } + Event.OpenGithub -> { uriHandler.openUri(TRMNL_ANDROID_APP_GITHUB_URL) } + Event.OpenTrmnlSite -> { uriHandler.openUri(TRMNL_SITE_URL) } diff --git a/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt index 311e641..c5db618 100644 --- a/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt @@ -185,6 +185,7 @@ class DeviceModelSelectorPresenter( is DeviceModelSelectorScreen.Event.BackPressed -> { navigator.pop() } + is DeviceModelSelectorScreen.Event.ModelSelected -> { // Pop with result to return the selected model to the previous screen navigator.pop( @@ -195,6 +196,7 @@ class DeviceModelSelectorPresenter( ), ) } + is DeviceModelSelectorScreen.Event.RetryLoad -> { scope.launch { isLoading = true @@ -298,6 +300,7 @@ fun DeviceModelSelectorContent( } } } + state.errorMessage != null -> { // Error state Box( @@ -323,6 +326,7 @@ fun DeviceModelSelectorContent( } } } + state.models.isEmpty() -> { // Empty state Box( @@ -335,6 +339,7 @@ fun DeviceModelSelectorContent( ) } } + else -> { // Success state - show list of models LazyColumn( diff --git a/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt b/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt index fbce972..453746a 100644 --- a/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/display/TrmnlMirrorDisplayScreen.kt @@ -283,12 +283,15 @@ class TrmnlMirrorDisplayPresenter( } } } + TrmnlMirrorDisplayScreen.Event.ConfigureRequested -> { navigator.goTo(AppSettingsScreen(returnToMirrorAfterSave = true)) } + TrmnlMirrorDisplayScreen.Event.BackPressed -> { navigator.pop() } + TrmnlMirrorDisplayScreen.Event.ViewLogsRequested -> { navigator.goTo(DisplayRefreshLogScreen) } @@ -361,16 +364,19 @@ fun TrmnlMirrorDisplayContent( LaunchedEffect(state.saveImageResult) { state.saveImageResult?.let { result -> when (result) { - is TrmnlMirrorDisplayScreen.SaveImageResult.Success -> + is TrmnlMirrorDisplayScreen.SaveImageResult.Success -> { snackbarHostState.showSnackbar( message = "Image saved to Pictures/TRMNL", duration = SnackbarDuration.Short, ) - is TrmnlMirrorDisplayScreen.SaveImageResult.Error -> + } + + is TrmnlMirrorDisplayScreen.SaveImageResult.Error -> { snackbarHostState.showSnackbar( message = result.message, duration = SnackbarDuration.Short, ) + } } } } diff --git a/app/src/main/java/ink/trmnl/android/ui/refreshlog/DisplayRefreshLogScreen.kt b/app/src/main/java/ink/trmnl/android/ui/refreshlog/DisplayRefreshLogScreen.kt index 26094f7..4142aad 100644 --- a/app/src/main/java/ink/trmnl/android/ui/refreshlog/DisplayRefreshLogScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/refreshlog/DisplayRefreshLogScreen.kt @@ -161,7 +161,10 @@ class DisplayRefreshLogPresenter( logs = logs, eventSink = { event -> when (event) { - DisplayRefreshLogScreen.Event.BackPressed -> navigator.pop() + DisplayRefreshLogScreen.Event.BackPressed -> { + navigator.pop() + } + DisplayRefreshLogScreen.Event.ClearLogs -> { scope.launch { refreshLogManager.clearLogs() @@ -178,6 +181,7 @@ class DisplayRefreshLogPresenter( ) } } + DisplayRefreshLogScreen.Event.AddSuccessLog -> { scope.launch { refreshLogManager.addLog( @@ -195,6 +199,7 @@ class DisplayRefreshLogPresenter( DisplayRefreshLogScreen.Event.StartRefreshWorker -> { trmnlWorkScheduler.startOneTimeImageRefreshWork() } + DisplayRefreshLogScreen.Event.ExportLogs -> { scope.launch { refreshLogExporter.exportLogsAndShare(logs) diff --git a/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt b/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt index 20dfd9b..e65dd77 100644 --- a/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt @@ -408,6 +408,7 @@ class AppSettingsPresenter( TrmnlDeviceType.TRMNL -> { displayRepository.getCurrentDisplayData(deviceConfig) } + else -> { displayRepository.getNextDisplayData(deviceConfig) } @@ -785,6 +786,7 @@ fun AppSettingsContent( } } } + is InvalidServerUrl -> { Column( modifier = Modifier.padding(16.dp), @@ -803,6 +805,7 @@ fun AppSettingsContent( ) } } + is ValidationResult.InvalidDeviceMacId -> { Column( modifier = Modifier.padding(16.dp), @@ -821,6 +824,7 @@ fun AppSettingsContent( ) } } + is Failure -> { // Error state remains the same Column( @@ -840,6 +844,7 @@ fun AppSettingsContent( ) } } + is ValidationResult.UserTokenSuccess -> { Column( modifier = Modifier.padding(16.dp), @@ -863,6 +868,7 @@ fun AppSettingsContent( ) } } + is ValidationResult.InvalidUserToken -> { Column( modifier = Modifier.padding(16.dp), diff --git a/app/src/main/java/ink/trmnl/android/ui/theme/Theme.kt b/app/src/main/java/ink/trmnl/android/ui/theme/Theme.kt index f55b643..9611316 100644 --- a/app/src/main/java/ink/trmnl/android/ui/theme/Theme.kt +++ b/app/src/main/java/ink/trmnl/android/ui/theme/Theme.kt @@ -37,8 +37,13 @@ fun TrmnlDisplayAppTheme( if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } - darkTheme -> DarkColorScheme - else -> LightColorScheme + darkTheme -> { + DarkColorScheme + } + + else -> { + LightColorScheme + } } MaterialTheme( diff --git a/app/src/main/java/ink/trmnl/android/util/DateTimeFormatter.kt b/app/src/main/java/ink/trmnl/android/util/DateTimeFormatter.kt index f8b874d..1fb6b6e 100644 --- a/app/src/main/java/ink/trmnl/android/util/DateTimeFormatter.kt +++ b/app/src/main/java/ink/trmnl/android/util/DateTimeFormatter.kt @@ -20,14 +20,20 @@ internal fun getTimeElapsedString(timestamp: Long): String { val remainingMinutes = minutes % 60 when { - remainingHours > 0 && remainingMinutes > 0 -> + remainingHours > 0 && remainingMinutes > 0 -> { "$days ${if (days == 1L) "day" else "days"} $remainingHours ${if (remainingHours == 1L) "hour" else "hours"} and $remainingMinutes ${if (remainingMinutes == 1L) "minute" else "minutes"} ago" - remainingHours > 0 -> + } + + remainingHours > 0 -> { "$days ${if (days == 1L) "day" else "days"} and $remainingHours ${if (remainingHours == 1L) "hour" else "hours"} ago" - else -> + } + + else -> { "$days ${if (days == 1L) "day" else "days"} ago" + } } } + hours > 0 -> { val remainingMinutes = minutes % 60 if (remainingMinutes > 0) { @@ -36,7 +42,13 @@ internal fun getTimeElapsedString(timestamp: Long): String { "$hours ${if (hours == 1L) "hour" else "hours"} ago" } } - minutes > 0 -> "$minutes ${if (minutes == 1L) "minute" else "minutes"} ago" - else -> "Just now" + + minutes > 0 -> { + "$minutes ${if (minutes == 1L) "minute" else "minutes"} ago" + } + + else -> { + "Just now" + } } } diff --git a/app/src/main/java/ink/trmnl/android/work/TrmnlWorkScheduler.kt b/app/src/main/java/ink/trmnl/android/work/TrmnlWorkScheduler.kt index 7079141..2c45cab 100644 --- a/app/src/main/java/ink/trmnl/android/work/TrmnlWorkScheduler.kt +++ b/app/src/main/java/ink/trmnl/android/work/TrmnlWorkScheduler.kt @@ -111,9 +111,15 @@ class TrmnlWorkScheduler( val deviceConfig = trmnlDeviceConfigDataStore.getDeviceConfigSync() val shouldAdvancePlaylist = when (deviceConfig?.type) { - ink.trmnl.android.model.TrmnlDeviceType.BYOS -> true // Always auto-advance - ink.trmnl.android.model.TrmnlDeviceType.BYOD -> deviceConfig.isMasterDevice ?: true // Default to master if not set - ink.trmnl.android.model.TrmnlDeviceType.TRMNL -> false // Always mirror + ink.trmnl.android.model.TrmnlDeviceType.BYOS -> true + + // Always auto-advance + ink.trmnl.android.model.TrmnlDeviceType.BYOD -> deviceConfig.isMasterDevice ?: true + + // Default to master if not set + ink.trmnl.android.model.TrmnlDeviceType.TRMNL -> false + + // Always mirror null -> false } diff --git a/app/src/main/java/ink/trmnl/android/work/TrmnlWorkerFactory.kt b/app/src/main/java/ink/trmnl/android/work/TrmnlWorkerFactory.kt index 9088d95..b315796 100644 --- a/app/src/main/java/ink/trmnl/android/work/TrmnlWorkerFactory.kt +++ b/app/src/main/java/ink/trmnl/android/work/TrmnlWorkerFactory.kt @@ -19,8 +19,12 @@ class TrmnlWorkerFactory( workerParameters: WorkerParameters, ): ListenableWorker? = when (workerClassName) { - TrmnlImageRefreshWorker::class.java.name -> + TrmnlImageRefreshWorker::class.java.name -> { imageRefreshWorkerFactory.create(appContext, workerParameters) - else -> null + } + + else -> { + null + } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2f6ef91..b2cc094 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ activityCompose = "1.12.1" # https://developer.android.com/jetpack/androidx/releases/compose-material3-adaptive adaptive = "1.2.0" -agp = "8.9.2" +agp = "8.13.2" circuit = "0.31.0" composeBom = "2025.12.00" coreKtx = "1.17.0" @@ -26,7 +26,7 @@ lifecycleRuntimeKtx = "2.10.0" # Kotlin linter with built-in formatter # https://github.com/jeremymailen/kotlinter-gradle # https://github.com/pinterest/ktlint -kotlinter = "5.0.2" +kotlinter = "5.4.2" # Http client for Android # https://square.github.io/okhttp/