diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 22ff259..189af5d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { minSdk = 31 targetSdk = 35 - versionCode = 28 - versionName = "1.3.1" + versionCode = 29 + versionName = "1.3.2" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/bartixxx/opflashcontrol/ExperimentalActivity.kt b/app/src/main/java/com/bartixxx/opflashcontrol/ExperimentalActivity.kt index 45380f9..29cd481 100644 --- a/app/src/main/java/com/bartixxx/opflashcontrol/ExperimentalActivity.kt +++ b/app/src/main/java/com/bartixxx/opflashcontrol/ExperimentalActivity.kt @@ -121,10 +121,10 @@ class ExperimentalActivity : BaseActivity() { try { Log.d("ExperimentalActivity", "Light cycle: white LED on") // Using the brightness from the slider - ledController.controlLeds("on", WHITE_LED_PATH, YELLOW_LED_PATH, whiteBrightness = lightCycleBrightness, yellowBrightness = 0, showToast = false) + ledController.controlLeds("on", WHITE_LED_PATH, YELLOW_LED_PATH, WHITE2_LED_PATH, YELLOW2_LED_PATH, whiteBrightness = lightCycleBrightness, white2Brightness = lightCycleBrightness, yellowBrightness = 0, yellow2Brightness = 0, showToast = false) sleep(lightCycleDelay) // Use slider value for delay Log.d("ExperimentalActivity", "Light cycle: yellow LED on") - ledController.controlLeds("on", WHITE_LED_PATH, YELLOW_LED_PATH, whiteBrightness = 0, yellowBrightness = lightCycleBrightness, showToast = false) + ledController.controlLeds("on", WHITE_LED_PATH, YELLOW_LED_PATH, WHITE2_LED_PATH, YELLOW2_LED_PATH, whiteBrightness = 0, white2Brightness = 0, yellowBrightness = lightCycleBrightness, yellow2Brightness = lightCycleBrightness, showToast = false) sleep(lightCycleDelay) } catch (e: InterruptedException) { Thread.currentThread().interrupt() @@ -144,7 +144,7 @@ class ExperimentalActivity : BaseActivity() { Log.d("ExperimentalActivity", "Light thread interrupted") } lightThread = null - ledController.controlLeds("off", WHITE_LED_PATH, YELLOW_LED_PATH, whiteBrightness = 0, yellowBrightness = 0, showToast = false) // Turn LEDs off + ledController.controlLeds("off", WHITE_LED_PATH, YELLOW_LED_PATH, WHITE2_LED_PATH, YELLOW2_LED_PATH, whiteBrightness = 0, yellowBrightness = 0, white2Brightness = 0, yellow2Brightness = 0, showToast = false) // Turn LEDs off } private fun startBrightnessCycle() { @@ -159,7 +159,7 @@ class ExperimentalActivity : BaseActivity() { try { Log.d("ExperimentalActivity", "Brightness cycle: setting brightness to $brightness") // Use the brightness from the slider - ledController.controlLeds("on", WHITE_LED_PATH, YELLOW_LED_PATH, whiteBrightness = brightness, yellowBrightness = brightness, showToast = false) + ledController.controlLeds("on", WHITE_LED_PATH, YELLOW_LED_PATH, WHITE2_LED_PATH, YELLOW2_LED_PATH, whiteBrightness = brightness, yellowBrightness = brightness, white2Brightness = brightness, yellow2Brightness = brightness, showToast = false) sleep(50) // Small delay for smooth transition brightness += increment if (brightness > 255 || brightness < 1) { @@ -184,7 +184,7 @@ class ExperimentalActivity : BaseActivity() { Log.d("ExperimentalActivity", "Brightness thread interrupted") } brightnessThread = null - ledController.controlLeds("off", WHITE_LED_PATH, YELLOW_LED_PATH, whiteBrightness = 0, yellowBrightness = 0, showToast = false) // Turn LEDs off + ledController.controlLeds("off", WHITE_LED_PATH, YELLOW_LED_PATH, WHITE2_LED_PATH, YELLOW2_LED_PATH, whiteBrightness = 0, yellowBrightness = 0, white2Brightness = 0, yellow2Brightness = 0, showToast = false) // Turn LEDs off } private fun navigateBackToMain() { diff --git a/app/src/main/java/com/bartixxx/opflashcontrol/LEDControlUtil.kt b/app/src/main/java/com/bartixxx/opflashcontrol/LEDControlUtil.kt index 58fe744..991b6c3 100644 --- a/app/src/main/java/com/bartixxx/opflashcontrol/LEDControlUtil.kt +++ b/app/src/main/java/com/bartixxx/opflashcontrol/LEDControlUtil.kt @@ -1,18 +1,18 @@ package com.bartixxx.opflashcontrol -import android.content.Context +import android.os.Handler +import android.os.Looper import android.util.Log import android.widget.Toast import java.io.DataOutputStream import java.io.IOException +import android.content.Context class LedController { companion object { const val WHITE_LED_PATH = "/sys/class/leds/led:torch_0/brightness" const val YELLOW_LED_PATH = "/sys/class/leds/led:torch_1/brightness" - const val WHITE2_LED_PATH = "/sys/class/leds/led:torch_2/brightness" - const val YELLOW2_LED_PATH = "/sys/class/leds/led:torch_3/brightness" val TOGGLE_PATHS = listOf("/sys/class/leds/led:switch_2/brightness") } @@ -30,8 +30,8 @@ class LedController { action: String, whiteLedPath: String = WHITE_LED_PATH, yellowLedPath: String = YELLOW_LED_PATH, - white2LedPath: String? = WHITE2_LED_PATH, - yellow2LedPath: String? = YELLOW2_LED_PATH, + white2LedPath: String? = null, + yellow2LedPath: String? = null, whiteBrightness: Int, yellowBrightness: Int, white2Brightness: Int = 0, @@ -46,14 +46,7 @@ class LedController { val commands = mutableListOf() if (action == "on") { - commands.addAll(commonOnCommands(whiteLedPath, yellowLedPath, white2LedPath, yellow2LedPath)) - commands.addAll(listOf( - "echo $sanitizedWhiteBrightness > $whiteLedPath", - "echo $sanitizedYellowBrightness > $yellowLedPath", - white2LedPath?.let { "echo $sanitizedWhite2Brightness > $it" }, - yellow2LedPath?.let { "echo $sanitizedYellow2Brightness > $it" } - ).filterNotNull()) - TOGGLE_PATHS.forEach { commands.add("echo 255 > $it") } + commands.addAll(commonOnCommands(whiteLedPath, yellowLedPath, white2LedPath, yellow2LedPath, sanitizedWhiteBrightness, sanitizedYellowBrightness, sanitizedWhite2Brightness, sanitizedYellow2Brightness)) } else if (action == "off") { commands.addAll(commonOffCommands(whiteLedPath, yellowLedPath, white2LedPath, yellow2LedPath)) } @@ -62,75 +55,121 @@ class LedController { } private fun commonOnCommands( - white: String, yellow: String, white2: String?, yellow2: String? + white: String, yellow: String, white2: String?, yellow2: String?, + whiteBrightness: Int, yellowBrightness: Int, white2Brightness: Int, yellow2Brightness: Int ): List { - return listOf( - "echo 80 > $white", - "echo 80 > $yellow", - white2?.let { "echo 80 > $it" }, - yellow2?.let { "echo 80 > $it" } - ).filterNotNull() + TOGGLE_PATHS.map { "echo 0 > $it" } + val commands = mutableListOf() + + // Reset toggle paths to 0, then back to 255 to ensure proper refresh + TOGGLE_PATHS.forEach { + commands.add("echo 0 > $it") + } + + commands.add("echo $whiteBrightness > $white") + commands.add("echo $yellowBrightness > $yellow") + + white2?.let { + commands.add("echo $white2Brightness > $it") + } + + yellow2?.let { + commands.add("echo $yellow2Brightness > $it") + } + + TOGGLE_PATHS.forEach { + commands.add("echo 255 > $it") + } + + return commands } private fun commonOffCommands( white: String, yellow: String, white2: String?, yellow2: String? ): List { - return listOf( - "echo 80 > $white", - "echo 80 > $yellow", - white2?.let { "echo 80 > $it" }, - yellow2?.let { "echo 80 > $it" } - ).filterNotNull() + TOGGLE_PATHS.map { "echo 0 > $it" } + val commands = mutableListOf() + + // Set brightness to 80 instead of 0 for "off" action + commands.add("echo 80 > $white") + commands.add("echo 80 > $yellow") + + white2?.let { + commands.add("echo 80 > $it") + } + + yellow2?.let { + commands.add("echo 80 > $it") + } + + // Reset toggle paths to 0 + TOGGLE_PATHS.forEach { + commands.add("echo 0 > $it") + } + + return commands } - private fun executeRootCommands(commands: List, showToast: Boolean = true) { + + protected fun executeRootCommands(commands: List, showToast: Boolean = true) { val maxRetries = 3 // Maximum number of retries val initialDelay = 1000L // Initial delay in milliseconds val maxDelay = 8000L // Maximum delay (8 seconds) for exponential backoff var attempt = 0 var delay = initialDelay + + // Create a Handler for the main thread + val mainHandler = Handler(Looper.getMainLooper()) + + // Check SELinux status + val selinuxProcess = Runtime.getRuntime().exec("getenforce") + val selinuxStatus = selinuxProcess.inputStream.bufferedReader().readText().trim() + if (selinuxStatus == "Enforcing") { + Log.d("LEDControlApp", "SELinux is in Enforcing mode.") + } else { + Log.d("LEDControlApp", "SELinux is in Permissive mode.") + } + while (attempt < maxRetries) { try { commands.forEach { Log.d("LEDControlApp", "Executing command: $it") } - val process = Runtime.getRuntime().exec("su") - process.outputStream.use { outputStream -> - DataOutputStream(outputStream).use { dataOutputStream -> - val batchCommands = commands.joinToString("\n") + "\nexit\n" - dataOutputStream.writeBytes(batchCommands) - dataOutputStream.flush() - } - } + // Use ProcessBuilder to execute the root commands + val process = ProcessBuilder("su").redirectErrorStream(true).start() + val outputStream = DataOutputStream(process.outputStream) + commands.forEach { outputStream.writeBytes("$it\n") } + outputStream.writeBytes("exit\n") + outputStream.flush() + outputStream.close() process.waitFor() // Show toast on the main thread if allowed if (showToast) { - Toast.makeText(context, "Command executed", Toast.LENGTH_SHORT).show() + mainHandler.post { + Toast.makeText(context, "Command executed successfully", Toast.LENGTH_SHORT).show() + } } return // Exit the method if the command was successfully executed } catch (e: IOException) { e.printStackTrace() if (showToast) { - Toast.makeText(context, "Network or IO error occurred. Please try again.", Toast.LENGTH_LONG).show() + mainHandler.post { + Toast.makeText(context, "IO error occurred", Toast.LENGTH_LONG).show() + } } } catch (e: SecurityException) { e.printStackTrace() if (showToast) { - Toast.makeText(context, "Permission denied. Please check app permissions.", Toast.LENGTH_LONG).show() + mainHandler.post { + Toast.makeText(context, "Permission denied", Toast.LENGTH_LONG).show() + } } - break // Exit loop for non-retryable error } catch (e: InterruptedException) { e.printStackTrace() if (showToast) { - Toast.makeText(context, "Operation interrupted. Please try again.", Toast.LENGTH_LONG).show() - } - } catch (e: Exception) { - e.printStackTrace() - if (showToast) { - Toast.makeText(context, "An unexpected error occurred.", Toast.LENGTH_LONG).show() + mainHandler.post { + Toast.makeText(context, "Operation interrupted", Toast.LENGTH_LONG).show() + } } - break // Exit loop for unexpected errors } // Retry mechanism with exponential backoff @@ -142,9 +181,13 @@ class LedController { } } - // If we've exhausted all retries, show toast on the main thread if allowed + // If we've exhausted all retries, show toast on the main thread if (showToast) { - Toast.makeText(context, "Retry Failed", Toast.LENGTH_LONG).show() + mainHandler.post { + Toast.makeText(context, "Retry failed", Toast.LENGTH_LONG).show() + } } } + + }