Skip to content

Commit

Permalink
Feat: Add support for two additional LEDs (led:torch_2 and led:torch_…
Browse files Browse the repository at this point in the history
…3) (#39)

* Fix selinux

* Control secondary flash LEDs in light and brightness cycles

This change modifies the light and brightness cycle functionality in the ExperimentalActivity to control both the primary and secondary flash LEDs.

The `controlLeds` function is updated to include brightness values for the secondary LEDs (WHITE2_LED_PATH, YELLOW2_LED_PATH), ensuring both primary and secondary LEDs are controlled during the cycles.

* Bump version to 1.3.2

- Update `versionCode` from 28 to 29.
- Update `versionName` from 1.3.1 to 1.3.2.
  • Loading branch information
Bartixxx32 authored Jan 24, 2025
1 parent 44571a5 commit 275d946
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 55 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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() {
Expand All @@ -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) {
Expand All @@ -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() {
Expand Down
139 changes: 91 additions & 48 deletions app/src/main/java/com/bartixxx/opflashcontrol/LEDControlUtil.kt
Original file line number Diff line number Diff line change
@@ -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")
}

Expand All @@ -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,
Expand All @@ -46,14 +46,7 @@ class LedController {
val commands = mutableListOf<String>()

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))
}
Expand All @@ -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<String> {
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<String>()

// 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<String> {
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<String>()

// 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<String>, showToast: Boolean = true) {

protected fun executeRootCommands(commands: List<String>, 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
Expand All @@ -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()
}
}
}


}

0 comments on commit 275d946

Please sign in to comment.