Merge branch 'main' into copilot/implement-in-app-purchase

# Conflicts:
#	CHANGELOG.md
#	lib/bluetooth/devices/base_device.dart
#	pubspec.yaml
This commit is contained in:
Jonas Bark
2025-12-17 08:41:47 +01:00
10 changed files with 52 additions and 18 deletions

View File

@@ -36,7 +36,7 @@ on:
env:
SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}
FLUTTER_VERSION: 3.38.5
FLUTTER_VERSION: 3.38.4
jobs:
build:
@@ -348,13 +348,13 @@ jobs:
name: Releases
path: |
build/windows/x64/runner/Release/bike_control.windows.zip
build/windows/x64/runner/Release/bike_control.windows.msix
build/windows/x64/runner/Release/bike_control.msix
- name: Update Release
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: "build/windows/x64/runner/Release/bike_control.windows.msix"
artifacts: "build/windows/x64/runner/Release/bike_control.msix"
prerelease: true
tag: v${{ env.VERSION }}
token: ${{ secrets.TOKEN }}

View File

@@ -2,13 +2,15 @@
BikeControl now offers a free trial period of 5 days for all features, so you can test everything before deciding to purchase a license. Please contact the support if you experience any issues!
### 4.1.0 (16-12-2025)
**Features**:
- control your trainer manually without requiring a controller - just like a Companion app
- support for Wahoo KICKR HEADWIND: control the fan via your controller
**Fixes**:
- Gamepads: handle analog values correctly on Windows
- MyWhoosh: updated default keymap to use steering instead of navigating
- MyWhoosh: updated default keymap to use the new A+D keys for steering
### 4.0.0 (07-12-2025)

View File

@@ -0,0 +1,4 @@
## Local Connection method
*
The local connection method (avalable on Android, Windows and macOS) allows BikeControl to directly control Rouvy either using touch or keyboard keys. This way you don't need to select any "Controllers" at all in Rouvy.
Make sure the "Virtual Shifting Controls" are enabled: https://support.rouvy.com/hc/en-us/articles/32452137189393-Virtual-Shifting#h_01K9SWGWYMAVQV108SQ9KWQAKC

View File

@@ -1 +1 @@
4.0.0
4.1.0

View File

@@ -128,10 +128,10 @@ abstract class BaseDevice {
// For repeated actions, don't trigger key down/up events (useful for long press)
final result = await core.actionHandler.performAction(action, isKeyDown: true, isKeyUp: false);
actionStreamInternal.add(LogNotification(result.message));
// Increment command count after successful execution
await IAPManager.instance.incrementCommandCount();
actionStreamInternal.add(ActionNotification(result));
}
}
@@ -160,10 +160,10 @@ abstract class BaseDevice {
}
final result = await core.actionHandler.performAction(action, isKeyDown: false, isKeyUp: true);
actionStreamInternal.add(ActionNotification(result));
// Increment command count after successful execution
await IAPManager.instance.incrementCommandCount();
actionStreamInternal.add(LogNotification(result.message));
}
}

View File

