From ada4cf0dfd7d63397a9558826ed47b5739bb3868 Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Mon, 7 Apr 2025 15:21:45 +0200 Subject: [PATCH] Android: better approximation of button placement for freeform windows --- .../accessibility/AccessibilityApi.kt | 41 ++++++++++--------- .../accessibility/AccessibilityPlugin.kt | 5 ++- .../accessibility/AccessibilityService.kt | 9 +--- .../de/jonasbark/accessibility/Listener.kt | 4 +- accessibility/api.dart | 14 +++++-- accessibility/lib/accessibility.dart | 32 ++++++++++----- lib/utils/keymap/apps/my_whoosh.dart | 15 ++++++- lib/utils/keymap/apps/training_peaks.dart | 5 ++- 8 files changed, 79 insertions(+), 46 deletions(-) diff --git a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityApi.kt b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityApi.kt index a3faf10..0b33f48 100644 --- a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityApi.kt +++ b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityApi.kt @@ -4,12 +4,7 @@ import android.util.Log -import io.flutter.plugin.common.BasicMessageChannel -import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel -import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec -import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.common.* import java.io.ByteArrayOutputStream import java.nio.ByteBuffer @@ -61,23 +56,29 @@ enum class MediaAction(val raw: Int) { /** Generated class from Pigeon that represents data sent in messages. */ data class WindowEvent ( val packageName: String, - val windowHeight: Long, - val windowWidth: Long + val top: Long, + val bottom: Long, + val right: Long, + val left: Long ) { companion object { fun fromList(pigeonVar_list: List): WindowEvent { val packageName = pigeonVar_list[0] as String - val windowHeight = pigeonVar_list[1] as Long - val windowWidth = pigeonVar_list[2] as Long - return WindowEvent(packageName, windowHeight, windowWidth) + val top = pigeonVar_list[1] as Long + val bottom = pigeonVar_list[2] as Long + val right = pigeonVar_list[3] as Long + val left = pigeonVar_list[4] as Long + return WindowEvent(packageName, top, bottom, right, left) } } fun toList(): List { return listOf( packageName, - windowHeight, - windowWidth, + top, + bottom, + right, + left, ) } override fun equals(other: Any?): Boolean { @@ -88,8 +89,10 @@ data class WindowEvent ( return true } return packageName == other.packageName - && windowHeight == other.windowHeight - && windowWidth == other.windowWidth + && top == other.top + && bottom == other.bottom + && right == other.right + && left == other.left } override fun hashCode(): Int = toList().hashCode() @@ -232,9 +235,9 @@ private class AccessibilityApiPigeonStreamHandler( } interface AccessibilityApiPigeonEventChannelWrapper { - open fun onListen(p0: Any?, sink: PigeonEventSink) {} + fun onListen(p0: Any?, sink: PigeonEventSink) {} - open fun onCancel(p0: Any?) {} + fun onCancel(p0: Any?) {} } class PigeonEventSink(private val sink: EventChannel.EventSink) { @@ -250,7 +253,7 @@ class PigeonEventSink(private val sink: EventChannel.EventSink) { sink.endOfStream() } } - + abstract class StreamEventsStreamHandler : AccessibilityApiPigeonEventChannelWrapper { companion object { fun register(messenger: BinaryMessenger, streamHandler: StreamEventsStreamHandler, instanceName: String = "") { @@ -263,4 +266,4 @@ abstract class StreamEventsStreamHandler : AccessibilityApiPigeonEventChannelWra } } } - + diff --git a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityPlugin.kt b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityPlugin.kt index 5eca6d5..53eec18 100644 --- a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityPlugin.kt +++ b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityPlugin.kt @@ -7,6 +7,7 @@ import StreamEventsStreamHandler import WindowEvent import android.content.Context import android.content.Intent +import android.graphics.Rect import android.os.Bundle import android.provider.Settings import androidx.core.content.ContextCompat.startActivity @@ -84,8 +85,8 @@ class EventListener : StreamEventsStreamHandler(), Receiver { eventSink = null } - override fun onChange(packageName: String, windowWidth: Int, windowHeight: Int) { - eventSink?.success(WindowEvent(packageName = packageName, windowWidth = windowWidth.toLong(), windowHeight = windowHeight.toLong())) + override fun onChange(packageName: String, window: Rect) { + eventSink?.success(WindowEvent(packageName = packageName, right = window.right.toLong(), left = window.left.toLong(), bottom = window.bottom.toLong(), top = window.top.toLong())) } } diff --git a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityService.kt b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityService.kt index 9ca85d6..72425c3 100644 --- a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityService.kt +++ b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/AccessibilityService.kt @@ -5,7 +5,6 @@ import android.accessibilityservice.GestureDescription import android.accessibilityservice.GestureDescription.StrokeDescription import android.graphics.Path import android.graphics.Rect -import android.os.Build import android.util.Log import android.view.ViewConfiguration import android.view.accessibility.AccessibilityEvent @@ -37,16 +36,12 @@ class AccessibilityService : AccessibilityService(), Listener { } val currentPackageName = event.packageName.toString() val windowSize = getWindowSize() - Observable.fromService?.onChange(packageName = currentPackageName, windowHeight = windowSize.bottom, windowWidth = windowSize.right) + Observable.fromService?.onChange(packageName = currentPackageName, window = windowSize) } private fun getWindowSize(): Rect { val outBounds = Rect() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - rootInActiveWindow.getBoundsInWindow(outBounds) - } else { - rootInActiveWindow.getBoundsInScreen(outBounds) - } + rootInActiveWindow.getBoundsInScreen(outBounds) return outBounds } diff --git a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/Listener.kt b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/Listener.kt index 95ce4b0..db5b439 100644 --- a/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/Listener.kt +++ b/accessibility/android/src/main/kotlin/de/jonasbark/accessibility/Listener.kt @@ -1,5 +1,7 @@ package de.jonasbark.accessibility +import android.graphics.Rect + object Observable { var toService: Listener? = null var fromService: Receiver? = null @@ -10,5 +12,5 @@ interface Listener { } interface Receiver { - fun onChange(packageName: String, windowWidth: Int, windowHeight: Int) + fun onChange(packageName: String, window: Rect) } \ No newline at end of file diff --git a/accessibility/api.dart b/accessibility/api.dart index 1fb1454..2d55b71 100644 --- a/accessibility/api.dart +++ b/accessibility/api.dart @@ -15,10 +15,18 @@ enum MediaAction { playPause, next, volumeUp, volumeDown } class WindowEvent { final String packageName; - final int windowHeight; - final int windowWidth; + final int top; + final int bottom; + final int right; + final int left; - WindowEvent({required this.packageName, required this.windowHeight, required this.windowWidth}); + WindowEvent({ + required this.packageName, + required this.left, + required this.right, + required this.top, + required this.bottom, + }); } @EventChannelApi() diff --git a/accessibility/lib/accessibility.dart b/accessibility/lib/accessibility.dart index aa0f034..e9df535 100644 --- a/accessibility/lib/accessibility.dart +++ b/accessibility/lib/accessibility.dart @@ -25,21 +25,29 @@ enum MediaAction { class WindowEvent { WindowEvent({ required this.packageName, - required this.windowHeight, - required this.windowWidth, + required this.top, + required this.bottom, + required this.right, + required this.left, }); String packageName; - int windowHeight; + int top; - int windowWidth; + int bottom; + + int right; + + int left; List _toList() { return [ packageName, - windowHeight, - windowWidth, + top, + bottom, + right, + left, ]; } @@ -50,8 +58,10 @@ class WindowEvent { result as List; return WindowEvent( packageName: result[0]! as String, - windowHeight: result[1]! as int, - windowWidth: result[2]! as int, + top: result[1]! as int, + bottom: result[2]! as int, + right: result[3]! as int, + left: result[4]! as int, ); } @@ -66,8 +76,10 @@ class WindowEvent { } return packageName == other.packageName - && windowHeight == other.windowHeight - && windowWidth == other.windowWidth; + && top == other.top + && bottom == other.bottom + && right == other.right + && left == other.left; } @override diff --git a/lib/utils/keymap/apps/my_whoosh.dart b/lib/utils/keymap/apps/my_whoosh.dart index d407ef4..d6c2498 100644 --- a/lib/utils/keymap/apps/my_whoosh.dart +++ b/lib/utils/keymap/apps/my_whoosh.dart @@ -53,9 +53,20 @@ class MyWhoosh extends SupportedApp { throw SingleLineException("Window size not known - open $this first"); } return switch (action.action) { - InGameAction.shiftUp => Offset(windowInfo.windowWidth * 0.98, windowInfo.windowHeight * 0.94), - InGameAction.shiftDown => Offset(windowInfo.windowWidth * 0.80, windowInfo.windowHeight * 0.94), + InGameAction.shiftUp => Offset( + windowInfo.right - windowInfo.width * 0.02, + windowInfo.bottom - windowInfo.height * 0.06, + ), + InGameAction.shiftDown => Offset( + windowInfo.right - windowInfo.width * 0.20, + windowInfo.bottom - windowInfo.height * 0.06, + ), _ => throw SingleLineException("Unsupported action for MyWhoosh: $action"), }; } } + +extension WindowSize on WindowEvent { + int get width => right - left; + int get height => bottom - top; +} diff --git a/lib/utils/keymap/apps/training_peaks.dart b/lib/utils/keymap/apps/training_peaks.dart index 86228e3..d40eb8c 100644 --- a/lib/utils/keymap/apps/training_peaks.dart +++ b/lib/utils/keymap/apps/training_peaks.dart @@ -1,6 +1,7 @@ import 'package:accessibility/accessibility.dart'; import 'package:dartx/dartx.dart'; import 'package:flutter/services.dart'; +import 'package:swift_control/utils/keymap/apps/my_whoosh.dart'; import 'package:swift_control/utils/keymap/apps/supported_app.dart'; import 'package:swift_control/utils/keymap/buttons.dart'; import 'package:swift_control/utils/single_line_exception.dart'; @@ -64,8 +65,8 @@ class TrainingPeaks extends SupportedApp { throw SingleLineException("Window size not known - open $this first"); } return switch (action.action) { - InGameAction.shiftUp => Offset(windowInfo.windowWidth / 2 * 1.32, windowInfo.windowHeight * 0.74), - InGameAction.shiftDown => Offset(windowInfo.windowWidth / 2 * 1.15, windowInfo.windowHeight * 0.74), + InGameAction.shiftUp => Offset(windowInfo.width / 2 * 1.32, windowInfo.height * 0.74), + InGameAction.shiftDown => Offset(windowInfo.width / 2 * 1.15, windowInfo.height * 0.74), _ => throw SingleLineException("Unsupported action for IndieVelo: $action"), }; }