add openbikecontrol as app

This commit is contained in:
Jonas Bark
2025-12-04 13:27:40 +00:00
parent 637bf87dad
commit 1f00fd9d6c
9 changed files with 58 additions and 33 deletions

View File

@@ -5,8 +5,10 @@ import 'package:swift_control/bluetooth/devices/zwift/constants.dart';
import 'package:swift_control/bluetooth/devices/zwift/protocol/zp.pbenum.dart';
import 'package:swift_control/bluetooth/devices/zwift/zwift_ride.dart';
import 'package:swift_control/bluetooth/messages/notification.dart';
import 'package:swift_control/gen/l10n.dart';
import 'package:swift_control/pages/markdown.dart';
import 'package:swift_control/utils/core.dart';
import 'package:swift_control/utils/i18n_extension.dart';
import 'package:swift_control/widgets/ui/warning.dart';
class ZwiftClickV2 extends ZwiftRide {
@@ -63,6 +65,7 @@ class ZwiftClickV2 extends ZwiftRide {
Widget showInformation(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
super.showInformation(context),
@@ -70,14 +73,11 @@ class ZwiftClickV2 extends ZwiftRide {
Warning(
children: [
Text(
'''To make your Zwift Click V2 work best you should connect it in the Zwift app once each day.\nIf you don't do that BikeControl will need to reconnect every minute.
1. Open Zwift app
2. Log in (subscription not required) and open the device connection screen
3. Connect your Trainer, then connect the Zwift Click V2
4. Close the Zwift app again and connect again in BikeControl''',
AppLocalizations.of(context).clickV2Instructions,
),
Row(
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextButton(
onPressed: () {
@@ -94,21 +94,13 @@ class ZwiftClickV2 extends ZwiftRide {
),
);
},
child: Text('Troubleshooting'),
child: Text(context.i18n.troubleshootingGuide),
),
if (kDebugMode && false)
TextButton(
onPressed: () {
test();
},
child: Text('Test'),
),
Expanded(child: SizedBox()),
TextButton(
onPressed: () {
core.settings.setShowZwiftClickV2ReconnectWarning(false);
},
child: Text('Dismiss'),
child: Text(context.i18n.close),
),
],
),

View File

@@ -448,5 +448,6 @@
"placeholders": {
"platform": { "type": "String" }
}
}
},
"clickV2Instructions": "To make your Zwift Click V2 work best you should connect it in the Zwift app once each day.\nIf you don't do that the Click V2 will stop working after a minute.\n\n1. Open Zwift app\n2. Log in (subscription not required) and open the device connection screen\n3. Connect your Trainer, then connect the Zwift Click V2\n4. Close the Zwift app again and connect again in BikeControl"
}

View File

@@ -163,7 +163,8 @@ class _TrainerPageState extends State<TrainerPage> with WidgetsBindingObserver {
},
),
if (core.settings.getTrainerApp() != null) ...[
if (_showAutoRotationWarning)
// show warning only for android when using local accessibility service
if (_showAutoRotationWarning && _isRunningAndroidService == true)
Warning(
important: false,
children: [

View File

@@ -0,0 +1,18 @@
import 'package:swift_control/utils/keymap/apps/supported_app.dart';
import 'package:swift_control/utils/requirements/multi.dart';
import '../keymap.dart';
class OpenBikeControl extends SupportedApp {
OpenBikeControl()
: super(
name: 'OpenBikeControl compatible app',
packageName: "org.openbikecontrol",
compatibleTargets: Target.values,
supportsZwiftEmulation: false,
supportsOpenBikeProtocol: true,
keymap: Keymap(
keyPairs: [],
),
);
}

View File

@@ -1,4 +1,5 @@
import 'package:swift_control/utils/keymap/apps/biketerra.dart';
import 'package:swift_control/utils/keymap/apps/openbikecontrol.dart';
import 'package:swift_control/utils/keymap/apps/rouvy.dart';
import 'package:swift_control/utils/keymap/apps/training_peaks.dart';
import 'package:swift_control/utils/keymap/apps/zwift.dart';
@@ -31,6 +32,7 @@ abstract class SupportedApp {
TrainingPeaks(),
Biketerra(),
Rouvy(),
OpenBikeControl(),
CustomApp(),
];

View File

@@ -192,21 +192,25 @@ enum Target {
Target.thisDevice => AppLocalizations.current.runAppOnThisDevice(appName),
Target.iOS => AppLocalizations.current.runAppOnPlatformRemotely(
appName,
'an Apple device',
'Apple',
preferredConnectionMethod,
),
Target.android => AppLocalizations.current.runAppOnPlatformRemotely(
appName,
'an Android device',
'Android',
preferredConnectionMethod,
),
Target.macOS => AppLocalizations.current.runAppOnPlatformRemotely(appName, 'a Mac', preferredConnectionMethod),
Target.macOS => AppLocalizations.current.runAppOnPlatformRemotely(appName, 'Mac', preferredConnectionMethod),
Target.windows => AppLocalizations.current.runAppOnPlatformRemotely(
appName,
'a Windows PC',
'Windows PC',
preferredConnectionMethod,
),
Target.otherDevice => AppLocalizations.current.runAppOnPlatformRemotely(
appName,
AppLocalizations.current.targetOtherDevice,
preferredConnectionMethod,
),
Target.otherDevice => AppLocalizations.current.runAppOnPlatformRemotely(appName, 'another device', ''),
};
}

View File

@@ -89,11 +89,11 @@ class _ScanWidgetState extends State<ScanWidget> {
tooltip: (c) => TooltipContainer(
child: Text(context.i18n.mediaKeyDetectionTooltip),
),
child: Switch(
value: value,
child: Checkbox(
state: value ? CheckboxState.checked : CheckboxState.unchecked,
trailing: Text(context.i18n.enableMediaKeyDetection),
onChanged: (change) {
core.connection.isMediaKeyDetectionEnabled.value = change;
core.connection.isMediaKeyDetectionEnabled.value = change == CheckboxState.checked;
},
),
);

View File

@@ -100,15 +100,15 @@ class _ConnectionMethodState extends State<ConnectionMethod> with WidgetsBinding
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [
Switch(
value: _isStarted,
Checkbox(
state: _isStarted ? CheckboxState.checked : CheckboxState.unchecked,
onChanged: _isStarted && widget.isStarted == null
? null
: (value) {
Future.wait(widget.requirements.map((e) => e.getStatus())).then((_) async {
final notDone = widget.requirements.filter((e) => !e.status).toList();
if (notDone.isEmpty) {
widget.onChange(value);
widget.onChange(value == CheckboxState.checked);
setState(() {
_isStarted = true;
});

View File

@@ -7,9 +7,16 @@ class Warning extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
filled: true,
fillColor: important ? Theme.of(context).colorScheme.destructive : Theme.of(context).colorScheme.secondary,
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: (important ? Theme.of(context).colorScheme.destructive : Theme.of(context).colorScheme.secondary)
.withAlpha(80),
border: Border.all(
color: important ? Theme.of(context).colorScheme.destructive : Theme.of(context).colorScheme.secondary,
),
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,