Implement percentage-based keymap storage for better device compatibility

Co-authored-by: jonasbark <1151304+jonasbark@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-10-09 19:30:34 +00:00
parent d26e937066
commit 45112ccfcf
4 changed files with 84 additions and 13 deletions

View File

@@ -31,11 +31,21 @@ class _DevicePageState extends State<DevicePage> {
List<SupportedApp> _getAllApps() {
final baseApps = SupportedApp.supportedApps.where((app) => app is! CustomApp).toList();
final customProfiles = settings.getCustomAppProfiles();
// Get screen size for percentage-based decoding
Size? screenSize;
try {
final view = WidgetsBinding.instance.platformDispatcher.views.first;
screenSize = view.physicalSize / view.devicePixelRatio;
} catch (e) {
screenSize = MediaQuery.maybeOf(context)?.size;
}
final customApps = customProfiles.map((profile) {
final customApp = CustomApp(profileName: profile);
final savedKeymap = settings.getCustomAppKeymap(profile);
if (savedKeymap != null) {
customApp.decodeKeymap(savedKeymap);
customApp.decodeKeymap(savedKeymap, screenSize: screenSize);
}
return customApp;
}).toList();
@@ -227,7 +237,14 @@ ${it.firmwareVersion != null ? ' - Firmware Version: ${it.firmwareVersion}' : ''
final customApp = CustomApp(profileName: newName);
final savedKeymap = settings.getCustomAppKeymap(newName);
if (savedKeymap != null) {
customApp.decodeKeymap(savedKeymap);
Size? screenSize;
try {
final view = WidgetsBinding.instance.platformDispatcher.views.first;
screenSize = view.physicalSize / view.devicePixelRatio;
} catch (e) {
screenSize = MediaQuery.maybeOf(context)?.size;
}
customApp.decodeKeymap(savedKeymap, screenSize: screenSize);
}
actionHandler.supportedApp = customApp;
await settings.setApp(customApp);
@@ -241,7 +258,14 @@ ${it.firmwareVersion != null ? ' - Firmware Version: ${it.firmwareVersion}' : ''
final customApp = CustomApp(profileName: newName);
final savedKeymap = settings.getCustomAppKeymap(newName);
if (savedKeymap != null) {
customApp.decodeKeymap(savedKeymap);
Size? screenSize;
try {
final view = WidgetsBinding.instance.platformDispatcher.views.first;
screenSize = view.physicalSize / view.devicePixelRatio;
} catch (e) {
screenSize = MediaQuery.maybeOf(context)?.size;
}
customApp.decodeKeymap(savedKeymap, screenSize: screenSize);
}
actionHandler.supportedApp = customApp;
await settings.setApp(customApp);

View File

@@ -22,19 +22,19 @@ class CustomApp extends SupportedApp {
return keyPair.touchPosition;
}
List<String> encodeKeymap() {
List<String> encodeKeymap({Size? screenSize}) {
// encode to save in preferences
return keymap.keyPairs.map((e) => e.encode()).toList();
return keymap.keyPairs.map((e) => e.encode(screenSize: screenSize)).toList();
}
void decodeKeymap(List<String> data) {
void decodeKeymap(List<String> data, {Size? screenSize}) {
// decode from preferences
if (data.isEmpty) {
return;
}
final keyPairs = data.map((e) => KeyPair.decode(e)).whereNotNull().toList();
final keyPairs = data.map((e) => KeyPair.decode(e, screenSize: screenSize)).whereNotNull().toList();
if (keyPairs.isEmpty) {
return;
}

View File

@@ -73,23 +73,52 @@ class KeyPair {
};
}
String encode() {
String encode({Size? screenSize}) {
// encode to save in preferences
// If screenSize is provided, store as percentages for better compatibility across devices
final touchPosData = screenSize != null && touchPosition != Offset.zero
? {
'x_percent': touchPosition.dx / screenSize.width,
'y_percent': touchPosition.dy / screenSize.height,
}
: {'x': touchPosition.dx, 'y': touchPosition.dy};
return jsonEncode({
'actions': buttons.map((e) => e.name).toList(),
'logicalKey': logicalKey?.keyId.toString() ?? '0',
'physicalKey': physicalKey?.usbHidUsage.toString() ?? '0',
'touchPosition': {'x': touchPosition.dx, 'y': touchPosition.dy},
'touchPosition': touchPosData,
'isLongPress': isLongPress,
});
}
static KeyPair? decode(String data) {
static KeyPair? decode(String data, {Size? screenSize}) {
// decode from preferences
final decoded = jsonDecode(data);
if (decoded['actions'] == null || decoded['logicalKey'] == null || decoded['physicalKey'] == null) {
return null;
}
// Support both percentage-based (new) and pixel-based (old) formats
final touchPosData = decoded['touchPosition'];
final Offset touchPosition;
if (touchPosData.containsKey('x_percent') && touchPosData.containsKey('y_percent')) {
// New percentage-based format
if (screenSize != null) {
touchPosition = Offset(
touchPosData['x_percent'] * screenSize.width,
touchPosData['y_percent'] * screenSize.height,
);
} else {
// Fallback if no screen size provided
touchPosition = Offset.zero;
}
} else {
// Old pixel-based format
touchPosition = Offset(touchPosData['x'], touchPosData['y']);
}
return KeyPair(
buttons:
decoded['actions']
@@ -98,7 +127,7 @@ class KeyPair {
logicalKey: int.parse(decoded['logicalKey']) != 0 ? LogicalKeyboardKey(int.parse(decoded['logicalKey'])) : null,
physicalKey:
int.parse(decoded['physicalKey']) != 0 ? PhysicalKeyboardKey(int.parse(decoded['physicalKey'])) : null,
touchPosition: Offset(decoded['touchPosition']['x'], decoded['touchPosition']['y']),
touchPosition: touchPosition,
isLongPress: decoded['isLongPress'] ?? false,
);
}

View File

@@ -1,4 +1,5 @@
import 'package:dartx/dartx.dart';
import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:swift_control/utils/keymap/apps/supported_app.dart';
@@ -31,7 +32,15 @@ class Settings {
final customApp = CustomApp(profileName: appName);
final appSetting = _prefs.getStringList('customapp_$appName');
if (appSetting != null) {
customApp.decodeKeymap(appSetting);
// Get screen size for percentage-based decoding
Size? screenSize;
try {
final view = WidgetsBinding.instance.platformDispatcher.views.first;
screenSize = view.physicalSize / view.devicePixelRatio;
} catch (e) {
screenSize = null;
}
customApp.decodeKeymap(appSetting, screenSize: screenSize);
}
actionHandler.init(customApp);
} else {
@@ -52,7 +61,16 @@ class Settings {
Future<void> setApp(SupportedApp app) async {
if (app is CustomApp) {
await _prefs.setStringList('customapp_${app.profileName}', app.encodeKeymap());
// Get screen size for percentage-based encoding
Size? screenSize;
try {
final view = WidgetsBinding.instance.platformDispatcher.views.first;
screenSize = view.physicalSize / view.devicePixelRatio;
} catch (e) {
// Fallback if screen size is not available
screenSize = null;
}
await _prefs.setStringList('customapp_${app.profileName}', app.encodeKeymap(screenSize: screenSize));
}
await _prefs.setString('app', app.name);
}