#2: Добавлено отслеживение длины нажатий кнопки. Это нужно для отключения самбы и размонтирования флешки. (спустя два часа страданий)

This commit is contained in:
2026-04-15 10:35:59 +05:00
parent a655f026b4
commit e622c95666
2 changed files with 106 additions and 24 deletions

View File

@@ -7,11 +7,11 @@ import java.io.BufferedReader
import java.io.InputStreamReader import java.io.InputStreamReader
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
// Событие клика по кнопке
class ButtonClickEvent class ButtonClickEvent
class ButtonLongPressEvent
// Слушатель событий клика
typealias ButtonClickListener = (ButtonClickEvent) -> Unit typealias ButtonClickListener = (ButtonClickEvent) -> Unit
typealias ButtonLongPressListener = (ButtonLongPressEvent) -> Unit
class ButtonListener( class ButtonListener(
private var process: Process? = null, private var process: Process? = null,
@@ -19,63 +19,128 @@ class ButtonListener(
private var isListening: AtomicBoolean = AtomicBoolean(false), private var isListening: AtomicBoolean = AtomicBoolean(false),
private var listenerJob: Job? = null private var listenerJob: Job? = null
) { ) {
private val clickListeners = mutableListOf<ButtonClickListener>() private val clickListeners = mutableListOf<ButtonClickListener>()
private val longPressListeners = mutableListOf<ButtonLongPressListener>()
var shortPressThresholdMs: Long = 100
var longPressThresholdMs: Long = 1000
private var pressStartTime: Long = 0
fun addClickListener(listener: ButtonClickListener) { fun addClickListener(listener: ButtonClickListener) {
clickListeners.add(listener) clickListeners.add(listener)
} }
fun removeClickListener(listener: ButtonClickListener) {
clickListeners.remove(listener)
}
fun addLongPressListener(listener: ButtonLongPressListener) {
longPressListeners.add(listener)
}
fun removeLongPressListener(listener: ButtonLongPressListener) {
longPressListeners.remove(listener)
}
private fun notifyClick() { private fun notifyClick() {
println("=== [CLICK] Короткое нажатие ===")
val event = ButtonClickEvent() val event = ButtonClickEvent()
clickListeners.forEach { it.invoke(event) } clickListeners.forEach { it.invoke(event) }
} }
private fun notifyLongPress() {
println("=== [LONG] Длинное нажатие ===")
val event = ButtonLongPressEvent()
longPressListeners.forEach { it.invoke(event) }
}
fun startListening(onStateChange: ((Boolean) -> Unit)? = null): Boolean { fun startListening(onStateChange: ((Boolean) -> Unit)? = null): Boolean {
if (isListening.get()) { if (isListening.get()) {
return currentButtonState.get() return currentButtonState.get()
} }
startListeningInternal(onStateChange)
return currentButtonState.get()
}
private fun startListeningInternal(onStateChange: ((Boolean) -> Unit)? = null) {
try { try {
val cmd = listOf("gpiomon", "--chip=0", "$BUTTON") val cmd = listOf("gpiomon", "--chip=0", "$BUTTON")
val processBuilder = ProcessBuilder(cmd) val processBuilder = ProcessBuilder(cmd)
.redirectErrorStream(true) .redirectErrorStream(true)
// Сохраняем процесс в поле
process = processBuilder.start() process = processBuilder.start()
isListening.set(true) isListening.set(true)
listenerJob = CoroutineScope(Dispatchers.IO).launch { listenerJob = CoroutineScope(Dispatchers.IO).launch {
// Используем process, который теперь не null
val reader = BufferedReader(InputStreamReader(process!!.inputStream)) val reader = BufferedReader(InputStreamReader(process!!.inputStream))
var line: String? var line: String?
while (reader.readLine().also { line = it } != null) { try {
println(if (isDebug) "$line" else "") while (reader.readLine().also { line = it } != null) {
when { println(if (isDebug) "$line" else "")
line?.contains("rising") == true -> { when {
currentButtonState.set(true) line?.contains("rising") == true -> {
onStateChange?.invoke(true) currentButtonState.set(true)
println(if (isDebug) "Кнопка нажата" else "") onStateChange?.invoke(true)
// Генерируем событие клика при нажатии pressStartTime = System.currentTimeMillis()
notifyClick() println(if (isDebug) "Кнопка нажата, время: $pressStartTime" else "")
} }
line?.contains("falling") == true -> { line?.contains("falling") == true -> {
currentButtonState.set(false) val pressDuration = System.currentTimeMillis() - pressStartTime
onStateChange?.invoke(false) currentButtonState.set(false)
println(if (isDebug) "Кнопка разжата" else "") onStateChange?.invoke(false)
// Убрана проверка на разжатие для генерации события println(if (isDebug) "Кнопка отпущена, длительность: ${pressDuration}ms" else "")
// Определяем тип нажатия по длительности
when {
pressDuration >= longPressThresholdMs -> {
println(if (isDebug) "Длинное нажатие (${pressDuration}ms)" else "")
notifyLongPress()
}
pressDuration >= shortPressThresholdMs -> {
println(if (isDebug) "Короткое нажатие (${pressDuration}ms)" else "")
notifyClick()
}
else -> {
println(if (isDebug) "Слишком короткое нажатие (${pressDuration}ms) - игнорируем" else "")
}
}
}
} }
} }
} catch (e: Exception) {
println("Ошибка чтения процесса: ${e.message}")
} }
val exitCode = process!!.waitFor() val exitCode = process!!.waitFor()
println(if (isDebug) "Процесс завершён ($exitCode)" else "") println("Процесс завершён ($exitCode)")
isListening.set(false) isListening.set(false)
// Автоматически перезапускаем прослушивание, если оно должно быть активно
if (isListening.get()) {
println("Перезапуск прослушивания...")
delay(100)
startListeningInternal(onStateChange)
}
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
isListening.set(false) isListening.set(false)
} }
return currentButtonState.get() }
fun stopListening() {
isListening.set(false)
listenerJob?.cancel()
process?.destroy()
process = null
}
fun restartListening(onStateChange: ((Boolean) -> Unit)? = null) {
stopListening()
Thread.sleep(200)
startListening(onStateChange)
} }
} }

View File

@@ -27,7 +27,7 @@ object DebugMode {
// Состояния системы // Состояния системы
enum class SystemState { enum class SystemState {
IDLE, SCANNING, CLEAN, INFECTED, ERROR, INIT IDLE, SCANNING, CLEAN, INFECTED, ERROR, INIT, SAMBA
} }
class USBVirusScanner { class USBVirusScanner {
@@ -159,6 +159,11 @@ class USBVirusScanner {
yellowLed.setValue(0) yellowLed.setValue(0)
blinkLed(redLed, 500) blinkLed(redLed, 500)
} }
SystemState.SAMBA -> {
blinkLed(greenLed, 1000)
blinkLed(yellowLed, 1000)
redLed.setValue(0)
}
} }
} }
} }
@@ -316,7 +321,7 @@ class USBVirusScanner {
try { try {
// Проверяем установлен ли ClamAV // Проверяем установлен ли ClamAV
val checkProcess = ProcessBuilder("which", "clamscan").start() val checkProcess = ProcessBuilder("which", "clamdscan").start()
val checkExitCode = checkProcess.waitFor() val checkExitCode = checkProcess.waitFor()
if (checkExitCode != 0) { if (checkExitCode != 0) {
@@ -538,9 +543,20 @@ fun main(args: Array<String>) {
scanner.initialize() scanner.initialize()
val buttonListener = ButtonListener() val buttonListener = ButtonListener()
buttonListener.shortPressThresholdMs = 100
buttonListener.longPressThresholdMs = 1000
buttonListener.addClickListener { event -> buttonListener.addClickListener { event ->
scanner.startScan() scanner.startScan()
} }
buttonListener.addLongPressListener { event ->
println(if (isDebug) "ДЛИННОЕ НАЖАТИЕ МЯУ" else "")
println("TODO(\"Добавить отключение samba и размонтирование флешки\")")
}
buttonListener.startListening() buttonListener.startListening()
println(if (isDebug) """ println(if (isDebug) """
@@ -564,4 +580,5 @@ fun main(args: Array<String>) {
scanner.shutdown() scanner.shutdown()
exitProcess(1) exitProcess(1)
} }
} }