Android: better approximation of button placement for freeform windows

This commit is contained in:
Jonas Bark
2025-04-07 15:21:45 +02:00
parent aff1137c3d
commit ada4cf0dfd
8 changed files with 79 additions and 46 deletions

View File

@@ -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<Any?>): 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<Any?> {
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<T>(
}
interface AccessibilityApiPigeonEventChannelWrapper<T> {
open fun onListen(p0: Any?, sink: PigeonEventSink<T>) {}
fun onListen(p0: Any?, sink: PigeonEventSink<T>) {}
open fun onCancel(p0: Any?) {}
fun onCancel(p0: Any?) {}
}
class PigeonEventSink<T>(private val sink: EventChannel.EventSink) {
@@ -250,7 +253,7 @@ class PigeonEventSink<T>(private val sink: EventChannel.EventSink) {
sink.endOfStream()
}
}
abstract class StreamEventsStreamHandler : AccessibilityApiPigeonEventChannelWrapper<WindowEvent> {
companion object {
fun register(messenger: BinaryMessenger, streamHandler: StreamEventsStreamHandler, instanceName: String = "") {
@@ -263,4 +266,4 @@ abstract class StreamEventsStreamHandler : AccessibilityApiPigeonEventChannelWra
}
}
}

View File

@@ -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()))
}
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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<Object?> _toList() {
return <Object?>[
packageName,
windowHeight,
windowWidth,
top,
bottom,
right,
left,
];
}
@@ -50,8 +58,10 @@ class WindowEvent {
result as List<Object?>;
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

View File

@@ -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;
}

View File

@@ -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"),
};
}