From 5207e4ea9b7e89cd7e312ab6429505d74f407634 Mon Sep 17 00:00:00 2001 From: Somasundaram Mahesh Date: Wed, 18 Feb 2026 07:47:28 +0530 Subject: [PATCH] Fix Settings UI reactivity, resolve compiler warnings, update .gitignore - Make screen capture permission and battery optimization states reactive - Add lifecycle observer to refresh battery status on resume - Add lifecycle-runtime-compose dependency for non-deprecated LocalLifecycleOwner - Replace deprecated LocalLifecycleOwner import - Remove unused REQUEST_CODE constant - Use KTX createBitmap() and bitmap[x,y] extensions - Add misc.xml and junie.xml to .gitignore --- android/.gitignore | 2 ++ android/.idea/misc.xml | 10 --------- android/app/build.gradle.kts | 1 + .../droidclaw/capture/ScreenCaptureManager.kt | 12 ++++++++--- .../droidclaw/ui/screens/SettingsScreen.kt | 21 +++++++++++++++++-- android/gradle/libs.versions.toml | 1 + 6 files changed, 32 insertions(+), 15 deletions(-) delete mode 100644 android/.idea/misc.xml diff --git a/android/.gitignore b/android/.gitignore index aa724b7..bc41352 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -7,6 +7,8 @@ /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml +/.idea/misc.xml +/.idea/junie.xml .DS_Store /build /captures diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml deleted file mode 100644 index 991a888..0000000 --- a/android/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 7eec416..3150bcd 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -64,6 +64,7 @@ dependencies { // Lifecycle service implementation(libs.lifecycle.service) + implementation(libs.lifecycle.runtime.compose) // Navigation implementation(libs.navigation.compose) diff --git a/android/app/src/main/java/com/thisux/droidclaw/capture/ScreenCaptureManager.kt b/android/app/src/main/java/com/thisux/droidclaw/capture/ScreenCaptureManager.kt index b88360f..d006d42 100644 --- a/android/app/src/main/java/com/thisux/droidclaw/capture/ScreenCaptureManager.kt +++ b/android/app/src/main/java/com/thisux/droidclaw/capture/ScreenCaptureManager.kt @@ -1,9 +1,12 @@ package com.thisux.droidclaw.capture +import android.app.Activity import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.graphics.PixelFormat +import androidx.core.graphics.createBitmap +import androidx.core.graphics.get import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay import android.media.ImageReader @@ -19,16 +22,19 @@ class ScreenCaptureManager(private val context: Context) { companion object { private const val TAG = "ScreenCapture" - const val REQUEST_CODE = 1001 val isAvailable = MutableStateFlow(false) // Stores MediaProjection consent for use by ConnectionService var consentResultCode: Int? = null var consentData: Intent? = null + // Expose consent as state so UI can react immediately + val hasConsentState = MutableStateFlow(false) + fun storeConsent(resultCode: Int, data: Intent?) { consentResultCode = resultCode consentData = data + hasConsentState.value = (resultCode == Activity.RESULT_OK && data != null) } fun hasConsent(): Boolean = consentResultCode != null && consentData != null @@ -86,7 +92,7 @@ class ScreenCaptureManager(private val context: Context) { val rowStride = planes[0].rowStride val rowPadding = rowStride - pixelStride * image.width - val bitmap = Bitmap.createBitmap( + val bitmap = createBitmap( image.width + rowPadding / pixelStride, image.height, Bitmap.Config.ARGB_8888 @@ -119,7 +125,7 @@ class ScreenCaptureManager(private val context: Context) { bitmap.width - 1 to bitmap.height - 1, bitmap.width / 2 to bitmap.height / 2 ) - return points.all { (x, y) -> bitmap.getPixel(x, y) == android.graphics.Color.BLACK } + return points.all { (x, y) -> bitmap[x, y] == android.graphics.Color.BLACK } } fun release() { diff --git a/android/app/src/main/java/com/thisux/droidclaw/ui/screens/SettingsScreen.kt b/android/app/src/main/java/com/thisux/droidclaw/ui/screens/SettingsScreen.kt index b9edcb0..bd0edda 100644 --- a/android/app/src/main/java/com/thisux/droidclaw/ui/screens/SettingsScreen.kt +++ b/android/app/src/main/java/com/thisux/droidclaw/ui/screens/SettingsScreen.kt @@ -26,6 +26,7 @@ import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -36,8 +37,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver import com.thisux.droidclaw.DroidClawApp import com.thisux.droidclaw.accessibility.DroidClawAccessibilityService import com.thisux.droidclaw.capture.ScreenCaptureManager @@ -58,8 +62,21 @@ fun SettingsScreen() { val isAccessibilityEnabled by DroidClawAccessibilityService.isRunning.collectAsState() val isCaptureAvailable by ScreenCaptureManager.isAvailable.collectAsState() - val hasCaptureConsent = isCaptureAvailable || ScreenCaptureManager.hasConsent() - val isBatteryExempt = remember { BatteryOptimization.isIgnoringBatteryOptimizations(context) } + val hasConsent by ScreenCaptureManager.hasConsentState.collectAsState() + val hasCaptureConsent = isCaptureAvailable || hasConsent + + var isBatteryExempt by remember { mutableStateOf(BatteryOptimization.isIgnoringBatteryOptimizations(context)) } + + val lifecycleOwner = LocalLifecycleOwner.current + DisposableEffect(lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + if (event == Lifecycle.Event.ON_RESUME) { + isBatteryExempt = BatteryOptimization.isIgnoringBatteryOptimizations(context) + } + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } + } val projectionLauncher = rememberLauncherForActivityResult( ActivityResultContracts.StartActivityForResult() diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 6f42528..3295033 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -39,6 +39,7 @@ kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx- kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" } lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "lifecycleService" } +lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleService" } navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } compose-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "composeIconsExtended" }