mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-18 00:17:40 +01:00
Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
455db754d8 | ||
|
|
cbef8fc044 | ||
|
|
d8e45f849a | ||
|
|
f83defb37b | ||
|
|
5c8db11536 | ||
|
|
30aa5b33a3 | ||
|
|
ca41e69a17 | ||
|
|
af4d8ab183 | ||
|
|
c1a24cfbd1 | ||
|
|
86b406e2a4 | ||
|
|
1ec93330b0 | ||
|
|
4ed3c5fefe | ||
|
|
54d106ff4e | ||
|
|
996669ec44 | ||
|
|
1d38ff521a | ||
|
|
f0c1409da4 | ||
|
|
9617198db7 | ||
|
|
e4863b1ebd | ||
|
|
d51a4cc29d | ||
|
|
dcbb225355 | ||
|
|
cba449b493 | ||
|
|
559fe1232b | ||
|
|
a7f9ca489e | ||
|
|
74bf75a82e | ||
|
|
747629cebf | ||
|
|
aca6e9272b | ||
|
|
18e6f9a1b5 | ||
|
|
c3532d5c35 | ||
|
|
1a88f45c93 | ||
|
|
b49eda7fc7 | ||
|
|
f0b3bc70b2 | ||
|
|
08700edc22 | ||
|
|
d698c9bbea | ||
|
|
eea1b8eb40 | ||
|
|
0118c5a87c | ||
|
|
65a3374d9c | ||
|
|
536225bf12 | ||
|
|
e858d35617 | ||
|
|
6d87e85353 | ||
|
|
d1fed35c3e | ||
|
|
d9297bd40e | ||
|
|
a1926dfc00 | ||
|
|
d55ba039af | ||
|
|
c9ebc5a9f6 | ||
|
|
be0c2d97ba | ||
|
|
a03cc76eaa | ||
|
|
504c71d5c4 | ||
|
|
d0291c68d7 | ||
|
|
33e5e41eff | ||
|
|
221d5a0b8d | ||
|
|
b899487ee9 | ||
|
|
ff0b724a73 | ||
|
|
647c20a6a3 | ||
|
|
c36e63aa8d | ||
|
|
cb523ea656 | ||
|
|
22b99f4f6d | ||
|
|
05e681b59a | ||
|
|
07ee91c17a | ||
|
|
323a344c3a | ||
|
|
0172b1cf90 | ||
|
|
5a5e4066f6 | ||
|
|
3256f5aa15 | ||
|
|
476a9a337f | ||
|
|
1f1ce58bd9 | ||
|
|
bbb3dd3397 | ||
|
|
d7cee77c8b | ||
|
|
e2ac975c75 | ||
|
|
5e9352316c | ||
|
|
c73adb7c0d | ||
|
|
c3b41f56d4 | ||
|
|
6fe841af58 | ||
|
|
d97307de6f | ||
|
|
826dc2327f | ||
|
|
3466e504e3 | ||
|
|
ebd7f80947 | ||
|
|
43e827d8f5 | ||
|
|
5d5dc2e152 | ||
|
|
c0d2eaa897 | ||
|
|
13c70fc445 | ||
|
|
1e11d28765 | ||
|
|
7ee9bc43a0 | ||
|
|
372085ec0e | ||
|
|
e758b35837 | ||
|
|
dee7b86120 | ||
|
|
b3ec7e7a3a | ||
|
|
bbd01d023a | ||
|
|
36282c9fa9 | ||
|
|
daea07c409 | ||
|
|
49d7445d0e | ||
|
|
9bb0e5616a | ||
|
|
7e98f595ee | ||
|
|
a9fdc4b16e | ||
|
|
c06819b502 | ||
|
|
969faca658 | ||
|
|
61fbb099e2 | ||
|
|
fbd6356be0 | ||
|
|
1c40455bf3 | ||
|
|
15129634a6 | ||
|
|
89d35d7734 | ||
|
|
d959bfb4c9 | ||
|
|
9bc25514ae | ||
|
|
25210b57ba | ||
|
|
c9317e369c | ||
|
|
2195c19ed9 | ||
|
|
d13a9d72c9 | ||
|
|
55d230e41c | ||
|
|
ffa604f921 | ||
|
|
93bdfeeaa7 | ||
|
|
336c64e5a9 | ||
|
|
20a706d93d | ||
|
|
21cb8844fc | ||
|
|
4bc1a3b1d0 | ||
|
|
9df1f7cfa6 | ||
|
|
72cdf86802 | ||
|
|
9a53d5fdab | ||
|
|
458e6333a0 | ||
|
|
f42e483260 | ||
|
|
dda2135129 | ||
|
|
bc2831c17e | ||
|
|
310313c3b2 | ||
|
|
2122568461 | ||
|
|
144fd5b740 | ||
|
|
5f7a1a8203 | ||
|
|
258b396444 | ||
|
|
5861533793 | ||
|
|
3106bd09e8 | ||
|
|
a3475a02d2 | ||
|
|
fb1a1f35ad | ||
|
|
71aadde901 | ||
|
|
f7bfd8c206 | ||
|
|
ff83e5271b | ||
|
|
ec6edb2864 | ||
|
|
4f4a6f60c5 | ||
|
|
354e13678b | ||
|
|
f1b8822e20 | ||
|
|
6bf83b1034 | ||
|
|
7b1e4ede2a | ||
|
|
a554820115 | ||
|
|
cb9f9ea5b3 | ||
|
|
4051553a56 | ||
|
|
01a213354b | ||
|
|
962abfb38e | ||
|
|
ada4cf0dfd | ||
|
|
aff1137c3d | ||
|
|
7f24c27201 | ||
|
|
51c5e34220 | ||
|
|
10c2cc64a2 | ||
|
|
a14d21f8e4 | ||
|
|
8de715a153 | ||
|
|
e9ebe832de | ||
|
|
2c8feccea1 | ||
|
|
36083e654f | ||
|
|
8790b1938a | ||
|
|
7cd48ce3c4 | ||
|
|
4300f1005d | ||
|
|
9dec9c370c | ||
|
|
e9f460279a | ||
|
|
06b322e575 | ||
|
|
80d8d8c0cd | ||
|
|
4450db3be9 | ||
|
|
b875489ad3 | ||
|
|
ece3f3822f | ||
|
|
0780cdc80b | ||
|
|
1c0e027abb | ||
|
|
0dcb666bbd | ||
|
|
00ebca7a01 | ||
|
|
f9c8820303 | ||
|
|
3d414edda9 | ||
|
|
0e3f6f1d5e | ||
|
|
c4b0ef38c0 | ||
|
|
2e800bb2de | ||
|
|
505b970497 | ||
|
|
986bfd481c | ||
|
|
ab8d480a01 | ||
|
|
7e19b76403 | ||
|
|
cbc2f103ac | ||
|
|
6a7922cf17 | ||
|
|
8e23de2718 | ||
|
|
11aec5fba1 | ||
|
|
c98f213e2e | ||
|
|
8c11cfcad6 | ||
|
|
5fe88ffc6a | ||
|
|
7a8c7a4ee1 | ||
|
|
3343325195 | ||
|
|
edda16dc06 | ||
|
|
7a3d120123 | ||
|
|
92419c9182 | ||
|
|
68bb5bf371 | ||
|
|
b0d8bfcadd | ||
|
|
a58ad1daf6 | ||
|
|
657c6056c4 | ||
|
|
84daba8902 | ||
|
|
3e37f8a269 | ||
|
|
28d178c4be | ||
|
|
f560cd5930 | ||
|
|
dbf24c6cd3 | ||
|
|
0a4989ca47 | ||
|
|
507dbf5d0f | ||
|
|
536f36f4e7 | ||
|
|
c523ba2287 | ||
|
|
a3f1cbb3b1 | ||
|
|
561bb2f0f4 | ||
|
|
dbbd1b5f2c | ||
|
|
7f6ec2f732 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +1,2 @@
|
||||
github: [jonasbark]
|
||||
open_collective: jonas-bark1
|
||||
custom: ["https://paypal.me/boni"]
|
||||
custom: ["https://paypal.me/boni", "https://donate.stripe.com/9B6aEX0muajY8bZ1Kl6J200"]
|
||||
|
||||
50
.github/workflows/build.yml
vendored
50
.github/workflows/build.yml
vendored
@@ -4,6 +4,12 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '.github/workflows/**'
|
||||
- 'lib/**'
|
||||
- 'accessibility/**'
|
||||
- 'keypress_simulator/**'
|
||||
- 'pubspec.yaml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -76,10 +82,12 @@ jobs:
|
||||
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/android.keystore;
|
||||
echo "${{ secrets.KEYSTORE_PROPERTIES }}" > android/keystore.properties;
|
||||
|
||||
#6 Building APK
|
||||
- name: Build APK
|
||||
run: flutter build apk --release
|
||||
|
||||
- name: Build Bundle
|
||||
run: flutter build appbundle --release
|
||||
|
||||
- name: Build Web
|
||||
run: flutter build web --release --base-href "/swiftcontrol/"
|
||||
|
||||
@@ -128,6 +136,9 @@ jobs:
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: "build/app/outputs/flutter-apk/SwiftControl.android.apk,build/macos/Build/Products/Release/SwiftControl.macos.zip"
|
||||
allowUpdates: true
|
||||
prerelease: ${{ endsWith(env.VERSION, '1337') }}
|
||||
body: "You can also download the Android version from the Play Store: https://play.google.com/store/apps/details?id=de.jonasbark.swiftcontrol"
|
||||
tag: v${{ env.VERSION }}
|
||||
token: ${{ secrets.TOKEN }}
|
||||
|
||||
@@ -140,6 +151,24 @@ jobs:
|
||||
- name: Web Deploy
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
- name: Extract latest changelog
|
||||
id: changelog
|
||||
run: |
|
||||
chmod +x scripts/get_latest_changelog.sh
|
||||
mkdir -p whatsnew
|
||||
./scripts/get_latest_changelog.sh > whatsnew/whatsnew-en-US
|
||||
|
||||
- name: Upload to Play Store
|
||||
# only upload when env.VERSION does not end with 1337, which is our indicator for beta releases
|
||||
if: "!endsWith(env.VERSION, '1337')"
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
||||
packageName: de.jonasbark.swiftcontrol
|
||||
releaseFiles: build/app/outputs/bundle/release/app-release.aab
|
||||
track: production
|
||||
whatsNewDirectory: whatsnew
|
||||
|
||||
windows:
|
||||
needs: build
|
||||
name: Build & Release on Windows
|
||||
@@ -173,6 +202,25 @@ jobs:
|
||||
- name: Zip directory (Windows)
|
||||
shell: pwsh
|
||||
run: |
|
||||
|
||||
$source = "C:\Windows\System32"
|
||||
$destination = "build\windows\x64\runner\Release"
|
||||
|
||||
# List of required DLLs
|
||||
$dlls = @("msvcp140.dll", "vcruntime140.dll", "vcruntime140_1.dll")
|
||||
|
||||
# Copy each file
|
||||
foreach ($dll in $dlls) {
|
||||
$srcPath = Join-Path $source $dll
|
||||
$destPath = Join-Path $destination $dll
|
||||
|
||||
if (Test-Path $srcPath) {
|
||||
Copy-Item -Path $srcPath -Destination $destPath -Force
|
||||
Write-Output "Copied $dll to $destination"
|
||||
} else {
|
||||
Write-Warning "$dll not found in $source"
|
||||
}
|
||||
}
|
||||
Compress-Archive -Path "build/windows/x64/runner/Release/*" -DestinationPath "build/windows/x64/runner/Release/SwiftControl.windows.zip"
|
||||
|
||||
#9 Upload Artifacts
|
||||
|
||||
49
.github/workflows/web.yml
vendored
Normal file
49
.github/workflows/web.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: "Build"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- web
|
||||
paths:
|
||||
- '.github/workflows/**'
|
||||
- 'lib/**'
|
||||
- 'accessibility/**'
|
||||
- 'keypress_simulator/**'
|
||||
- 'pubspec.yaml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build & Release
|
||||
runs-on: macos-latest
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
pages: write
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
#1 Checkout Repository
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
#3 Setup Flutter
|
||||
- name: Set Up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
|
||||
#4 Install Dependencies
|
||||
- name: Install Dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Web
|
||||
run: flutter build web --release --base-href "/swiftcontrol/"
|
||||
|
||||
- name: Upload static files as artifact
|
||||
id: deployment
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: build/web
|
||||
|
||||
- name: Web Deploy
|
||||
uses: actions/deploy-pages@v4
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -45,3 +45,5 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
service-account.json
|
||||
|
||||
23
.vscode/launch.json
vendored
Normal file
23
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "swiftcontrol",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
},
|
||||
{
|
||||
"name": "swiftcontrol (profile mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "profile"
|
||||
},
|
||||
{
|
||||
"name": "swiftcontrol (release mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "release"
|
||||
}
|
||||
]
|
||||
}
|
||||
110
CHANGELOG.md
110
CHANGELOG.md
@@ -1,3 +1,113 @@
|
||||
### 2.6.2 (2025-10-01)
|
||||
- fix a few issues with the new touch placement feature
|
||||
- add a workaround for Zwift Click V2 which resets the device when button events are no longer sent
|
||||
- fix issue on Android and Desktop where only a "touch down" was sent, but no "touch up"
|
||||
|
||||
### 2.6.0 (2025-09-30)
|
||||
- refactor touch placements: show touches on screen, fix misplaced coordinates - should fix #64
|
||||
- show firmware version of connected device
|
||||
- Fix crashes on some Android devices
|
||||
- warn the user how to make Zwift Click V2 work properly
|
||||
- many UI improvements
|
||||
- add setting to enable or disable vibration on button press for Zwift Ride and Zwift Play controllers
|
||||
|
||||
### 2.5.0 (2025-09-25)
|
||||
- Improve usability
|
||||
- SwiftControl is now available via the Play Store: https://play.google.com/store/apps/details?id=de.jonasbark.swiftcontrol
|
||||
- SwiftControl will continue to be available to download for free on GitHub
|
||||
- contact me if you already donated and I'll get a voucher for you :)
|
||||
|
||||
### 2.4.0+1 (2025-09-17)
|
||||
- Windows: fix mouse clicks at wrong location due to display scaling (fixes #64)
|
||||
|
||||
### 2.4.0 (2025-09-16)
|
||||
- Show an overview of the keymap bindings
|
||||
- Allow customizing an existing keymap
|
||||
- Add more donation options
|
||||
|
||||
### 2.3.0 (2025-09-11)
|
||||
- Add support for latest Zwift Click v2
|
||||
|
||||
### 2.2.0 (2025-09-08)
|
||||
- Add Long Press Mode option for custom keymaps - buttons can now send sustained key presses instead of repeated taps, perfect for movement controls in games (fixes #61)
|
||||
- Windows: adjust key sending method to improve compatibility with more apps (fixes #62)
|
||||
|
||||
### 2.1.0 (2025-07-03)
|
||||
- Windows: automatically focus compatible training apps (MyWhoosh, IndieVelo, Biketerra) when sending keystrokes, enabling seamless multi-window usage
|
||||
|
||||
### 2.0.9 (2025-05-04)
|
||||
- you can now assign Escape and arrow down key to your custom keymap (#18)
|
||||
|
||||
### 2.0.8 (2025-05-02)
|
||||
- only use the light theme for the app
|
||||
- more troubleshooting information
|
||||
|
||||
### 2.0.7 (2025-04-18)
|
||||
- add Biketerra.com keymap
|
||||
- some UX improvements
|
||||
|
||||
### 2.0.6 (2025-04-15)
|
||||
- fix MyWhoosh up / downshift button assignment (I key vs K key)
|
||||
|
||||
### 2.0.5 (2025-04-13)
|
||||
- fix Zwift Click button assignment (#12)
|
||||
|
||||
### 2.0.4 (2025-04-10)
|
||||
- vibrate Zwift Play / Zwift Ride controller on gear shift (thanks @cagnulein, closes #16)
|
||||
|
||||
### 2.0.3 (2025-04-08)
|
||||
- adjust TrainingPeaks Virtual key mapping (#12)
|
||||
- attempt to reconnect device if connection is lost
|
||||
- Android: detect freeform windows for MyWhoosh + TrainingPeaks Virtual keymaps
|
||||
|
||||
### 2.0.2 (2025-04-07)
|
||||
- fix bluetooth scan issues on older Android devices by asking for location permission
|
||||
|
||||
### 2.0.1 (2025-04-06)
|
||||
- long pressing a button will trigger the action again every 250ms
|
||||
|
||||
### 2.0.0 (2025-04-06)
|
||||
- You can now customize the actions (touches, mouse clicks or keyboard keys) for all buttons on all supported Zwift devices
|
||||
- now shows the battery level of the connected devices
|
||||
- add more troubleshooting information
|
||||
|
||||
### 1.1.10 (2025-04-03)
|
||||
- Add more troubleshooting during connection
|
||||
|
||||
### 1.1.8 (2025-04-02)
|
||||
- Android: make sure the touch reassignment page is fullscreen
|
||||
|
||||
### 1.1.7 (2025-04-01)
|
||||
- Zwift Ride: fix connection issues by connecting only to the left controller
|
||||
- Windows: connect sequentially to fix (finally?) fix connection issues
|
||||
- Windows: change the way keyboard is simulated, should fix glitches
|
||||
|
||||
### 1.1.6 (2025-03-31)
|
||||
- Zwift Ride: add buttonPowerDown to shift gears
|
||||
- Zwift Play: Fix buttonShift assignment
|
||||
- Android: fix action to go to next song
|
||||
- App now checks if you run the latest available version
|
||||
|
||||
### 1.1.5 (2025-03-30)
|
||||
- fix bluetooth connection #6, also add missing entitlement on macOS
|
||||
|
||||
### 1.1.3 (2025-03-30)
|
||||
- Windows: fix custom keyboard profile recreation after restart, also warn when choosing MyWhoosh profile (may fix #7)
|
||||
- Zwift Ride: button map adjustments to prevent double shifting
|
||||
- potential fix for #6
|
||||
|
||||
### 1.1.1 (2025-03-30)
|
||||
- potential fix for Bluetooth device detection
|
||||
|
||||
### 1.1.0 (2025-03-30)
|
||||
- Windows & macOS: allow setting custom keymap and store the setting
|
||||
- Android: allow customizing the touch area, so it can work with any device without guesswork where the buttons are (#4)
|
||||
- Zwift Ride: update Zwift Ride decoding based on Feedback from @JayyajGH (#3)
|
||||
|
||||
### 1.0.6 (2025-03-29)
|
||||
- Another potential keyboard fix for Windows
|
||||
- Zwift Play: actually also use the dedicated shift buttons
|
||||
|
||||
### 1.0.5 (2025-03-29)
|
||||
- Zwift Ride: remap the shifter buttons to the correct values
|
||||
|
||||
|
||||
52
README.md
52
README.md
@@ -4,7 +4,14 @@
|
||||
|
||||
## Description
|
||||
|
||||
With SwiftControl you can control your favorite trainer app using your Zwift Click, Zwift Ride or Zwift Play devices. Primarily useful to perform virtual gear shifting.
|
||||
With SwiftControl you can **control your favorite trainer app** using your Zwift Click, Zwift Ride or Zwift Play devices. Here's what you can do with it, depending on your configuration:
|
||||
- Virtual Gear shifting
|
||||
- Steering / turning
|
||||
- adjust workout intensity
|
||||
- control music on your device
|
||||
- more? If you can do it via keyboard, mouse or touch, you can do it with SwiftControl
|
||||
|
||||
**Android AccessibilityService Usage**: On Android, SwiftControl uses the AccessibilityService API to simulate touch gestures on your screen, allowing your Zwift devices to control training apps. This service only monitors which app window is active and performs touch gestures at the locations you configure. No personal data is accessed or collected.
|
||||
|
||||
|
||||
https://github.com/user-attachments/assets/1f81b674-1628-4763-ad66-5f3ed7a3f159
|
||||
@@ -13,36 +20,53 @@ https://github.com/user-attachments/assets/1f81b674-1628-4763-ad66-5f3ed7a3f159
|
||||
|
||||
|
||||
## Downloads
|
||||
Get the latest version here: https://github.com/jonasbark/swiftcontrol/releases
|
||||
<a href="https://play.google.com/store/apps/details?id=de.jonasbark.swiftcontrol"><img width="270" height="80" alt="GetItOnGooglePlay_Badge_Web_color_English" src="https://github.com/user-attachments/assets/a059d5a1-2efb-4f65-8117-ef6a99823b21" /></a>
|
||||
|
||||
Get the latest version for free for Windows, macOS and Android here: https://github.com/jonasbark/swiftcontrol/releases
|
||||
|
||||
## Supported Apps
|
||||
- MyWhoosh
|
||||
- indieVelo / Training Peaks
|
||||
- let me know if you know others that can benefit
|
||||
- Biketerra.com
|
||||
- any other:
|
||||
- Android: you can customize simulated touch points of all your buttons in the app
|
||||
- Desktop: you can customize keyboard shortcuts and mouse clicks in the app
|
||||
|
||||
## Supported Devices
|
||||
- Zwift Click
|
||||
- Zwift Click v2 (mostly, see #68)
|
||||
- Zwift Ride
|
||||
- Zwift Play
|
||||
|
||||
## Supported Platforms
|
||||
- Android
|
||||
- App is losing connection over time? Read about how to [keep the app alive](https://dontkillmyapp.com/).
|
||||
- macOS
|
||||
- Windows
|
||||
- Windows
|
||||
- Windows may flag the app as virus. I think it does so because the app does control the mouse and keyboard.
|
||||
- Bluetooth connection unstable? You may need to use an [external Bluetooth adapter](https://github.com/jonasbark/swiftcontrol/issues/14#issuecomment-3193839509).
|
||||
- Make sure your Zwift device is not paired with Windows Bluetooth settings: [more information](https://github.com/jonasbark/swiftcontrol/issues/70).
|
||||
- [Web](https://jonasbark.github.io/swiftcontrol/) (you won't be able to do much)
|
||||
- NOT SUPPORTED: iOS (iPhone, iPad) as Apple does not provide any way to simulate touches or keyboard events
|
||||
|
||||
## Troubleshooting
|
||||
- Your Zwift device is found but connection does not work properly? You may need to update the firmware in Zwift Companion app.
|
||||
|
||||
## How does it work?
|
||||
The app connects to your Zwift device automatically.
|
||||
The app connects to your Zwift device automatically. It does not connect to your trainer itself.
|
||||
|
||||
- When using Android a "click" on a certain part of the screen is simulated to trigger the action.
|
||||
- When using macOS or Windows a keyboard click is used to trigger the action. Typically + and - keys are used to shift gears, while MyWhoosh uses K and I keys.
|
||||
- When using Android: SwiftControl uses the AccessibilityService API to simulate touch gestures on specific parts of your screen to trigger actions in training apps. The service monitors which training app window is currently active to ensure gestures are sent to the correct app.
|
||||
- When using macOS or Windows a keyboard or mouse click is used to trigger the action.
|
||||
- there are predefined Keymaps for MyWhoosh, indieVelo / Training Peaks, and others
|
||||
- you can also create your own Keymaps for any other app
|
||||
- you can also use the mouse to click on a certain part of the screen, or use keyboard shortcuts
|
||||
|
||||
## Alternatives
|
||||
- [qdomyos-zwift](https://www.qzfitness.com/) directly controls the trainer (as opposed to controlling the trainer app)
|
||||
|
||||
## Donate
|
||||
Please consider donating to support the development of this app.
|
||||
Please consider donating to support the development of this app :)
|
||||
|
||||
[](https://paypal.me/boni)
|
||||
|
||||
## TODO
|
||||
- test Zwift Ride
|
||||
- confirm that Windows release works
|
||||
- implement more actions for Play + Ride
|
||||
- [via PayPal](https://paypal.me/boni)
|
||||
- [via Credit Card, Google Pay, Apple Pay, etc (USD)](https://donate.stripe.com/8x24gzc5c4ZE3VJdt36J201)
|
||||
- [via Credit Card, Google Pay, Apple Pay, etc (EUR)](https://donate.stripe.com/9B6aEX0muajY8bZ1Kl6J200)
|
||||
|
||||
@@ -61,23 +61,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 +94,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()
|
||||
@@ -131,7 +139,7 @@ val AccessibilityApiPigeonMethodCodec = StandardMethodCodec(AccessibilityApiPige
|
||||
interface Accessibility {
|
||||
fun hasPermission(): Boolean
|
||||
fun openPermissions()
|
||||
fun performTouch(x: Double, y: Double)
|
||||
fun performTouch(x: Double, y: Double, isKeyDown: Boolean, isKeyUp: Boolean)
|
||||
fun controlMedia(action: MediaAction)
|
||||
|
||||
companion object {
|
||||
@@ -181,8 +189,10 @@ interface Accessibility {
|
||||
val args = message as List<Any?>
|
||||
val xArg = args[0] as Double
|
||||
val yArg = args[1] as Double
|
||||
val isKeyDownArg = args[2] as Boolean
|
||||
val isKeyUpArg = args[3] as Boolean
|
||||
val wrapped: List<Any?> = try {
|
||||
api.performTouch(xArg, yArg)
|
||||
api.performTouch(xArg, yArg, isKeyDownArg, isKeyUpArg)
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapError(exception)
|
||||
|
||||
@@ -7,18 +7,16 @@ 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
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
|
||||
|
||||
/** AccessibilityPlugin */
|
||||
class AccessibilityPlugin: FlutterPlugin, MethodCallHandler, Accessibility {
|
||||
class AccessibilityPlugin: FlutterPlugin, Accessibility {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
@@ -38,14 +36,6 @@ class AccessibilityPlugin: FlutterPlugin, MethodCallHandler, Accessibility {
|
||||
Observable.fromService = eventHandler
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: Result) {
|
||||
if (call.method == "getPlatformVersion") {
|
||||
result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
}
|
||||
@@ -61,20 +51,27 @@ class AccessibilityPlugin: FlutterPlugin, MethodCallHandler, Accessibility {
|
||||
}, Bundle.EMPTY)
|
||||
}
|
||||
|
||||
override fun performTouch(x: Double, y: Double) {
|
||||
Observable.toService?.performTouch(x = x, y = y) ?: error("Service not running")
|
||||
override fun performTouch(x: Double, y: Double, isKeyDown: Boolean, isKeyUp: Boolean) {
|
||||
Observable.toService?.performTouch(x = x, y = y, isKeyUp = isKeyUp, isKeyDown = isKeyDown) ?: error("Service not running")
|
||||
}
|
||||
|
||||
override fun controlMedia(action: MediaAction) {
|
||||
val audioService = context.getSystemService(Context.AUDIO_SERVICE) as android.media.AudioManager
|
||||
when (action) {
|
||||
MediaAction.PLAY_PAUSE -> {
|
||||
audioService.dispatchMediaKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_DOWN, android.view.KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))
|
||||
audioService.dispatchMediaKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_UP, android.view.KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))
|
||||
audioService.dispatchMediaKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_DOWN, android.view.KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))
|
||||
audioService.dispatchMediaKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_UP, android.view.KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))
|
||||
}
|
||||
MediaAction.NEXT -> {
|
||||
audioService.dispatchMediaKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_DOWN, android.view.KeyEvent.KEYCODE_MEDIA_NEXT))
|
||||
audioService.dispatchMediaKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_UP, android.view.KeyEvent.KEYCODE_MEDIA_NEXT))
|
||||
}
|
||||
MediaAction.VOLUME_DOWN -> {
|
||||
audioService.adjustVolume(android.media.AudioManager.ADJUST_LOWER, android.media.AudioManager.FLAG_SHOW_UI)
|
||||
}
|
||||
MediaAction.VOLUME_UP -> {
|
||||
audioService.adjustVolume(android.media.AudioManager.ADJUST_RAISE, android.media.AudioManager.FLAG_SHOW_UI)
|
||||
}
|
||||
MediaAction.NEXT -> audioService.dispatchMediaKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_DOWN, android.view.KeyEvent.KEYCODE_MEDIA_NEXT))
|
||||
MediaAction.VOLUME_DOWN -> audioService.adjustVolume(android.media.AudioManager.ADJUST_LOWER, android.media.AudioManager.FLAG_SHOW_UI)
|
||||
MediaAction.VOLUME_UP -> audioService.adjustVolume(android.media.AudioManager.ADJUST_RAISE, android.media.AudioManager.FLAG_SHOW_UI)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +89,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()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,36 +36,29 @@ 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
|
||||
}
|
||||
|
||||
private fun simulateTap(x: Double, y: Double) {
|
||||
val gestureBuilder = GestureDescription.Builder()
|
||||
val path = Path()
|
||||
path.moveTo(x.toFloat(), y.toFloat())
|
||||
path.lineTo(x.toFloat()+1, y.toFloat())
|
||||
|
||||
val stroke = StrokeDescription(path, 0, ViewConfiguration.getTapTimeout().toLong())
|
||||
gestureBuilder.addStroke(stroke)
|
||||
|
||||
dispatchGesture(gestureBuilder.build(), null, null)
|
||||
}
|
||||
|
||||
override fun onInterrupt() {
|
||||
Log.d("AccessibilityService", "Service Interrupted")
|
||||
}
|
||||
|
||||
override fun performTouch(x: Double, y: Double) {
|
||||
simulateTap(x, y)
|
||||
override fun performTouch(x: Double, y: Double, isKeyDown: Boolean, isKeyUp: Boolean) {
|
||||
val gestureBuilder = GestureDescription.Builder()
|
||||
val path = Path()
|
||||
path.moveTo(x.toFloat(), y.toFloat())
|
||||
path.lineTo(x.toFloat()+1, y.toFloat())
|
||||
|
||||
val stroke = StrokeDescription(path, 0, ViewConfiguration.getTapTimeout().toLong(), isKeyDown)
|
||||
gestureBuilder.addStroke(stroke)
|
||||
|
||||
dispatchGesture(gestureBuilder.build(), null, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package de.jonasbark.accessibility
|
||||
|
||||
import android.graphics.Rect
|
||||
|
||||
object Observable {
|
||||
var toService: Listener? = null
|
||||
var fromService: Receiver? = null
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun performTouch(x: Double, y: Double)
|
||||
fun performTouch(x: Double, y: Double, isKeyDown: Boolean, isKeyUp: Boolean)
|
||||
}
|
||||
|
||||
interface Receiver {
|
||||
fun onChange(packageName: String, windowWidth: Int, windowHeight: Int)
|
||||
}
|
||||
fun onChange(packageName: String, window: Rect)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ abstract class Accessibility {
|
||||
|
||||
void openPermissions();
|
||||
|
||||
void performTouch(double x, double y);
|
||||
void performTouch(double x, double y, {bool isKeyDown = true, bool isKeyUp = false});
|
||||
|
||||
void controlMedia(MediaAction action);
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -175,14 +187,14 @@ class Accessibility {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> performTouch(double x, double y) async {
|
||||
Future<void> performTouch(double x, double y, {bool isKeyDown = true, bool isKeyUp = false, }) async {
|
||||
final String pigeonVar_channelName = 'dev.flutter.pigeon.accessibility.Accessibility.performTouch$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[x, y]);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[x, y, isKeyDown, isKeyUp]);
|
||||
final List<Object?>? pigeonVar_replyList =
|
||||
await pigeonVar_sendFuture as List<Object?>?;
|
||||
if (pigeonVar_replyList == null) {
|
||||
|
||||
@@ -26,3 +26,5 @@ linter:
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
formatter:
|
||||
page_width: 120
|
||||
|
||||
@@ -14,7 +14,7 @@ val keystoreProperties = Properties()
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
|
||||
android {
|
||||
namespace = "de.jonasbark.swift_play"
|
||||
namespace = "de.jonasbark.swiftcontrol"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = "27.0.12077973"
|
||||
|
||||
@@ -32,7 +32,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "de.jonasbark.swift_play"
|
||||
applicationId = "de.jonasbark.swiftcontrol"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = 24
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
<!-- to check if you have the latest version -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:label="SwiftControl"
|
||||
android:name="${applicationName}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.jonasbark.swift_play
|
||||
package de.jonasbark.swiftcontrol
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
@@ -18,8 +18,8 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.7.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
|
||||
id("com.android.application") version "8.7.3" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>12.0</string>
|
||||
<string>13.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '12.0'
|
||||
# platform :ios, '13.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
66
ios/Podfile.lock
Normal file
66
ios/Podfile.lock
Normal file
@@ -0,0 +1,66 @@
|
||||
PODS:
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- universal_ble (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- universal_ble (from `.symlinks/plugins/universal_ble/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_local_notifications:
|
||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
universal_ble:
|
||||
:path: ".symlinks/plugins/universal_ble/darwin"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
|
||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
|
||||
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
@@ -10,10 +10,12 @@
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
3E50CA021EFA25CF89FE46AB /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C0E42A04700D6B661C7EE82 /* Pods_RunnerTests.framework */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
9DEFD285994D09CFCE400F36 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE7ADD07A99710C0FB974A8 /* Pods_Runner.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -40,14 +42,20 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0CF32F9ECDBEA4B014717FF8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
2C0E42A04700D6B661C7EE82 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
5CE7ADD07A99710C0FB974A8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
7D133E5D5548E2EF2879734F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
86D436F6DAF367742EF27F51 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
8AA6D129479129F106E2298A /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -55,19 +63,44 @@
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
DFFDC4B9C4D6EF6A3BDE2E73 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
EFDECED99A47773C293F8819 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
5046C8DCA17DB268ED17F005 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3E50CA021EFA25CF89FE46AB /* Pods_RunnerTests.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9DEFD285994D09CFCE400F36 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
31E2F9ED567016937E8AEA3B /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
86D436F6DAF367742EF27F51 /* Pods-Runner.debug.xcconfig */,
|
||||
0CF32F9ECDBEA4B014717FF8 /* Pods-Runner.release.xcconfig */,
|
||||
7D133E5D5548E2EF2879734F /* Pods-Runner.profile.xcconfig */,
|
||||
DFFDC4B9C4D6EF6A3BDE2E73 /* Pods-RunnerTests.debug.xcconfig */,
|
||||
8AA6D129479129F106E2298A /* Pods-RunnerTests.release.xcconfig */,
|
||||
EFDECED99A47773C293F8819 /* Pods-RunnerTests.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -76,6 +109,15 @@
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6A38311855DC1CB8C0E2FD04 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CE7ADD07A99710C0FB974A8 /* Pods_Runner.framework */,
|
||||
2C0E42A04700D6B661C7EE82 /* Pods_RunnerTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -94,6 +136,8 @@
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||
31E2F9ED567016937E8AEA3B /* Pods */,
|
||||
6A38311855DC1CB8C0E2FD04 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -128,8 +172,10 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
5E1D2B1ED00966C758CA2289 /* [CP] Check Pods Manifest.lock */,
|
||||
331C807D294A63A400263BE5 /* Sources */,
|
||||
331C807F294A63A400263BE5 /* Resources */,
|
||||
5046C8DCA17DB268ED17F005 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -145,12 +191,15 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
AF2FDC69578083D4D16AB4D6 /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
EEF1FBDEE98BA93C4FBDB3AE /* [CP] Embed Pods Frameworks */,
|
||||
1F0C44A79AE73641A1C3FF47 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -222,6 +271,23 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
1F0C44A79AE73641A1C3FF47 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
@@ -238,6 +304,28 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
5E1D2B1ED00966C758CA2289 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
@@ -253,6 +341,45 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
AF2FDC69578083D4D16AB4D6 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
EEF1FBDEE98BA93C4FBDB3AE /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -346,7 +473,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@@ -379,6 +506,7 @@
|
||||
};
|
||||
331C8088294A63A400263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = DFFDC4B9C4D6EF6A3BDE2E73 /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -396,6 +524,7 @@
|
||||
};
|
||||
331C8089294A63A400263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8AA6D129479129F106E2298A /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -411,6 +540,7 @@
|
||||
};
|
||||
331C808A294A63A400263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = EFDECED99A47773C293F8819 /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -473,7 +603,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -524,7 +654,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
@@ -54,6 +55,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
@@ -4,4 +4,7 @@
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
||||
@@ -45,5 +45,7 @@
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>SwiftControl</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
12
keypress_simulator/.clang-format
Normal file
12
keypress_simulator/.clang-format
Normal file
@@ -0,0 +1,12 @@
|
||||
# Defines the Chromium style for automatic reformatting.
|
||||
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
BasedOnStyle: Chromium
|
||||
# This defaults to 'Auto'. Explicitly set it for a while, so that
|
||||
# 'vector<vector<int> >' in existing files gets formatted to
|
||||
# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
|
||||
# 'int>>' if the file already contains at least one such instance.)
|
||||
Standard: Cpp11
|
||||
SortIncludes: true
|
||||
---
|
||||
Language: ObjC
|
||||
ColumnLimit: 100
|
||||
1
keypress_simulator/.github/FUNDING.yml
vendored
Normal file
1
keypress_simulator/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
liberapay: lijy91
|
||||
50
keypress_simulator/.github/workflows/build.yml
vendored
Normal file
50
keypress_simulator/.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: "3.16.8"
|
||||
channel: "stable"
|
||||
- uses: bluefireteam/melos-action@v2
|
||||
- working-directory: ./packages/keypress_simulator/example
|
||||
run: |
|
||||
melos bs
|
||||
flutter build macos --release
|
||||
|
||||
build-web:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: "3.16.8"
|
||||
channel: "stable"
|
||||
- uses: bluefireteam/melos-action@v2
|
||||
- working-directory: ./packages/keypress_simulator/example
|
||||
run: |
|
||||
melos bs
|
||||
flutter build web --release
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: "3.16.8"
|
||||
channel: "stable"
|
||||
- uses: bluefireteam/melos-action@v2
|
||||
- working-directory: ./packages/keypress_simulator/example
|
||||
run: |
|
||||
melos bs
|
||||
flutter build windows --release
|
||||
31
keypress_simulator/.github/workflows/lint.yml
vendored
Normal file
31
keypress_simulator/.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: "3.16.8"
|
||||
channel: "stable"
|
||||
- uses: bluefireteam/melos-action@v2
|
||||
- run: melos run analyze
|
||||
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: "3.16.8"
|
||||
channel: "stable"
|
||||
cache: true
|
||||
- uses: bluefireteam/melos-action@v2
|
||||
- run: melos run format-check
|
||||
20
keypress_simulator/.github/workflows/test.yml
vendored
Normal file
20
keypress_simulator/.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: "3.16.8"
|
||||
channel: "stable"
|
||||
cache: true
|
||||
- uses: bluefireteam/melos-action@v2
|
||||
- run: melos run test --no-select
|
||||
6
keypress_simulator/.gitignore
vendored
Normal file
6
keypress_simulator/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.dart_tool/
|
||||
.idea/
|
||||
|
||||
*.iml
|
||||
pubspec_overrides.yaml
|
||||
pubspec.lock
|
||||
21
keypress_simulator/LICENSE
Normal file
21
keypress_simulator/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2024 LiJianying <lijy91@foxmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
93
keypress_simulator/README-ZH.md
Normal file
93
keypress_simulator/README-ZH.md
Normal file
@@ -0,0 +1,93 @@
|
||||
> **🚀 快速发布您的应用**: 试试 [Fastforge](https://fastforge.dev) - 构建、打包和分发您的 Flutter 应用最简单的方式。
|
||||
|
||||
# keypress_simulator
|
||||
|
||||
[![pub version][pub-image]][pub-url] [![][discord-image]][discord-url]
|
||||
|
||||
[pub-image]: https://img.shields.io/pub/v/keypress_simulator.svg
|
||||
[pub-url]: https://pub.dev/packages/keypress_simulator
|
||||
[discord-image]: https://img.shields.io/discord/884679008049037342.svg
|
||||
[discord-url]: https://discord.gg/zPa6EZ2jqb
|
||||
|
||||
这个插件允许 Flutter 桌面应用模拟按键操作。
|
||||
|
||||
---
|
||||
|
||||
[English](./README.md) | 简体中文
|
||||
|
||||
---
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
- [平台支持](#%E5%B9%B3%E5%8F%B0%E6%94%AF%E6%8C%81)
|
||||
- [快速开始](#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)
|
||||
- [安装](#%E5%AE%89%E8%A3%85)
|
||||
- [用法](#%E7%94%A8%E6%B3%95)
|
||||
- [谁在用使用它?](#%E8%B0%81%E5%9C%A8%E7%94%A8%E4%BD%BF%E7%94%A8%E5%AE%83)
|
||||
- [许可证](#%E8%AE%B8%E5%8F%AF%E8%AF%81)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## 平台支持
|
||||
|
||||
| Linux | macOS | Windows |
|
||||
| :---: | :---: | :-----: |
|
||||
| ➖ | ✔️ | ✔️ |
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 安装
|
||||
|
||||
将此添加到你的软件包的 pubspec.yaml 文件:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
keypress_simulator: ^0.2.0
|
||||
```
|
||||
|
||||
### 用法
|
||||
|
||||
```dart
|
||||
import 'package:keypress_simulator/keypress_simulator.dart';
|
||||
|
||||
// 1. Simulate pressing ⌘ + C
|
||||
|
||||
// 1.1 Simulate key down
|
||||
await keyPressSimulator.simulateKeyDown(
|
||||
PhysicalKeyboardKey.keyC,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
|
||||
// 1.2 Simulate key up
|
||||
await keyPressSimulator.simulateKeyUp(
|
||||
PhysicalKeyboardKey.keyC,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
|
||||
// 2. Simulate long pressing ⌘ + space
|
||||
|
||||
// 2.1. Simulate key down
|
||||
await keyPressSimulator.simulateKeyDown(
|
||||
PhysicalKeyboardKey.space,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
|
||||
// 2.2. Simulate key up
|
||||
await keyPressSimulator.simulateKeyUp(
|
||||
PhysicalKeyboardKey.space,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
```
|
||||
|
||||
> 请看这个插件的示例应用,以了解完整的例子。
|
||||
|
||||
## 谁在用使用它?
|
||||
|
||||
- [Biyi (比译)](https://biyidev.com/) - 一个便捷的翻译和词典应用程序。
|
||||
|
||||
## 许可证
|
||||
|
||||
[MIT](./LICENSE)
|
||||
93
keypress_simulator/README.md
Normal file
93
keypress_simulator/README.md
Normal file
@@ -0,0 +1,93 @@
|
||||
> **🚀 Ship Your App Faster**: Try [Fastforge](https://fastforge.dev) - The simplest way to build, package and distribute your Flutter apps.
|
||||
|
||||
# keypress_simulator
|
||||
|
||||
[![pub version][pub-image]][pub-url] [![][discord-image]][discord-url]
|
||||
|
||||
[pub-image]: https://img.shields.io/pub/v/keypress_simulator.svg
|
||||
[pub-url]: https://pub.dev/packages/keypress_simulator
|
||||
[discord-image]: https://img.shields.io/discord/884679008049037342.svg
|
||||
[discord-url]: https://discord.gg/zPa6EZ2jqb
|
||||
|
||||
This plugin allows Flutter desktop apps to simulate key presses.
|
||||
|
||||
---
|
||||
|
||||
English | [简体中文](./README-ZH.md)
|
||||
|
||||
---
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
- [Platform Support](#platform-support)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Who's using it?](#whos-using-it)
|
||||
- [License](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## Platform Support
|
||||
|
||||
| Linux | macOS | Windows |
|
||||
| :---: | :---: | :-----: |
|
||||
| ➖ | ✔️ | ✔️ |
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Installation
|
||||
|
||||
Add this to your package's pubspec.yaml file:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
keypress_simulator: ^0.2.0
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```dart
|
||||
import 'package:keypress_simulator/keypress_simulator.dart';
|
||||
|
||||
// 1. Simulate pressing ⌘ + C
|
||||
|
||||
// 1.1 Simulate key down
|
||||
await keyPressSimulator.simulateKeyDown(
|
||||
PhysicalKeyboardKey.keyC,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
|
||||
// 1.2 Simulate key up
|
||||
await keyPressSimulator.simulateKeyUp(
|
||||
PhysicalKeyboardKey.keyC,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
|
||||
// 2. Simulate long pressing ⌘ + space
|
||||
|
||||
// 2.1. Simulate key down
|
||||
await keyPressSimulator.simulateKeyDown(
|
||||
PhysicalKeyboardKey.space,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
|
||||
// 2.2. Simulate key up
|
||||
await keyPressSimulator.simulateKeyUp(
|
||||
PhysicalKeyboardKey.space,
|
||||
[ModifierKey.metaModifier],
|
||||
);
|
||||
```
|
||||
|
||||
> Please see the example app of this plugin for a full example.
|
||||
|
||||
## Who's using it?
|
||||
|
||||
- [Biyi (比译)](https://biyidev.com/) - A convenient translation and dictionary app.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
35
keypress_simulator/melos.yaml
Normal file
35
keypress_simulator/melos.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
name: keypress_simulator_workspace
|
||||
repository: https://github.com/leanflutter/keypress_simulator
|
||||
|
||||
packages:
|
||||
- examples/**
|
||||
- packages/**
|
||||
|
||||
command:
|
||||
bootstrap:
|
||||
# Uses the pubspec_overrides.yaml instead of having Melos modifying the lock file.
|
||||
usePubspecOverrides: true
|
||||
|
||||
scripts:
|
||||
analyze:
|
||||
exec: flutter analyze --fatal-infos
|
||||
description: Run `flutter analyze` for all packages.
|
||||
|
||||
test:
|
||||
exec: flutter test
|
||||
description: Run `flutter test` for a specific package.
|
||||
packageFilters:
|
||||
dirExists:
|
||||
- test
|
||||
|
||||
format:
|
||||
exec: dart format . --fix
|
||||
description: Run `dart format` for all packages.
|
||||
|
||||
format-check:
|
||||
exec: dart format . --fix --set-exit-if-changed
|
||||
description: Run `dart format` checks for all packages.
|
||||
|
||||
fix:
|
||||
exec: dart fix . --apply
|
||||
description: Run `dart fix` for all packages.
|
||||
29
keypress_simulator/packages/keypress_simulator/.gitignore
vendored
Normal file
29
keypress_simulator/packages/keypress_simulator/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
||||
10
keypress_simulator/packages/keypress_simulator/.metadata
Normal file
10
keypress_simulator/packages/keypress_simulator/.metadata
Normal file
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: db747aa1331bd95bc9b3874c842261ca2d302cd5
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
@@ -0,0 +1,7 @@
|
||||
## 0.2.0
|
||||
|
||||
* feat: Convert to federated plugin
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* First release.
|
||||
21
keypress_simulator/packages/keypress_simulator/LICENSE
Normal file
21
keypress_simulator/packages/keypress_simulator/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2024 LiJianying <lijy91@foxmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
keypress_simulator/packages/keypress_simulator/README-ZH.md
Symbolic link
1
keypress_simulator/packages/keypress_simulator/README-ZH.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../README-ZH.md
|
||||
1
keypress_simulator/packages/keypress_simulator/README.md
Symbolic link
1
keypress_simulator/packages/keypress_simulator/README.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../README.md
|
||||
@@ -0,0 +1 @@
|
||||
include: package:mostly_reasonable_lints/flutter.yaml
|
||||
@@ -0,0 +1 @@
|
||||
export 'src/keypress_simulator.dart';
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:keypress_simulator_platform_interface/keypress_simulator_platform_interface.dart';
|
||||
|
||||
class KeyPressSimulator {
|
||||
KeyPressSimulator._();
|
||||
|
||||
/// The shared instance of [KeyPressSimulator].
|
||||
static final KeyPressSimulator instance = KeyPressSimulator._();
|
||||
|
||||
KeyPressSimulatorPlatform get _platform => KeyPressSimulatorPlatform.instance;
|
||||
|
||||
Future<bool> isAccessAllowed() {
|
||||
return _platform.isAccessAllowed();
|
||||
}
|
||||
|
||||
Future<void> requestAccess({bool onlyOpenPrefPane = false}) {
|
||||
return _platform.requestAccess(onlyOpenPrefPane: onlyOpenPrefPane);
|
||||
}
|
||||
|
||||
Future<void> simulateMouseClickDown(Offset position) {
|
||||
return _platform.simulateMouseClick(position, keyDown: true);
|
||||
}
|
||||
|
||||
Future<void> simulateMouseClickUp(Offset position) {
|
||||
return _platform.simulateMouseClick(position, keyDown: false);
|
||||
}
|
||||
|
||||
/// Simulate key down.
|
||||
Future<void> simulateKeyDown(PhysicalKeyboardKey? key, [List<ModifierKey> modifiers = const []]) {
|
||||
return _platform.simulateKeyPress(key: key, modifiers: modifiers, keyDown: true);
|
||||
}
|
||||
|
||||
/// Simulate key up.
|
||||
Future<void> simulateKeyUp(PhysicalKeyboardKey? key, [List<ModifierKey> modifiers = const []]) {
|
||||
return _platform.simulateKeyPress(key: key, modifiers: modifiers, keyDown: false);
|
||||
}
|
||||
|
||||
@Deprecated('Please use simulateKeyDown & simulateKeyUp methods.')
|
||||
Future<void> simulateCtrlCKeyPress() async {
|
||||
const key = PhysicalKeyboardKey.keyC;
|
||||
final modifiers = Platform.isMacOS ? [ModifierKey.metaModifier] : [ModifierKey.controlModifier];
|
||||
await simulateKeyDown(key, modifiers);
|
||||
await simulateKeyUp(key, modifiers);
|
||||
}
|
||||
|
||||
@Deprecated('Please use simulateKeyDown & simulateKeyUp methods.')
|
||||
Future<void> simulateCtrlVKeyPress() async {
|
||||
const key = PhysicalKeyboardKey.keyV;
|
||||
final modifiers = Platform.isMacOS ? [ModifierKey.metaModifier] : [ModifierKey.controlModifier];
|
||||
await simulateKeyDown(key, modifiers);
|
||||
await simulateKeyUp(key, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
final keyPressSimulator = KeyPressSimulator.instance;
|
||||
36
keypress_simulator/packages/keypress_simulator/pubspec.yaml
Normal file
36
keypress_simulator/packages/keypress_simulator/pubspec.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
name: keypress_simulator
|
||||
description: This plugin allows Flutter desktop apps to simulate key presses.
|
||||
version: 0.2.0
|
||||
homepage: https://github.com/leanflutter/keypress_simulator
|
||||
|
||||
platforms:
|
||||
macos:
|
||||
windows:
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
keypress_simulator_macos:
|
||||
path: ../keypress_simulator_macos
|
||||
keypress_simulator_platform_interface:
|
||||
path: ../keypress_simulator_platform_interface
|
||||
keypress_simulator_windows:
|
||||
path: ../keypress_simulator_windows
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mostly_reasonable_lints: ^0.1.1
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
macos:
|
||||
default_package: keypress_simulator_macos
|
||||
windows:
|
||||
default_package: keypress_simulator_windows
|
||||
|
||||
29
keypress_simulator/packages/keypress_simulator_macos/.gitignore
vendored
Normal file
29
keypress_simulator/packages/keypress_simulator_macos/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
build/
|
||||
@@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "67457e669f79e9f8d13d7a68fe09775fefbb79f4"
|
||||
channel: "stable"
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
- platform: macos
|
||||
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
@@ -0,0 +1,3 @@
|
||||
## 0.2.0
|
||||
|
||||
* First release.
|
||||
21
keypress_simulator/packages/keypress_simulator_macos/LICENSE
Normal file
21
keypress_simulator/packages/keypress_simulator_macos/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2024 LiJianying <lijy91@foxmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,12 @@
|
||||
# keypress_simulator_macos
|
||||
|
||||
[![pub version][pub-image]][pub-url]
|
||||
|
||||
[pub-image]: https://img.shields.io/pub/v/keypress_simulator_macos.svg
|
||||
[pub-url]: https://pub.dev/packages/keypress_simulator_macos
|
||||
|
||||
The macOS implementation of [keypress_simulator](https://pub.dev/packages/keypress_simulator).
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
@@ -0,0 +1 @@
|
||||
include: package:mostly_reasonable_lints/flutter.yaml
|
||||
@@ -0,0 +1,117 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
public class KeypressSimulatorMacosPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "dev.leanflutter.plugins/keypress_simulator", binaryMessenger: registrar.messenger)
|
||||
let instance = KeypressSimulatorMacosPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "isAccessAllowed":
|
||||
isAccessAllowed(call, result: result)
|
||||
break
|
||||
case "requestAccess":
|
||||
requestAccess(call, result: result)
|
||||
break
|
||||
case "simulateKeyPress":
|
||||
simulateKeyPress(call, result: result)
|
||||
break
|
||||
case "simulateMouseClick":
|
||||
simulateMouseClick(call, result: result)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
public func isAccessAllowed(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
result(AXIsProcessTrusted())
|
||||
}
|
||||
|
||||
public func requestAccess(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let args:[String: Any] = call.arguments as! [String: Any]
|
||||
let onlyOpenPrefPane: Bool = args["onlyOpenPrefPane"] as! Bool
|
||||
|
||||
if (!onlyOpenPrefPane) {
|
||||
let options = [kAXTrustedCheckOptionPrompt.takeRetainedValue(): true] as CFDictionary
|
||||
AXIsProcessTrustedWithOptions(options)
|
||||
} else {
|
||||
let prefpaneUrl = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!
|
||||
NSWorkspace.shared.open(prefpaneUrl)
|
||||
}
|
||||
result(true)
|
||||
}
|
||||
|
||||
public func simulateKeyPress(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let args:[String: Any] = call.arguments as! [String: Any]
|
||||
|
||||
let keyCode: Int? = args["keyCode"] as? Int
|
||||
let modifiers: Array<String> = args["modifiers"] as! Array<String>
|
||||
let keyDown: Bool = args["keyDown"] as! Bool
|
||||
|
||||
let event = _createKeyPressEvent(keyCode, modifiers, keyDown);
|
||||
event.post(tap: .cghidEventTap);
|
||||
result(true)
|
||||
}
|
||||
|
||||
|
||||
public func simulateMouseClick(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let args:[String: Any] = call.arguments as! [String: Any]
|
||||
|
||||
let x: Double = args["x"] as! Double
|
||||
let y: Double = args["y"] as! Double
|
||||
let keyDown: Bool = args["keyDown"] as! Bool
|
||||
|
||||
let point = CGPoint(x: x, y: y)
|
||||
|
||||
// Move mouse to the point
|
||||
/*let move = CGEvent(mouseEventSource: nil,
|
||||
mouseType: .mouseMoved,
|
||||
mouseCursorPosition: point,
|
||||
mouseButton: .left)
|
||||
move?.post(tap: .cghidEventTap)*/
|
||||
|
||||
if (keyDown) {
|
||||
// Mouse down
|
||||
let mouseDown = CGEvent(mouseEventSource: nil,
|
||||
mouseType: .leftMouseDown,
|
||||
mouseCursorPosition: point,
|
||||
mouseButton: .left)
|
||||
mouseDown?.post(tap: .cghidEventTap)
|
||||
} else {
|
||||
// Mouse up
|
||||
let mouseUp = CGEvent(mouseEventSource: nil,
|
||||
mouseType: .leftMouseUp,
|
||||
mouseCursorPosition: point,
|
||||
mouseButton: .left)
|
||||
mouseUp?.post(tap: .cghidEventTap)
|
||||
}
|
||||
result(true)
|
||||
}
|
||||
|
||||
public func _createKeyPressEvent(_ keyCode: Int?, _ modifiers: Array<String>, _ keyDown: Bool) -> CGEvent {
|
||||
let virtualKey: CGKeyCode = CGKeyCode(UInt32(keyCode ?? 0))
|
||||
var flags: CGEventFlags = CGEventFlags()
|
||||
|
||||
if (modifiers.contains("shiftModifier")) {
|
||||
flags.insert(CGEventFlags.maskShift)
|
||||
}
|
||||
if (modifiers.contains("controlModifier")) {
|
||||
flags.insert(CGEventFlags.maskControl)
|
||||
}
|
||||
if (modifiers.contains("altModifier")) {
|
||||
flags.insert(CGEventFlags.maskAlternate)
|
||||
}
|
||||
if (modifiers.contains("metaModifier")) {
|
||||
flags.insert(CGEventFlags.maskCommand)
|
||||
}
|
||||
if (modifiers.contains("functionModifier")) {
|
||||
flags.insert(CGEventFlags.maskSecondaryFn)
|
||||
}
|
||||
let eventKeyPress = CGEvent(keyboardEventSource: nil, virtualKey: virtualKey, keyDown: keyDown);
|
||||
eventKeyPress!.flags = flags
|
||||
return eventKeyPress!
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint keypress_simulator_macos.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'keypress_simulator_macos'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A new Flutter plugin project.'
|
||||
s.description = <<-DESC
|
||||
A new Flutter plugin project.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.11'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
name: keypress_simulator_macos
|
||||
description: macOS implementation of the keypress_simulator plugin.
|
||||
version: 0.2.0
|
||||
repository: https://github.com/leanflutter/keypress_simulator/tree/main/packages/keypress_simulator_macos
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
flutter: '>=3.3.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
keypress_simulator_platform_interface:
|
||||
path: ../keypress_simulator_platform_interface
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mostly_reasonable_lints: ^0.1.1
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
implements: keypress_simulator
|
||||
platforms:
|
||||
macos:
|
||||
pluginClass: KeypressSimulatorMacosPlugin
|
||||
|
||||
29
keypress_simulator/packages/keypress_simulator_platform_interface/.gitignore
vendored
Normal file
29
keypress_simulator/packages/keypress_simulator_platform_interface/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
build/
|
||||
@@ -0,0 +1,27 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "67457e669f79e9f8d13d7a68fe09775fefbb79f4"
|
||||
channel: "stable"
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
@@ -0,0 +1,3 @@
|
||||
## 0.2.0
|
||||
|
||||
* First release.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2024 LiJianying <lijy91@foxmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,16 @@
|
||||
# keypress_simulator_platform_interface
|
||||
|
||||
[![pub version][pub-image]][pub-url]
|
||||
|
||||
[pub-image]: https://img.shields.io/pub/v/keypress_simulator_platform_interface.svg
|
||||
[pub-url]: https://pub.dev/packages/keypress_simulator_platform_interface
|
||||
|
||||
A common platform interface for the [keypress_simulator](https://pub.dev/packages/keypress_simulator) plugin.
|
||||
|
||||
## Usage
|
||||
|
||||
To implement a new platform-specific implementation of keypress_simulator, extend `KeyPressSimulatorPlatform` with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `KeyPressSimulatorPlatform` by calling `KeyPressSimulatorPlatform.instance = MyPlatformKeyPressSimulator()`.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
@@ -0,0 +1 @@
|
||||
include: package:mostly_reasonable_lints/flutter.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
library keypress_simulator_platform_interface;
|
||||
|
||||
export 'src/keypress_simulator_method_channel.dart';
|
||||
export 'src/keypress_simulator_platform_interface.dart';
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:keypress_simulator_platform_interface/src/keypress_simulator_platform_interface.dart';
|
||||
import 'package:uni_platform/uni_platform.dart';
|
||||
|
||||
/// An implementation of [KeyPressSimulatorPlatform] that uses method channels.
|
||||
class MethodChannelKeyPressSimulator extends KeyPressSimulatorPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel(
|
||||
'dev.leanflutter.plugins/keypress_simulator',
|
||||
);
|
||||
|
||||
@override
|
||||
Future<bool> isAccessAllowed() async {
|
||||
if (UniPlatform.isMacOS) {
|
||||
return await methodChannel.invokeMethod('isAccessAllowed');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> requestAccess({
|
||||
bool onlyOpenPrefPane = false,
|
||||
}) async {
|
||||
if (UniPlatform.isMacOS) {
|
||||
final Map<String, dynamic> arguments = {
|
||||
'onlyOpenPrefPane': onlyOpenPrefPane,
|
||||
};
|
||||
await methodChannel.invokeMethod('requestAccess', arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> simulateKeyPress({
|
||||
KeyboardKey? key,
|
||||
List<ModifierKey> modifiers = const [],
|
||||
bool keyDown = true,
|
||||
}) async {
|
||||
PhysicalKeyboardKey? physicalKey = key is PhysicalKeyboardKey ? key : null;
|
||||
if (key is LogicalKeyboardKey) {
|
||||
physicalKey = key.physicalKey;
|
||||
}
|
||||
if (key != null && physicalKey == null) {
|
||||
throw UnsupportedError('Unsupported key: $key.');
|
||||
}
|
||||
final Map<Object?, Object?> arguments = {
|
||||
'keyCode': physicalKey?.keyCode,
|
||||
'modifiers': modifiers.map((e) => e.name).toList(),
|
||||
'keyDown': keyDown,
|
||||
}..removeWhere((key, value) => value == null);
|
||||
await methodChannel.invokeMethod('simulateKeyPress', arguments);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> simulateMouseClick(Offset position, {required bool keyDown}) async {
|
||||
final Map<String, Object?> arguments = {
|
||||
'x': position.dx,
|
||||
'y': position.dy,
|
||||
'keyDown': keyDown,
|
||||
};
|
||||
await methodChannel.invokeMethod('simulateMouseClick', arguments);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:keypress_simulator_platform_interface/src/keypress_simulator_method_channel.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
abstract class KeyPressSimulatorPlatform extends PlatformInterface {
|
||||
/// Constructs a KeyPressSimulatorPlatform.
|
||||
KeyPressSimulatorPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static KeyPressSimulatorPlatform _instance = MethodChannelKeyPressSimulator();
|
||||
|
||||
/// The default instance of [KeyPressSimulatorPlatform] to use.
|
||||
///
|
||||
/// Defaults to [MethodChannelKeyPressSimulator].
|
||||
static KeyPressSimulatorPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [KeyPressSimulatorPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(KeyPressSimulatorPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<bool> isAccessAllowed() {
|
||||
throw UnimplementedError('isAccessAllowed() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<void> requestAccess({
|
||||
bool onlyOpenPrefPane = false,
|
||||
}) {
|
||||
throw UnimplementedError('requestAccess() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<void> simulateKeyPress({
|
||||
KeyboardKey? key,
|
||||
List<ModifierKey> modifiers = const [],
|
||||
bool keyDown = true,
|
||||
}) {
|
||||
throw UnimplementedError('simulateKeyPress() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<void> simulateMouseClick(Offset position, {required bool keyDown}) {
|
||||
throw UnimplementedError('simulateKeyPress() has not been implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
name: keypress_simulator_platform_interface
|
||||
description: A common platform interface for the keypress_simulator plugin.
|
||||
version: 0.2.0
|
||||
homepage: https://github.com/leanflutter/keypress_simulator/blob/main/packages/keypress_simulator_platform_interface
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
flutter: '>=3.3.0'
|
||||
|
||||
dependencies:
|
||||
collection: ^1.18.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
plugin_platform_interface: ^2.1.8
|
||||
uni_platform: ^0.1.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mostly_reasonable_lints: ^0.1.1
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:keypress_simulator_platform_interface/src/keypress_simulator_method_channel.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
MethodChannelKeyPressSimulator platform = MethodChannelKeyPressSimulator();
|
||||
const MethodChannel channel = MethodChannel(
|
||||
'dev.leanflutter.plugins/keypress_simulator',
|
||||
);
|
||||
|
||||
setUp(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(
|
||||
channel,
|
||||
(MethodCall methodCall) async {
|
||||
if (methodCall.method == 'isAccessAllowed') return true;
|
||||
return '42';
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
|
||||
test('isAccessAllowed', () async {
|
||||
expect(await platform.isAccessAllowed(), true);
|
||||
});
|
||||
}
|
||||
29
keypress_simulator/packages/keypress_simulator_windows/.gitignore
vendored
Normal file
29
keypress_simulator/packages/keypress_simulator_windows/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
build/
|
||||
@@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "67457e669f79e9f8d13d7a68fe09775fefbb79f4"
|
||||
channel: "stable"
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
- platform: windows
|
||||
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
@@ -0,0 +1,3 @@
|
||||
## 0.2.0
|
||||
|
||||
* First release.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2024 LiJianying <lijy91@foxmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,12 @@
|
||||
# keypress_simulator_windows
|
||||
|
||||
[![pub version][pub-image]][pub-url]
|
||||
|
||||
[pub-image]: https://img.shields.io/pub/v/keypress_simulator_windows.svg
|
||||
[pub-url]: https://pub.dev/packages/keypress_simulator_windows
|
||||
|
||||
The Windows implementation of [keypress_simulator](https://pub.dev/packages/keypress_simulator).
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
@@ -0,0 +1 @@
|
||||
include: package:mostly_reasonable_lints/flutter.yaml
|
||||
@@ -0,0 +1,26 @@
|
||||
name: keypress_simulator_windows
|
||||
description: Windows implementation of the keypress_simulator plugin.
|
||||
version: 0.2.0
|
||||
repository: https://github.com/leanflutter/keypress_simulator/tree/main/packages/keypress_simulator_windows
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
flutter: '>=3.3.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
keypress_simulator_platform_interface:
|
||||
path: ../keypress_simulator_platform_interface
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mostly_reasonable_lints: ^0.1.1
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
implements: keypress_simulator
|
||||
platforms:
|
||||
windows:
|
||||
pluginClass: KeypressSimulatorWindowsPluginCApi
|
||||
17
keypress_simulator/packages/keypress_simulator_windows/windows/.gitignore
vendored
Normal file
17
keypress_simulator/packages/keypress_simulator_windows/windows/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
flutter/
|
||||
|
||||
# Visual Studio user-specific files.
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Visual Studio build-related files.
|
||||
x64/
|
||||
x86/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
@@ -0,0 +1,100 @@
|
||||
# The Flutter tooling requires that developers have a version of Visual Studio
|
||||
# installed that includes CMake 3.14 or later. You should not increase this
|
||||
# version, as doing so will cause the plugin to fail to compile for some
|
||||
# customers of the plugin.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
# Project-level configuration.
|
||||
set(PROJECT_NAME "keypress_simulator_windows")
|
||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
cmake_policy(VERSION 3.14...3.25)
|
||||
|
||||
# This value is used when generating builds using this plugin, so it must
|
||||
# not be changed
|
||||
set(PLUGIN_NAME "keypress_simulator_windows_plugin")
|
||||
|
||||
# Any new source files that you add to the plugin should be added here.
|
||||
list(APPEND PLUGIN_SOURCES
|
||||
"keypress_simulator_windows_plugin.cpp"
|
||||
"keypress_simulator_windows_plugin.h"
|
||||
)
|
||||
|
||||
# Define the plugin library target. Its name must not be changed (see comment
|
||||
# on PLUGIN_NAME above).
|
||||
add_library(${PLUGIN_NAME} SHARED
|
||||
"include/keypress_simulator_windows/keypress_simulator_windows_plugin_c_api.h"
|
||||
"keypress_simulator_windows_plugin_c_api.cpp"
|
||||
${PLUGIN_SOURCES}
|
||||
)
|
||||
|
||||
# Apply a standard set of build settings that are configured in the
|
||||
# application-level CMakeLists.txt. This can be removed for plugins that want
|
||||
# full control over build settings.
|
||||
apply_standard_settings(${PLUGIN_NAME})
|
||||
|
||||
# Symbols are hidden by default to reduce the chance of accidental conflicts
|
||||
# between plugins. This should not be removed; any symbols that should be
|
||||
# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.
|
||||
set_target_properties(${PLUGIN_NAME} PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
|
||||
|
||||
# Source include directories and library dependencies. Add any plugin-specific
|
||||
# dependencies here.
|
||||
target_include_directories(${PLUGIN_NAME} INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
|
||||
|
||||
# List of absolute paths to libraries that should be bundled with the plugin.
|
||||
# This list could contain prebuilt libraries, or libraries created by an
|
||||
# external build triggered from this build file.
|
||||
set(keypress_simulator_windows_bundled_libraries
|
||||
""
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
# === Tests ===
|
||||
# These unit tests can be run from a terminal after building the example, or
|
||||
# from Visual Studio after opening the generated solution file.
|
||||
|
||||
# Only enable test builds when building the example (which sets this variable)
|
||||
# so that plugin clients aren't building the tests.
|
||||
if (${include_${PROJECT_NAME}_tests})
|
||||
set(TEST_RUNNER "${PROJECT_NAME}_test")
|
||||
enable_testing()
|
||||
|
||||
# Add the Google Test dependency.
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/release-1.11.0.zip
|
||||
)
|
||||
# Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
# Disable install commands for gtest so it doesn't end up in the bundle.
|
||||
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
|
||||
# The plugin's C API is not very useful for unit testing, so build the sources
|
||||
# directly into the test binary rather than using the DLL.
|
||||
add_executable(${TEST_RUNNER}
|
||||
test/keypress_simulator_windows_plugin_test.cpp
|
||||
${PLUGIN_SOURCES}
|
||||
)
|
||||
apply_standard_settings(${TEST_RUNNER})
|
||||
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)
|
||||
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
|
||||
# flutter_wrapper_plugin has link dependencies on the Flutter DLL.
|
||||
add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${FLUTTER_LIBRARY}" $<TARGET_FILE_DIR:${TEST_RUNNER}>
|
||||
)
|
||||
|
||||
# Enable automatic test discovery.
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(${TEST_RUNNER})
|
||||
endif()
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef FLUTTER_PLUGIN_KEYPRESS_SIMULATOR_WINDOWS_PLUGIN_C_API_H_
|
||||
#define FLUTTER_PLUGIN_KEYPRESS_SIMULATOR_WINDOWS_PLUGIN_C_API_H_
|
||||
|
||||
#include <flutter_plugin_registrar.h>
|
||||
|
||||
#ifdef FLUTTER_PLUGIN_IMPL
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void KeypressSimulatorWindowsPluginCApiRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // FLUTTER_PLUGIN_KEYPRESS_SIMULATOR_WINDOWS_PLUGIN_C_API_H_
|
||||
@@ -0,0 +1,233 @@
|
||||
#include "keypress_simulator_windows_plugin.h"
|
||||
|
||||
// This must be included before many other Windows headers.
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <string.h>
|
||||
#include <flutter_windows.h>
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
using flutter::EncodableList;
|
||||
using flutter::EncodableMap;
|
||||
using flutter::EncodableValue;
|
||||
|
||||
namespace keypress_simulator_windows {
|
||||
|
||||
// Forward declarations
|
||||
struct FindWindowData {
|
||||
std::string targetProcessName;
|
||||
std::string targetWindowTitle;
|
||||
HWND foundWindow;
|
||||
};
|
||||
|
||||
BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam);
|
||||
HWND FindTargetWindow(const std::string& processName, const std::string& windowTitle);
|
||||
|
||||
// static
|
||||
void KeypressSimulatorWindowsPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarWindows* registrar) {
|
||||
auto channel =
|
||||
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||||
registrar->messenger(), "dev.leanflutter.plugins/keypress_simulator",
|
||||
&flutter::StandardMethodCodec::GetInstance());
|
||||
|
||||
auto plugin = std::make_unique<KeypressSimulatorWindowsPlugin>();
|
||||
|
||||
channel->SetMethodCallHandler(
|
||||
[plugin_pointer = plugin.get()](const auto& call, auto result) {
|
||||
plugin_pointer->HandleMethodCall(call, std::move(result));
|
||||
});
|
||||
|
||||
registrar->AddPlugin(std::move(plugin));
|
||||
}
|
||||
|
||||
KeypressSimulatorWindowsPlugin::KeypressSimulatorWindowsPlugin() {}
|
||||
|
||||
KeypressSimulatorWindowsPlugin::~KeypressSimulatorWindowsPlugin() {}
|
||||
|
||||
void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
const EncodableMap& args = std::get<EncodableMap>(*method_call.arguments());
|
||||
|
||||
UINT keyCode = std::get<int>(args.at(EncodableValue("keyCode")));
|
||||
std::vector<std::string> modifiers;
|
||||
bool keyDown = std::get<bool>(args.at(EncodableValue("keyDown")));
|
||||
|
||||
EncodableList key_modifier_list =
|
||||
std::get<EncodableList>(args.at(EncodableValue("modifiers")));
|
||||
for (flutter::EncodableValue key_modifier_value : key_modifier_list) {
|
||||
std::string key_modifier = std::get<std::string>(key_modifier_value);
|
||||
modifiers.push_back(key_modifier);
|
||||
}
|
||||
|
||||
// List of compatible training apps to look for
|
||||
std::vector<std::string> compatibleApps = {
|
||||
"MyWhooshHD.exe",
|
||||
"indieVelo.exe",
|
||||
"biketerra.exe"
|
||||
};
|
||||
|
||||
// Try to find and focus a compatible app
|
||||
HWND targetWindow = NULL;
|
||||
for (const std::string& processName : compatibleApps) {
|
||||
targetWindow = FindTargetWindow(processName, "");
|
||||
if (targetWindow != NULL) {
|
||||
// Only focus the window if it's not already in the foreground
|
||||
if (GetForegroundWindow() != targetWindow) {
|
||||
SetForegroundWindow(targetWindow);
|
||||
Sleep(50); // Brief delay to ensure window is focused
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WORD sc = (WORD)MapVirtualKey(keyCode, MAPVK_VK_TO_VSC);
|
||||
|
||||
INPUT in = {0};
|
||||
in.type = INPUT_KEYBOARD;
|
||||
in.ki.wVk = 0; // when using SCANCODE, set VK=0
|
||||
in.ki.wScan = sc;
|
||||
in.ki.dwFlags = KEYEVENTF_SCANCODE | (keyDown ? 0 : KEYEVENTF_KEYUP);
|
||||
if (keyCode == VK_LEFT || keyCode == VK_RIGHT || keyCode == VK_UP || keyCode == VK_DOWN ||
|
||||
keyCode == VK_INSERT || keyCode == VK_DELETE || keyCode == VK_HOME || keyCode == VK_END ||
|
||||
keyCode == VK_PRIOR || keyCode == VK_NEXT) {
|
||||
in.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
}
|
||||
SendInput(1, &in, sizeof(INPUT));
|
||||
|
||||
/*BYTE byteValue = static_cast<BYTE>(keyCode);
|
||||
keybd_event(byteValue, 0x45, keyDown ? 0 : KEYEVENTF_KEYUP, 0);*/
|
||||
|
||||
result->Success(flutter::EncodableValue(true));
|
||||
}
|
||||
|
||||
void KeypressSimulatorWindowsPlugin::SimulateMouseClick(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
|
||||
const EncodableMap& args = std::get<EncodableMap>(*method_call.arguments());
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
|
||||
bool keyDown = std::get<bool>(args.at(EncodableValue("keyDown")));
|
||||
auto it_x = args.find(EncodableValue("x"));
|
||||
if (it_x != args.end() && std::holds_alternative<double>(it_x->second)) {
|
||||
x = std::get<double>(it_x->second);
|
||||
}
|
||||
|
||||
auto it_y = args.find(EncodableValue("y"));
|
||||
if (it_y != args.end() && std::holds_alternative<double>(it_y->second)) {
|
||||
y = std::get<double>(it_y->second);
|
||||
}
|
||||
|
||||
// Get the monitor containing the target point and its DPI
|
||||
const POINT target_point = {static_cast<LONG>(x), static_cast<LONG>(y)};
|
||||
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
|
||||
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
|
||||
double scale_factor = dpi / 96.0;
|
||||
|
||||
// Scale the coordinates according to the DPI scaling
|
||||
int scaled_x = static_cast<int>(x * scale_factor);
|
||||
int scaled_y = static_cast<int>(y * scale_factor);
|
||||
|
||||
// Move the mouse to the specified coordinates
|
||||
SetCursorPos(scaled_x, scaled_y);
|
||||
|
||||
// Prepare input for mouse down and up
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_MOUSE;
|
||||
|
||||
if (keyDown) {
|
||||
// Mouse left button down
|
||||
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
|
||||
} else {
|
||||
// Mouse left button up
|
||||
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
}
|
||||
|
||||
result->Success(flutter::EncodableValue(true));
|
||||
}
|
||||
|
||||
BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam) {
|
||||
FindWindowData* data = reinterpret_cast<FindWindowData*>(lParam);
|
||||
|
||||
// Check if window is visible and not minimized
|
||||
if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) {
|
||||
return TRUE; // Continue enumeration
|
||||
}
|
||||
|
||||
// Get window title
|
||||
char windowTitle[256];
|
||||
GetWindowTextA(hwnd, windowTitle, sizeof(windowTitle));
|
||||
|
||||
// Get process name
|
||||
DWORD processId;
|
||||
GetWindowThreadProcessId(hwnd, &processId);
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
|
||||
char processName[MAX_PATH];
|
||||
if (hProcess) {
|
||||
DWORD size = sizeof(processName);
|
||||
if (QueryFullProcessImageNameA(hProcess, 0, processName, &size)) {
|
||||
// Extract just the filename from the full path
|
||||
char* filename = strrchr(processName, '\\');
|
||||
if (filename) {
|
||||
filename++; // Skip the backslash
|
||||
} else {
|
||||
filename = processName;
|
||||
}
|
||||
|
||||
// Check if this matches our target
|
||||
if (!data->targetProcessName.empty() &&
|
||||
_stricmp(filename, data->targetProcessName.c_str()) == 0) {
|
||||
data->foundWindow = hwnd;
|
||||
return FALSE; // Stop enumeration
|
||||
}
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
|
||||
// Check window title if process name didn't match
|
||||
if (!data->targetWindowTitle.empty() &&
|
||||
_stricmp(windowTitle, data->targetWindowTitle.c_str()) == 0) {
|
||||
data->foundWindow = hwnd;
|
||||
return FALSE; // Stop enumeration
|
||||
}
|
||||
|
||||
return TRUE; // Continue enumeration
|
||||
}
|
||||
|
||||
HWND FindTargetWindow(const std::string& processName, const std::string& windowTitle) {
|
||||
FindWindowData data;
|
||||
data.targetProcessName = processName;
|
||||
data.targetWindowTitle = windowTitle;
|
||||
data.foundWindow = NULL;
|
||||
|
||||
EnumWindows(EnumWindowsCallback, reinterpret_cast<LPARAM>(&data));
|
||||
return data.foundWindow;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void KeypressSimulatorWindowsPlugin::HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
if (method_call.method_name().compare("simulateKeyPress") == 0) {
|
||||
SimulateKeyPress(method_call, std::move(result));
|
||||
} else if (method_call.method_name().compare("simulateMouseClick") == 0) {
|
||||
SimulateMouseClick(method_call, std::move(result));
|
||||
} else {
|
||||
result->NotImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace keypress_simulator_windows
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef FLUTTER_PLUGIN_KEYPRESS_SIMULATOR_WINDOWS_PLUGIN_H_
|
||||
#define FLUTTER_PLUGIN_KEYPRESS_SIMULATOR_WINDOWS_PLUGIN_H_
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace keypress_simulator_windows {
|
||||
|
||||
class KeypressSimulatorWindowsPlugin : public flutter::Plugin {
|
||||
public:
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);
|
||||
|
||||
KeypressSimulatorWindowsPlugin();
|
||||
|
||||
virtual ~KeypressSimulatorWindowsPlugin();
|
||||
|
||||
// Disallow copy and assign.
|
||||
KeypressSimulatorWindowsPlugin(const KeypressSimulatorWindowsPlugin&) =
|
||||
delete;
|
||||
KeypressSimulatorWindowsPlugin& operator=(
|
||||
const KeypressSimulatorWindowsPlugin&) = delete;
|
||||
|
||||
void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
void KeypressSimulatorWindowsPlugin::SimulateMouseClick(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
|
||||
|
||||
// Called when a method is called on this plugin's channel from Dart.
|
||||
void HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
};
|
||||
|
||||
} // namespace keypress_simulator_windows
|
||||
|
||||
#endif // FLUTTER_PLUGIN_KEYPRESS_SIMULATOR_WINDOWS_PLUGIN_H_
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "include/keypress_simulator_windows/keypress_simulator_windows_plugin_c_api.h"
|
||||
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
|
||||
#include "keypress_simulator_windows_plugin.h"
|
||||
|
||||
void KeypressSimulatorWindowsPluginCApiRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar) {
|
||||
keypress_simulator_windows::KeypressSimulatorWindowsPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarManager::GetInstance()
|
||||
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <flutter/method_call.h>
|
||||
#include <flutter/method_result_functions.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "keypress_simulator_windows_plugin.h"
|
||||
|
||||
namespace keypress_simulator_windows {
|
||||
namespace test {
|
||||
|
||||
namespace {
|
||||
|
||||
using flutter::EncodableMap;
|
||||
using flutter::EncodableValue;
|
||||
using flutter::MethodCall;
|
||||
using flutter::MethodResultFunctions;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(KeypressSimulatorWindowsPlugin, GetPlatformVersion) {
|
||||
KeypressSimulatorWindowsPlugin plugin;
|
||||
// Save the reply value from the success callback.
|
||||
std::string result_string;
|
||||
plugin.HandleMethodCall(
|
||||
MethodCall("getPlatformVersion", std::make_unique<EncodableValue>()),
|
||||
std::make_unique<MethodResultFunctions<>>(
|
||||
[&result_string](const EncodableValue* result) {
|
||||
result_string = std::get<std::string>(*result);
|
||||
},
|
||||
nullptr, nullptr));
|
||||
|
||||
// Since the exact string varies by host, just ensure that it's a string
|
||||
// with the expected format.
|
||||
EXPECT_TRUE(result_string.rfind("Windows ", 0) == 0);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace keypress_simulator_windows
|
||||
9
keypress_simulator/pubspec.yaml
Normal file
9
keypress_simulator/pubspec.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
name: keypress_simulator_workspace
|
||||
homepage: https://github.com/leanflutter/keypress_simulator
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
dev_dependencies:
|
||||
melos: ^3.1.0
|
||||
@@ -1,6 +1,12 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
class BleUuid {
|
||||
static final DEVICE_INFORMATION_SERVICE_UUID = "0000180a-0000-1000-8000-00805f9b34fb".toLowerCase();
|
||||
static final DEVICE_INFORMATION_CHARACTERISTIC_FIRMWARE_REVISION =
|
||||
"00002a26-0000-1000-8000-00805f9b34fb".toLowerCase();
|
||||
|
||||
static final DEVICE_BATTERY_SERVICE_UUID = "0000180f-0000-1000-8000-00805f9b34fb".toLowerCase();
|
||||
static final DEVICE_INFORMATION_CHARACTERISTIC_BATTERY_LEVEL = "00002a19-0000-1000-8000-00805f9b34fb".toLowerCase();
|
||||
static final ZWIFT_CUSTOM_SERVICE_UUID = "00000001-19CA-4651-86E5-FA29DCDD09D1".toLowerCase();
|
||||
static final ZWIFT_RIDE_CUSTOM_SERVICE_UUID = "0000fc82-0000-1000-8000-00805f9b34fb".toLowerCase();
|
||||
static final ZWIFT_ASYNC_CHARACTERISTIC_UUID = "00000002-19CA-4651-86E5-FA29DCDD09D1".toLowerCase();
|
||||
@@ -9,7 +15,7 @@ class BleUuid {
|
||||
}
|
||||
|
||||
class Constants {
|
||||
static const ZWIFT_MANUFACTURER_ID = 2378; // Zwift, Inc
|
||||
static const ZWIFT_MANUFACTURER_ID = 2378; // Zwift, Inc => 0x094A
|
||||
|
||||
// Zwift Play = RC1
|
||||
static const RC1_LEFT_SIDE = 0x03;
|
||||
@@ -22,17 +28,57 @@ class Constants {
|
||||
// Zwift Click = BC1
|
||||
static const BC1 = 0x09;
|
||||
|
||||
// Zwift Click v2 Right (unconfirmed)
|
||||
static const CLICK_V2_RIGHT_SIDE = 0x0A;
|
||||
// Zwift Click v2 Right (unconfirmed)
|
||||
static const CLICK_V2_LEFT_SIDE = 0x0B;
|
||||
|
||||
static final RIDE_ON = Uint8List.fromList([0x52, 0x69, 0x64, 0x65, 0x4f, 0x6e]);
|
||||
static final VIBRATE_PATTERN = Uint8List.fromList([0x12, 0x12, 0x08, 0x0A, 0x06, 0x08, 0x02, 0x10, 0x00, 0x18]);
|
||||
|
||||
// these don't actually seem to matter, its just the header has to be 7 bytes RIDEON + 2
|
||||
static final REQUEST_START = Uint8List.fromList([0, 9]); //byteArrayOf(1, 2)
|
||||
static final RESPONSE_START_CLICK = Uint8List.fromList([1, 3]); // from device
|
||||
static final RESPONSE_START_PLAY = Uint8List.fromList([1, 4]); // from device
|
||||
static final RESPONSE_START_CLICK_V2 = Uint8List.fromList([0x02, 0x03]); // from device
|
||||
static final RESPONSE_STOPPED_CLICK_V2 = Uint8List.fromList([
|
||||
0xff,
|
||||
0x05,
|
||||
0x00,
|
||||
0xea,
|
||||
0x05,
|
||||
0x19,
|
||||
0x0a,
|
||||
0x0c,
|
||||
0x35,
|
||||
0x38,
|
||||
0x44,
|
||||
0x31,
|
||||
0x35,
|
||||
0x41,
|
||||
0x42,
|
||||
0x42,
|
||||
0x34,
|
||||
0x33,
|
||||
0x36,
|
||||
0x33,
|
||||
0x10,
|
||||
0x01,
|
||||
0x18,
|
||||
0x84,
|
||||
0x07,
|
||||
0x20,
|
||||
0x08,
|
||||
0x28,
|
||||
0x09,
|
||||
0x30,
|
||||
]); // from device
|
||||
|
||||
// Message types received from device
|
||||
static const CONTROLLER_NOTIFICATION_MESSAGE_TYPE = 07;
|
||||
static const EMPTY_MESSAGE_TYPE = 21;
|
||||
static const BATTERY_LEVEL_TYPE = 25;
|
||||
static const UNKNOWN_CLICKV2_TYPE = 0x3C;
|
||||
|
||||
// not figured out the protobuf type this really is, the content is just two varints.
|
||||
static const int CLICK_NOTIFICATION_MESSAGE_TYPE = 55;
|
||||
@@ -45,9 +91,12 @@ class Constants {
|
||||
|
||||
enum DeviceType {
|
||||
click,
|
||||
ride,
|
||||
clickV2Right,
|
||||
clickV2Left,
|
||||
playLeft,
|
||||
playRight;
|
||||
playRight,
|
||||
rideRight,
|
||||
rideLeft;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -59,10 +108,18 @@ enum DeviceType {
|
||||
switch (data) {
|
||||
case Constants.BC1:
|
||||
return DeviceType.click;
|
||||
case Constants.CLICK_V2_RIGHT_SIDE:
|
||||
return DeviceType.clickV2Right;
|
||||
case Constants.CLICK_V2_LEFT_SIDE:
|
||||
return DeviceType.clickV2Left;
|
||||
case Constants.RC1_LEFT_SIDE:
|
||||
return DeviceType.playLeft;
|
||||
case Constants.RC1_RIGHT_SIDE:
|
||||
return DeviceType.playRight;
|
||||
case Constants.RIDE_RIGHT_SIDE:
|
||||
return DeviceType.rideRight;
|
||||
case Constants.RIDE_LEFT_SIDE:
|
||||
return DeviceType.rideLeft;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
192
lib/bluetooth/connection.dart
Normal file
192
lib/bluetooth/connection.dart
Normal file
@@ -0,0 +1,192 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:swift_control/main.dart';
|
||||
import 'package:swift_control/utils/actions/android.dart';
|
||||
import 'package:swift_control/utils/requirements/android.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
import '../bluetooth/ble.dart';
|
||||
import 'devices/base_device.dart';
|
||||
import 'messages/notification.dart';
|
||||
|
||||
class Connection {
|
||||
final devices = <BaseDevice>[];
|
||||
var _androidNotificationsSetup = false;
|
||||
|
||||
final _connectionQueue = <BaseDevice>[];
|
||||
var _handlingConnectionQueue = false;
|
||||
|
||||
final Map<BaseDevice, StreamSubscription<BaseNotification>> _streamSubscriptions = {};
|
||||
final StreamController<BaseNotification> _actionStreams = StreamController<BaseNotification>.broadcast();
|
||||
Stream<BaseNotification> get actionStream => _actionStreams.stream;
|
||||
|
||||
final Map<BaseDevice, StreamSubscription<bool>> _connectionSubscriptions = {};
|
||||
final StreamController<BaseDevice> _connectionStreams = StreamController<BaseDevice>.broadcast();
|
||||
Stream<BaseDevice> get connectionStream => _connectionStreams.stream;
|
||||
|
||||
final _lastScanResult = <BleDevice>[];
|
||||
final ValueNotifier<bool> hasDevices = ValueNotifier(false);
|
||||
final ValueNotifier<bool> isScanning = ValueNotifier(false);
|
||||
|
||||
void initialize() {
|
||||
UniversalBle.onScanResult = (result) {
|
||||
if (_lastScanResult.none((e) => e.deviceId == result.deviceId)) {
|
||||
_lastScanResult.add(result);
|
||||
final scanResult = BaseDevice.fromScanResult(result);
|
||||
|
||||
if (scanResult != null) {
|
||||
_actionStreams.add(LogNotification('Found new device: ${scanResult.runtimeType}'));
|
||||
_addDevices([scanResult]);
|
||||
} else {
|
||||
final manufacturerData = result.manufacturerDataList;
|
||||
final data =
|
||||
manufacturerData.firstOrNullWhere((e) => e.companyId == Constants.ZWIFT_MANUFACTURER_ID)?.payload;
|
||||
_actionStreams.add(LogNotification('Found unknown device with identifier: ${data?.firstOrNull}'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UniversalBle.onValueChange = (deviceId, characteristicUuid, value) {
|
||||
final device = devices.firstOrNullWhere((e) => e.device.deviceId == deviceId);
|
||||
if (device == null) {
|
||||
_actionStreams.add(LogNotification('Device not found: $deviceId'));
|
||||
UniversalBle.disconnect(deviceId);
|
||||
return;
|
||||
} else {
|
||||
device.processCharacteristic(characteristicUuid, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> performScanning() async {
|
||||
isScanning.value = true;
|
||||
_actionStreams.add(LogNotification('Scanning for devices...'));
|
||||
|
||||
// does not work on web, may not work on Windows
|
||||
if (!kIsWeb && !Platform.isWindows) {
|
||||
UniversalBle.getSystemDevices(
|
||||
withServices: [BleUuid.ZWIFT_CUSTOM_SERVICE_UUID, BleUuid.ZWIFT_RIDE_CUSTOM_SERVICE_UUID],
|
||||
).then((devices) async {
|
||||
final baseDevices = devices.mapNotNull(BaseDevice.fromScanResult).toList();
|
||||
if (baseDevices.isNotEmpty) {
|
||||
_addDevices(baseDevices);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await UniversalBle.startScan(
|
||||
scanFilter: ScanFilter(withServices: [BleUuid.ZWIFT_CUSTOM_SERVICE_UUID, BleUuid.ZWIFT_RIDE_CUSTOM_SERVICE_UUID]),
|
||||
platformConfig: PlatformConfig(web: WebOptions(optionalServices: [BleUuid.ZWIFT_CUSTOM_SERVICE_UUID])),
|
||||
);
|
||||
Future.delayed(Duration(seconds: 30)).then((_) {
|
||||
if (isScanning.value) {
|
||||
UniversalBle.stopScan();
|
||||
isScanning.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _addDevices(List<BaseDevice> dev) {
|
||||
final newDevices = dev.where((device) => !devices.contains(device)).toList();
|
||||
devices.addAll(newDevices);
|
||||
|
||||
_connectionQueue.addAll(newDevices);
|
||||
_handleConnectionQueue();
|
||||
|
||||
hasDevices.value = devices.isNotEmpty;
|
||||
if (devices.isNotEmpty && !_androidNotificationsSetup && !kIsWeb && Platform.isAndroid) {
|
||||
_androidNotificationsSetup = true;
|
||||
NotificationRequirement.setup().catchError((e) {
|
||||
_actionStreams.add(LogNotification(e.toString()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _handleConnectionQueue() {
|
||||
// windows apparently has issues when connecting to multiple devices at once, so don't
|
||||
if (_connectionQueue.isNotEmpty && !_handlingConnectionQueue) {
|
||||
_handlingConnectionQueue = true;
|
||||
final device = _connectionQueue.removeAt(0);
|
||||
_actionStreams.add(LogNotification('Connecting to: ${device.device.name ?? device.runtimeType}'));
|
||||
_connect(device)
|
||||
.then((_) {
|
||||
_handlingConnectionQueue = false;
|
||||
_actionStreams.add(LogNotification('Connection finished: ${device.device.name ?? device.runtimeType}'));
|
||||
if (_connectionQueue.isNotEmpty) {
|
||||
_handleConnectionQueue();
|
||||
}
|
||||
})
|
||||
.catchError((e) {
|
||||
_handlingConnectionQueue = false;
|
||||
_actionStreams.add(LogNotification('Connection failed: ${device.device.name ?? device.runtimeType} - $e'));
|
||||
if (_connectionQueue.isNotEmpty) {
|
||||
_handleConnectionQueue();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _connect(BaseDevice bleDevice) async {
|
||||
try {
|
||||
final actionSubscription = bleDevice.actionStream.listen((data) {
|
||||
_actionStreams.add(data);
|
||||
});
|
||||
final connectionStateSubscription = UniversalBle.connectionStream(bleDevice.device.deviceId).listen((state) {
|
||||
bleDevice.isConnected = state;
|
||||
_connectionStreams.add(bleDevice);
|
||||
if (!bleDevice.isConnected) {
|
||||
devices.remove(bleDevice);
|
||||
_streamSubscriptions[bleDevice]?.cancel();
|
||||
_streamSubscriptions.remove(bleDevice);
|
||||
_connectionSubscriptions[bleDevice]?.cancel();
|
||||
_connectionSubscriptions.remove(bleDevice);
|
||||
_lastScanResult.clear();
|
||||
// try reconnect
|
||||
if (!isScanning.value) {
|
||||
performScanning();
|
||||
}
|
||||
}
|
||||
});
|
||||
_connectionSubscriptions[bleDevice] = connectionStateSubscription;
|
||||
|
||||
await bleDevice.connect();
|
||||
|
||||
_streamSubscriptions[bleDevice] = actionSubscription;
|
||||
} catch (e, backtrace) {
|
||||
_actionStreams.add(LogNotification("$e\n$backtrace"));
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
print("backtrace: $backtrace");
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_actionStreams.add(LogNotification('Disconnecting all devices'));
|
||||
if (actionHandler is AndroidActions) {
|
||||
AndroidFlutterLocalNotificationsPlugin().stopForegroundService();
|
||||
_androidNotificationsSetup = false;
|
||||
}
|
||||
UniversalBle.stopScan();
|
||||
isScanning.value = false;
|
||||
for (var device in devices) {
|
||||
_streamSubscriptions[device]?.cancel();
|
||||
_streamSubscriptions.remove(device);
|
||||
_connectionSubscriptions[device]?.cancel();
|
||||
_connectionSubscriptions.remove(device);
|
||||
UniversalBle.disconnect(device.device.deviceId);
|
||||
}
|
||||
_lastScanResult.clear();
|
||||
hasDevices.value = false;
|
||||
devices.clear();
|
||||
}
|
||||
|
||||
void signalChange(BaseDevice baseDevice) {
|
||||
_connectionStreams.add(baseDevice);
|
||||
}
|
||||
}
|
||||
359
lib/bluetooth/devices/base_device.dart
Normal file
359
lib/bluetooth/devices/base_device.dart
Normal file
@@ -0,0 +1,359 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:swift_control/bluetooth/ble.dart';
|
||||
import 'package:swift_control/bluetooth/devices/zwift_click.dart';
|
||||
import 'package:swift_control/bluetooth/devices/zwift_clickv2.dart';
|
||||
import 'package:swift_control/bluetooth/devices/zwift_play.dart';
|
||||
import 'package:swift_control/bluetooth/devices/zwift_ride.dart';
|
||||
import 'package:swift_control/main.dart';
|
||||
import 'package:swift_control/utils/actions/desktop.dart';
|
||||
import 'package:swift_control/utils/crypto/local_key_provider.dart';
|
||||
import 'package:swift_control/utils/crypto/zap_crypto.dart';
|
||||
import 'package:swift_control/utils/single_line_exception.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
import '../../utils/crypto/encryption_utils.dart';
|
||||
import '../../utils/keymap/buttons.dart';
|
||||
import '../messages/notification.dart';
|
||||
|
||||
abstract class BaseDevice {
|
||||
final BleDevice scanResult;
|
||||
final List<ZwiftButton> availableButtons;
|
||||
|
||||
BaseDevice(this.scanResult, {required this.availableButtons});
|
||||
|
||||
final zapEncryption = ZapCrypto(LocalKeyProvider());
|
||||
|
||||
bool isConnected = false;
|
||||
bool _isInited = false;
|
||||
int? batteryLevel;
|
||||
String? firmwareVersion;
|
||||
|
||||
bool supportsEncryption = true;
|
||||
|
||||
BleCharacteristic? syncRxCharacteristic;
|
||||
Timer? _longPressTimer;
|
||||
Set<ZwiftButton> _previouslyPressedButtons = <ZwiftButton>{};
|
||||
|
||||
List<int> get startCommand => Constants.RIDE_ON + Constants.RESPONSE_START_CLICK;
|
||||
String get customServiceId => BleUuid.ZWIFT_CUSTOM_SERVICE_UUID;
|
||||
|
||||
static BaseDevice? fromScanResult(BleDevice scanResult) {
|
||||
// Use the name first as the "System Devices" and Web (android sometimes Windows) don't have manufacturer data
|
||||
final device =
|
||||
kIsWeb
|
||||
? switch (scanResult.name) {
|
||||
'Zwift Ride' => ZwiftRide(scanResult),
|
||||
'Zwift Play' => ZwiftPlay(scanResult),
|
||||
'Zwift Click' => ZwiftClickV2(scanResult),
|
||||
_ => null,
|
||||
}
|
||||
: switch (scanResult.name) {
|
||||
//'Zwift Ride' => ZwiftRide(scanResult), special case for Zwift Ride: we must only connect to the left controller
|
||||
// https://www.makinolo.com/blog/2024/07/26/zwift-ride-protocol/
|
||||
'Zwift Play' => ZwiftPlay(scanResult),
|
||||
//'Zwift Click' => ZwiftClick(scanResult), special case for Zwift Click v2: we must only connect to the left controller
|
||||
_ => null,
|
||||
};
|
||||
|
||||
if (device != null) {
|
||||
return device;
|
||||
} else {
|
||||
// otherwise use the manufacturer data to identify the device
|
||||
final manufacturerData = scanResult.manufacturerDataList;
|
||||
final data = manufacturerData.firstOrNullWhere((e) => e.companyId == Constants.ZWIFT_MANUFACTURER_ID)?.payload;
|
||||
|
||||
if (data == null || data.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final type = DeviceType.fromManufacturerData(data.first);
|
||||
return switch (type) {
|
||||
DeviceType.click => ZwiftClick(scanResult),
|
||||
DeviceType.playRight => ZwiftPlay(scanResult),
|
||||
DeviceType.playLeft => ZwiftPlay(scanResult),
|
||||
DeviceType.rideLeft => ZwiftRide(scanResult),
|
||||
//DeviceType.rideRight => ZwiftRide(scanResult), // see comment above
|
||||
DeviceType.clickV2Left => ZwiftClickV2(scanResult),
|
||||
//DeviceType.clickV2Right => ZwiftClickV2(scanResult), // see comment above
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is BaseDevice && runtimeType == other.runtimeType && scanResult == other.scanResult;
|
||||
|
||||
@override
|
||||
int get hashCode => scanResult.hashCode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return runtimeType.toString();
|
||||
}
|
||||
|
||||
BleDevice get device => scanResult;
|
||||
final StreamController<BaseNotification> actionStreamInternal = StreamController<BaseNotification>.broadcast();
|
||||
|
||||
Stream<BaseNotification> get actionStream => actionStreamInternal.stream;
|
||||
|
||||
Future<void> connect() async {
|
||||
actionStream.listen((message) {
|
||||
print("Received message: $message");
|
||||
});
|
||||
|
||||
await UniversalBle.connect(device.deviceId);
|
||||
|
||||
if (!kIsWeb) {
|
||||
await UniversalBle.requestMtu(device.deviceId, 517);
|
||||
}
|
||||
|
||||
final services = await UniversalBle.discoverServices(device.deviceId);
|
||||
await _handleServices(services);
|
||||
}
|
||||
|
||||
Future<void> _handleServices(List<BleService> services) async {
|
||||
final customService = services.firstOrNullWhere((service) => service.uuid == customServiceId);
|
||||
|
||||
if (customService == null) {
|
||||
throw Exception(
|
||||
'Custom service $customServiceId not found for device $this ${device.name ?? device.rawName}.\nYou may need to update the firmware in Zwift Companion app.\nWe found: ${services.joinToString(transform: (s) => s.uuid)}',
|
||||
);
|
||||
}
|
||||
|
||||
final deviceInformationService = services.firstOrNullWhere(
|
||||
(service) => service.uuid == BleUuid.DEVICE_INFORMATION_SERVICE_UUID,
|
||||
);
|
||||
final firmwareCharacteristic = deviceInformationService?.characteristics.firstOrNullWhere(
|
||||
(c) => c.uuid == BleUuid.DEVICE_INFORMATION_CHARACTERISTIC_FIRMWARE_REVISION,
|
||||
);
|
||||
if (firmwareCharacteristic != null) {
|
||||
final firmwareData = await UniversalBle.read(
|
||||
device.deviceId,
|
||||
deviceInformationService!.uuid,
|
||||
firmwareCharacteristic.uuid,
|
||||
);
|
||||
firmwareVersion = String.fromCharCodes(firmwareData);
|
||||
connection.signalChange(this);
|
||||
}
|
||||
|
||||
final asyncCharacteristic = customService.characteristics.firstOrNullWhere(
|
||||
(characteristic) => characteristic.uuid == BleUuid.ZWIFT_ASYNC_CHARACTERISTIC_UUID,
|
||||
);
|
||||
final syncTxCharacteristic = customService.characteristics.firstOrNullWhere(
|
||||
(characteristic) => characteristic.uuid == BleUuid.ZWIFT_SYNC_TX_CHARACTERISTIC_UUID,
|
||||
);
|
||||
syncRxCharacteristic = customService.characteristics.firstOrNullWhere(
|
||||
(characteristic) => characteristic.uuid == BleUuid.ZWIFT_SYNC_RX_CHARACTERISTIC_UUID,
|
||||
);
|
||||
|
||||
if (asyncCharacteristic == null || syncTxCharacteristic == null || syncRxCharacteristic == null) {
|
||||
throw Exception('Characteristics not found');
|
||||
}
|
||||
|
||||
await UniversalBle.subscribeNotifications(device.deviceId, customService.uuid, asyncCharacteristic.uuid);
|
||||
await UniversalBle.subscribeIndications(device.deviceId, customService.uuid, syncTxCharacteristic.uuid);
|
||||
|
||||
await setupHandshake();
|
||||
}
|
||||
|
||||
Future<void> setupHandshake() async {
|
||||
if (supportsEncryption) {
|
||||
await UniversalBle.write(
|
||||
device.deviceId,
|
||||
customServiceId,
|
||||
syncRxCharacteristic!.uuid,
|
||||
Uint8List.fromList([
|
||||
...Constants.RIDE_ON,
|
||||
...Constants.REQUEST_START,
|
||||
...zapEncryption.localKeyProvider.getPublicKeyBytes(),
|
||||
]),
|
||||
withoutResponse: true,
|
||||
);
|
||||
} else {
|
||||
await UniversalBle.write(
|
||||
device.deviceId,
|
||||
customServiceId,
|
||||
syncRxCharacteristic!.uuid,
|
||||
Constants.RIDE_ON,
|
||||
withoutResponse: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> processCharacteristic(String characteristic, Uint8List bytes) async {
|
||||
if (kDebugMode && false) {
|
||||
print(
|
||||
"${DateTime.now().toString().split(" ").last} Received data on $characteristic: ${bytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')}",
|
||||
);
|
||||
}
|
||||
if (bytes.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (bytes.startsWith(startCommand)) {
|
||||
_processDevicePublicKeyResponse(bytes);
|
||||
} else if (!supportsEncryption || (bytes.length > Int32List.bytesPerElement + EncryptionUtils.MAC_LENGTH)) {
|
||||
processData(bytes);
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
print("Error processing data: $e");
|
||||
print("Stack Trace: $stackTrace");
|
||||
if (e is SingleLineException) {
|
||||
actionStreamInternal.add(LogNotification(e.message));
|
||||
} else {
|
||||
actionStreamInternal.add(LogNotification("$e\n$stackTrace"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _processDevicePublicKeyResponse(Uint8List bytes) {
|
||||
final devicePublicKeyBytes = bytes.sublist(Constants.RIDE_ON.length + Constants.RESPONSE_START_CLICK.length);
|
||||
if (kDebugMode) {
|
||||
print("Device Public Key - ${devicePublicKeyBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')}");
|
||||
}
|
||||
zapEncryption.initialise(devicePublicKeyBytes);
|
||||
}
|
||||
|
||||
Future<void> processData(Uint8List bytes) async {
|
||||
int type;
|
||||
Uint8List message;
|
||||
|
||||
if (supportsEncryption) {
|
||||
final counter = bytes.sublist(0, 4); // Int.SIZE_BYTES is 4
|
||||
final payload = bytes.sublist(4);
|
||||
|
||||
if (zapEncryption.encryptionKeyBytes == null) {
|
||||
actionStreamInternal.add(
|
||||
LogNotification(
|
||||
'Encryption not initialized, yet. You may need to update the firmware of your device with the Zwift Companion app.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final data = zapEncryption.decrypt(counter, payload);
|
||||
type = data[0];
|
||||
message = data.sublist(1);
|
||||
} else {
|
||||
type = bytes[0];
|
||||
message = bytes.sublist(1);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Constants.EMPTY_MESSAGE_TYPE:
|
||||
//print("Empty Message"); // expected when nothing happening
|
||||
break;
|
||||
case Constants.BATTERY_LEVEL_TYPE:
|
||||
if (batteryLevel != message[1]) {
|
||||
batteryLevel = message[1];
|
||||
connection.signalChange(this);
|
||||
}
|
||||
break;
|
||||
case Constants.CLICK_NOTIFICATION_MESSAGE_TYPE:
|
||||
case Constants.PLAY_NOTIFICATION_MESSAGE_TYPE:
|
||||
case Constants.RIDE_NOTIFICATION_MESSAGE_TYPE:
|
||||
processClickNotification(message)
|
||||
.then((buttonsClicked) async {
|
||||
return handleButtonsClicked(buttonsClicked);
|
||||
})
|
||||
.catchError((e) {
|
||||
actionStreamInternal.add(LogNotification(e.toString()));
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ZwiftButton>?> processClickNotification(Uint8List message);
|
||||
|
||||
Future<void> handleButtonsClicked(List<ZwiftButton>? buttonsClicked) async {
|
||||
if (buttonsClicked == null) {
|
||||
// ignore, no changes
|
||||
} else if (buttonsClicked.isEmpty) {
|
||||
actionStreamInternal.add(LogNotification('Buttons released'));
|
||||
_longPressTimer?.cancel();
|
||||
|
||||
// Handle release events for long press keys
|
||||
final buttonsReleased = _previouslyPressedButtons.toList();
|
||||
if (buttonsReleased.isNotEmpty) {
|
||||
await _performRelease(buttonsReleased);
|
||||
}
|
||||
_previouslyPressedButtons.clear();
|
||||
} else {
|
||||
// Handle release events for buttons that are no longer pressed
|
||||
final buttonsReleased = _previouslyPressedButtons.difference(buttonsClicked.toSet()).toList();
|
||||
if (buttonsReleased.isNotEmpty) {
|
||||
await _performRelease(buttonsReleased);
|
||||
}
|
||||
|
||||
final isLongPress =
|
||||
buttonsClicked.singleOrNull != null &&
|
||||
actionHandler.supportedApp?.keymap.getKeyPair(buttonsClicked.single)?.isLongPress == true;
|
||||
|
||||
if (!isLongPress &&
|
||||
!(buttonsClicked.singleOrNull == ZwiftButton.onOffLeft ||
|
||||
buttonsClicked.singleOrNull == ZwiftButton.onOffRight)) {
|
||||
// we don't want to trigger the long press timer for the on/off buttons, also not when it's a long press key
|
||||
_longPressTimer?.cancel();
|
||||
_longPressTimer = Timer.periodic(const Duration(milliseconds: 350), (timer) async {
|
||||
_performActions(buttonsClicked, true);
|
||||
});
|
||||
}
|
||||
// Update currently pressed buttons
|
||||
_previouslyPressedButtons = buttonsClicked.toSet();
|
||||
|
||||
return _performActions(buttonsClicked, false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _performActions(List<ZwiftButton> buttonsClicked, bool repeated) async {
|
||||
if (!repeated &&
|
||||
buttonsClicked.any(((e) => e.action == InGameAction.shiftDown || e.action == InGameAction.shiftUp)) &&
|
||||
settings.getVibrationEnabled()) {
|
||||
await _vibrate();
|
||||
}
|
||||
for (final action in buttonsClicked) {
|
||||
// For repeated actions, don't trigger key down/up events (useful for long press)
|
||||
final isKeyDown = !repeated;
|
||||
actionStreamInternal.add(
|
||||
LogNotification(await actionHandler.performAction(action, isKeyDown: isKeyDown, isKeyUp: false)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _performRelease(List<ZwiftButton> buttonsReleased) async {
|
||||
for (final action in buttonsReleased) {
|
||||
actionStreamInternal.add(
|
||||
LogNotification(await actionHandler.performAction(action, isKeyDown: false, isKeyUp: true)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _vibrate() async {
|
||||
final vibrateCommand = Uint8List.fromList([...Constants.VIBRATE_PATTERN, 0x20]);
|
||||
await UniversalBle.write(
|
||||
device.deviceId,
|
||||
customServiceId,
|
||||
syncRxCharacteristic!.uuid,
|
||||
supportsEncryption ? zapEncryption.encrypt(vibrateCommand) : vibrateCommand,
|
||||
withoutResponse: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
_isInited = false;
|
||||
_longPressTimer?.cancel();
|
||||
// Release any held keys in long press mode
|
||||
if (actionHandler is DesktopActions) {
|
||||
await (actionHandler as DesktopActions).releaseAllHeldKeys(_previouslyPressedButtons.toList());
|
||||
}
|
||||
_previouslyPressedButtons.clear();
|
||||
await UniversalBle.disconnect(device.deviceId);
|
||||
isConnected = false;
|
||||
}
|
||||
}
|
||||
26
lib/bluetooth/devices/zwift_click.dart
Normal file
26
lib/bluetooth/devices/zwift_click.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:swift_control/bluetooth/devices/base_device.dart';
|
||||
import 'package:swift_control/utils/keymap/buttons.dart';
|
||||
|
||||
import '../messages/click_notification.dart';
|
||||
|
||||
class ZwiftClick extends BaseDevice {
|
||||
ZwiftClick(super.scanResult) : super(availableButtons: [ZwiftButton.shiftUpRight, ZwiftButton.shiftDownLeft]);
|
||||
|
||||
ClickNotification? _lastClickNotification;
|
||||
|
||||
@override
|
||||
Future<List<ZwiftButton>?> processClickNotification(Uint8List message) async {
|
||||
final ClickNotification clickNotification = ClickNotification(message);
|
||||
if (_lastClickNotification == null || _lastClickNotification != clickNotification) {
|
||||
_lastClickNotification = clickNotification;
|
||||
|
||||
if (clickNotification.buttonsClicked.isNotEmpty) {
|
||||
actionStreamInternal.add(clickNotification);
|
||||
}
|
||||
return clickNotification.buttonsClicked;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
lib/bluetooth/devices/zwift_clickv2.dart
Normal file
71
lib/bluetooth/devices/zwift_clickv2.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:swift_control/bluetooth/devices/zwift_ride.dart';
|
||||
|
||||
import '../ble.dart';
|
||||
import '../protocol/zp.pbenum.dart';
|
||||
|
||||
class ZwiftClickV2 extends ZwiftRide {
|
||||
ZwiftClickV2(super.scanResult);
|
||||
|
||||
@override
|
||||
bool get supportsEncryption => false;
|
||||
|
||||
@override
|
||||
List<int> get startCommand => Constants.RIDE_ON + Constants.RESPONSE_START_CLICK_V2;
|
||||
|
||||
@override
|
||||
Future<void> setupHandshake() async {
|
||||
super.setupHandshake();
|
||||
await sendCommandBuffer(Uint8List.fromList([0xFF, 0x04, 0x00]));
|
||||
}
|
||||
|
||||
Future<void> test() async {
|
||||
await sendCommand(Opcode.RESET, null);
|
||||
//await sendCommand(Opcode.GET, Get(dataObjectId: VendorDO.PAGE_DEVICE_PAIRING.value)); // 0008 82E0 03
|
||||
|
||||
/*await sendCommand(Opcode.GET, Get(dataObjectId: DO.PAGE_DEV_INFO.value)); // 0008 00
|
||||
await sendCommand(Opcode.LOG_LEVEL_SET, LogLevelSet(logLevel: LogLevel.LOGLEVEL_TRACE)); // 4108 05
|
||||
|
||||
await sendCommand(Opcode.GET, Get(dataObjectId: DO.PAGE_CLIENT_SERVER_CONFIGURATION.value)); // 0008 10
|
||||
await sendCommand(Opcode.GET, Get(dataObjectId: DO.PAGE_CLIENT_SERVER_CONFIGURATION.value)); // 0008 10
|
||||
await sendCommand(Opcode.GET, Get(dataObjectId: DO.PAGE_CLIENT_SERVER_CONFIGURATION.value)); // 0008 10
|
||||
|
||||
await sendCommand(Opcode.GET, Get(dataObjectId: DO.PAGE_CONTROLLER_INPUT_CONFIG.value)); // 0008 80 08
|
||||
|
||||
await sendCommand(Opcode.GET, Get(dataObjectId: DO.BATTERY_STATE.value)); // 0008 83 06
|
||||
|
||||
// Value: FF04 000A 1540 E9D9 C96B 7463 C27F 1B4E 4D9F 1CB1 205D 882E D7CE
|
||||
// Value: FF04 000A 15B2 6324 0A31 D6C6 B81F C129 D6A4 E99D FFFC B9FC 418D
|
||||
await sendCommandBuffer(
|
||||
Uint8List.fromList([
|
||||
0xFF,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0A,
|
||||
0x15,
|
||||
0xC2,
|
||||
0x63,
|
||||
0x24,
|
||||
0x0A,
|
||||
0x31,
|
||||
0xD6,
|
||||
0xC6,
|
||||
0xB8,
|
||||
0x1F,
|
||||
0xC1,
|
||||
0x29,
|
||||
0xD6,
|
||||
0xA4,
|
||||
0xE9,
|
||||
0x9D,
|
||||
0xFF,
|
||||
0xFC,
|
||||
0xB9,
|
||||
0xFC,
|
||||
0x41,
|
||||
0x8D,
|
||||
]),
|
||||
);*/
|
||||
}
|
||||
}
|
||||
51
lib/bluetooth/devices/zwift_play.dart
Normal file
51
lib/bluetooth/devices/zwift_play.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:swift_control/bluetooth/devices/base_device.dart';
|
||||
import 'package:swift_control/bluetooth/messages/play_notification.dart';
|
||||
import 'package:swift_control/utils/keymap/buttons.dart';
|
||||
|
||||
import '../ble.dart';
|
||||
|
||||
class ZwiftPlay extends BaseDevice {
|
||||
ZwiftPlay(super.scanResult)
|
||||
: super(
|
||||
availableButtons: [
|
||||
ZwiftButton.y,
|
||||
ZwiftButton.z,
|
||||
ZwiftButton.a,
|
||||
ZwiftButton.b,
|
||||
ZwiftButton.onOffRight,
|
||||
ZwiftButton.sideButtonRight,
|
||||
ZwiftButton.paddleRight,
|
||||
ZwiftButton.navigationUp,
|
||||
ZwiftButton.navigationLeft,
|
||||
ZwiftButton.navigationRight,
|
||||
ZwiftButton.navigationDown,
|
||||
ZwiftButton.onOffLeft,
|
||||
ZwiftButton.sideButtonLeft,
|
||||
ZwiftButton.paddleLeft,
|
||||
],
|
||||
);
|
||||
|
||||
PlayNotification? _lastControllerNotification;
|
||||
|
||||
@override
|
||||
List<int> get startCommand => Constants.RIDE_ON + Constants.RESPONSE_START_PLAY;
|
||||
|
||||
@override
|
||||
Future<List<ZwiftButton>?> processClickNotification(Uint8List message) async {
|
||||
final PlayNotification clickNotification = PlayNotification(message);
|
||||
if (_lastControllerNotification == null || _lastControllerNotification != clickNotification) {
|
||||
_lastControllerNotification = clickNotification;
|
||||
|
||||
if (clickNotification.buttonsClicked.isNotEmpty) {
|
||||
actionStreamInternal.add(clickNotification);
|
||||
}
|
||||
|
||||
return clickNotification.buttonsClicked;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
243
lib/bluetooth/devices/zwift_ride.dart
Normal file
243
lib/bluetooth/devices/zwift_ride.dart
Normal file
@@ -0,0 +1,243 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
import 'package:swift_control/bluetooth/devices/base_device.dart';
|
||||
import 'package:swift_control/bluetooth/devices/zwift_clickv2.dart';
|
||||
import 'package:swift_control/bluetooth/messages/ride_notification.dart';
|
||||
import 'package:swift_control/bluetooth/protocol/zp_vendor.pb.dart';
|
||||
import 'package:swift_control/utils/keymap/buttons.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
import '../../main.dart';
|
||||
import '../ble.dart';
|
||||
import '../messages/notification.dart';
|
||||
import '../protocol/zp.pb.dart';
|
||||
|
||||
class ZwiftRide extends BaseDevice {
|
||||
ZwiftRide(super.scanResult)
|
||||
: super(
|
||||
availableButtons: [
|
||||
ZwiftButton.navigationLeft,
|
||||
ZwiftButton.navigationRight,
|
||||
ZwiftButton.navigationUp,
|
||||
ZwiftButton.navigationDown,
|
||||
ZwiftButton.a,
|
||||
ZwiftButton.b,
|
||||
ZwiftButton.y,
|
||||
ZwiftButton.z,
|
||||
ZwiftButton.shiftUpLeft,
|
||||
ZwiftButton.shiftDownLeft,
|
||||
ZwiftButton.shiftUpRight,
|
||||
ZwiftButton.shiftDownRight,
|
||||
ZwiftButton.powerUpLeft,
|
||||
ZwiftButton.powerUpRight,
|
||||
ZwiftButton.onOffLeft,
|
||||
ZwiftButton.onOffRight,
|
||||
ZwiftButton.paddleLeft,
|
||||
ZwiftButton.paddleRight,
|
||||
],
|
||||
);
|
||||
|
||||
@override
|
||||
String get customServiceId => BleUuid.ZWIFT_RIDE_CUSTOM_SERVICE_UUID;
|
||||
|
||||
@override
|
||||
bool get supportsEncryption => false;
|
||||
|
||||
RideNotification? _lastControllerNotification;
|
||||
|
||||
@override
|
||||
Future<void> processData(Uint8List bytes) async {
|
||||
Opcode? opcode;
|
||||
Uint8List message;
|
||||
|
||||
if (supportsEncryption) {
|
||||
final counter = bytes.sublist(0, 4); // Int.SIZE_BYTES is 4
|
||||
final payload = bytes.sublist(4);
|
||||
|
||||
if (zapEncryption.encryptionKeyBytes == null) {
|
||||
actionStreamInternal.add(
|
||||
LogNotification(
|
||||
'Encryption not initialized, yet. You may need to update the firmware of your device with the Zwift Companion app.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final data = zapEncryption.decrypt(counter, payload);
|
||||
opcode = Opcode.valueOf(data[0]);
|
||||
message = data.sublist(1);
|
||||
} else {
|
||||
opcode = Opcode.valueOf(bytes[0]);
|
||||
message = bytes.sublist(1);
|
||||
}
|
||||
|
||||
if (kDebugMode) {
|
||||
print(
|
||||
'${DateTime.now().toString().split(" ").last} Received $opcode: ${bytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')} => ${String.fromCharCodes(bytes)} ',
|
||||
);
|
||||
}
|
||||
|
||||
if (bytes.startsWith(Constants.RESPONSE_STOPPED_CLICK_V2) && this is ZwiftClickV2) {
|
||||
actionStreamInternal.add(
|
||||
LogNotification(
|
||||
'Your Zwift Click V2 no longer sends events. Connect it in the Zwift app once per day. Resetting the device now.',
|
||||
),
|
||||
);
|
||||
if (!kDebugMode) {
|
||||
sendCommand(Opcode.RESET, null);
|
||||
}
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case Opcode.RIDE_ON:
|
||||
//print("Empty RideOn response - unencrypted mode");
|
||||
|
||||
break;
|
||||
case Opcode.STATUS_RESPONSE:
|
||||
final status = StatusResponse.fromBuffer(message);
|
||||
if (kDebugMode) {
|
||||
print('StatusResponse: ${status.command} status: ${Status.valueOf(status.status)}');
|
||||
}
|
||||
break;
|
||||
case Opcode.GET_RESPONSE:
|
||||
final response = GetResponse.fromBuffer(message);
|
||||
final dataObjectType = DO.valueOf(response.dataObjectId);
|
||||
if (kDebugMode) {
|
||||
print(
|
||||
'GetResponse: ${dataObjectType?.value.toRadixString(16).padLeft(4, '0') ?? response.dataObjectId} $dataObjectType',
|
||||
);
|
||||
}
|
||||
|
||||
switch (dataObjectType) {
|
||||
case DO.PAGE_DEV_INFO:
|
||||
final pageDevInfo = DevInfoPage.fromBuffer(response.dataObjectData);
|
||||
if (kDebugMode) {
|
||||
print('PageDevInfo: $pageDevInfo');
|
||||
}
|
||||
break;
|
||||
case DO.PAGE_DATE_TIME:
|
||||
final pageDateTime = DateTimePage.fromBuffer(response.dataObjectData);
|
||||
if (kDebugMode) {
|
||||
print('PageDateTime: $pageDateTime');
|
||||
}
|
||||
break;
|
||||
case DO.PAGE_CONTROLLER_INPUT_CONFIG:
|
||||
final pageDateTime = ControllerInputConfigPage.fromBuffer(response.dataObjectData);
|
||||
if (kDebugMode) {
|
||||
print('PageDateTime: $pageDateTime');
|
||||
}
|
||||
break;
|
||||
case null:
|
||||
final vendorDO = VendorDO.valueOf(response.dataObjectId);
|
||||
if (kDebugMode) {
|
||||
print('VendorDO: $vendorDO');
|
||||
}
|
||||
switch (vendorDO) {
|
||||
case VendorDO.DEVICE_COUNT:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
case VendorDO.NO_CLUE:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
case VendorDO.PAGE_DEVICE_PAIRING:
|
||||
final page = DevicePairingDataPage.fromBuffer(response.dataObjectData);
|
||||
if (kDebugMode) {
|
||||
// this should show the right click device
|
||||
// pairingStatus = 1 => connected and paired, otherwise it can be paired but not connected
|
||||
print(
|
||||
'PageDevicePairing: $page => ${page.pairingDevList.map((e) => e.device.reversed.map((d) => d.toRadixString(16).padLeft(2, '0'))).join(', ')}',
|
||||
);
|
||||
}
|
||||
break;
|
||||
case VendorDO.PAIRED_DEVICE:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
case VendorDO.PAIRING_STATUS:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Opcode.VENDOR_MESSAGE:
|
||||
final vendorOpCode = VendorOpcode.valueOf(message.second);
|
||||
print('VendorOpcode: $vendorOpCode');
|
||||
break;
|
||||
case Opcode.LOG_DATA:
|
||||
final logMessage = LogDataNotification.fromBuffer(message);
|
||||
if (kDebugMode) {
|
||||
actionStreamInternal.add(LogNotification(logMessage.toString()));
|
||||
}
|
||||
break;
|
||||
case Opcode.BATTERY_NOTIF:
|
||||
final notification = BatteryNotification.fromBuffer(message);
|
||||
if (batteryLevel != notification.newPercLevel) {
|
||||
batteryLevel = notification.newPercLevel;
|
||||
connection.signalChange(this);
|
||||
}
|
||||
break;
|
||||
case Opcode.CONTROLLER_NOTIFICATION:
|
||||
processClickNotification(message)
|
||||
.then((buttonsClicked) async {
|
||||
return handleButtonsClicked(buttonsClicked);
|
||||
})
|
||||
.catchError((e) {
|
||||
actionStreamInternal.add(LogNotification(e.toString()));
|
||||
});
|
||||
break;
|
||||
case null:
|
||||
if (bytes[0] == 0x1A) {
|
||||
final batteryStatus = BatteryStatus.fromBuffer(message);
|
||||
if (kDebugMode) {
|
||||
print('BatteryStatus: $batteryStatus');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ZwiftButton>?> processClickNotification(Uint8List message) async {
|
||||
final RideNotification clickNotification = RideNotification(message);
|
||||
if (_lastControllerNotification == null || _lastControllerNotification != clickNotification) {
|
||||
_lastControllerNotification = clickNotification;
|
||||
|
||||
if (clickNotification.buttonsClicked.isNotEmpty) {
|
||||
actionStreamInternal.add(clickNotification);
|
||||
}
|
||||
return clickNotification.buttonsClicked;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> sendCommand(Opcode opCode, $pb.GeneratedMessage? message) async {
|
||||
final buffer = Uint8List.fromList([opCode.value, ...message?.writeToBuffer() ?? []]);
|
||||
if (kDebugMode) {
|
||||
print("Sending $opCode: ${buffer.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')}");
|
||||
}
|
||||
await UniversalBle.write(
|
||||
device.deviceId,
|
||||
customServiceId,
|
||||
syncRxCharacteristic!.uuid,
|
||||
buffer,
|
||||
withoutResponse: true,
|
||||
);
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
Future<void> sendCommandBuffer(Uint8List buffer) async {
|
||||
if (kDebugMode) {
|
||||
print("Sending ${buffer.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')}");
|
||||
}
|
||||
await UniversalBle.write(
|
||||
device.deviceId,
|
||||
customServiceId,
|
||||
syncRxCharacteristic!.uuid,
|
||||
buffer,
|
||||
withoutResponse: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
35
lib/bluetooth/messages/click_notification.dart
Normal file
35
lib/bluetooth/messages/click_notification.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:swift_control/utils/keymap/buttons.dart';
|
||||
import 'package:swift_control/widgets/keymap_explanation.dart';
|
||||
|
||||
import '../protocol/zwift.pb.dart';
|
||||
import 'notification.dart';
|
||||
|
||||
class ClickNotification extends BaseNotification {
|
||||
late List<ZwiftButton> buttonsClicked;
|
||||
|
||||
ClickNotification(Uint8List message) {
|
||||
final status = ClickKeyPadStatus.fromBuffer(message);
|
||||
buttonsClicked = [
|
||||
if (status.buttonPlus == PlayButtonStatus.ON) ZwiftButton.shiftUpRight,
|
||||
if (status.buttonMinus == PlayButtonStatus.ON) ZwiftButton.shiftDownLeft,
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Buttons: ${buttonsClicked.joinToString(transform: (e) => e.name.splitByUpperCase())}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ClickNotification &&
|
||||
runtimeType == other.runtimeType &&
|
||||
buttonsClicked.contentEquals(other.buttonsClicked);
|
||||
|
||||
@override
|
||||
int get hashCode => buttonsClicked.hashCode;
|
||||
}
|
||||
51
lib/bluetooth/messages/play_notification.dart
Normal file
51
lib/bluetooth/messages/play_notification.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:swift_control/bluetooth/messages/notification.dart';
|
||||
import 'package:swift_control/bluetooth/protocol/zwift.pb.dart';
|
||||
import 'package:swift_control/utils/keymap/buttons.dart';
|
||||
import 'package:swift_control/widgets/keymap_explanation.dart';
|
||||
|
||||
class PlayNotification extends BaseNotification {
|
||||
late List<ZwiftButton> buttonsClicked;
|
||||
|
||||
PlayNotification(Uint8List message) {
|
||||
final status = PlayKeyPadStatus.fromBuffer(message);
|
||||
|
||||
buttonsClicked = [
|
||||
if (status.rightPad == PlayButtonStatus.ON) ...[
|
||||
if (status.buttonYUp == PlayButtonStatus.ON) ZwiftButton.y,
|
||||
if (status.buttonZLeft == PlayButtonStatus.ON) ZwiftButton.z,
|
||||
if (status.buttonARight == PlayButtonStatus.ON) ZwiftButton.a,
|
||||
if (status.buttonBDown == PlayButtonStatus.ON) ZwiftButton.b,
|
||||
if (status.buttonOn == PlayButtonStatus.ON) ZwiftButton.onOffRight,
|
||||
if (status.buttonShift == PlayButtonStatus.ON) ZwiftButton.sideButtonRight,
|
||||
if (status.analogLR.abs() == 100) ZwiftButton.paddleRight,
|
||||
],
|
||||
if (status.rightPad == PlayButtonStatus.OFF) ...[
|
||||
if (status.buttonYUp == PlayButtonStatus.ON) ZwiftButton.navigationUp,
|
||||
if (status.buttonZLeft == PlayButtonStatus.ON) ZwiftButton.navigationLeft,
|
||||
if (status.buttonARight == PlayButtonStatus.ON) ZwiftButton.navigationRight,
|
||||
if (status.buttonBDown == PlayButtonStatus.ON) ZwiftButton.navigationDown,
|
||||
if (status.buttonOn == PlayButtonStatus.ON) ZwiftButton.onOffLeft,
|
||||
if (status.buttonShift == PlayButtonStatus.ON) ZwiftButton.sideButtonLeft,
|
||||
if (status.analogLR.abs() == 100) ZwiftButton.paddleLeft,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Buttons: ${buttonsClicked.joinToString(transform: (e) => e.name.splitByUpperCase())}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is PlayNotification &&
|
||||
runtimeType == other.runtimeType &&
|
||||
buttonsClicked.contentEquals(other.buttonsClicked);
|
||||
|
||||
@override
|
||||
int get hashCode => buttonsClicked.hashCode;
|
||||
}
|
||||
88
lib/bluetooth/messages/ride_notification.dart
Normal file
88
lib/bluetooth/messages/ride_notification.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:swift_control/bluetooth/messages/notification.dart';
|
||||
import 'package:swift_control/bluetooth/protocol/zwift.pb.dart';
|
||||
import 'package:swift_control/utils/keymap/buttons.dart';
|
||||
import 'package:swift_control/widgets/keymap_explanation.dart';
|
||||
|
||||
enum _RideButtonMask {
|
||||
LEFT_BTN(0x00001),
|
||||
UP_BTN(0x00002),
|
||||
RIGHT_BTN(0x00004),
|
||||
DOWN_BTN(0x00008),
|
||||
|
||||
A_BTN(0x00010),
|
||||
B_BTN(0x00020),
|
||||
Y_BTN(0x00040),
|
||||
Z_BTN(0x00080),
|
||||
|
||||
SHFT_UP_L_BTN(0x00100),
|
||||
SHFT_DN_L_BTN(0x00200),
|
||||
SHFT_UP_R_BTN(0x01000),
|
||||
SHFT_DN_R_BTN(0x02000),
|
||||
|
||||
POWERUP_L_BTN(0x00400),
|
||||
POWERUP_R_BTN(0x04000),
|
||||
ONOFF_L_BTN(0x00800),
|
||||
ONOFF_R_BTN(0x08000);
|
||||
|
||||
final int mask;
|
||||
|
||||
const _RideButtonMask(this.mask);
|
||||
}
|
||||
|
||||
class RideNotification extends BaseNotification {
|
||||
late List<ZwiftButton> buttonsClicked;
|
||||
|
||||
RideNotification(Uint8List message) {
|
||||
final status = RideKeyPadStatus.fromBuffer(message);
|
||||
|
||||
buttonsClicked = [
|
||||
if (status.buttonMap & _RideButtonMask.LEFT_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.navigationLeft,
|
||||
if (status.buttonMap & _RideButtonMask.RIGHT_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.navigationRight,
|
||||
if (status.buttonMap & _RideButtonMask.UP_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.navigationUp,
|
||||
if (status.buttonMap & _RideButtonMask.DOWN_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.navigationDown,
|
||||
if (status.buttonMap & _RideButtonMask.A_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.a,
|
||||
if (status.buttonMap & _RideButtonMask.B_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.b,
|
||||
if (status.buttonMap & _RideButtonMask.Y_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.y,
|
||||
if (status.buttonMap & _RideButtonMask.Z_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.z,
|
||||
if (status.buttonMap & _RideButtonMask.SHFT_UP_L_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.shiftUpLeft,
|
||||
if (status.buttonMap & _RideButtonMask.SHFT_DN_L_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.shiftDownLeft,
|
||||
if (status.buttonMap & _RideButtonMask.SHFT_UP_R_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.shiftUpRight,
|
||||
if (status.buttonMap & _RideButtonMask.SHFT_DN_R_BTN.mask == PlayButtonStatus.ON.value)
|
||||
ZwiftButton.shiftDownRight,
|
||||
if (status.buttonMap & _RideButtonMask.POWERUP_L_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.powerUpLeft,
|
||||
if (status.buttonMap & _RideButtonMask.POWERUP_R_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.powerUpRight,
|
||||
if (status.buttonMap & _RideButtonMask.ONOFF_L_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.onOffLeft,
|
||||
if (status.buttonMap & _RideButtonMask.ONOFF_R_BTN.mask == PlayButtonStatus.ON.value) ZwiftButton.onOffRight,
|
||||
];
|
||||
|
||||
for (final analogue in status.analogButtons.groupStatus) {
|
||||
if (analogue.analogValue.abs() == 100) {
|
||||
if (analogue.location == RideAnalogLocation.LEFT) {
|
||||
buttonsClicked.add(ZwiftButton.paddleLeft);
|
||||
} else if (analogue.location == RideAnalogLocation.RIGHT) {
|
||||
buttonsClicked.add(ZwiftButton.paddleRight);
|
||||
} else if (analogue.location == RideAnalogLocation.DOWN || analogue.location == RideAnalogLocation.UP) {
|
||||
// TODO what is this even?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Buttons: ${buttonsClicked.joinToString(transform: (e) => e.name.splitByUpperCase())}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is RideNotification &&
|
||||
runtimeType == other.runtimeType &&
|
||||
buttonsClicked.contentEquals(other.buttonsClicked);
|
||||
|
||||
@override
|
||||
int get hashCode => buttonsClicked.hashCode;
|
||||
}
|
||||
6146
lib/bluetooth/protocol/zp.pb.dart
Normal file
6146
lib/bluetooth/protocol/zp.pb.dart
Normal file
File diff suppressed because it is too large
Load Diff
583
lib/bluetooth/protocol/zp.pbenum.dart
Normal file
583
lib/bluetooth/protocol/zp.pbenum.dart
Normal file
@@ -0,0 +1,583 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: zp.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
/// ///////////////////////////////////////////////////////////////
|
||||
/// Enumerations
|
||||
/// ///////////////////////////////////////////////////////////////
|
||||
class Opcode extends $pb.ProtobufEnum {
|
||||
static const Opcode GET = Opcode._(0, _omitEnumNames ? '' : 'GET');
|
||||
static const Opcode DEV_INFO_STATUS = Opcode._(1, _omitEnumNames ? '' : 'DEV_INFO_STATUS');
|
||||
static const Opcode BLE_SECURITY_REQUEST = Opcode._(2, _omitEnumNames ? '' : 'BLE_SECURITY_REQUEST');
|
||||
static const Opcode TRAINER_NOTIF = Opcode._(3, _omitEnumNames ? '' : 'TRAINER_NOTIF');
|
||||
static const Opcode TRAINER_CONFIG_SET = Opcode._(4, _omitEnumNames ? '' : 'TRAINER_CONFIG_SET');
|
||||
static const Opcode TRAINER_CONFIG_STATUS = Opcode._(5, _omitEnumNames ? '' : 'TRAINER_CONFIG_STATUS');
|
||||
static const Opcode DEV_INFO_SET = Opcode._(12, _omitEnumNames ? '' : 'DEV_INFO_SET');
|
||||
static const Opcode POWER_OFF = Opcode._(15, _omitEnumNames ? '' : 'POWER_OFF');
|
||||
static const Opcode RESET = Opcode._(24, _omitEnumNames ? '' : 'RESET');
|
||||
static const Opcode BATTERY_NOTIF = Opcode._(25, _omitEnumNames ? '' : 'BATTERY_NOTIF');
|
||||
static const Opcode CONTROLLER_NOTIFICATION = Opcode._(35, _omitEnumNames ? '' : 'CONTROLLER_NOTIFICATION');
|
||||
static const Opcode LOG_DATA = Opcode._(42, _omitEnumNames ? '' : 'LOG_DATA');
|
||||
static const Opcode SPINDOWN_REQUEST = Opcode._(58, _omitEnumNames ? '' : 'SPINDOWN_REQUEST');
|
||||
static const Opcode SPINDOWN_NOTIFICATION = Opcode._(59, _omitEnumNames ? '' : 'SPINDOWN_NOTIFICATION');
|
||||
static const Opcode GET_RESPONSE = Opcode._(60, _omitEnumNames ? '' : 'GET_RESPONSE');
|
||||
static const Opcode STATUS_RESPONSE = Opcode._(62, _omitEnumNames ? '' : 'STATUS_RESPONSE');
|
||||
static const Opcode SET = Opcode._(63, _omitEnumNames ? '' : 'SET');
|
||||
static const Opcode SET_RESPONSE = Opcode._(64, _omitEnumNames ? '' : 'SET_RESPONSE');
|
||||
static const Opcode LOG_LEVEL_SET = Opcode._(65, _omitEnumNames ? '' : 'LOG_LEVEL_SET');
|
||||
static const Opcode DATA_CHANGE_NOTIFICATION = Opcode._(66, _omitEnumNames ? '' : 'DATA_CHANGE_NOTIFICATION');
|
||||
static const Opcode GAME_STATE_NOTIFICATION = Opcode._(67, _omitEnumNames ? '' : 'GAME_STATE_NOTIFICATION');
|
||||
static const Opcode SENSOR_RELAY_CONFIG = Opcode._(68, _omitEnumNames ? '' : 'SENSOR_RELAY_CONFIG');
|
||||
static const Opcode SENSOR_RELAY_GET = Opcode._(69, _omitEnumNames ? '' : 'SENSOR_RELAY_GET');
|
||||
static const Opcode SENSOR_RELAY_RESPONSE = Opcode._(70, _omitEnumNames ? '' : 'SENSOR_RELAY_RESPONSE');
|
||||
static const Opcode SENSOR_RELAY_NOTIFICATION = Opcode._(71, _omitEnumNames ? '' : 'SENSOR_RELAY_NOTIFICATION');
|
||||
static const Opcode HRM_DATA_NOTIFICATION = Opcode._(72, _omitEnumNames ? '' : 'HRM_DATA_NOTIFICATION');
|
||||
static const Opcode WIFI_CONFIG_REQUEST = Opcode._(73, _omitEnumNames ? '' : 'WIFI_CONFIG_REQUEST');
|
||||
static const Opcode WIFI_NOTIFICATION = Opcode._(74, _omitEnumNames ? '' : 'WIFI_NOTIFICATION');
|
||||
static const Opcode POWER_METER_NOTIFICATION = Opcode._(75, _omitEnumNames ? '' : 'POWER_METER_NOTIFICATION');
|
||||
static const Opcode CADENCE_SENSOR_NOTIFICATION = Opcode._(76, _omitEnumNames ? '' : 'CADENCE_SENSOR_NOTIFICATION');
|
||||
static const Opcode DEVICE_UPDATE_REQUEST = Opcode._(77, _omitEnumNames ? '' : 'DEVICE_UPDATE_REQUEST');
|
||||
static const Opcode RELAY_ZP_MESSAGE = Opcode._(78, _omitEnumNames ? '' : 'RELAY_ZP_MESSAGE');
|
||||
static const Opcode RIDE_ON = Opcode._(82, _omitEnumNames ? '' : 'RIDE_ON');
|
||||
static const Opcode RESERVED = Opcode._(253, _omitEnumNames ? '' : 'RESERVED');
|
||||
static const Opcode LOST_CONTROL = Opcode._(254, _omitEnumNames ? '' : 'LOST_CONTROL');
|
||||
static const Opcode VENDOR_MESSAGE = Opcode._(255, _omitEnumNames ? '' : 'VENDOR_MESSAGE');
|
||||
|
||||
static const $core.List<Opcode> values = <Opcode> [
|
||||
GET,
|
||||
DEV_INFO_STATUS,
|
||||
BLE_SECURITY_REQUEST,
|
||||
TRAINER_NOTIF,
|
||||
TRAINER_CONFIG_SET,
|
||||
TRAINER_CONFIG_STATUS,
|
||||
DEV_INFO_SET,
|
||||
POWER_OFF,
|
||||
RESET,
|
||||
BATTERY_NOTIF,
|
||||
CONTROLLER_NOTIFICATION,
|
||||
LOG_DATA,
|
||||
SPINDOWN_REQUEST,
|
||||
SPINDOWN_NOTIFICATION,
|
||||
GET_RESPONSE,
|
||||
STATUS_RESPONSE,
|
||||
SET,
|
||||
SET_RESPONSE,
|
||||
LOG_LEVEL_SET,
|
||||
DATA_CHANGE_NOTIFICATION,
|
||||
GAME_STATE_NOTIFICATION,
|
||||
SENSOR_RELAY_CONFIG,
|
||||
SENSOR_RELAY_GET,
|
||||
SENSOR_RELAY_RESPONSE,
|
||||
SENSOR_RELAY_NOTIFICATION,
|
||||
HRM_DATA_NOTIFICATION,
|
||||
WIFI_CONFIG_REQUEST,
|
||||
WIFI_NOTIFICATION,
|
||||
POWER_METER_NOTIFICATION,
|
||||
CADENCE_SENSOR_NOTIFICATION,
|
||||
DEVICE_UPDATE_REQUEST,
|
||||
RELAY_ZP_MESSAGE,
|
||||
RIDE_ON,
|
||||
RESERVED,
|
||||
LOST_CONTROL,
|
||||
VENDOR_MESSAGE,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, Opcode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static Opcode? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const Opcode._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
/// Data Objects
|
||||
class DO extends $pb.ProtobufEnum {
|
||||
static const DO PAGE_DEV_INFO = DO._(0, _omitEnumNames ? '' : 'PAGE_DEV_INFO');
|
||||
static const DO PROTOCOL_VERSION = DO._(1, _omitEnumNames ? '' : 'PROTOCOL_VERSION');
|
||||
static const DO SYSTEM_FW_VERSION = DO._(2, _omitEnumNames ? '' : 'SYSTEM_FW_VERSION');
|
||||
static const DO DEVICE_NAME = DO._(3, _omitEnumNames ? '' : 'DEVICE_NAME');
|
||||
static const DO SERIAL_NUMBER = DO._(5, _omitEnumNames ? '' : 'SERIAL_NUMBER');
|
||||
static const DO SYSTEM_HW_REVISION = DO._(6, _omitEnumNames ? '' : 'SYSTEM_HW_REVISION');
|
||||
static const DO DEVICE_CAPABILITIES = DO._(7, _omitEnumNames ? '' : 'DEVICE_CAPABILITIES');
|
||||
static const DO MANUFACTURER_ID = DO._(8, _omitEnumNames ? '' : 'MANUFACTURER_ID');
|
||||
static const DO PRODUCT_ID = DO._(9, _omitEnumNames ? '' : 'PRODUCT_ID');
|
||||
static const DO DEVICE_UID = DO._(10, _omitEnumNames ? '' : 'DEVICE_UID');
|
||||
static const DO PAGE_CLIENT_SERVER_CONFIGURATION = DO._(16, _omitEnumNames ? '' : 'PAGE_CLIENT_SERVER_CONFIGURATION');
|
||||
static const DO CLIENT_SERVER_NOTIFICATIONS = DO._(17, _omitEnumNames ? '' : 'CLIENT_SERVER_NOTIFICATIONS');
|
||||
static const DO PAGE_DEVICE_UPDATE_INFO = DO._(32, _omitEnumNames ? '' : 'PAGE_DEVICE_UPDATE_INFO');
|
||||
static const DO DEVICE_UPDATE_STATUS = DO._(33, _omitEnumNames ? '' : 'DEVICE_UPDATE_STATUS');
|
||||
static const DO DEVICE_UPDATE_NEW_VERSION = DO._(34, _omitEnumNames ? '' : 'DEVICE_UPDATE_NEW_VERSION');
|
||||
static const DO PAGE_DATE_TIME = DO._(48, _omitEnumNames ? '' : 'PAGE_DATE_TIME');
|
||||
static const DO UTC_DATE_TIME = DO._(49, _omitEnumNames ? '' : 'UTC_DATE_TIME');
|
||||
static const DO PAGE_BLE_SECURITY = DO._(64, _omitEnumNames ? '' : 'PAGE_BLE_SECURITY');
|
||||
static const DO BLE_SECURE_CONNECTION_STATUS = DO._(65, _omitEnumNames ? '' : 'BLE_SECURE_CONNECTION_STATUS');
|
||||
static const DO BLE_SECURE_CONNECTION_WINDOW_STATUS = DO._(66, _omitEnumNames ? '' : 'BLE_SECURE_CONNECTION_WINDOW_STATUS');
|
||||
static const DO PAGE_TRAINER_CONFIG = DO._(512, _omitEnumNames ? '' : 'PAGE_TRAINER_CONFIG');
|
||||
static const DO TRAINER_MODE = DO._(513, _omitEnumNames ? '' : 'TRAINER_MODE');
|
||||
static const DO CFG_RESISTANCE = DO._(514, _omitEnumNames ? '' : 'CFG_RESISTANCE');
|
||||
static const DO ERG_POWER = DO._(515, _omitEnumNames ? '' : 'ERG_POWER');
|
||||
static const DO AVERAGING_WINDOW = DO._(516, _omitEnumNames ? '' : 'AVERAGING_WINDOW');
|
||||
static const DO SIM_WIND = DO._(517, _omitEnumNames ? '' : 'SIM_WIND');
|
||||
static const DO SIM_GRADE = DO._(518, _omitEnumNames ? '' : 'SIM_GRADE');
|
||||
static const DO SIM_REAL_GEAR_RATIO = DO._(519, _omitEnumNames ? '' : 'SIM_REAL_GEAR_RATIO');
|
||||
static const DO SIM_VIRT_GEAR_RATIO = DO._(520, _omitEnumNames ? '' : 'SIM_VIRT_GEAR_RATIO');
|
||||
static const DO SIM_CW = DO._(521, _omitEnumNames ? '' : 'SIM_CW');
|
||||
static const DO SIM_WHEEL_DIAMETER = DO._(522, _omitEnumNames ? '' : 'SIM_WHEEL_DIAMETER');
|
||||
static const DO SIM_BIKE_MASS = DO._(523, _omitEnumNames ? '' : 'SIM_BIKE_MASS');
|
||||
static const DO SIM_RIDER_MASS = DO._(524, _omitEnumNames ? '' : 'SIM_RIDER_MASS');
|
||||
static const DO SIM_CRR = DO._(525, _omitEnumNames ? '' : 'SIM_CRR');
|
||||
static const DO SIM_RESERVED_FRONTAL_AREA = DO._(526, _omitEnumNames ? '' : 'SIM_RESERVED_FRONTAL_AREA');
|
||||
static const DO SIM_EBRAKE = DO._(527, _omitEnumNames ? '' : 'SIM_EBRAKE');
|
||||
static const DO PAGE_TRAINER_GEAR_INDEX_CONFIG = DO._(528, _omitEnumNames ? '' : 'PAGE_TRAINER_GEAR_INDEX_CONFIG');
|
||||
static const DO FRONT_GEAR_INDEX = DO._(529, _omitEnumNames ? '' : 'FRONT_GEAR_INDEX');
|
||||
static const DO FRONT_GEAR_INDEX_MAX = DO._(530, _omitEnumNames ? '' : 'FRONT_GEAR_INDEX_MAX');
|
||||
static const DO FRONT_GEAR_INDEX_MIN = DO._(531, _omitEnumNames ? '' : 'FRONT_GEAR_INDEX_MIN');
|
||||
static const DO REAR_GEAR_INDEX = DO._(532, _omitEnumNames ? '' : 'REAR_GEAR_INDEX');
|
||||
static const DO REAR_GEAR_INDEX_MAX = DO._(533, _omitEnumNames ? '' : 'REAR_GEAR_INDEX_MAX');
|
||||
static const DO REAR_GEAR_INDEX_MIN = DO._(534, _omitEnumNames ? '' : 'REAR_GEAR_INDEX_MIN');
|
||||
static const DO PAGE_TRAINER_CONFIG2 = DO._(544, _omitEnumNames ? '' : 'PAGE_TRAINER_CONFIG2');
|
||||
static const DO HIGH_SPEED_DATA = DO._(545, _omitEnumNames ? '' : 'HIGH_SPEED_DATA');
|
||||
static const DO ERG_POWER_SMOOTHING = DO._(546, _omitEnumNames ? '' : 'ERG_POWER_SMOOTHING');
|
||||
static const DO VIRTUAL_SHIFTING_MODE = DO._(547, _omitEnumNames ? '' : 'VIRTUAL_SHIFTING_MODE');
|
||||
static const DO PAGE_DEVICE_TILT_CONFIG = DO._(560, _omitEnumNames ? '' : 'PAGE_DEVICE_TILT_CONFIG');
|
||||
static const DO DEVICE_TILT_ENABLED = DO._(561, _omitEnumNames ? '' : 'DEVICE_TILT_ENABLED');
|
||||
static const DO DEVICE_TILT_GRADIENT_MIN = DO._(562, _omitEnumNames ? '' : 'DEVICE_TILT_GRADIENT_MIN');
|
||||
static const DO DEVICE_TILT_GRADIENT_MAX = DO._(563, _omitEnumNames ? '' : 'DEVICE_TILT_GRADIENT_MAX');
|
||||
static const DO DEVICE_TILT_GRADIENT = DO._(564, _omitEnumNames ? '' : 'DEVICE_TILT_GRADIENT');
|
||||
static const DO BATTERY_STATE = DO._(771, _omitEnumNames ? '' : 'BATTERY_STATE');
|
||||
static const DO PAGE_CONTROLLER_INPUT_CONFIG = DO._(1024, _omitEnumNames ? '' : 'PAGE_CONTROLLER_INPUT_CONFIG');
|
||||
static const DO INPUT_SUPPORTED_DIGITAL_INPUTS = DO._(1025, _omitEnumNames ? '' : 'INPUT_SUPPORTED_DIGITAL_INPUTS');
|
||||
static const DO INPUT_SUPPORTED_ANALOG_INPUTS = DO._(1026, _omitEnumNames ? '' : 'INPUT_SUPPORTED_ANALOG_INPUTS');
|
||||
static const DO INPUT_ANALOG_INPUT_RANGE = DO._(1027, _omitEnumNames ? '' : 'INPUT_ANALOG_INPUT_RANGE');
|
||||
static const DO INPUT_ANALOG_INPUT_DEADZONE = DO._(1028, _omitEnumNames ? '' : 'INPUT_ANALOG_INPUT_DEADZONE');
|
||||
static const DO PAGE_WIFI_CONFIGURATION = DO._(1056, _omitEnumNames ? '' : 'PAGE_WIFI_CONFIGURATION');
|
||||
static const DO WIFI_ENABLED = DO._(1057, _omitEnumNames ? '' : 'WIFI_ENABLED');
|
||||
static const DO WIFI_STATUS = DO._(1058, _omitEnumNames ? '' : 'WIFI_STATUS');
|
||||
static const DO WIFI_SSID = DO._(1059, _omitEnumNames ? '' : 'WIFI_SSID');
|
||||
static const DO WIFI_BAND = DO._(1060, _omitEnumNames ? '' : 'WIFI_BAND');
|
||||
static const DO WIFI_RSSI = DO._(1061, _omitEnumNames ? '' : 'WIFI_RSSI');
|
||||
static const DO WIFI_REGION_CODE = DO._(1062, _omitEnumNames ? '' : 'WIFI_REGION_CODE');
|
||||
static const DO SENSOR_RELAY_DATA_PAGE = DO._(1280, _omitEnumNames ? '' : 'SENSOR_RELAY_DATA_PAGE');
|
||||
static const DO SENSOR_RELAY_SUPPORTED_SENSORS = DO._(1281, _omitEnumNames ? '' : 'SENSOR_RELAY_SUPPORTED_SENSORS');
|
||||
static const DO SENSOR_RELAY_PAIRED_SENSORS = DO._(1282, _omitEnumNames ? '' : 'SENSOR_RELAY_PAIRED_SENSORS');
|
||||
|
||||
static const $core.List<DO> values = <DO> [
|
||||
PAGE_DEV_INFO,
|
||||
PROTOCOL_VERSION,
|
||||
SYSTEM_FW_VERSION,
|
||||
DEVICE_NAME,
|
||||
SERIAL_NUMBER,
|
||||
SYSTEM_HW_REVISION,
|
||||
DEVICE_CAPABILITIES,
|
||||
MANUFACTURER_ID,
|
||||
PRODUCT_ID,
|
||||
DEVICE_UID,
|
||||
PAGE_CLIENT_SERVER_CONFIGURATION,
|
||||
CLIENT_SERVER_NOTIFICATIONS,
|
||||
PAGE_DEVICE_UPDATE_INFO,
|
||||
DEVICE_UPDATE_STATUS,
|
||||
DEVICE_UPDATE_NEW_VERSION,
|
||||
PAGE_DATE_TIME,
|
||||
UTC_DATE_TIME,
|
||||
PAGE_BLE_SECURITY,
|
||||
BLE_SECURE_CONNECTION_STATUS,
|
||||
BLE_SECURE_CONNECTION_WINDOW_STATUS,
|
||||
PAGE_TRAINER_CONFIG,
|
||||
TRAINER_MODE,
|
||||
CFG_RESISTANCE,
|
||||
ERG_POWER,
|
||||
AVERAGING_WINDOW,
|
||||
SIM_WIND,
|
||||
SIM_GRADE,
|
||||
SIM_REAL_GEAR_RATIO,
|
||||
SIM_VIRT_GEAR_RATIO,
|
||||
SIM_CW,
|
||||
SIM_WHEEL_DIAMETER,
|
||||
SIM_BIKE_MASS,
|
||||
SIM_RIDER_MASS,
|
||||
SIM_CRR,
|
||||
SIM_RESERVED_FRONTAL_AREA,
|
||||
SIM_EBRAKE,
|
||||
PAGE_TRAINER_GEAR_INDEX_CONFIG,
|
||||
FRONT_GEAR_INDEX,
|
||||
FRONT_GEAR_INDEX_MAX,
|
||||
FRONT_GEAR_INDEX_MIN,
|
||||
REAR_GEAR_INDEX,
|
||||
REAR_GEAR_INDEX_MAX,
|
||||
REAR_GEAR_INDEX_MIN,
|
||||
PAGE_TRAINER_CONFIG2,
|
||||
HIGH_SPEED_DATA,
|
||||
ERG_POWER_SMOOTHING,
|
||||
VIRTUAL_SHIFTING_MODE,
|
||||
PAGE_DEVICE_TILT_CONFIG,
|
||||
DEVICE_TILT_ENABLED,
|
||||
DEVICE_TILT_GRADIENT_MIN,
|
||||
DEVICE_TILT_GRADIENT_MAX,
|
||||
DEVICE_TILT_GRADIENT,
|
||||
BATTERY_STATE,
|
||||
PAGE_CONTROLLER_INPUT_CONFIG,
|
||||
INPUT_SUPPORTED_DIGITAL_INPUTS,
|
||||
INPUT_SUPPORTED_ANALOG_INPUTS,
|
||||
INPUT_ANALOG_INPUT_RANGE,
|
||||
INPUT_ANALOG_INPUT_DEADZONE,
|
||||
PAGE_WIFI_CONFIGURATION,
|
||||
WIFI_ENABLED,
|
||||
WIFI_STATUS,
|
||||
WIFI_SSID,
|
||||
WIFI_BAND,
|
||||
WIFI_RSSI,
|
||||
WIFI_REGION_CODE,
|
||||
SENSOR_RELAY_DATA_PAGE,
|
||||
SENSOR_RELAY_SUPPORTED_SENSORS,
|
||||
SENSOR_RELAY_PAIRED_SENSORS,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, DO> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static DO? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const DO._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class Status extends $pb.ProtobufEnum {
|
||||
static const Status SUCCESS = Status._(0, _omitEnumNames ? '' : 'SUCCESS');
|
||||
static const Status FAILURE = Status._(1, _omitEnumNames ? '' : 'FAILURE');
|
||||
static const Status BUSY = Status._(2, _omitEnumNames ? '' : 'BUSY');
|
||||
static const Status INVALID_PARAM = Status._(3, _omitEnumNames ? '' : 'INVALID_PARAM');
|
||||
static const Status NOT_PERMITTED = Status._(4, _omitEnumNames ? '' : 'NOT_PERMITTED');
|
||||
static const Status NOT_SUPPORTED = Status._(5, _omitEnumNames ? '' : 'NOT_SUPPORTED');
|
||||
static const Status INVALID_MODE = Status._(6, _omitEnumNames ? '' : 'INVALID_MODE');
|
||||
|
||||
static const $core.List<Status> values = <Status> [
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
BUSY,
|
||||
INVALID_PARAM,
|
||||
NOT_PERMITTED,
|
||||
NOT_SUPPORTED,
|
||||
INVALID_MODE,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, Status> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static Status? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const Status._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class DeviceType extends $pb.ProtobufEnum {
|
||||
static const DeviceType UNDEFINED = DeviceType._(0, _omitEnumNames ? '' : 'UNDEFINED');
|
||||
static const DeviceType CYCLING_TURBO_TRAINER = DeviceType._(1, _omitEnumNames ? '' : 'CYCLING_TURBO_TRAINER');
|
||||
static const DeviceType USER_INPUT_DEVICE = DeviceType._(2, _omitEnumNames ? '' : 'USER_INPUT_DEVICE');
|
||||
static const DeviceType TREADMILL = DeviceType._(3, _omitEnumNames ? '' : 'TREADMILL');
|
||||
static const DeviceType SENSOR_RELAY = DeviceType._(4, _omitEnumNames ? '' : 'SENSOR_RELAY');
|
||||
static const DeviceType HEART_RATE_MONITOR = DeviceType._(5, _omitEnumNames ? '' : 'HEART_RATE_MONITOR');
|
||||
static const DeviceType POWER_METER = DeviceType._(6, _omitEnumNames ? '' : 'POWER_METER');
|
||||
static const DeviceType CADENCE_SENSOR = DeviceType._(7, _omitEnumNames ? '' : 'CADENCE_SENSOR');
|
||||
static const DeviceType WIFI = DeviceType._(8, _omitEnumNames ? '' : 'WIFI');
|
||||
|
||||
static const $core.List<DeviceType> values = <DeviceType> [
|
||||
UNDEFINED,
|
||||
CYCLING_TURBO_TRAINER,
|
||||
USER_INPUT_DEVICE,
|
||||
TREADMILL,
|
||||
SENSOR_RELAY,
|
||||
HEART_RATE_MONITOR,
|
||||
POWER_METER,
|
||||
CADENCE_SENSOR,
|
||||
WIFI,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, DeviceType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static DeviceType? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const DeviceType._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class TrainerMode extends $pb.ProtobufEnum {
|
||||
static const TrainerMode MODE_UNKNOWN = TrainerMode._(0, _omitEnumNames ? '' : 'MODE_UNKNOWN');
|
||||
static const TrainerMode MODE_ERG = TrainerMode._(1, _omitEnumNames ? '' : 'MODE_ERG');
|
||||
static const TrainerMode MODE_RESISTANCE = TrainerMode._(2, _omitEnumNames ? '' : 'MODE_RESISTANCE');
|
||||
static const TrainerMode MODE_SIM = TrainerMode._(3, _omitEnumNames ? '' : 'MODE_SIM');
|
||||
|
||||
static const $core.List<TrainerMode> values = <TrainerMode> [
|
||||
MODE_UNKNOWN,
|
||||
MODE_ERG,
|
||||
MODE_RESISTANCE,
|
||||
MODE_SIM,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, TrainerMode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static TrainerMode? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const TrainerMode._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class ChargingState extends $pb.ProtobufEnum {
|
||||
static const ChargingState CHARGING_IDLE = ChargingState._(0, _omitEnumNames ? '' : 'CHARGING_IDLE');
|
||||
static const ChargingState CHARGING_PROGRESS = ChargingState._(1, _omitEnumNames ? '' : 'CHARGING_PROGRESS');
|
||||
static const ChargingState CHARGING_DONE = ChargingState._(2, _omitEnumNames ? '' : 'CHARGING_DONE');
|
||||
|
||||
static const $core.List<ChargingState> values = <ChargingState> [
|
||||
CHARGING_IDLE,
|
||||
CHARGING_PROGRESS,
|
||||
CHARGING_DONE,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, ChargingState> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static ChargingState? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const ChargingState._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class SpindownStatus extends $pb.ProtobufEnum {
|
||||
static const SpindownStatus SPINDOWN_IDLE = SpindownStatus._(0, _omitEnumNames ? '' : 'SPINDOWN_IDLE');
|
||||
static const SpindownStatus SPINDOWN_REQUESTED = SpindownStatus._(1, _omitEnumNames ? '' : 'SPINDOWN_REQUESTED');
|
||||
static const SpindownStatus SPINDOWN_SUCCESS = SpindownStatus._(2, _omitEnumNames ? '' : 'SPINDOWN_SUCCESS');
|
||||
static const SpindownStatus SPINDOWN_ERROR = SpindownStatus._(3, _omitEnumNames ? '' : 'SPINDOWN_ERROR');
|
||||
static const SpindownStatus SPINDOWN_STOP_PEDALLING = SpindownStatus._(4, _omitEnumNames ? '' : 'SPINDOWN_STOP_PEDALLING');
|
||||
static const SpindownStatus SPINDOWN_ERROR_TIMEOUT = SpindownStatus._(5, _omitEnumNames ? '' : 'SPINDOWN_ERROR_TIMEOUT');
|
||||
static const SpindownStatus SPINDOWN_ERROR_TOSHORT = SpindownStatus._(6, _omitEnumNames ? '' : 'SPINDOWN_ERROR_TOSHORT');
|
||||
static const SpindownStatus SPINDOWN_ERROR_TOSLOW = SpindownStatus._(7, _omitEnumNames ? '' : 'SPINDOWN_ERROR_TOSLOW');
|
||||
static const SpindownStatus SPINDOWN_ERROR_TOFAST = SpindownStatus._(8, _omitEnumNames ? '' : 'SPINDOWN_ERROR_TOFAST');
|
||||
static const SpindownStatus SPINDOWN_ERROR_SAMPLEERROR = SpindownStatus._(9, _omitEnumNames ? '' : 'SPINDOWN_ERROR_SAMPLEERROR');
|
||||
static const SpindownStatus SPINDOWN_ERROR_ABORT = SpindownStatus._(10, _omitEnumNames ? '' : 'SPINDOWN_ERROR_ABORT');
|
||||
|
||||
static const $core.List<SpindownStatus> values = <SpindownStatus> [
|
||||
SPINDOWN_IDLE,
|
||||
SPINDOWN_REQUESTED,
|
||||
SPINDOWN_SUCCESS,
|
||||
SPINDOWN_ERROR,
|
||||
SPINDOWN_STOP_PEDALLING,
|
||||
SPINDOWN_ERROR_TIMEOUT,
|
||||
SPINDOWN_ERROR_TOSHORT,
|
||||
SPINDOWN_ERROR_TOSLOW,
|
||||
SPINDOWN_ERROR_TOFAST,
|
||||
SPINDOWN_ERROR_SAMPLEERROR,
|
||||
SPINDOWN_ERROR_ABORT,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, SpindownStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static SpindownStatus? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const SpindownStatus._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class LogLevel extends $pb.ProtobufEnum {
|
||||
static const LogLevel LOGLEVEL_OFF = LogLevel._(0, _omitEnumNames ? '' : 'LOGLEVEL_OFF');
|
||||
static const LogLevel LOGLEVEL_ERROR = LogLevel._(1, _omitEnumNames ? '' : 'LOGLEVEL_ERROR');
|
||||
static const LogLevel LOGLEVEL_WARNING = LogLevel._(2, _omitEnumNames ? '' : 'LOGLEVEL_WARNING');
|
||||
static const LogLevel LOGLEVEL_INFO = LogLevel._(3, _omitEnumNames ? '' : 'LOGLEVEL_INFO');
|
||||
static const LogLevel LOGLEVEL_DEBUG = LogLevel._(4, _omitEnumNames ? '' : 'LOGLEVEL_DEBUG');
|
||||
static const LogLevel LOGLEVEL_TRACE = LogLevel._(5, _omitEnumNames ? '' : 'LOGLEVEL_TRACE');
|
||||
|
||||
static const $core.List<LogLevel> values = <LogLevel> [
|
||||
LOGLEVEL_OFF,
|
||||
LOGLEVEL_ERROR,
|
||||
LOGLEVEL_WARNING,
|
||||
LOGLEVEL_INFO,
|
||||
LOGLEVEL_DEBUG,
|
||||
LOGLEVEL_TRACE,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, LogLevel> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static LogLevel? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const LogLevel._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class RoadSurfaceType extends $pb.ProtobufEnum {
|
||||
static const RoadSurfaceType ROAD_SURFACE_SMOOTH_TARMAC = RoadSurfaceType._(0, _omitEnumNames ? '' : 'ROAD_SURFACE_SMOOTH_TARMAC');
|
||||
static const RoadSurfaceType ROAD_SURFACE_BRICK_ROAD = RoadSurfaceType._(1, _omitEnumNames ? '' : 'ROAD_SURFACE_BRICK_ROAD');
|
||||
static const RoadSurfaceType ROAD_SURFACE_HARD_COBBLES = RoadSurfaceType._(2, _omitEnumNames ? '' : 'ROAD_SURFACE_HARD_COBBLES');
|
||||
static const RoadSurfaceType ROAD_SURFACE_SOFT_COBBLES = RoadSurfaceType._(3, _omitEnumNames ? '' : 'ROAD_SURFACE_SOFT_COBBLES');
|
||||
static const RoadSurfaceType ROAD_SURFACE_NARROW_WOODEN_PLANKS = RoadSurfaceType._(4, _omitEnumNames ? '' : 'ROAD_SURFACE_NARROW_WOODEN_PLANKS');
|
||||
static const RoadSurfaceType ROAD_SURFACE_WIDE_WOODEN_PLANKS = RoadSurfaceType._(5, _omitEnumNames ? '' : 'ROAD_SURFACE_WIDE_WOODEN_PLANKS');
|
||||
static const RoadSurfaceType ROAD_SURFACE_DIRT = RoadSurfaceType._(6, _omitEnumNames ? '' : 'ROAD_SURFACE_DIRT');
|
||||
static const RoadSurfaceType ROAD_SURFACE_GRAVEL = RoadSurfaceType._(7, _omitEnumNames ? '' : 'ROAD_SURFACE_GRAVEL');
|
||||
static const RoadSurfaceType ROAD_SURFACE_CATTLE_GRID = RoadSurfaceType._(8, _omitEnumNames ? '' : 'ROAD_SURFACE_CATTLE_GRID');
|
||||
static const RoadSurfaceType ROAD_SURFACE_CONCRETE_FLAG_STONES = RoadSurfaceType._(9, _omitEnumNames ? '' : 'ROAD_SURFACE_CONCRETE_FLAG_STONES');
|
||||
static const RoadSurfaceType ROAD_SURFACE_ICE = RoadSurfaceType._(10, _omitEnumNames ? '' : 'ROAD_SURFACE_ICE');
|
||||
|
||||
static const $core.List<RoadSurfaceType> values = <RoadSurfaceType> [
|
||||
ROAD_SURFACE_SMOOTH_TARMAC,
|
||||
ROAD_SURFACE_BRICK_ROAD,
|
||||
ROAD_SURFACE_HARD_COBBLES,
|
||||
ROAD_SURFACE_SOFT_COBBLES,
|
||||
ROAD_SURFACE_NARROW_WOODEN_PLANKS,
|
||||
ROAD_SURFACE_WIDE_WOODEN_PLANKS,
|
||||
ROAD_SURFACE_DIRT,
|
||||
ROAD_SURFACE_GRAVEL,
|
||||
ROAD_SURFACE_CATTLE_GRID,
|
||||
ROAD_SURFACE_CONCRETE_FLAG_STONES,
|
||||
ROAD_SURFACE_ICE,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, RoadSurfaceType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static RoadSurfaceType? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const RoadSurfaceType._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class WifiStatusCode extends $pb.ProtobufEnum {
|
||||
static const WifiStatusCode WIFI_STATUS_DISABLED = WifiStatusCode._(0, _omitEnumNames ? '' : 'WIFI_STATUS_DISABLED');
|
||||
static const WifiStatusCode WIFI_STATUS_NOT_PROVISIONED = WifiStatusCode._(1, _omitEnumNames ? '' : 'WIFI_STATUS_NOT_PROVISIONED');
|
||||
static const WifiStatusCode WIFI_STATUS_SCANNING = WifiStatusCode._(2, _omitEnumNames ? '' : 'WIFI_STATUS_SCANNING');
|
||||
static const WifiStatusCode WIFI_STATUS_DISCONNECTED = WifiStatusCode._(3, _omitEnumNames ? '' : 'WIFI_STATUS_DISCONNECTED');
|
||||
static const WifiStatusCode WIFI_STATUS_CONNECTING = WifiStatusCode._(4, _omitEnumNames ? '' : 'WIFI_STATUS_CONNECTING');
|
||||
static const WifiStatusCode WIFI_STATUS_CONNECTED = WifiStatusCode._(5, _omitEnumNames ? '' : 'WIFI_STATUS_CONNECTED');
|
||||
static const WifiStatusCode WIFI_STATUS_ERROR = WifiStatusCode._(6, _omitEnumNames ? '' : 'WIFI_STATUS_ERROR');
|
||||
|
||||
static const $core.List<WifiStatusCode> values = <WifiStatusCode> [
|
||||
WIFI_STATUS_DISABLED,
|
||||
WIFI_STATUS_NOT_PROVISIONED,
|
||||
WIFI_STATUS_SCANNING,
|
||||
WIFI_STATUS_DISCONNECTED,
|
||||
WIFI_STATUS_CONNECTING,
|
||||
WIFI_STATUS_CONNECTED,
|
||||
WIFI_STATUS_ERROR,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, WifiStatusCode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static WifiStatusCode? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const WifiStatusCode._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class WifiErrorCode extends $pb.ProtobufEnum {
|
||||
static const WifiErrorCode WIFI_ERROR_UNKNOWN = WifiErrorCode._(0, _omitEnumNames ? '' : 'WIFI_ERROR_UNKNOWN');
|
||||
static const WifiErrorCode WIFI_ERROR_NO_MEMORY = WifiErrorCode._(1, _omitEnumNames ? '' : 'WIFI_ERROR_NO_MEMORY');
|
||||
static const WifiErrorCode WIFI_ERROR_INVALID_PARAMETERS = WifiErrorCode._(2, _omitEnumNames ? '' : 'WIFI_ERROR_INVALID_PARAMETERS');
|
||||
static const WifiErrorCode WIFI_ERROR_INVALID_STATE = WifiErrorCode._(3, _omitEnumNames ? '' : 'WIFI_ERROR_INVALID_STATE');
|
||||
static const WifiErrorCode WIFI_ERROR_NOT_FOUND = WifiErrorCode._(4, _omitEnumNames ? '' : 'WIFI_ERROR_NOT_FOUND');
|
||||
static const WifiErrorCode WIFI_ERROR_NOT_SUPPORTED = WifiErrorCode._(5, _omitEnumNames ? '' : 'WIFI_ERROR_NOT_SUPPORTED');
|
||||
static const WifiErrorCode WIFI_ERROR_NOT_ALLOWED = WifiErrorCode._(6, _omitEnumNames ? '' : 'WIFI_ERROR_NOT_ALLOWED');
|
||||
static const WifiErrorCode WIFI_ERROR_NOT_INITIALISED = WifiErrorCode._(7, _omitEnumNames ? '' : 'WIFI_ERROR_NOT_INITIALISED');
|
||||
static const WifiErrorCode WIFI_ERROR_NOT_STARTED = WifiErrorCode._(8, _omitEnumNames ? '' : 'WIFI_ERROR_NOT_STARTED');
|
||||
static const WifiErrorCode WIFI_ERROR_TIMEOUT = WifiErrorCode._(9, _omitEnumNames ? '' : 'WIFI_ERROR_TIMEOUT');
|
||||
static const WifiErrorCode WIFI_ERROR_MODE = WifiErrorCode._(10, _omitEnumNames ? '' : 'WIFI_ERROR_MODE');
|
||||
static const WifiErrorCode WIFI_ERROR_SSID_INVALID = WifiErrorCode._(11, _omitEnumNames ? '' : 'WIFI_ERROR_SSID_INVALID');
|
||||
|
||||
static const $core.List<WifiErrorCode> values = <WifiErrorCode> [
|
||||
WIFI_ERROR_UNKNOWN,
|
||||
WIFI_ERROR_NO_MEMORY,
|
||||
WIFI_ERROR_INVALID_PARAMETERS,
|
||||
WIFI_ERROR_INVALID_STATE,
|
||||
WIFI_ERROR_NOT_FOUND,
|
||||
WIFI_ERROR_NOT_SUPPORTED,
|
||||
WIFI_ERROR_NOT_ALLOWED,
|
||||
WIFI_ERROR_NOT_INITIALISED,
|
||||
WIFI_ERROR_NOT_STARTED,
|
||||
WIFI_ERROR_TIMEOUT,
|
||||
WIFI_ERROR_MODE,
|
||||
WIFI_ERROR_SSID_INVALID,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, WifiErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static WifiErrorCode? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const WifiErrorCode._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class InterfaceType extends $pb.ProtobufEnum {
|
||||
static const InterfaceType INTERFACE_BLE = InterfaceType._(1, _omitEnumNames ? '' : 'INTERFACE_BLE');
|
||||
static const InterfaceType INTERFACE_ANT = InterfaceType._(2, _omitEnumNames ? '' : 'INTERFACE_ANT');
|
||||
static const InterfaceType INTERFACE_USB = InterfaceType._(3, _omitEnumNames ? '' : 'INTERFACE_USB');
|
||||
static const InterfaceType INTERFACE_ETH = InterfaceType._(4, _omitEnumNames ? '' : 'INTERFACE_ETH');
|
||||
static const InterfaceType INTERFACE_WIFI = InterfaceType._(5, _omitEnumNames ? '' : 'INTERFACE_WIFI');
|
||||
|
||||
static const $core.List<InterfaceType> values = <InterfaceType> [
|
||||
INTERFACE_BLE,
|
||||
INTERFACE_ANT,
|
||||
INTERFACE_USB,
|
||||
INTERFACE_ETH,
|
||||
INTERFACE_WIFI,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, InterfaceType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static InterfaceType? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const InterfaceType._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class SensorConnectionStatus extends $pb.ProtobufEnum {
|
||||
static const SensorConnectionStatus SENSOR_STATUS_DISCOVERED = SensorConnectionStatus._(1, _omitEnumNames ? '' : 'SENSOR_STATUS_DISCOVERED');
|
||||
static const SensorConnectionStatus SENSOR_STATUS_DISCONNECTED = SensorConnectionStatus._(2, _omitEnumNames ? '' : 'SENSOR_STATUS_DISCONNECTED');
|
||||
static const SensorConnectionStatus SENSOR_STATUS_PAIRING = SensorConnectionStatus._(3, _omitEnumNames ? '' : 'SENSOR_STATUS_PAIRING');
|
||||
static const SensorConnectionStatus SENSOR_STATUS_CONNECTED = SensorConnectionStatus._(4, _omitEnumNames ? '' : 'SENSOR_STATUS_CONNECTED');
|
||||
|
||||
static const $core.List<SensorConnectionStatus> values = <SensorConnectionStatus> [
|
||||
SENSOR_STATUS_DISCOVERED,
|
||||
SENSOR_STATUS_DISCONNECTED,
|
||||
SENSOR_STATUS_PAIRING,
|
||||
SENSOR_STATUS_CONNECTED,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, SensorConnectionStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static SensorConnectionStatus? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const SensorConnectionStatus._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class BleSecureConnectionStatus extends $pb.ProtobufEnum {
|
||||
static const BleSecureConnectionStatus BLE_CONNECTION_SECURITY_STATUS_NONE = BleSecureConnectionStatus._(0, _omitEnumNames ? '' : 'BLE_CONNECTION_SECURITY_STATUS_NONE');
|
||||
static const BleSecureConnectionStatus BLE_CONNECTION_SECURITY_STATUS_INPROGRESS = BleSecureConnectionStatus._(1, _omitEnumNames ? '' : 'BLE_CONNECTION_SECURITY_STATUS_INPROGRESS');
|
||||
static const BleSecureConnectionStatus BLE_CONNECTION_SECURITY_STATUS_ACTIVE = BleSecureConnectionStatus._(2, _omitEnumNames ? '' : 'BLE_CONNECTION_SECURITY_STATUS_ACTIVE');
|
||||
static const BleSecureConnectionStatus BLE_CONNECTION_SECURITY_STATUS_REJECTED = BleSecureConnectionStatus._(3, _omitEnumNames ? '' : 'BLE_CONNECTION_SECURITY_STATUS_REJECTED');
|
||||
|
||||
static const $core.List<BleSecureConnectionStatus> values = <BleSecureConnectionStatus> [
|
||||
BLE_CONNECTION_SECURITY_STATUS_NONE,
|
||||
BLE_CONNECTION_SECURITY_STATUS_INPROGRESS,
|
||||
BLE_CONNECTION_SECURITY_STATUS_ACTIVE,
|
||||
BLE_CONNECTION_SECURITY_STATUS_REJECTED,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, BleSecureConnectionStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static BleSecureConnectionStatus? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const BleSecureConnectionStatus._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class BleSecureConnectionWindowStatus extends $pb.ProtobufEnum {
|
||||
static const BleSecureConnectionWindowStatus BLE_SECURE_CONNECTION_WINDOW_STATUS_CLOSED = BleSecureConnectionWindowStatus._(0, _omitEnumNames ? '' : 'BLE_SECURE_CONNECTION_WINDOW_STATUS_CLOSED');
|
||||
static const BleSecureConnectionWindowStatus BLE_SECURE_CONNECTION_WINDOW_STATUS_OPEN = BleSecureConnectionWindowStatus._(1, _omitEnumNames ? '' : 'BLE_SECURE_CONNECTION_WINDOW_STATUS_OPEN');
|
||||
|
||||
static const $core.List<BleSecureConnectionWindowStatus> values = <BleSecureConnectionWindowStatus> [
|
||||
BLE_SECURE_CONNECTION_WINDOW_STATUS_CLOSED,
|
||||
BLE_SECURE_CONNECTION_WINDOW_STATUS_OPEN,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, BleSecureConnectionWindowStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static BleSecureConnectionWindowStatus? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const BleSecureConnectionWindowStatus._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class WifiRegionCode_RegionCodeType extends $pb.ProtobufEnum {
|
||||
static const WifiRegionCode_RegionCodeType ALPHA_2 = WifiRegionCode_RegionCodeType._(0, _omitEnumNames ? '' : 'ALPHA_2');
|
||||
static const WifiRegionCode_RegionCodeType ALPHA_3 = WifiRegionCode_RegionCodeType._(1, _omitEnumNames ? '' : 'ALPHA_3');
|
||||
static const WifiRegionCode_RegionCodeType NUMERIC = WifiRegionCode_RegionCodeType._(2, _omitEnumNames ? '' : 'NUMERIC');
|
||||
static const WifiRegionCode_RegionCodeType UNKNOWN = WifiRegionCode_RegionCodeType._(3, _omitEnumNames ? '' : 'UNKNOWN');
|
||||
|
||||
static const $core.List<WifiRegionCode_RegionCodeType> values = <WifiRegionCode_RegionCodeType> [
|
||||
ALPHA_2,
|
||||
ALPHA_3,
|
||||
NUMERIC,
|
||||
UNKNOWN,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, WifiRegionCode_RegionCodeType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static WifiRegionCode_RegionCodeType? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const WifiRegionCode_RegionCodeType._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
|
||||
const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');
|
||||
1692
lib/bluetooth/protocol/zp.pbjson.dart
Normal file
1692
lib/bluetooth/protocol/zp.pbjson.dart
Normal file
File diff suppressed because it is too large
Load Diff
14
lib/bluetooth/protocol/zp.pbserver.dart
Normal file
14
lib/bluetooth/protocol/zp.pbserver.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: zp.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
export 'zp.pb.dart';
|
||||
|
||||
896
lib/bluetooth/protocol/zp_vendor.pb.dart
Normal file
896
lib/bluetooth/protocol/zp_vendor.pb.dart
Normal file
@@ -0,0 +1,896 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: zp_vendor.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'zp_vendor.pbenum.dart';
|
||||
|
||||
export 'zp_vendor.pbenum.dart';
|
||||
|
||||
class ControllerSync extends $pb.GeneratedMessage {
|
||||
factory ControllerSync({
|
||||
ControllerSyncStatus? status,
|
||||
$core.int? timeStamp,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (status != null) {
|
||||
$result.status = status;
|
||||
}
|
||||
if (timeStamp != null) {
|
||||
$result.timeStamp = timeStamp;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
ControllerSync._() : super();
|
||||
factory ControllerSync.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory ControllerSync.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ControllerSync', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..e<ControllerSyncStatus>(1, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: ControllerSyncStatus.NOT_CONNECTED, valueOf: ControllerSyncStatus.valueOf, enumValues: ControllerSyncStatus.values)
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'timeStamp', $pb.PbFieldType.O3, protoName: 'timeStamp')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
ControllerSync clone() => ControllerSync()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
ControllerSync copyWith(void Function(ControllerSync) updates) => super.copyWith((message) => updates(message as ControllerSync)) as ControllerSync;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ControllerSync create() => ControllerSync._();
|
||||
ControllerSync createEmptyInstance() => create();
|
||||
static $pb.PbList<ControllerSync> createRepeated() => $pb.PbList<ControllerSync>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ControllerSync getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ControllerSync>(create);
|
||||
static ControllerSync? _defaultInstance;
|
||||
|
||||
/// optional in code; proto3 treats as present when non-zero
|
||||
@$pb.TagNumber(1)
|
||||
ControllerSyncStatus get status => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set status(ControllerSyncStatus v) { setField(1, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasStatus() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearStatus() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get timeStamp => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set timeStamp($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasTimeStamp() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearTimeStamp() => clearField(2);
|
||||
}
|
||||
|
||||
class EnableTestMode extends $pb.GeneratedMessage {
|
||||
factory EnableTestMode({
|
||||
$core.bool? enable,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (enable != null) {
|
||||
$result.enable = enable;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
EnableTestMode._() : super();
|
||||
factory EnableTestMode.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory EnableTestMode.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EnableTestMode', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..aOB(1, _omitFieldNames ? '' : 'enable')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
EnableTestMode clone() => EnableTestMode()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
EnableTestMode copyWith(void Function(EnableTestMode) updates) => super.copyWith((message) => updates(message as EnableTestMode)) as EnableTestMode;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EnableTestMode create() => EnableTestMode._();
|
||||
EnableTestMode createEmptyInstance() => create();
|
||||
static $pb.PbList<EnableTestMode> createRepeated() => $pb.PbList<EnableTestMode>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EnableTestMode getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EnableTestMode>(create);
|
||||
static EnableTestMode? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool get enable => $_getBF(0);
|
||||
@$pb.TagNumber(1)
|
||||
set enable($core.bool v) { $_setBool(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasEnable() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearEnable() => clearField(1);
|
||||
}
|
||||
|
||||
class PairDevices extends $pb.GeneratedMessage {
|
||||
factory PairDevices({
|
||||
$core.bool? pair,
|
||||
PairDeviceType? type,
|
||||
$core.List<$core.int>? deviceId,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (pair != null) {
|
||||
$result.pair = pair;
|
||||
}
|
||||
if (type != null) {
|
||||
$result.type = type;
|
||||
}
|
||||
if (deviceId != null) {
|
||||
$result.deviceId = deviceId;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
PairDevices._() : super();
|
||||
factory PairDevices.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory PairDevices.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PairDevices', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..aOB(1, _omitFieldNames ? '' : 'pair')
|
||||
..e<PairDeviceType>(2, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, defaultOrMaker: PairDeviceType.BLE, valueOf: PairDeviceType.valueOf, enumValues: PairDeviceType.values)
|
||||
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'deviceId', $pb.PbFieldType.OY, protoName: 'deviceId')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
PairDevices clone() => PairDevices()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
PairDevices copyWith(void Function(PairDevices) updates) => super.copyWith((message) => updates(message as PairDevices)) as PairDevices;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static PairDevices create() => PairDevices._();
|
||||
PairDevices createEmptyInstance() => create();
|
||||
static $pb.PbList<PairDevices> createRepeated() => $pb.PbList<PairDevices>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static PairDevices getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<PairDevices>(create);
|
||||
static PairDevices? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool get pair => $_getBF(0);
|
||||
@$pb.TagNumber(1)
|
||||
set pair($core.bool v) { $_setBool(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasPair() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearPair() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
PairDeviceType get type => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set type(PairDeviceType v) { setField(2, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasType() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearType() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get deviceId => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set deviceId($core.List<$core.int> v) { $_setBytes(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasDeviceId() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearDeviceId() => clearField(3);
|
||||
}
|
||||
|
||||
class DevicePairingDataPage_PairedDevice extends $pb.GeneratedMessage {
|
||||
factory DevicePairingDataPage_PairedDevice({
|
||||
$core.List<$core.int>? device,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (device != null) {
|
||||
$result.device = device;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
DevicePairingDataPage_PairedDevice._() : super();
|
||||
factory DevicePairingDataPage_PairedDevice.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory DevicePairingDataPage_PairedDevice.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DevicePairingDataPage.PairedDevice', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'device', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
DevicePairingDataPage_PairedDevice clone() => DevicePairingDataPage_PairedDevice()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
DevicePairingDataPage_PairedDevice copyWith(void Function(DevicePairingDataPage_PairedDevice) updates) => super.copyWith((message) => updates(message as DevicePairingDataPage_PairedDevice)) as DevicePairingDataPage_PairedDevice;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static DevicePairingDataPage_PairedDevice create() => DevicePairingDataPage_PairedDevice._();
|
||||
DevicePairingDataPage_PairedDevice createEmptyInstance() => create();
|
||||
static $pb.PbList<DevicePairingDataPage_PairedDevice> createRepeated() => $pb.PbList<DevicePairingDataPage_PairedDevice>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static DevicePairingDataPage_PairedDevice getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DevicePairingDataPage_PairedDevice>(create);
|
||||
static DevicePairingDataPage_PairedDevice? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get device => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set device($core.List<$core.int> v) { $_setBytes(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasDevice() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearDevice() => clearField(1);
|
||||
}
|
||||
|
||||
class DevicePairingDataPage extends $pb.GeneratedMessage {
|
||||
factory DevicePairingDataPage({
|
||||
$core.int? devicesCount,
|
||||
$core.int? pairingStatus,
|
||||
$core.Iterable<DevicePairingDataPage_PairedDevice>? pairingDevList,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (devicesCount != null) {
|
||||
$result.devicesCount = devicesCount;
|
||||
}
|
||||
if (pairingStatus != null) {
|
||||
$result.pairingStatus = pairingStatus;
|
||||
}
|
||||
if (pairingDevList != null) {
|
||||
$result.pairingDevList.addAll(pairingDevList);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
DevicePairingDataPage._() : super();
|
||||
factory DevicePairingDataPage.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory DevicePairingDataPage.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DevicePairingDataPage', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'devicesCount', $pb.PbFieldType.O3, protoName: 'devicesCount')
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'pairingStatus', $pb.PbFieldType.O3, protoName: 'pairingStatus')
|
||||
..pc<DevicePairingDataPage_PairedDevice>(3, _omitFieldNames ? '' : 'pairingDevList', $pb.PbFieldType.PM, protoName: 'pairingDevList', subBuilder: DevicePairingDataPage_PairedDevice.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
DevicePairingDataPage clone() => DevicePairingDataPage()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
DevicePairingDataPage copyWith(void Function(DevicePairingDataPage) updates) => super.copyWith((message) => updates(message as DevicePairingDataPage)) as DevicePairingDataPage;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static DevicePairingDataPage create() => DevicePairingDataPage._();
|
||||
DevicePairingDataPage createEmptyInstance() => create();
|
||||
static $pb.PbList<DevicePairingDataPage> createRepeated() => $pb.PbList<DevicePairingDataPage>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static DevicePairingDataPage getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DevicePairingDataPage>(create);
|
||||
static DevicePairingDataPage? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get devicesCount => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set devicesCount($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasDevicesCount() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearDevicesCount() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get pairingStatus => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set pairingStatus($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasPairingStatus() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearPairingStatus() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<DevicePairingDataPage_PairedDevice> get pairingDevList => $_getList(2);
|
||||
}
|
||||
|
||||
enum SetDfuTest_TestCase {
|
||||
failedEnterDfu,
|
||||
failedStartAdvertising,
|
||||
crcFailure,
|
||||
notSet
|
||||
}
|
||||
|
||||
class SetDfuTest extends $pb.GeneratedMessage {
|
||||
factory SetDfuTest({
|
||||
$core.bool? failedEnterDfu,
|
||||
$core.bool? failedStartAdvertising,
|
||||
$core.int? crcFailure,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (failedEnterDfu != null) {
|
||||
$result.failedEnterDfu = failedEnterDfu;
|
||||
}
|
||||
if (failedStartAdvertising != null) {
|
||||
$result.failedStartAdvertising = failedStartAdvertising;
|
||||
}
|
||||
if (crcFailure != null) {
|
||||
$result.crcFailure = crcFailure;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SetDfuTest._() : super();
|
||||
factory SetDfuTest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SetDfuTest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static const $core.Map<$core.int, SetDfuTest_TestCase> _SetDfuTest_TestCaseByTag = {
|
||||
1 : SetDfuTest_TestCase.failedEnterDfu,
|
||||
2 : SetDfuTest_TestCase.failedStartAdvertising,
|
||||
3 : SetDfuTest_TestCase.crcFailure,
|
||||
0 : SetDfuTest_TestCase.notSet
|
||||
};
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetDfuTest', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..oo(0, [1, 2, 3])
|
||||
..aOB(1, _omitFieldNames ? '' : 'failedEnterDfu', protoName: 'failedEnterDfu')
|
||||
..aOB(2, _omitFieldNames ? '' : 'failedStartAdvertising', protoName: 'failedStartAdvertising')
|
||||
..a<$core.int>(3, _omitFieldNames ? '' : 'crcFailure', $pb.PbFieldType.O3, protoName: 'crcFailure')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetDfuTest clone() => SetDfuTest()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetDfuTest copyWith(void Function(SetDfuTest) updates) => super.copyWith((message) => updates(message as SetDfuTest)) as SetDfuTest;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetDfuTest create() => SetDfuTest._();
|
||||
SetDfuTest createEmptyInstance() => create();
|
||||
static $pb.PbList<SetDfuTest> createRepeated() => $pb.PbList<SetDfuTest>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetDfuTest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SetDfuTest>(create);
|
||||
static SetDfuTest? _defaultInstance;
|
||||
|
||||
SetDfuTest_TestCase whichTestCase() => _SetDfuTest_TestCaseByTag[$_whichOneof(0)]!;
|
||||
void clearTestCase() => clearField($_whichOneof(0));
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool get failedEnterDfu => $_getBF(0);
|
||||
@$pb.TagNumber(1)
|
||||
set failedEnterDfu($core.bool v) { $_setBool(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasFailedEnterDfu() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearFailedEnterDfu() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool get failedStartAdvertising => $_getBF(1);
|
||||
@$pb.TagNumber(2)
|
||||
set failedStartAdvertising($core.bool v) { $_setBool(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasFailedStartAdvertising() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearFailedStartAdvertising() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get crcFailure => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set crcFailure($core.int v) { $_setSignedInt32(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasCrcFailure() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearCrcFailure() => clearField(3);
|
||||
}
|
||||
|
||||
class SetGearTestData extends $pb.GeneratedMessage {
|
||||
factory SetGearTestData({
|
||||
$core.int? frontGearIdx,
|
||||
$core.int? rearGearIdx,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (frontGearIdx != null) {
|
||||
$result.frontGearIdx = frontGearIdx;
|
||||
}
|
||||
if (rearGearIdx != null) {
|
||||
$result.rearGearIdx = rearGearIdx;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SetGearTestData._() : super();
|
||||
factory SetGearTestData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SetGearTestData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetGearTestData', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'frontGearIdx', $pb.PbFieldType.O3, protoName: 'frontGearIdx')
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'rearGearIdx', $pb.PbFieldType.O3, protoName: 'rearGearIdx')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetGearTestData clone() => SetGearTestData()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetGearTestData copyWith(void Function(SetGearTestData) updates) => super.copyWith((message) => updates(message as SetGearTestData)) as SetGearTestData;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetGearTestData create() => SetGearTestData._();
|
||||
SetGearTestData createEmptyInstance() => create();
|
||||
static $pb.PbList<SetGearTestData> createRepeated() => $pb.PbList<SetGearTestData>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetGearTestData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SetGearTestData>(create);
|
||||
static SetGearTestData? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get frontGearIdx => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set frontGearIdx($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasFrontGearIdx() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearFrontGearIdx() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get rearGearIdx => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set rearGearIdx($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasRearGearIdx() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearRearGearIdx() => clearField(2);
|
||||
}
|
||||
|
||||
class SetHrmTestData extends $pb.GeneratedMessage {
|
||||
factory SetHrmTestData({
|
||||
$core.bool? hrmPresent,
|
||||
$core.int? heartRate,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (hrmPresent != null) {
|
||||
$result.hrmPresent = hrmPresent;
|
||||
}
|
||||
if (heartRate != null) {
|
||||
$result.heartRate = heartRate;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SetHrmTestData._() : super();
|
||||
factory SetHrmTestData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SetHrmTestData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetHrmTestData', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..aOB(1, _omitFieldNames ? '' : 'hrmPresent', protoName: 'hrmPresent')
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'heartRate', $pb.PbFieldType.O3, protoName: 'heartRate')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetHrmTestData clone() => SetHrmTestData()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetHrmTestData copyWith(void Function(SetHrmTestData) updates) => super.copyWith((message) => updates(message as SetHrmTestData)) as SetHrmTestData;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetHrmTestData create() => SetHrmTestData._();
|
||||
SetHrmTestData createEmptyInstance() => create();
|
||||
static $pb.PbList<SetHrmTestData> createRepeated() => $pb.PbList<SetHrmTestData>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetHrmTestData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SetHrmTestData>(create);
|
||||
static SetHrmTestData? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool get hrmPresent => $_getBF(0);
|
||||
@$pb.TagNumber(1)
|
||||
set hrmPresent($core.bool v) { $_setBool(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasHrmPresent() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearHrmPresent() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get heartRate => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set heartRate($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasHeartRate() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearHeartRate() => clearField(2);
|
||||
}
|
||||
|
||||
class SetInputDeviceTestData_ControllerAnalogEvent extends $pb.GeneratedMessage {
|
||||
factory SetInputDeviceTestData_ControllerAnalogEvent({
|
||||
$core.int? sensorId,
|
||||
$core.int? value,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (sensorId != null) {
|
||||
$result.sensorId = sensorId;
|
||||
}
|
||||
if (value != null) {
|
||||
$result.value = value;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SetInputDeviceTestData_ControllerAnalogEvent._() : super();
|
||||
factory SetInputDeviceTestData_ControllerAnalogEvent.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SetInputDeviceTestData_ControllerAnalogEvent.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetInputDeviceTestData.ControllerAnalogEvent', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'sensorId', $pb.PbFieldType.O3, protoName: 'sensorId')
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.O3)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetInputDeviceTestData_ControllerAnalogEvent clone() => SetInputDeviceTestData_ControllerAnalogEvent()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetInputDeviceTestData_ControllerAnalogEvent copyWith(void Function(SetInputDeviceTestData_ControllerAnalogEvent) updates) => super.copyWith((message) => updates(message as SetInputDeviceTestData_ControllerAnalogEvent)) as SetInputDeviceTestData_ControllerAnalogEvent;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetInputDeviceTestData_ControllerAnalogEvent create() => SetInputDeviceTestData_ControllerAnalogEvent._();
|
||||
SetInputDeviceTestData_ControllerAnalogEvent createEmptyInstance() => create();
|
||||
static $pb.PbList<SetInputDeviceTestData_ControllerAnalogEvent> createRepeated() => $pb.PbList<SetInputDeviceTestData_ControllerAnalogEvent>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetInputDeviceTestData_ControllerAnalogEvent getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SetInputDeviceTestData_ControllerAnalogEvent>(create);
|
||||
static SetInputDeviceTestData_ControllerAnalogEvent? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get sensorId => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set sensorId($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasSensorId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearSensorId() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get value => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set value($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasValue() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearValue() => clearField(2);
|
||||
}
|
||||
|
||||
class SetInputDeviceTestData extends $pb.GeneratedMessage {
|
||||
factory SetInputDeviceTestData({
|
||||
$core.int? duration,
|
||||
$core.int? buttonEvent,
|
||||
$core.Iterable<SetInputDeviceTestData_ControllerAnalogEvent>? analogEventList,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (duration != null) {
|
||||
$result.duration = duration;
|
||||
}
|
||||
if (buttonEvent != null) {
|
||||
$result.buttonEvent = buttonEvent;
|
||||
}
|
||||
if (analogEventList != null) {
|
||||
$result.analogEventList.addAll(analogEventList);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SetInputDeviceTestData._() : super();
|
||||
factory SetInputDeviceTestData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SetInputDeviceTestData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetInputDeviceTestData', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'duration', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'buttonEvent', $pb.PbFieldType.O3, protoName: 'buttonEvent')
|
||||
..pc<SetInputDeviceTestData_ControllerAnalogEvent>(3, _omitFieldNames ? '' : 'analogEventList', $pb.PbFieldType.PM, protoName: 'analogEventList', subBuilder: SetInputDeviceTestData_ControllerAnalogEvent.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetInputDeviceTestData clone() => SetInputDeviceTestData()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetInputDeviceTestData copyWith(void Function(SetInputDeviceTestData) updates) => super.copyWith((message) => updates(message as SetInputDeviceTestData)) as SetInputDeviceTestData;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetInputDeviceTestData create() => SetInputDeviceTestData._();
|
||||
SetInputDeviceTestData createEmptyInstance() => create();
|
||||
static $pb.PbList<SetInputDeviceTestData> createRepeated() => $pb.PbList<SetInputDeviceTestData>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetInputDeviceTestData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SetInputDeviceTestData>(create);
|
||||
static SetInputDeviceTestData? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get duration => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set duration($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasDuration() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearDuration() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get buttonEvent => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set buttonEvent($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasButtonEvent() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearButtonEvent() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<SetInputDeviceTestData_ControllerAnalogEvent> get analogEventList => $_getList(2);
|
||||
}
|
||||
|
||||
class SetTrainerTestData extends $pb.GeneratedMessage {
|
||||
factory SetTrainerTestData({
|
||||
$core.int? dataMode,
|
||||
$core.int? interfaces,
|
||||
TestTrainerData? testTrainerData,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (dataMode != null) {
|
||||
$result.dataMode = dataMode;
|
||||
}
|
||||
if (interfaces != null) {
|
||||
$result.interfaces = interfaces;
|
||||
}
|
||||
if (testTrainerData != null) {
|
||||
$result.testTrainerData = testTrainerData;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
SetTrainerTestData._() : super();
|
||||
factory SetTrainerTestData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory SetTrainerTestData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetTrainerTestData', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'dataMode', $pb.PbFieldType.O3, protoName: 'dataMode')
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'interfaces', $pb.PbFieldType.O3)
|
||||
..aOM<TestTrainerData>(3, _omitFieldNames ? '' : 'testTrainerData', protoName: 'testTrainerData', subBuilder: TestTrainerData.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetTrainerTestData clone() => SetTrainerTestData()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
SetTrainerTestData copyWith(void Function(SetTrainerTestData) updates) => super.copyWith((message) => updates(message as SetTrainerTestData)) as SetTrainerTestData;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetTrainerTestData create() => SetTrainerTestData._();
|
||||
SetTrainerTestData createEmptyInstance() => create();
|
||||
static $pb.PbList<SetTrainerTestData> createRepeated() => $pb.PbList<SetTrainerTestData>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SetTrainerTestData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SetTrainerTestData>(create);
|
||||
static SetTrainerTestData? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get dataMode => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set dataMode($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasDataMode() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearDataMode() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get interfaces => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set interfaces($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasInterfaces() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearInterfaces() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
TestTrainerData get testTrainerData => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set testTrainerData(TestTrainerData v) { setField(3, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasTestTrainerData() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearTestTrainerData() => clearField(3);
|
||||
@$pb.TagNumber(3)
|
||||
TestTrainerData ensureTestTrainerData() => $_ensure(2);
|
||||
}
|
||||
|
||||
class TestTrainerData extends $pb.GeneratedMessage {
|
||||
factory TestTrainerData({
|
||||
$core.int? power,
|
||||
$core.int? cadence,
|
||||
$core.int? bikeSpeed,
|
||||
$core.int? averagedPower,
|
||||
$core.int? wheelSpeed,
|
||||
$core.int? calculatedRealGearRatio,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (power != null) {
|
||||
$result.power = power;
|
||||
}
|
||||
if (cadence != null) {
|
||||
$result.cadence = cadence;
|
||||
}
|
||||
if (bikeSpeed != null) {
|
||||
$result.bikeSpeed = bikeSpeed;
|
||||
}
|
||||
if (averagedPower != null) {
|
||||
$result.averagedPower = averagedPower;
|
||||
}
|
||||
if (wheelSpeed != null) {
|
||||
$result.wheelSpeed = wheelSpeed;
|
||||
}
|
||||
if (calculatedRealGearRatio != null) {
|
||||
$result.calculatedRealGearRatio = calculatedRealGearRatio;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
TestTrainerData._() : super();
|
||||
factory TestTrainerData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory TestTrainerData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TestTrainerData', package: const $pb.PackageName(_omitMessageNames ? '' : 'com.zwift.protobuf'), createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'power', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'cadence', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(3, _omitFieldNames ? '' : 'bikeSpeed', $pb.PbFieldType.O3, protoName: 'bikeSpeed')
|
||||
..a<$core.int>(4, _omitFieldNames ? '' : 'averagedPower', $pb.PbFieldType.O3, protoName: 'averagedPower')
|
||||
..a<$core.int>(5, _omitFieldNames ? '' : 'wheelSpeed', $pb.PbFieldType.O3, protoName: 'wheelSpeed')
|
||||
..a<$core.int>(6, _omitFieldNames ? '' : 'calculatedRealGearRatio', $pb.PbFieldType.O3, protoName: 'calculatedRealGearRatio')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
TestTrainerData clone() => TestTrainerData()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
TestTrainerData copyWith(void Function(TestTrainerData) updates) => super.copyWith((message) => updates(message as TestTrainerData)) as TestTrainerData;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static TestTrainerData create() => TestTrainerData._();
|
||||
TestTrainerData createEmptyInstance() => create();
|
||||
static $pb.PbList<TestTrainerData> createRepeated() => $pb.PbList<TestTrainerData>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static TestTrainerData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<TestTrainerData>(create);
|
||||
static TestTrainerData? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get power => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set power($core.int v) { $_setSignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasPower() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearPower() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get cadence => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set cadence($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasCadence() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearCadence() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get bikeSpeed => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set bikeSpeed($core.int v) { $_setSignedInt32(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasBikeSpeed() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearBikeSpeed() => clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.int get averagedPower => $_getIZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set averagedPower($core.int v) { $_setSignedInt32(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasAveragedPower() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearAveragedPower() => clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$core.int get wheelSpeed => $_getIZ(4);
|
||||
@$pb.TagNumber(5)
|
||||
set wheelSpeed($core.int v) { $_setSignedInt32(4, v); }
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasWheelSpeed() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearWheelSpeed() => clearField(5);
|
||||
|
||||
@$pb.TagNumber(6)
|
||||
$core.int get calculatedRealGearRatio => $_getIZ(5);
|
||||
@$pb.TagNumber(6)
|
||||
set calculatedRealGearRatio($core.int v) { $_setSignedInt32(5, v); }
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasCalculatedRealGearRatio() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearCalculatedRealGearRatio() => clearField(6);
|
||||
}
|
||||
|
||||
|
||||
const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
101
lib/bluetooth/protocol/zp_vendor.pbenum.dart
Normal file
101
lib/bluetooth/protocol/zp_vendor.pbenum.dart
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: zp_vendor.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class VendorOpcode extends $pb.ProtobufEnum {
|
||||
static const VendorOpcode UNDEFINED = VendorOpcode._(0, _omitEnumNames ? '' : 'UNDEFINED');
|
||||
static const VendorOpcode CONTROLLER_SYNC = VendorOpcode._(1, _omitEnumNames ? '' : 'CONTROLLER_SYNC');
|
||||
static const VendorOpcode PAIR_DEVICES = VendorOpcode._(2, _omitEnumNames ? '' : 'PAIR_DEVICES');
|
||||
static const VendorOpcode ENABLE_TEST_MODE = VendorOpcode._(65280, _omitEnumNames ? '' : 'ENABLE_TEST_MODE');
|
||||
static const VendorOpcode SET_DFU_TEST = VendorOpcode._(65281, _omitEnumNames ? '' : 'SET_DFU_TEST');
|
||||
static const VendorOpcode SET_TRAINER_TEST_DATA = VendorOpcode._(65282, _omitEnumNames ? '' : 'SET_TRAINER_TEST_DATA');
|
||||
static const VendorOpcode SET_INPUT_DEVICE_TEST_DATA = VendorOpcode._(65283, _omitEnumNames ? '' : 'SET_INPUT_DEVICE_TEST_DATA');
|
||||
static const VendorOpcode SET_GEAR_TEST_DATA = VendorOpcode._(65284, _omitEnumNames ? '' : 'SET_GEAR_TEST_DATA');
|
||||
static const VendorOpcode SET_HRM_TEST_DATA = VendorOpcode._(65285, _omitEnumNames ? '' : 'SET_HRM_TEST_DATA');
|
||||
static const VendorOpcode SET_TEST_DATA = VendorOpcode._(65286, _omitEnumNames ? '' : 'SET_TEST_DATA');
|
||||
|
||||
static const $core.List<VendorOpcode> values = <VendorOpcode> [
|
||||
UNDEFINED,
|
||||
CONTROLLER_SYNC,
|
||||
PAIR_DEVICES,
|
||||
ENABLE_TEST_MODE,
|
||||
SET_DFU_TEST,
|
||||
SET_TRAINER_TEST_DATA,
|
||||
SET_INPUT_DEVICE_TEST_DATA,
|
||||
SET_GEAR_TEST_DATA,
|
||||
SET_HRM_TEST_DATA,
|
||||
SET_TEST_DATA,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, VendorOpcode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static VendorOpcode? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const VendorOpcode._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class PairDeviceType extends $pb.ProtobufEnum {
|
||||
static const PairDeviceType BLE = PairDeviceType._(0, _omitEnumNames ? '' : 'BLE');
|
||||
static const PairDeviceType ANT = PairDeviceType._(1, _omitEnumNames ? '' : 'ANT');
|
||||
|
||||
static const $core.List<PairDeviceType> values = <PairDeviceType> [
|
||||
BLE,
|
||||
ANT,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, PairDeviceType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static PairDeviceType? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const PairDeviceType._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
/// Status used by ControllerSync
|
||||
class ControllerSyncStatus extends $pb.ProtobufEnum {
|
||||
static const ControllerSyncStatus NOT_CONNECTED = ControllerSyncStatus._(0, _omitEnumNames ? '' : 'NOT_CONNECTED');
|
||||
static const ControllerSyncStatus CONNECTED = ControllerSyncStatus._(1, _omitEnumNames ? '' : 'CONNECTED');
|
||||
|
||||
static const $core.List<ControllerSyncStatus> values = <ControllerSyncStatus> [
|
||||
NOT_CONNECTED,
|
||||
CONNECTED,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, ControllerSyncStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static ControllerSyncStatus? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const ControllerSyncStatus._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
/// Looks like “data object / page” IDs used with pairing pages
|
||||
class VendorDO extends $pb.ProtobufEnum {
|
||||
static const VendorDO NO_CLUE = VendorDO._(0, _omitEnumNames ? '' : 'NO_CLUE');
|
||||
static const VendorDO PAGE_DEVICE_PAIRING = VendorDO._(61440, _omitEnumNames ? '' : 'PAGE_DEVICE_PAIRING');
|
||||
static const VendorDO DEVICE_COUNT = VendorDO._(61441, _omitEnumNames ? '' : 'DEVICE_COUNT');
|
||||
static const VendorDO PAIRING_STATUS = VendorDO._(61442, _omitEnumNames ? '' : 'PAIRING_STATUS');
|
||||
static const VendorDO PAIRED_DEVICE = VendorDO._(61443, _omitEnumNames ? '' : 'PAIRED_DEVICE');
|
||||
|
||||
static const $core.List<VendorDO> values = <VendorDO> [
|
||||
NO_CLUE,
|
||||
PAGE_DEVICE_PAIRING,
|
||||
DEVICE_COUNT,
|
||||
PAIRING_STATUS,
|
||||
PAIRED_DEVICE,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, VendorDO> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static VendorDO? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const VendorDO._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
|
||||
const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');
|
||||
267
lib/bluetooth/protocol/zp_vendor.pbjson.dart
Normal file
267
lib/bluetooth/protocol/zp_vendor.pbjson.dart
Normal file
@@ -0,0 +1,267 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: zp_vendor.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:convert' as $convert;
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
|
||||
@$core.Deprecated('Use vendorOpcodeDescriptor instead')
|
||||
const VendorOpcode$json = {
|
||||
'1': 'VendorOpcode',
|
||||
'2': [
|
||||
{'1': 'UNDEFINED', '2': 0},
|
||||
{'1': 'CONTROLLER_SYNC', '2': 1},
|
||||
{'1': 'PAIR_DEVICES', '2': 2},
|
||||
{'1': 'ENABLE_TEST_MODE', '2': 65280},
|
||||
{'1': 'SET_DFU_TEST', '2': 65281},
|
||||
{'1': 'SET_TRAINER_TEST_DATA', '2': 65282},
|
||||
{'1': 'SET_INPUT_DEVICE_TEST_DATA', '2': 65283},
|
||||
{'1': 'SET_GEAR_TEST_DATA', '2': 65284},
|
||||
{'1': 'SET_HRM_TEST_DATA', '2': 65285},
|
||||
{'1': 'SET_TEST_DATA', '2': 65286},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `VendorOpcode`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List vendorOpcodeDescriptor = $convert.base64Decode(
|
||||
'CgxWZW5kb3JPcGNvZGUSDQoJVU5ERUZJTkVEEAASEwoPQ09OVFJPTExFUl9TWU5DEAESEAoMUE'
|
||||
'FJUl9ERVZJQ0VTEAISFgoQRU5BQkxFX1RFU1RfTU9ERRCA/gMSEgoMU0VUX0RGVV9URVNUEIH+'
|
||||
'AxIbChVTRVRfVFJBSU5FUl9URVNUX0RBVEEQgv4DEiAKGlNFVF9JTlBVVF9ERVZJQ0VfVEVTVF'
|
||||
'9EQVRBEIP+AxIYChJTRVRfR0VBUl9URVNUX0RBVEEQhP4DEhcKEVNFVF9IUk1fVEVTVF9EQVRB'
|
||||
'EIX+AxITCg1TRVRfVEVTVF9EQVRBEIb+Aw==');
|
||||
|
||||
@$core.Deprecated('Use pairDeviceTypeDescriptor instead')
|
||||
const PairDeviceType$json = {
|
||||
'1': 'PairDeviceType',
|
||||
'2': [
|
||||
{'1': 'BLE', '2': 0},
|
||||
{'1': 'ANT', '2': 1},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `PairDeviceType`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List pairDeviceTypeDescriptor = $convert.base64Decode(
|
||||
'Cg5QYWlyRGV2aWNlVHlwZRIHCgNCTEUQABIHCgNBTlQQAQ==');
|
||||
|
||||
@$core.Deprecated('Use controllerSyncStatusDescriptor instead')
|
||||
const ControllerSyncStatus$json = {
|
||||
'1': 'ControllerSyncStatus',
|
||||
'2': [
|
||||
{'1': 'NOT_CONNECTED', '2': 0},
|
||||
{'1': 'CONNECTED', '2': 1},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `ControllerSyncStatus`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List controllerSyncStatusDescriptor = $convert.base64Decode(
|
||||
'ChRDb250cm9sbGVyU3luY1N0YXR1cxIRCg1OT1RfQ09OTkVDVEVEEAASDQoJQ09OTkVDVEVEEA'
|
||||
'E=');
|
||||
|
||||
@$core.Deprecated('Use vendorDODescriptor instead')
|
||||
const VendorDO$json = {
|
||||
'1': 'VendorDO',
|
||||
'2': [
|
||||
{'1': 'NO_CLUE', '2': 0},
|
||||
{'1': 'PAGE_DEVICE_PAIRING', '2': 61440},
|
||||
{'1': 'DEVICE_COUNT', '2': 61441},
|
||||
{'1': 'PAIRING_STATUS', '2': 61442},
|
||||
{'1': 'PAIRED_DEVICE', '2': 61443},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `VendorDO`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List vendorDODescriptor = $convert.base64Decode(
|
||||
'CghWZW5kb3JETxILCgdOT19DTFVFEAASGQoTUEFHRV9ERVZJQ0VfUEFJUklORxCA4AMSEgoMRE'
|
||||
'VWSUNFX0NPVU5UEIHgAxIUCg5QQUlSSU5HX1NUQVRVUxCC4AMSEwoNUEFJUkVEX0RFVklDRRCD'
|
||||
'4AM=');
|
||||
|
||||
@$core.Deprecated('Use controllerSyncDescriptor instead')
|
||||
const ControllerSync$json = {
|
||||
'1': 'ControllerSync',
|
||||
'2': [
|
||||
{'1': 'status', '3': 1, '4': 1, '5': 14, '6': '.com.zwift.protobuf.ControllerSyncStatus', '10': 'status'},
|
||||
{'1': 'timeStamp', '3': 2, '4': 1, '5': 5, '10': 'timeStamp'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `ControllerSync`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List controllerSyncDescriptor = $convert.base64Decode(
|
||||
'Cg5Db250cm9sbGVyU3luYxJACgZzdGF0dXMYASABKA4yKC5jb20uendpZnQucHJvdG9idWYuQ2'
|
||||
'9udHJvbGxlclN5bmNTdGF0dXNSBnN0YXR1cxIcCgl0aW1lU3RhbXAYAiABKAVSCXRpbWVTdGFt'
|
||||
'cA==');
|
||||
|
||||
@$core.Deprecated('Use enableTestModeDescriptor instead')
|
||||
const EnableTestMode$json = {
|
||||
'1': 'EnableTestMode',
|
||||
'2': [
|
||||
{'1': 'enable', '3': 1, '4': 1, '5': 8, '10': 'enable'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `EnableTestMode`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List enableTestModeDescriptor = $convert.base64Decode(
|
||||
'Cg5FbmFibGVUZXN0TW9kZRIWCgZlbmFibGUYASABKAhSBmVuYWJsZQ==');
|
||||
|
||||
@$core.Deprecated('Use pairDevicesDescriptor instead')
|
||||
const PairDevices$json = {
|
||||
'1': 'PairDevices',
|
||||
'2': [
|
||||
{'1': 'pair', '3': 1, '4': 1, '5': 8, '10': 'pair'},
|
||||
{'1': 'type', '3': 2, '4': 1, '5': 14, '6': '.com.zwift.protobuf.PairDeviceType', '10': 'type'},
|
||||
{'1': 'deviceId', '3': 3, '4': 1, '5': 12, '10': 'deviceId'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `PairDevices`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List pairDevicesDescriptor = $convert.base64Decode(
|
||||
'CgtQYWlyRGV2aWNlcxISCgRwYWlyGAEgASgIUgRwYWlyEjYKBHR5cGUYAiABKA4yIi5jb20uen'
|
||||
'dpZnQucHJvdG9idWYuUGFpckRldmljZVR5cGVSBHR5cGUSGgoIZGV2aWNlSWQYAyABKAxSCGRl'
|
||||
'dmljZUlk');
|
||||
|
||||
@$core.Deprecated('Use devicePairingDataPageDescriptor instead')
|
||||
const DevicePairingDataPage$json = {
|
||||
'1': 'DevicePairingDataPage',
|
||||
'2': [
|
||||
{'1': 'devicesCount', '3': 1, '4': 1, '5': 5, '10': 'devicesCount'},
|
||||
{'1': 'pairingStatus', '3': 2, '4': 1, '5': 5, '10': 'pairingStatus'},
|
||||
{'1': 'pairingDevList', '3': 3, '4': 3, '5': 11, '6': '.com.zwift.protobuf.DevicePairingDataPage.PairedDevice', '10': 'pairingDevList'},
|
||||
],
|
||||
'3': [DevicePairingDataPage_PairedDevice$json],
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use devicePairingDataPageDescriptor instead')
|
||||
const DevicePairingDataPage_PairedDevice$json = {
|
||||
'1': 'PairedDevice',
|
||||
'2': [
|
||||
{'1': 'device', '3': 1, '4': 1, '5': 12, '10': 'device'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `DevicePairingDataPage`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List devicePairingDataPageDescriptor = $convert.base64Decode(
|
||||
'ChVEZXZpY2VQYWlyaW5nRGF0YVBhZ2USIgoMZGV2aWNlc0NvdW50GAEgASgFUgxkZXZpY2VzQ2'
|
||||
'91bnQSJAoNcGFpcmluZ1N0YXR1cxgCIAEoBVINcGFpcmluZ1N0YXR1cxJeCg5wYWlyaW5nRGV2'
|
||||
'TGlzdBgDIAMoCzI2LmNvbS56d2lmdC5wcm90b2J1Zi5EZXZpY2VQYWlyaW5nRGF0YVBhZ2UuUG'
|
||||
'FpcmVkRGV2aWNlUg5wYWlyaW5nRGV2TGlzdBomCgxQYWlyZWREZXZpY2USFgoGZGV2aWNlGAEg'
|
||||
'ASgMUgZkZXZpY2U=');
|
||||
|
||||
@$core.Deprecated('Use setDfuTestDescriptor instead')
|
||||
const SetDfuTest$json = {
|
||||
'1': 'SetDfuTest',
|
||||
'2': [
|
||||
{'1': 'failedEnterDfu', '3': 1, '4': 1, '5': 8, '9': 0, '10': 'failedEnterDfu'},
|
||||
{'1': 'failedStartAdvertising', '3': 2, '4': 1, '5': 8, '9': 0, '10': 'failedStartAdvertising'},
|
||||
{'1': 'crcFailure', '3': 3, '4': 1, '5': 5, '9': 0, '10': 'crcFailure'},
|
||||
],
|
||||
'8': [
|
||||
{'1': 'test_case'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `SetDfuTest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List setDfuTestDescriptor = $convert.base64Decode(
|
||||
'CgpTZXREZnVUZXN0EigKDmZhaWxlZEVudGVyRGZ1GAEgASgISABSDmZhaWxlZEVudGVyRGZ1Ej'
|
||||
'gKFmZhaWxlZFN0YXJ0QWR2ZXJ0aXNpbmcYAiABKAhIAFIWZmFpbGVkU3RhcnRBZHZlcnRpc2lu'
|
||||
'ZxIgCgpjcmNGYWlsdXJlGAMgASgFSABSCmNyY0ZhaWx1cmVCCwoJdGVzdF9jYXNl');
|
||||
|
||||
@$core.Deprecated('Use setGearTestDataDescriptor instead')
|
||||
const SetGearTestData$json = {
|
||||
'1': 'SetGearTestData',
|
||||
'2': [
|
||||
{'1': 'frontGearIdx', '3': 1, '4': 1, '5': 5, '10': 'frontGearIdx'},
|
||||
{'1': 'rearGearIdx', '3': 2, '4': 1, '5': 5, '10': 'rearGearIdx'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `SetGearTestData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List setGearTestDataDescriptor = $convert.base64Decode(
|
||||
'Cg9TZXRHZWFyVGVzdERhdGESIgoMZnJvbnRHZWFySWR4GAEgASgFUgxmcm9udEdlYXJJZHgSIA'
|
||||
'oLcmVhckdlYXJJZHgYAiABKAVSC3JlYXJHZWFySWR4');
|
||||
|
||||
@$core.Deprecated('Use setHrmTestDataDescriptor instead')
|
||||
const SetHrmTestData$json = {
|
||||
'1': 'SetHrmTestData',
|
||||
'2': [
|
||||
{'1': 'hrmPresent', '3': 1, '4': 1, '5': 8, '10': 'hrmPresent'},
|
||||
{'1': 'heartRate', '3': 2, '4': 1, '5': 5, '10': 'heartRate'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `SetHrmTestData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List setHrmTestDataDescriptor = $convert.base64Decode(
|
||||
'Cg5TZXRIcm1UZXN0RGF0YRIeCgpocm1QcmVzZW50GAEgASgIUgpocm1QcmVzZW50EhwKCWhlYX'
|
||||
'J0UmF0ZRgCIAEoBVIJaGVhcnRSYXRl');
|
||||
|
||||
@$core.Deprecated('Use setInputDeviceTestDataDescriptor instead')
|
||||
const SetInputDeviceTestData$json = {
|
||||
'1': 'SetInputDeviceTestData',
|
||||
'2': [
|
||||
{'1': 'duration', '3': 1, '4': 1, '5': 5, '10': 'duration'},
|
||||
{'1': 'buttonEvent', '3': 2, '4': 1, '5': 5, '10': 'buttonEvent'},
|
||||
{'1': 'analogEventList', '3': 3, '4': 3, '5': 11, '6': '.com.zwift.protobuf.SetInputDeviceTestData.ControllerAnalogEvent', '10': 'analogEventList'},
|
||||
],
|
||||
'3': [SetInputDeviceTestData_ControllerAnalogEvent$json],
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use setInputDeviceTestDataDescriptor instead')
|
||||
const SetInputDeviceTestData_ControllerAnalogEvent$json = {
|
||||
'1': 'ControllerAnalogEvent',
|
||||
'2': [
|
||||
{'1': 'sensorId', '3': 1, '4': 1, '5': 5, '10': 'sensorId'},
|
||||
{'1': 'value', '3': 2, '4': 1, '5': 5, '10': 'value'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `SetInputDeviceTestData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List setInputDeviceTestDataDescriptor = $convert.base64Decode(
|
||||
'ChZTZXRJbnB1dERldmljZVRlc3REYXRhEhoKCGR1cmF0aW9uGAEgASgFUghkdXJhdGlvbhIgCg'
|
||||
'tidXR0b25FdmVudBgCIAEoBVILYnV0dG9uRXZlbnQSagoPYW5hbG9nRXZlbnRMaXN0GAMgAygL'
|
||||
'MkAuY29tLnp3aWZ0LnByb3RvYnVmLlNldElucHV0RGV2aWNlVGVzdERhdGEuQ29udHJvbGxlck'
|
||||
'FuYWxvZ0V2ZW50Ug9hbmFsb2dFdmVudExpc3QaSQoVQ29udHJvbGxlckFuYWxvZ0V2ZW50EhoK'
|
||||
'CHNlbnNvcklkGAEgASgFUghzZW5zb3JJZBIUCgV2YWx1ZRgCIAEoBVIFdmFsdWU=');
|
||||
|
||||
@$core.Deprecated('Use setTrainerTestDataDescriptor instead')
|
||||
const SetTrainerTestData$json = {
|
||||
'1': 'SetTrainerTestData',
|
||||
'2': [
|
||||
{'1': 'dataMode', '3': 1, '4': 1, '5': 5, '10': 'dataMode'},
|
||||
{'1': 'interfaces', '3': 2, '4': 1, '5': 5, '10': 'interfaces'},
|
||||
{'1': 'testTrainerData', '3': 3, '4': 1, '5': 11, '6': '.com.zwift.protobuf.TestTrainerData', '10': 'testTrainerData'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `SetTrainerTestData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List setTrainerTestDataDescriptor = $convert.base64Decode(
|
||||
'ChJTZXRUcmFpbmVyVGVzdERhdGESGgoIZGF0YU1vZGUYASABKAVSCGRhdGFNb2RlEh4KCmludG'
|
||||
'VyZmFjZXMYAiABKAVSCmludGVyZmFjZXMSTQoPdGVzdFRyYWluZXJEYXRhGAMgASgLMiMuY29t'
|
||||
'Lnp3aWZ0LnByb3RvYnVmLlRlc3RUcmFpbmVyRGF0YVIPdGVzdFRyYWluZXJEYXRh');
|
||||
|
||||
@$core.Deprecated('Use testTrainerDataDescriptor instead')
|
||||
const TestTrainerData$json = {
|
||||
'1': 'TestTrainerData',
|
||||
'2': [
|
||||
{'1': 'power', '3': 1, '4': 1, '5': 5, '10': 'power'},
|
||||
{'1': 'cadence', '3': 2, '4': 1, '5': 5, '10': 'cadence'},
|
||||
{'1': 'bikeSpeed', '3': 3, '4': 1, '5': 5, '10': 'bikeSpeed'},
|
||||
{'1': 'averagedPower', '3': 4, '4': 1, '5': 5, '10': 'averagedPower'},
|
||||
{'1': 'wheelSpeed', '3': 5, '4': 1, '5': 5, '10': 'wheelSpeed'},
|
||||
{'1': 'calculatedRealGearRatio', '3': 6, '4': 1, '5': 5, '10': 'calculatedRealGearRatio'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `TestTrainerData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List testTrainerDataDescriptor = $convert.base64Decode(
|
||||
'Cg9UZXN0VHJhaW5lckRhdGESFAoFcG93ZXIYASABKAVSBXBvd2VyEhgKB2NhZGVuY2UYAiABKA'
|
||||
'VSB2NhZGVuY2USHAoJYmlrZVNwZWVkGAMgASgFUgliaWtlU3BlZWQSJAoNYXZlcmFnZWRQb3dl'
|
||||
'chgEIAEoBVINYXZlcmFnZWRQb3dlchIeCgp3aGVlbFNwZWVkGAUgASgFUgp3aGVlbFNwZWVkEj'
|
||||
'gKF2NhbGN1bGF0ZWRSZWFsR2VhclJhdGlvGAYgASgFUhdjYWxjdWxhdGVkUmVhbEdlYXJSYXRp'
|
||||
'bw==');
|
||||
|
||||
14
lib/bluetooth/protocol/zp_vendor.pbserver.dart
Normal file
14
lib/bluetooth/protocol/zp_vendor.pbserver.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: zp_vendor.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
export 'zp_vendor.pb.dart';
|
||||
|
||||
@@ -25,8 +25,8 @@ class PlayKeyPadStatus extends $pb.GeneratedMessage {
|
||||
PlayButtonStatus? buttonZLeft,
|
||||
PlayButtonStatus? buttonARight,
|
||||
PlayButtonStatus? buttonBDown,
|
||||
PlayButtonStatus? buttonOn,
|
||||
PlayButtonStatus? buttonShift,
|
||||
PlayButtonStatus? buttonOn,
|
||||
$core.int? analogLR,
|
||||
$core.int? analogUD,
|
||||
}) {
|
||||
@@ -46,12 +46,12 @@ class PlayKeyPadStatus extends $pb.GeneratedMessage {
|
||||
if (buttonBDown != null) {
|
||||
$result.buttonBDown = buttonBDown;
|
||||
}
|
||||
if (buttonOn != null) {
|
||||
$result.buttonOn = buttonOn;
|
||||
}
|
||||
if (buttonShift != null) {
|
||||
$result.buttonShift = buttonShift;
|
||||
}
|
||||
if (buttonOn != null) {
|
||||
$result.buttonOn = buttonOn;
|
||||
}
|
||||
if (analogLR != null) {
|
||||
$result.analogLR = analogLR;
|
||||
}
|
||||
@@ -70,8 +70,8 @@ class PlayKeyPadStatus extends $pb.GeneratedMessage {
|
||||
..e<PlayButtonStatus>(3, _omitFieldNames ? '' : 'ButtonZLeft', $pb.PbFieldType.OE, protoName: 'Button_Z_Left', defaultOrMaker: PlayButtonStatus.ON, valueOf: PlayButtonStatus.valueOf, enumValues: PlayButtonStatus.values)
|
||||
..e<PlayButtonStatus>(4, _omitFieldNames ? '' : 'ButtonARight', $pb.PbFieldType.OE, protoName: 'Button_A_Right', defaultOrMaker: PlayButtonStatus.ON, valueOf: PlayButtonStatus.valueOf, enumValues: PlayButtonStatus.values)
|
||||
..e<PlayButtonStatus>(5, _omitFieldNames ? '' : 'ButtonBDown', $pb.PbFieldType.OE, protoName: 'Button_B_Down', defaultOrMaker: PlayButtonStatus.ON, valueOf: PlayButtonStatus.valueOf, enumValues: PlayButtonStatus.values)
|
||||
..e<PlayButtonStatus>(6, _omitFieldNames ? '' : 'ButtonOn', $pb.PbFieldType.OE, protoName: 'Button_On', defaultOrMaker: PlayButtonStatus.ON, valueOf: PlayButtonStatus.valueOf, enumValues: PlayButtonStatus.values)
|
||||
..e<PlayButtonStatus>(7, _omitFieldNames ? '' : 'ButtonShift', $pb.PbFieldType.OE, protoName: 'Button_Shift', defaultOrMaker: PlayButtonStatus.ON, valueOf: PlayButtonStatus.valueOf, enumValues: PlayButtonStatus.values)
|
||||
..e<PlayButtonStatus>(6, _omitFieldNames ? '' : 'ButtonShift', $pb.PbFieldType.OE, protoName: 'Button_Shift', defaultOrMaker: PlayButtonStatus.ON, valueOf: PlayButtonStatus.valueOf, enumValues: PlayButtonStatus.values)
|
||||
..e<PlayButtonStatus>(7, _omitFieldNames ? '' : 'ButtonOn', $pb.PbFieldType.OE, protoName: 'Button_On', defaultOrMaker: PlayButtonStatus.ON, valueOf: PlayButtonStatus.valueOf, enumValues: PlayButtonStatus.values)
|
||||
..a<$core.int>(8, _omitFieldNames ? '' : 'AnalogLR', $pb.PbFieldType.OS3, protoName: 'Analog_LR')
|
||||
..a<$core.int>(9, _omitFieldNames ? '' : 'AnalogUD', $pb.PbFieldType.OS3, protoName: 'Analog_UD')
|
||||
..hasRequiredFields = false
|
||||
@@ -144,22 +144,22 @@ class PlayKeyPadStatus extends $pb.GeneratedMessage {
|
||||
void clearButtonBDown() => clearField(5);
|
||||
|
||||
@$pb.TagNumber(6)
|
||||
PlayButtonStatus get buttonOn => $_getN(5);
|
||||
PlayButtonStatus get buttonShift => $_getN(5);
|
||||
@$pb.TagNumber(6)
|
||||
set buttonOn(PlayButtonStatus v) { setField(6, v); }
|
||||
set buttonShift(PlayButtonStatus v) { setField(6, v); }
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasButtonOn() => $_has(5);
|
||||
$core.bool hasButtonShift() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearButtonOn() => clearField(6);
|
||||
void clearButtonShift() => clearField(6);
|
||||
|
||||
@$pb.TagNumber(7)
|
||||
PlayButtonStatus get buttonShift => $_getN(6);
|
||||
PlayButtonStatus get buttonOn => $_getN(6);
|
||||
@$pb.TagNumber(7)
|
||||
set buttonShift(PlayButtonStatus v) { setField(7, v); }
|
||||
set buttonOn(PlayButtonStatus v) { setField(7, v); }
|
||||
@$pb.TagNumber(7)
|
||||
$core.bool hasButtonShift() => $_has(6);
|
||||
$core.bool hasButtonOn() => $_has(6);
|
||||
@$pb.TagNumber(7)
|
||||
void clearButtonShift() => clearField(7);
|
||||
void clearButtonOn() => clearField(7);
|
||||
|
||||
@$pb.TagNumber(8)
|
||||
$core.int get analogLR => $_getIZ(7);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user