Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,7 @@ dependencies {

// Xposed Framework API dependencies
compileOnly("de.robv.android.xposed:api:82")

// RemotePreferences
implementation("com.crossbowffs.remotepreferences:remotepreferences:0.8")
}
15 changes: 12 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
android:icon="@mipmap/ic_launcher"
Expand All @@ -27,11 +28,19 @@
</intent-filter>
</activity>

<provider
android:name="ru.hepolise.volumekeytrackcontrol.provider.RemotePrefProvider"
android:authorities="${applicationId}"
android:directBootAware="true"
android:exported="true"
tools:ignore="ExportedContentProvider" />

<receiver
android:name="ru.hepolise.volumekeytrackcontrol.receiver.HookBroadcastReceiver"
android:exported="false">
android:name="ru.hepolise.volumekeytrackcontrol.receiver.BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="${applicationId}.HOOK_UPDATE" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,41 +54,47 @@ class VolumeControlModule : IXposedHookLoadPackage {
@Throws(Throwable::class)
override fun handleLoadPackage(lpparam: LoadPackageParam) {
log("handleLoadPackage: ${lpparam.packageName}")
if (lpparam.packageName != "android") {
return
with(lpparam) {
when (packageName) {
"android" -> classLoader.init()
}
}
init(lpparam.classLoader)
}

private fun init(classLoader: ClassLoader) {
private fun ClassLoader.init() {
initMethodSignatures.any { (params, logMessage) ->
tryHookInitMethod(classLoader, params, logMessage)
tryHookInitMethod(params, logMessage)
}.also { hooked ->
if (!hooked) {
log("Method hook failed for init!")
return
}
}

// https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-14.0.0_r18/services/core/java/com/android/server/policy/PhoneWindowManager.java#4117
XposedHelpers.findAndHookMethod(
CLASS_PHONE_WINDOW_MANAGER,
classLoader,
"interceptKeyBeforeQueueing",
KeyEvent::class.java,
Int::class.javaPrimitiveType,
handleInterceptKeyBeforeQueueing
)
try {
// https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-14.0.0_r18/services/core/java/com/android/server/policy/PhoneWindowManager.java#4117
XposedHelpers.findAndHookMethod(
CLASS_PHONE_WINDOW_MANAGER,
this,
"interceptKeyBeforeQueueing",
KeyEvent::class.java,
Int::class.javaPrimitiveType,
handleInterceptKeyBeforeQueueing
)
} catch (t: Throwable) {
log("Method hook failed for interceptKeyBeforeQueueing!")
t.message?.let { log(it) }
}

}

private fun tryHookInitMethod(
classLoader: ClassLoader,
private fun ClassLoader.tryHookInitMethod(
params: Array<Serializable>,
logMessage: String
): Boolean {
return try {
XposedHelpers.findAndHookMethod(
CLASS_PHONE_WINDOW_MANAGER, classLoader, "init",
CLASS_PHONE_WINDOW_MANAGER, this, "init",
*params, handleConstructPhoneWindowManager
)
log(logMessage)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package ru.hepolise.volumekeytrackcontrol.module

import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.hardware.display.DisplayManager
import android.media.AudioManager
Expand All @@ -15,20 +11,23 @@ import android.os.PowerManager
import android.os.Vibrator
import android.view.Display
import android.view.KeyEvent
import androidx.core.content.edit
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
import de.robv.android.xposed.XposedHelpers
import ru.hepolise.volumekeytrackcontrol.module.util.LogHelper
import ru.hepolise.volumekeytrackcontrol.receiver.HookBroadcastReceiver
import ru.hepolise.volumekeytrackcontrol.util.Constants
import ru.hepolise.volumekeytrackcontrol.module.util.RemotePrefsHelper
import ru.hepolise.volumekeytrackcontrol.module.util.StatusHelper
import ru.hepolise.volumekeytrackcontrol.util.AppFilterType
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.LAUNCHED_COUNT
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getAppFilterType
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getApps
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getLaunchedCount
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.getLongPressDuration
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.isSwapButtons
import ru.hepolise.volumekeytrackcontrol.util.VibratorUtil.getVibrator
import ru.hepolise.volumekeytrackcontrol.util.VibratorUtil.triggerVibration
import ru.hepolise.volumekeytrackcontrolmodule.BuildConfig


object VolumeKeyControlModuleHandlers {
Expand Down Expand Up @@ -82,15 +81,7 @@ object VolumeKeyControlModuleHandlers {
XposedHelpers.setAdditionalInstanceField(param.thisObject, event.field, runnable)
}

val filter = IntentFilter(Intent.ACTION_USER_UNLOCKED)
context.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(ctx: Context, intent: Intent) {
context.sendBroadcast {
putExtra(Constants.HOOKED, true)
}
ctx.unregisterReceiver(this)
}
}, filter)
StatusHelper.handleSuccessHook(context)
}
}

Expand Down Expand Up @@ -224,9 +215,9 @@ object VolumeKeyControlModuleHandlers {
val apps = prefs.getApps(filterType)
return mediaControllers?.find {
when (filterType) {
SharedPreferencesUtil.AppFilterType.DISABLED -> true
SharedPreferencesUtil.AppFilterType.WHITE_LIST -> it.packageName in apps
SharedPreferencesUtil.AppFilterType.BLACK_LIST -> it.packageName !in apps
AppFilterType.DISABLED -> true
AppFilterType.WHITE_LIST -> it.packageName in apps
AppFilterType.BLACK_LIST -> it.packageName !in apps
}
}?.also { log("Chosen media controller: ${it.packageName}") }
}
Expand Down Expand Up @@ -255,21 +246,6 @@ object VolumeKeyControlModuleHandlers {
}
}

private fun Context.sendBroadcast(block: Intent.() -> Unit) {
val modulePackage = BuildConfig.APPLICATION_ID

val intent = Intent().apply {
component = ComponentName(
modulePackage,
HookBroadcastReceiver::class.java.name
)
action = Constants.HOOK_UPDATE
block()
setPackage(modulePackage)
}
sendBroadcast(intent)
}

private fun MethodHookParam.delay(event: MediaEvent) {
val handler = getHandler()
handler.postDelayed(
Expand Down Expand Up @@ -311,8 +287,13 @@ object VolumeKeyControlModuleHandlers {
log("Sending ${this::class.simpleName}")
isLongPress = true
sendMediaButtonEventAndTriggerVibration(this)
context.sendBroadcast {
putExtra(Constants.INCREMENT_LAUNCH_COUNT, true)
runCatching {
RemotePrefsHelper.withRemotePrefs(context) {
val count = getLaunchedCount()
edit {
putInt(LAUNCHED_COUNT, count + 1)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ru.hepolise.volumekeytrackcontrol.module.util

import android.content.Context
import android.content.SharedPreferences
import com.crossbowffs.remotepreferences.RemotePreferences
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil
import ru.hepolise.volumekeytrackcontrolmodule.BuildConfig

object RemotePrefsHelper {
private fun log(text: String) = LogHelper.log(RemotePrefsHelper::class.java.simpleName, text)

fun withRemotePrefs(context: Context, block: SharedPreferences.() -> Unit) {
val prefs = RemotePreferences(
context,
BuildConfig.APPLICATION_ID,
SharedPreferencesUtil.STATUS_PREFS,
true
)
block.invoke(prefs)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ru.hepolise.volumekeytrackcontrol.module.util

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.core.content.edit
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil.LAST_INIT_HOOK_TIME
import ru.hepolise.volumekeytrackcontrol.util.StatusSysPropsHelper.dynamicKey
import ru.hepolise.volumekeytrackcontrol.util.SystemProps

object StatusHelper {
private fun log(text: String) = LogHelper.log(StatusHelper::class.java.simpleName, text)

fun handleSuccessHook(context: Context) {
val eventsLock = Any()
var bootReceived = false
var unlockReceived = false
var anySuccess = false
var remotePrefsSuccess = false

fun handleEvent(ctx: Context, action: String) {
synchronized(eventsLock) {
if (!remotePrefsSuccess) {
try {
RemotePrefsHelper.withRemotePrefs(ctx) {
edit {
putLong(LAST_INIT_HOOK_TIME, System.currentTimeMillis())
}
}
remotePrefsSuccess = true
log("Remote prefs updated successfully for $action")
} catch (t: Throwable) {
log("Remote preferences failed for $action (${t.message})")
}
}

when (action) {
Intent.ACTION_BOOT_COMPLETED -> bootReceived = true
Intent.ACTION_USER_UNLOCKED -> unlockReceived = true
}
anySuccess = anySuccess || remotePrefsSuccess

if (bootReceived && unlockReceived) {
if (!anySuccess) {
log("Neither event could connect to content-provider, writing to sysprops")
try {
SystemProps.set(dynamicKey(), "1")
} catch (t: Throwable) {
log("Failed to write to sysprops (${t.message})")
}
}
}
}
}

listOf(Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_USER_UNLOCKED).forEach { intentAction ->
val receiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context, intent: Intent) {
val action = intent.action.also { log("onReceive: $it") } ?: return
handleEvent(ctx, action)
ctx.unregisterReceiver(this)
}
}
context.registerReceiver(receiver, IntentFilter(intentAction))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.hepolise.volumekeytrackcontrol.provider

import com.crossbowffs.remotepreferences.RemotePreferenceFile
import com.crossbowffs.remotepreferences.RemotePreferenceProvider
import ru.hepolise.volumekeytrackcontrol.util.SharedPreferencesUtil
import ru.hepolise.volumekeytrackcontrolmodule.BuildConfig

class RemotePrefProvider : RemotePreferenceProvider(
BuildConfig.APPLICATION_ID,
arrayOf(
RemotePreferenceFile(
SharedPreferencesUtil.STATUS_PREFS,
true
)
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ru.hepolise.volumekeytrackcontrol.receiver

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import ru.hepolise.volumekeytrackcontrol.repository.BootRepository
import ru.hepolise.volumekeytrackcontrol.util.LSPosedLogger


class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
LSPosedLogger.log("Setting last boot completed time from receiver")
BootRepository.getBootRepository(context).setBootCompleted()
}
}
}

This file was deleted.

Loading