@@ -1,7 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:bike_control/bluetooth/devices/trainer_connection.dart';
import 'package:bike_control/bluetooth/devices/zwift/protocol/zp.pb.dart';
import 'package:bike_control/bluetooth/messages/notification.dart';
@@ -11,6 +10,7 @@ import 'package:bike_control/utils/core.dart';
import 'package:bike_control/utils/keymap/buttons.dart';
import 'package:bike_control/utils/keymap/keymap.dart';
import 'package:bike_control/utils/requirements/multi.dart';
import 'package:flutter/foundation.dart';
class WhooshLink extends TrainerConnection {
Socket? _socket;
@@ -155,7 +155,7 @@ class WhooshLink extends TrainerConnection {
InGameAction.steerRight,
];
if (jsonObject != null && !isKeyDown && !supportsIsKeyUpActions.contains(keyPair.inGameAction)) {
return Success('No Action sent on key down for action: ${keyPair.inGameAction}');
return Ignored('No Action sent on key down for action: ${keyPair.inGameAction}');
} else if (jsonObject != null) {
final jsonString = jsonEncode(jsonObject);
_socket?.writeln(jsonString);

View File

@@ -32,6 +32,10 @@ class NotHandled extends ActionResult {
const NotHandled(super.message);
}
class Ignored extends ActionResult {
const Ignored(super.message);
}
class Error extends ActionResult {
const Error(super.message);
}

View File

@@ -1,8 +1,8 @@
import 'package:dartx/dartx.dart';
import 'package:flutter/services.dart';
import 'package:bike_control/main.dart';
import 'package:bike_control/utils/keymap/apps/supported_app.dart';
import 'package:bike_control/utils/requirements/multi.dart';
import 'package:dartx/dartx.dart';
import 'package:flutter/services.dart';
import '../buttons.dart';
import '../keymap.dart';
@@ -44,8 +44,8 @@ class MyWhoosh extends SupportedApp {
.map(
(b) => KeyPair(
buttons: [b],
physicalKey: PhysicalKeyboardKey.arrowRight,
logicalKey: LogicalKeyboardKey.arrowRight,
physicalKey: PhysicalKeyboardKey.keyD,
logicalKey: LogicalKeyboardKey.keyD,
touchPosition: Offset(60, 80),
isLongPress: true,
inGameAction: InGameAction.steerRight,
@@ -53,6 +53,18 @@ class MyWhoosh extends SupportedApp {
),
...ControllerButton.values
.filter((e) => e.action == InGameAction.steerLeft)
.map(
(b) => KeyPair(
buttons: [b],
physicalKey: PhysicalKeyboardKey.keyA,
logicalKey: LogicalKeyboardKey.keyA,
touchPosition: Offset(32, 80),
isLongPress: true,
inGameAction: InGameAction.steerLeft,
),
),
...ControllerButton.values
.filter((e) => e.action == InGameAction.navigateLeft)
.map(
(b) => KeyPair(
buttons: [b],
@@ -63,6 +75,18 @@ class MyWhoosh extends SupportedApp {
inGameAction: InGameAction.steerLeft,
),
),
...ControllerButton.values
.filter((e) => e.action == InGameAction.navigateRight)
.map(
(b) => KeyPair(
buttons: [b],
physicalKey: PhysicalKeyboardKey.arrowRight,
logicalKey: LogicalKeyboardKey.arrowRight,
touchPosition: Offset(32, 80),
isLongPress: true,
inGameAction: InGameAction.steerLeft,
),
),
...ControllerButton.values
.filter((e) => e.action == InGameAction.toggleUi)
.map(

View File

@@ -2,9 +2,6 @@ import 'dart:async';
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:bike_control/bluetooth/devices/zwift/protocol/zp.pb.dart';
import 'package:bike_control/utils/actions/base_actions.dart' as actions;
import 'package:bike_control/utils/core.dart';
@@ -13,6 +10,9 @@ import 'package:bike_control/utils/keymap/apps/custom_app.dart';
import 'package:bike_control/utils/keymap/buttons.dart';
import 'package:bike_control/widgets/ui/button_widget.dart';
import 'package:bike_control/widgets/ui/toast.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import '../bluetooth/messages/notification.dart';
@@ -125,7 +125,7 @@ class _TestbedState extends State<Testbed> with SingleTickerProviderStateMixin {
}
}
}
} else if (data is ActionNotification) {
} else if (data is ActionNotification && data.result is! actions.Ignored) {
buildToast(
context,
location: ToastLocation.bottomLeft,

View File

@@ -1,7 +1,7 @@
name: bike_control
description: "BikeControl - Control your virtual riding"
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 4.2.0+51
version: 4.2.0+53
environment:
sdk: ^3.9.0