From d71a6b2f35b3dcdebd50f0e604b76c6bc7e86eb7 Mon Sep 17 00:00:00 2001 From: paphonb Date: Tue, 3 Jul 2018 21:18:26 +0700 Subject: [PATCH] Rewrite sleep root gesture handler It now runs on its own thread and keeps old su session for later use. --- .../lawnchair/LawnchairPreferences.kt | 23 +++++- .../lawnchair/gestures/GestureController.kt | 2 +- .../lawnchair/gestures/GestureHandler.kt | 4 + .../gestures/handlers/SleepGestureHandler.kt | 81 ++++++++++++++++--- 4 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/ch/deletescape/lawnchair/LawnchairPreferences.kt b/src/ch/deletescape/lawnchair/LawnchairPreferences.kt index d2274b6e2e..4ed153ea79 100644 --- a/src/ch/deletescape/lawnchair/LawnchairPreferences.kt +++ b/src/ch/deletescape/lawnchair/LawnchairPreferences.kt @@ -302,13 +302,19 @@ class LawnchairPreferences(val context: Context) : SharedPreferences.OnSharedPre } open inner class StringBasedPref(key: String, defaultValue: T, onChange: () -> Unit = doNothing, - private val fromString: (String) -> T, private val toString: (T) -> String) : + private val fromString: (String) -> T, + private val toString: (T) -> String, + private val dispose: (T) -> Unit) : PrefDelegate(key, defaultValue, onChange) { override fun onGetValue(): T = sharedPrefs.getString(getKey(), null)?.run(fromString) ?: defaultValue override fun onSetValue(value: T) { edit { putString(getKey(), toString(value)) } } + + override fun disposeOldValue(oldValue: T) { + dispose(oldValue) + } } open inner class StringPref(key: String, defaultValue: String = "", onChange: () -> Unit = doNothing) : @@ -442,7 +448,7 @@ class LawnchairPreferences(val context: Context) : SharedPreferences.OnSharedPre } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - cached = false + discardCachedValue() onSetValue(value) } @@ -461,9 +467,20 @@ class LawnchairPreferences(val context: Context) : SharedPreferences.OnSharedPre internal fun getKey() = key private fun onValueChanged() { - cached = false + discardCachedValue() onChange.invoke() } + + private fun discardCachedValue() { + if (cached) { + cached = false + value.let(::disposeOldValue) + } + } + + open fun disposeOldValue(oldValue: T) { + + } } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String) { diff --git a/src/ch/deletescape/lawnchair/gestures/GestureController.kt b/src/ch/deletescape/lawnchair/gestures/GestureController.kt index bf715b98fd..603d9824ce 100644 --- a/src/ch/deletescape/lawnchair/gestures/GestureController.kt +++ b/src/ch/deletescape/lawnchair/gestures/GestureController.kt @@ -46,7 +46,7 @@ class GestureController(val launcher: LawnchairLauncher) : TouchController { } fun createHandlerPref(key: String, defaultValue: GestureHandler = blankGestureHandler) = prefs.StringBasedPref( - key, defaultValue, prefs.doNothing, ::createGestureHandler, GestureHandler::toString) + key, defaultValue, prefs.doNothing, ::createGestureHandler, GestureHandler::toString, GestureHandler::onDestroy) private fun createGestureHandler(jsonString: String) = createGestureHandler(launcher, jsonString, blankGestureHandler) diff --git a/src/ch/deletescape/lawnchair/gestures/GestureHandler.kt b/src/ch/deletescape/lawnchair/gestures/GestureHandler.kt index dc50de1608..c830e01dba 100644 --- a/src/ch/deletescape/lawnchair/gestures/GestureHandler.kt +++ b/src/ch/deletescape/lawnchair/gestures/GestureHandler.kt @@ -21,6 +21,10 @@ abstract class GestureHandler(val context: Context, val config: JSONObject?) { } + open fun onDestroy() { + + } + override fun toString(): String { return JSONObject().apply { put("class", this@GestureHandler::class.java.name) diff --git a/src/ch/deletescape/lawnchair/gestures/handlers/SleepGestureHandler.kt b/src/ch/deletescape/lawnchair/gestures/handlers/SleepGestureHandler.kt index 863e772ade..dcc858f1d0 100644 --- a/src/ch/deletescape/lawnchair/gestures/handlers/SleepGestureHandler.kt +++ b/src/ch/deletescape/lawnchair/gestures/handlers/SleepGestureHandler.kt @@ -3,9 +3,10 @@ package ch.deletescape.lawnchair.gestures.handlers import android.content.Context import android.content.Intent import android.net.Uri +import android.os.Handler +import android.os.HandlerThread import android.provider.Settings import android.support.annotation.Keep -import android.view.KeyEvent import ch.deletescape.lawnchair.gestures.GestureController import ch.deletescape.lawnchair.gestures.GestureHandler import com.android.launcher3.R @@ -14,23 +15,79 @@ import org.json.JSONObject import java.io.DataOutputStream import java.io.IOException +private val suThread = HandlerThread("su").apply { start() } +private val suHandler = Handler(suThread.looper) + @Keep class SleepGestureHandlerRoot(context: Context, config: JSONObject?) : GestureHandler(context, config) { override val displayName = context.getString(R.string.action_sleep_root)!! + private var currentSession: Session? = null + private val destroy = Runnable { currentSession?.destroy() } + private val sleep = Runnable { + getSuSession().run { + write("sendevent /dev/input/event1 1 116 1\n") + write("sendevent /dev/input/event1 0 0 0\n") + write("sendevent /dev/input/event1 1 116 0\n") + write("sendevent /dev/input/event1 0 0 0\n") + } + } + + private fun getSuSession(): Session { + if (currentSession == null || !currentSession!!.isAlive) { + currentSession = Session() + } + return currentSession!! + } + + override fun onDestroy() { + super.onDestroy() + suHandler.post(destroy) + } + override fun onGestureTrigger(controller: GestureController) { - try { - val p = Runtime.getRuntime().exec("su") - val outputStream = DataOutputStream(p.outputStream) - outputStream.writeBytes("input keyevent ${KeyEvent.KEYCODE_POWER}\n") - outputStream.writeBytes("exit\n") - outputStream.flush() - outputStream.close() - p.waitFor() - p.destroy() - } catch (e: IOException) { - } catch (e: InterruptedException) { + suHandler.post(sleep) + } + + class Session { + + private val process = Runtime.getRuntime().exec("su")!! + private val outputStream = DataOutputStream(process.outputStream) + + val isAlive get() = isAlive(process) + + fun write(string: String) { + try { + outputStream.writeBytes(string) + outputStream.flush() + } catch (e: IOException) { + } catch (e: InterruptedException) { + } + } + + fun destroy() { + try { + if (isAlive) { + outputStream.writeBytes("exit\n") + outputStream.flush() + outputStream.close() + } + process.waitFor() + process.destroy() + } catch (e: IOException) { + } catch (e: InterruptedException) { + } + } + + private fun isAlive(process: Process?): Boolean { + if (process == null) return false + return try { + process.exitValue() + false + } catch (e: IllegalThreadStateException) { + true + } } } }