Merge pull request #5 from msomu/main

Fix Settings UI reactivity, resolve compiler warnings, update .gitignore
This commit is contained in:
Somasundaram M
2026-02-18 08:46:41 +05:30
committed by GitHub
6 changed files with 32 additions and 15 deletions

2
android/.gitignore vendored
View File

@@ -7,6 +7,8 @@
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
/.idea/misc.xml
/.idea/junie.xml
.DS_Store
/build
/captures

10
android/.idea/misc.xml generated
View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@@ -64,6 +64,7 @@ dependencies {
// Lifecycle service
implementation(libs.lifecycle.service)
implementation(libs.lifecycle.runtime.compose)
// Navigation
implementation(libs.navigation.compose)

View File

@@ -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() {

View File

@@ -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()

View File

@@ -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" }