mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-17 16:07:41 +01:00
Di2: resolve issue #233
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
### 4.8.0 (15-02-2026)
|
||||
|
||||
**Features**:
|
||||
- Bluetooth media buttons are now supported on iOS
|
||||
- Shimano Di2: long press and double clicks are now supported:
|
||||
- perform steering using long presses
|
||||
- gear changes are now reflected properly without losing any button presses
|
||||
|
||||
### 4.7.0 (04-02-2026)
|
||||
|
||||
**Features**:
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'package:bike_control/utils/core.dart';
|
||||
import 'package:bike_control/utils/keymap/buttons.dart';
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:prop/emulators/constants.dart';
|
||||
import 'package:prop/emulators/shared.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
import '../bluetooth_device.dart';
|
||||
@@ -25,7 +27,7 @@ class ShimanoDi2 extends BluetoothDevice {
|
||||
await UniversalBle.subscribeIndications(device.deviceId, service.uuid, characteristic.uuid);
|
||||
}
|
||||
|
||||
final _lastButtons = <int, int>{};
|
||||
final _lastButtons = <int, ({int value, _Di2State type})>{};
|
||||
bool _isInitialized = false;
|
||||
|
||||
@override
|
||||
@@ -33,6 +35,7 @@ class ShimanoDi2 extends BluetoothDevice {
|
||||
|
||||
@override
|
||||
Future<void> processCharacteristic(String characteristic, Uint8List bytes) async {
|
||||
Logger.info('Received data from $characteristic: ${bytesToReadableHex(bytes)}');
|
||||
if (characteristic.toLowerCase() == ShimanoDi2Constants.D_FLY_CHANNEL_UUID) {
|
||||
final channels = bytes.sublist(1);
|
||||
|
||||
@@ -40,7 +43,7 @@ class ShimanoDi2 extends BluetoothDevice {
|
||||
if (!_isInitialized) {
|
||||
channels.forEachIndexed((int value, int index) {
|
||||
final readableIndex = index + 1;
|
||||
_lastButtons[index] = value;
|
||||
_lastButtons[index] = (value: value, type: _Di2State.released);
|
||||
|
||||
getOrAddButton(
|
||||
'D-Fly Channel $readableIndex',
|
||||
@@ -51,28 +54,61 @@ class ShimanoDi2 extends BluetoothDevice {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
final clickedButtons = <ControllerButton>[];
|
||||
|
||||
var actualChange = false;
|
||||
channels.forEachIndexed((int value, int index) {
|
||||
final didChange = _lastButtons[index] != value;
|
||||
_lastButtons[index] = value;
|
||||
final didChange = _lastButtons[index]?.value != value;
|
||||
|
||||
final readableIndex = index + 1;
|
||||
|
||||
final button = getOrAddButton(
|
||||
'D-Fly Channel $readableIndex',
|
||||
() => ControllerButton('D-Fly Channel $readableIndex', sourceDeviceId: device.deviceId),
|
||||
);
|
||||
if (didChange) {
|
||||
clickedButtons.add(button);
|
||||
if ((value & 0x10) != 0) {
|
||||
if (_lastButtons[index]?.type == _Di2State.longPress || _lastButtons[index]?.type == _Di2State.keep) {
|
||||
// short press is triggered after long press, until it's released later on
|
||||
_lastButtons[index] = (value: value, type: _Di2State.keep);
|
||||
Logger.info('Button $readableIndex still long pressed');
|
||||
} else {
|
||||
_lastButtons[index] = (value: value, type: _Di2State.shortPress);
|
||||
actualChange = true;
|
||||
Logger.info('Button $readableIndex short pressed');
|
||||
}
|
||||
} else if ((value & 0x20) != 0) {
|
||||
_lastButtons[index] = (value: value, type: _Di2State.longPress);
|
||||
actualChange = true;
|
||||
Logger.info('Button $readableIndex long pressed');
|
||||
} else if ((value & 0x40) != 0) {
|
||||
_lastButtons[index] = (value: value, type: _Di2State.doublePress);
|
||||
actualChange = true;
|
||||
Logger.info('Button $readableIndex double pressed');
|
||||
} else {
|
||||
_lastButtons[index] = (value: value, type: _Di2State.released);
|
||||
actualChange = true;
|
||||
Logger.info('Button $readableIndex released');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (clickedButtons.isNotEmpty) {
|
||||
await handleButtonsClickedWithoutLongPressSupport(clickedButtons);
|
||||
if (actualChange) {
|
||||
final buttonsToTrigger = _lastButtons.entries
|
||||
.where((entry) {
|
||||
final type = entry.value.type;
|
||||
return type != _Di2State.released;
|
||||
})
|
||||
.map((entry) => availableButtons.firstWhere((button) => button.name == 'D-Fly Channel ${entry.key + 1}'))
|
||||
.toList();
|
||||
|
||||
Logger.debug('Buttons to trigger: ${buttonsToTrigger.map((b) => b.name).join(', ')}');
|
||||
handleButtonsClicked(buttonsToTrigger);
|
||||
|
||||
final doublePress = _lastButtons.entries
|
||||
.filter((entry) => entry.value.type == _Di2State.doublePress)
|
||||
.map((entry) => availableButtons.firstWhere((button) => button.name == 'D-Fly Channel ${entry.key + 1}'))
|
||||
.toList();
|
||||
if (doublePress.isNotEmpty) {
|
||||
Logger.debug('Buttons to still trigger: ${doublePress.map((b) => b.name).join(', ')}');
|
||||
handleButtonsClicked(doublePress);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -97,3 +133,11 @@ class ShimanoDi2Constants {
|
||||
|
||||
static const String D_FLY_CHANNEL_UUID = "00002ac2-5348-494d-414e-4f5f424c4500";
|
||||
}
|
||||
|
||||
enum _Di2State {
|
||||
shortPress,
|
||||
longPress,
|
||||
keep,
|
||||
doublePress,
|
||||
released,
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ class _ButtonEditPageState extends State<ButtonEditPage> {
|
||||
),
|
||||
],
|
||||
|
||||
if (core.connection.accessories.isNotEmpty || kDebugMode) ...[
|
||||
if (core.connection.accessories.isNotEmpty) ...[
|
||||
SizedBox(height: 8),
|
||||
ColoredTitle(text: 'Accessory Actions'),
|
||||
Builder(
|
||||
|
||||
Reference in New Issue
Block a user