mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
153 Commits
Mobi-Rower
...
nightly
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a3696886f | ||
|
|
9bb1bd68cd | ||
|
|
61f3602980 | ||
|
|
ddab20e841 | ||
|
|
a0eb19690a | ||
|
|
694c895fac | ||
|
|
f48f28df1f | ||
|
|
24f9b72875 | ||
|
|
7c2f97fe31 | ||
|
|
ae7fe8d2db | ||
|
|
2458d009bd | ||
|
|
8b90ab8b00 | ||
|
|
c7bace3112 | ||
|
|
9a97eee780 | ||
|
|
ad39c8d51d | ||
|
|
c0ba8dcf62 | ||
|
|
fda71cda7a | ||
|
|
3c4c654378 | ||
|
|
40f9926ea0 | ||
|
|
730e78c042 | ||
|
|
6ea8ba581d | ||
|
|
0499272421 | ||
|
|
27f8883830 | ||
|
|
539b930164 | ||
|
|
0663bca5e0 | ||
|
|
bcec1b1978 | ||
|
|
b154e98289 | ||
|
|
2e98769ef1 | ||
|
|
e8f6ea07ac | ||
|
|
651cf6a59c | ||
|
|
3c229b9ae8 | ||
|
|
fab3026b84 | ||
|
|
0ce8bc9efc | ||
|
|
4201478c59 | ||
|
|
67b845b5fe | ||
|
|
c968d8ad57 | ||
|
|
8905b1ab4d | ||
|
|
ef2c6f662b | ||
|
|
5596a6cd4f | ||
|
|
fef8abea6d | ||
|
|
3889fac141 | ||
|
|
f9d8ba6925 | ||
|
|
40219ebda9 | ||
|
|
12b0cc7924 | ||
|
|
025406e170 | ||
|
|
df369471aa | ||
|
|
7df442b528 | ||
|
|
b5c4da9420 | ||
|
|
d9e1d9a1be | ||
|
|
1c85feedca | ||
|
|
660f55ad48 | ||
|
|
b871c795b8 | ||
|
|
9256af6391 | ||
|
|
c844276d86 | ||
|
|
71648a6305 | ||
|
|
a9b60bb193 | ||
|
|
7f4f652a5d | ||
|
|
1cd106b026 | ||
|
|
fc00fbf9cc | ||
|
|
2120ff6f6a | ||
|
|
bd92a66e09 | ||
|
|
ed45eac44a | ||
|
|
4d667e9ba4 | ||
|
|
0b5c2745b7 | ||
|
|
3436a6e43c | ||
|
|
e8e64e040a | ||
|
|
11c6f3b52c | ||
|
|
0b126a0aae | ||
|
|
bfe296c3a3 | ||
|
|
7f474580a2 | ||
|
|
828bb350d0 | ||
|
|
4532b05e7e | ||
|
|
277d1d7390 | ||
|
|
cd4e6b0335 | ||
|
|
57f929a3bf | ||
|
|
9728af939e | ||
|
|
a3c4916ded | ||
|
|
e5b5ba1e1e | ||
|
|
326ea8c2a2 | ||
|
|
c9c8e2ce16 | ||
|
|
8f6930709c | ||
|
|
a6c66ab9ee | ||
|
|
9f42d6a6ac | ||
|
|
0b20087da6 | ||
|
|
e37a6b28d6 | ||
|
|
26c89b0d80 | ||
|
|
7c8e411374 | ||
|
|
a31bf49121 | ||
|
|
c2ec6a9a9b | ||
|
|
fa2ff41e4e | ||
|
|
0ff3fb3651 | ||
|
|
39cc4f75f4 | ||
|
|
8b6ce6fa9d | ||
|
|
f84ec511ad | ||
|
|
019264c6c0 | ||
|
|
b4a9369a43 | ||
|
|
d1bd43ea2b | ||
|
|
21e7b0b1ce | ||
|
|
6b85ba1d3a | ||
|
|
99eb5c5f57 | ||
|
|
59f9d0a553 | ||
|
|
9d3039d748 | ||
|
|
249e0191fb | ||
|
|
7a4861f265 | ||
|
|
f85e1fd39e | ||
|
|
a2ba9c69f7 | ||
|
|
df58ff226f | ||
|
|
bd95b67e06 | ||
|
|
f1d1929846 | ||
|
|
fa4bdb2a6b | ||
|
|
a84b57f1d9 | ||
|
|
cc86e26eac | ||
|
|
87a1e125ca | ||
|
|
6bdf6170c3 | ||
|
|
7369623dfd | ||
|
|
a00ddc5890 | ||
|
|
74fbfcda63 | ||
|
|
22b5ba6a02 | ||
|
|
49bdea89a3 | ||
|
|
42c9d170c3 | ||
|
|
89896c5ee9 | ||
|
|
1c9044a66d | ||
|
|
eb573d1029 | ||
|
|
29a93eb315 | ||
|
|
54bc585323 | ||
|
|
f5b26776d2 | ||
|
|
fccf1f2073 | ||
|
|
6e9093bc3c | ||
|
|
df37d4f2a6 | ||
|
|
d9dbe5db20 | ||
|
|
dd1c0c1cb0 | ||
|
|
b8c0a560bf | ||
|
|
89b62c8b6d | ||
|
|
70ea4bfc24 | ||
|
|
00c9d28af0 | ||
|
|
752f3aaf19 | ||
|
|
7e8f744c7b | ||
|
|
1dbdd63b3c | ||
|
|
6b8d96cf7c | ||
|
|
a0bcd8caab | ||
|
|
e46e4daf64 | ||
|
|
8fbd55262d | ||
|
|
487ec5d187 | ||
|
|
090e68979e | ||
|
|
23eebc8be1 | ||
|
|
2eee3e3cc3 | ||
|
|
1f371248d5 | ||
|
|
2bb1cb20de | ||
|
|
16b8805164 | ||
|
|
ae149876a5 | ||
|
|
9042f4857d | ||
|
|
45e06cc807 | ||
|
|
21e341d3d4 |
384
.github/workflows/main.yml
vendored
384
.github/workflows/main.yml
vendored
@@ -21,7 +21,7 @@ on:
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
window-build:
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
@@ -37,6 +37,14 @@ jobs:
|
||||
path: "src/smtpclient/"
|
||||
ref: 3fa4a0fe5797070339422cf18b5e9ed8dcb91f9c
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: cagnulein/qmdnsengine
|
||||
path: "src/qmdnsengine/"
|
||||
ref: "zwift"
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
@@ -109,7 +117,7 @@ jobs:
|
||||
|
||||
- name: Build qthttpserver
|
||||
run: |
|
||||
cd src\qthttpserver
|
||||
cd src\qthttpserver
|
||||
qmake
|
||||
make -j8
|
||||
make install
|
||||
@@ -124,8 +132,6 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
cd ..
|
||||
|
||||
@@ -264,6 +270,13 @@ jobs:
|
||||
# path: "src/smtpclient/"
|
||||
# ref: 3fa4a0fe5797070339422cf18b5e9ed8dcb91f9c
|
||||
#
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Checkout submodule repo
|
||||
# uses: actions/checkout@v2
|
||||
# with:
|
||||
# repository: cagnulein/qmdnsengine
|
||||
# path: "src/qmdnsengine/"
|
||||
# ref: "zwift"
|
||||
#
|
||||
# - uses: msys2/setup-msys2@v2
|
||||
# with:
|
||||
@@ -372,6 +385,14 @@ jobs:
|
||||
path: "src/smtpclient/"
|
||||
ref: 3fa4a0fe5797070339422cf18b5e9ed8dcb91f9c
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: cagnulein/qmdnsengine
|
||||
path: "src/qmdnsengine/"
|
||||
ref: "zwift"
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
@@ -395,7 +416,7 @@ jobs:
|
||||
with:
|
||||
version: '5.15.2'
|
||||
host: 'linux'
|
||||
modules: 'qtnetworkauth qtcharts'
|
||||
modules: 'qtnetworkauth qtcharts qtsql'
|
||||
cache: 'true'
|
||||
cache-key-prefix: 'install-qt-action-linux'
|
||||
|
||||
@@ -428,16 +449,7 @@ jobs:
|
||||
if: failure()
|
||||
with:
|
||||
name: test_results_xml
|
||||
path: tst/test-results/**/*.xml
|
||||
|
||||
- name: Upload test FIT files and database
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test_fit_files_and_db
|
||||
path: |
|
||||
tst/test-artifacts/*.fit
|
||||
tst/test-artifacts/*.sqlite
|
||||
path: tst/test-results/**/*.xml
|
||||
|
||||
# - name: Test Peloton API
|
||||
# if: github.event_name == 'push' || github.event_name == 'schedule'
|
||||
@@ -532,6 +544,12 @@ jobs:
|
||||
git submodule init
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Fix qmdnsengine submodule
|
||||
run: |
|
||||
cd src/qmdnsengine
|
||||
git fetch
|
||||
git checkout 602da51dc43c55bd9aa8a83c47ea3594a9b01b98
|
||||
|
||||
- name: Install packages required to run QZ inside workflow
|
||||
run: sudo apt update -y && sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
|
||||
|
||||
@@ -604,11 +622,9 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
cd ..
|
||||
|
||||
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK
|
||||
rm -rf /usr/local/lib/android/sdk/ndk/25.1.8937393
|
||||
@@ -630,8 +646,8 @@ jobs:
|
||||
- name: Archive apk binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fdroid-android-trial
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug.apk
|
||||
name: peloton-v1-grupetto
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/
|
||||
|
||||
android-emulator-test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -639,7 +655,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
api-level: [24, 26, 28, 29, 30, 31, 33, 35, 36]
|
||||
api-level: [24, 26, 28, 29, 30, 31, 33, 34, 35, 36]
|
||||
include:
|
||||
- api-level: 24
|
||||
target: default
|
||||
@@ -669,6 +685,10 @@ jobs:
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
android-version: "Android 13"
|
||||
- api-level: 34
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
android-version: "Android 14"
|
||||
- api-level: 35
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
@@ -692,7 +712,7 @@ jobs:
|
||||
- name: Download APK Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: fdroid-android-trial
|
||||
name: peloton-v1-grupetto
|
||||
path: apk-debug
|
||||
|
||||
- name: Setup Java for Android Emulator
|
||||
@@ -761,7 +781,7 @@ jobs:
|
||||
adb shell am start -n org.cagnulen.qdomyoszwift/org.cagnulen.qdomyoszwift.CustomQtActivity
|
||||
|
||||
# Wait for app to start
|
||||
sleep 90
|
||||
sleep 60
|
||||
|
||||
# Verify the app is running
|
||||
echo "Checking if app is running..."
|
||||
@@ -822,7 +842,7 @@ jobs:
|
||||
|
||||
ios-build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: macos-14
|
||||
runs-on: macos-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -836,6 +856,14 @@ jobs:
|
||||
path: "src/smtpclient/"
|
||||
ref: 3fa4a0fe5797070339422cf18b5e9ed8dcb91f9c
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: cagnulein/qmdnsengine
|
||||
path: "src/qmdnsengine/"
|
||||
ref: "zwift"
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
@@ -875,11 +903,9 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
cd ..
|
||||
qmake CONFIG+=debug CONFIG+=iphonesimulator && make -j4
|
||||
qmake CONFIG+=debug && make -j4
|
||||
|
||||
# causes iOS build on Mac to fail
|
||||
# - name: Commit moc files
|
||||
@@ -906,6 +932,14 @@ jobs:
|
||||
path: "src/smtpclient/"
|
||||
ref: 3fa4a0fe5797070339422cf18b5e9ed8dcb91f9c
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: cagnulein/qmdnsengine
|
||||
path: "src/qmdnsengine/"
|
||||
ref: "zwift"
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
@@ -980,8 +1014,6 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
cd ..
|
||||
|
||||
@@ -1103,6 +1135,14 @@ jobs:
|
||||
path: "src/smtpclient/"
|
||||
ref: 3fa4a0fe5797070339422cf18b5e9ed8dcb91f9c
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: cagnulein/qmdnsengine
|
||||
path: "src/qmdnsengine/"
|
||||
ref: "zwift"
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout submodule repo
|
||||
uses: actions/checkout@v2
|
||||
@@ -1161,11 +1201,9 @@ jobs:
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
cd ..
|
||||
cd ..
|
||||
|
||||
- name: Clone vcpkg
|
||||
run: git clone https://github.com/microsoft/vcpkg.git
|
||||
@@ -1259,8 +1297,6 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
@@ -1271,8 +1307,8 @@ jobs:
|
||||
args: >
|
||||
bash -c "
|
||||
set -ex &&
|
||||
for i in 1 2 3; do apt-get update && break || sleep 5; done &&
|
||||
for i in 1 2 3; do apt-get install -y --fix-missing build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 qtbase5-dev libqt5sql5-sqlite libqt5sql5 libqt5sql5-mysql libqt5sql5-psql && break || sleep 5; done &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 qtbase5-dev libqt5sql5-sqlite libqt5sql5 libqt5sql5-mysql libqt5sql5-psql &&
|
||||
export QT_SELECT=qt5 &&
|
||||
export PATH=/usr/lib/qt5/bin:$PATH &&
|
||||
cd /github/workspace &&
|
||||
@@ -1320,8 +1356,6 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
@@ -1332,8 +1366,8 @@ jobs:
|
||||
args: >
|
||||
bash -c "
|
||||
set -ex &&
|
||||
for i in 1 2 3; do apt-get update && break || sleep 5; done &&
|
||||
for i in 1 2 3; do apt-get install -y --fix-missing build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 qtbase5-dev libqt5sql5-sqlite libqt5sql5 libqt5sql5-mysql libqt5sql5-psql && break || sleep 5; done &&
|
||||
apt-get update &&
|
||||
apt-get install -y build-essential git cmake qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools libqt5svg5-dev qtmultimedia5-dev libqt5charts5-dev qtpositioning5-dev qtconnectivity5-dev libqt5websockets5-dev libqt5texttospeech5-dev libqt5bluetooth5 libqt5networkauth5-dev qml-module-qtlocation qml-module-qtpositioning qtlocation5-dev libqt5quickcontrols2-5 qtquickcontrols2-5-dev qml-module-qtquick-controls2 qtbase5-dev libqt5sql5-sqlite libqt5sql5 libqt5sql5-mysql libqt5sql5-psql &&
|
||||
export QT_SELECT=qt5 &&
|
||||
export PATH=/usr/lib/qt5/bin:$PATH &&
|
||||
cd /github/workspace &&
|
||||
@@ -1429,8 +1463,6 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
cd ..
|
||||
|
||||
@@ -1545,20 +1577,7 @@ jobs:
|
||||
nordictrack-build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
variant:
|
||||
- name: treadmill
|
||||
setting_key: nordictrack_2950_ip
|
||||
setting_value: localhost
|
||||
- name: bike
|
||||
setting_key: tdf_10_ip
|
||||
setting_value: localhost
|
||||
- name: rower
|
||||
setting_key: proform_rower_ip
|
||||
setting_value: localhost
|
||||
if: github.event_name == 'schedule'
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
@@ -1579,6 +1598,12 @@ jobs:
|
||||
git submodule init
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Fix qmdnsengine submodule
|
||||
run: |
|
||||
cd src/qmdnsengine
|
||||
git fetch
|
||||
git checkout 602da51dc43c55bd9aa8a83c47ea3594a9b01b98
|
||||
|
||||
- name: Install packages required to run QZ inside workflow
|
||||
run: sudo apt update -y && sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
|
||||
|
||||
@@ -1622,12 +1647,8 @@ jobs:
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
# Set variant-specific IP setting
|
||||
sed -i 's/property string ${{ matrix.variant.setting_key }}: ""/property string ${{ matrix.variant.setting_key }}: "${{ matrix.variant.setting_value }}"/' settings.qml
|
||||
cd ..
|
||||
|
||||
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK
|
||||
@@ -1641,232 +1662,31 @@ jobs:
|
||||
cd ../..
|
||||
|
||||
qmake -spec android-clang 'ANDROID_ABIS=armeabi-v7a arm64-v8a x86 x86_64' 'ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/21.4.7075529' && make -j4 && make INSTALL_ROOT=${{ github.workspace }}/output/android/ install
|
||||
cp src/android-qdomyos-zwift-deployment-settings.json src/android-qdomyos-zwift-nordictrack-${{ matrix.variant.name }}-deployment-settings.json
|
||||
sed -i '1s|{|{\n "android-extra-libs": "${{ github.workspace }}/android_openssl/no-asm/latest/arm/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libssl_1_1.so",|' src/android-qdomyos-zwift-nordictrack-${{ matrix.variant.name }}-deployment-settings.json
|
||||
sed -i 's/"android-debug"/"android-nordictrack-${{ matrix.variant.name }}"/g' src/android-qdomyos-zwift-nordictrack-${{ matrix.variant.name }}-deployment-settings.json
|
||||
sed -i 's/android-debug\.apk/android-debug-nordictrack-${{ matrix.variant.name }}.apk/g' src/android-qdomyos-zwift-nordictrack-${{ matrix.variant.name }}-deployment-settings.json
|
||||
cat src/android-qdomyos-zwift-nordictrack-${{ matrix.variant.name }}-deployment-settings.json
|
||||
cp src/android-qdomyos-zwift-deployment-settings.json src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
sed -i '1s|{|{\n "android-extra-libs": "${{ github.workspace }}/android_openssl/no-asm/latest/arm/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libssl_1_1.so",|' src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
sed -i 's/"android-debug"/"android-nordictrack"/g' src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
sed -i 's/android-debug\.apk/android-debug-nordictrack.apk/g' src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
cat src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
|
||||
- name: Build APK (not usable for production due to unpatched QT library)
|
||||
run: cd src; androiddeployqt --input android-qdomyos-zwift-nordictrack-${{ matrix.variant.name }}-deployment-settings.json --output ${{ github.workspace }}/output/android/ --android-platform android-31 --gradle --aab; mv ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug.apk ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-nordictrack-${{ matrix.variant.name }}.apk
|
||||
run: cd src; androiddeployqt --input android-qdomyos-zwift-nordictrack-deployment-settings.json --output ${{ github.workspace }}/output/android/ --android-platform android-31 --gradle --aab; mv ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug.apk ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-nordictrack.apk
|
||||
|
||||
- name: Archive nordictrack binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nordictrack-${{ matrix.variant.name }}-android-trial
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-nordictrack-${{ matrix.variant.name }}.apk
|
||||
|
||||
peloton-bike-plus-build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name == 'schedule'
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- name: Xvfb install and run
|
||||
run: |
|
||||
sudo apt-get install -y xvfb
|
||||
Xvfb -ac ${{ env.DISPLAY }} -screen 0 1280x780x24 &
|
||||
|
||||
- name: Checkout PR code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: refs/pull/3632/head
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
submodules: 'false' # Prima disattiva il checkout automatico dei submodule
|
||||
|
||||
- name: Checkout submodules with specific branches
|
||||
run: |
|
||||
git submodule init
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Install packages required to run QZ inside workflow
|
||||
run: sudo apt update -y && sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
|
||||
|
||||
- name: Install Qt Android
|
||||
uses: jdpurcell/install-qt-action@v5
|
||||
with:
|
||||
version: '5.15.0'
|
||||
host: 'linux'
|
||||
target: 'android'
|
||||
arch: 'android'
|
||||
modules: 'qtcharts qtnetworkauth'
|
||||
dir: '${{ github.workspace }}/output/android/'
|
||||
cache: 'true'
|
||||
cache-key-prefix: 'install-qt-action-android'
|
||||
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '11.0.23+9'
|
||||
|
||||
- name: patching qt for bluetooth
|
||||
run: cp qt-patches/android/5.15.0/jar/*.* ${{ github.workspace }}/output/android/Qt/5.15.0/android/jar/
|
||||
|
||||
- name: download 3rd party files for qthttpserver
|
||||
run: cp qHttpServerBin/5.15.2/headers/* src/qthttpserver/src/3rdparty/http-parser/
|
||||
|
||||
- name: Set Android NDK 21 && build
|
||||
run: |
|
||||
# Install NDK 21 after GitHub update
|
||||
# https://github.com/actions/virtual-environments/issues/5595
|
||||
ANDROID_ROOT="/usr/local/lib/android"
|
||||
ANDROID_SDK_ROOT="${ANDROID_ROOT}/sdk"
|
||||
SDKMANAGER="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager"
|
||||
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
|
||||
export ANDROID_NDK="${ANDROID_SDK_ROOT}/ndk-bundle"
|
||||
export ANDROID_NDK_ROOT="${ANDROID_NDK}"
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
|
||||
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK
|
||||
rm -rf /usr/local/lib/android/sdk/ndk/25.1.8937393
|
||||
|
||||
# QTHTTPSERVER must use the same NDK
|
||||
cd src/qthttpserver
|
||||
qmake
|
||||
make -j8
|
||||
make install
|
||||
cd ../..
|
||||
|
||||
qmake -spec android-clang 'ANDROID_ABIS=armeabi-v7a arm64-v8a x86 x86_64' 'ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/21.4.7075529' && make -j4 && make INSTALL_ROOT=${{ github.workspace }}/output/android/ install
|
||||
cp src/android-qdomyos-zwift-deployment-settings.json src/android-qdomyos-zwift-peloton-bike-plus-deployment-settings.json
|
||||
sed -i '1s|{|{\n "android-extra-libs": "${{ github.workspace }}/android_openssl/no-asm/latest/arm/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libssl_1_1.so",|' src/android-qdomyos-zwift-peloton-bike-plus-deployment-settings.json
|
||||
sed -i 's/"android-debug"/"android-peloton-bike-plus"/g' src/android-qdomyos-zwift-peloton-bike-plus-deployment-settings.json
|
||||
sed -i 's/android-debug\.apk/android-debug-peloton-bike-plus.apk/g' src/android-qdomyos-zwift-peloton-bike-plus-deployment-settings.json
|
||||
cat src/android-qdomyos-zwift-peloton-bike-plus-deployment-settings.json
|
||||
|
||||
- name: Build APK (not usable for production due to unpatched QT library)
|
||||
run: cd src; androiddeployqt --input android-qdomyos-zwift-peloton-bike-plus-deployment-settings.json --output ${{ github.workspace }}/output/android/ --android-platform android-31 --gradle --aab; mv ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug.apk ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-peloton-bike-plus.apk
|
||||
|
||||
- name: Archive peloton-bike-plus binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: peloton-bike-plus-android-trial
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-peloton-bike-plus.apk
|
||||
|
||||
peloton-bike-build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name == 'schedule'
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- name: Xvfb install and run
|
||||
run: |
|
||||
sudo apt-get install -y xvfb
|
||||
Xvfb -ac ${{ env.DISPLAY }} -screen 0 1280x780x24 &
|
||||
|
||||
- name: Checkout PR code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: refs/pull/3639/head
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
submodules: 'false' # Prima disattiva il checkout automatico dei submodule
|
||||
|
||||
- name: Checkout submodules with specific branches
|
||||
run: |
|
||||
git submodule init
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Install packages required to run QZ inside workflow
|
||||
run: sudo apt update -y && sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
|
||||
|
||||
- name: Install Qt Android
|
||||
uses: jdpurcell/install-qt-action@v5
|
||||
with:
|
||||
version: '5.15.0'
|
||||
host: 'linux'
|
||||
target: 'android'
|
||||
arch: 'android'
|
||||
modules: 'qtcharts qtnetworkauth'
|
||||
dir: '${{ github.workspace }}/output/android/'
|
||||
cache: 'true'
|
||||
cache-key-prefix: 'install-qt-action-android'
|
||||
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '11.0.23+9'
|
||||
|
||||
- name: patching qt for bluetooth
|
||||
run: cp qt-patches/android/5.15.0/jar/*.* ${{ github.workspace }}/output/android/Qt/5.15.0/android/jar/
|
||||
|
||||
- name: download 3rd party files for qthttpserver
|
||||
run: cp qHttpServerBin/5.15.2/headers/* src/qthttpserver/src/3rdparty/http-parser/
|
||||
|
||||
- name: Set Android NDK 21 && build
|
||||
run: |
|
||||
# Install NDK 21 after GitHub update
|
||||
# https://github.com/actions/virtual-environments/issues/5595
|
||||
ANDROID_ROOT="/usr/local/lib/android"
|
||||
ANDROID_SDK_ROOT="${ANDROID_ROOT}/sdk"
|
||||
SDKMANAGER="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager"
|
||||
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
|
||||
export ANDROID_NDK="${ANDROID_SDK_ROOT}/ndk-bundle"
|
||||
export ANDROID_NDK_ROOT="${ANDROID_NDK}"
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
|
||||
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
|
||||
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK
|
||||
rm -rf /usr/local/lib/android/sdk/ndk/25.1.8937393
|
||||
|
||||
# QTHTTPSERVER must use the same NDK
|
||||
cd src/qthttpserver
|
||||
qmake
|
||||
make -j8
|
||||
make install
|
||||
cd ../..
|
||||
|
||||
qmake -spec android-clang 'ANDROID_ABIS=armeabi-v7a arm64-v8a x86 x86_64' 'ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/21.4.7075529' && make -j4 && make INSTALL_ROOT=${{ github.workspace }}/output/android/ install
|
||||
cp src/android-qdomyos-zwift-deployment-settings.json src/android-qdomyos-zwift-peloton-bike-deployment-settings.json
|
||||
sed -i '1s|{|{\n "android-extra-libs": "${{ github.workspace }}/android_openssl/no-asm/latest/arm/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libssl_1_1.so",|' src/android-qdomyos-zwift-peloton-bike-deployment-settings.json
|
||||
sed -i 's/"android-debug"/"android-peloton-bike"/g' src/android-qdomyos-zwift-peloton-bike-deployment-settings.json
|
||||
sed -i 's/android-debug\.apk/android-debug-peloton-bike.apk/g' src/android-qdomyos-zwift-peloton-bike-deployment-settings.json
|
||||
cat src/android-qdomyos-zwift-peloton-bike-deployment-settings.json
|
||||
|
||||
- name: Build APK (not usable for production due to unpatched QT library)
|
||||
run: cd src; androiddeployqt --input android-qdomyos-zwift-peloton-bike-deployment-settings.json --output ${{ github.workspace }}/output/android/ --android-platform android-31 --gradle --aab; mv ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug.apk ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-peloton-bike.apk
|
||||
|
||||
- name: Archive peloton-bike binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: peloton-bike-android-trial
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-peloton-bike.apk
|
||||
name: nordictrack-android-trial
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-nordictrack.apk
|
||||
|
||||
upload_to_release:
|
||||
permissions: write-all
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name == 'schedule'
|
||||
needs: [window-msvc2019-build, window-msvc2022-build, window-build, android-build, raspberry-pi-build, nordictrack-build, peloton-bike-plus-build, peloton-bike-build, raspberry-pi-build-and-image-64bit]
|
||||
#if: github.event_name == 'schedule'
|
||||
if: always()
|
||||
needs: [linux-x86-build, window-msvc2019-build, window-msvc2022-build, ios-build, window-build, android-build, raspberry-pi-build]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
@@ -1875,9 +1695,9 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: nightly-${{ steps.date.outputs.date }}
|
||||
tag_name: nightly
|
||||
prerelease: false
|
||||
name: 'QZ nightly build - ${{ steps.date.outputs.date }}'
|
||||
name: 'QZ nightly build $$'
|
||||
body: |
|
||||
This is a nightly build of QZ.
|
||||
|
||||
@@ -1893,12 +1713,7 @@ jobs:
|
||||
- **windows**: MinGW build (alternative version)
|
||||
|
||||
## Other Platforms:
|
||||
- **fdroid-android-trial**: Android build
|
||||
- **nordictrack-treadmill-android-trial**: Nordictrack Treadmill build for iFIT2 Tablets
|
||||
- **nordictrack-bike-android-trial**: Nordictrack Bike build for iFIT2 Tablets
|
||||
- **nordictrack-rower-android-trial**: Nordictrack Rower build for iFIT2 Tablets
|
||||
- **peloton-bike-plus-android-trial**: Peloton Bike Plus build with Grupetto backend
|
||||
- **peloton-bike-android-trial**: Peloton Bike build with Grupetto backend
|
||||
- **peloton-v1-grupetto**: Android build with Peloton v1 Grupetto callback-based integration
|
||||
- **raspberry-pi-binary**: Raspberry Pi build
|
||||
|
||||
__Please help us improve QZ by reporting any issues you encounter!__ :wink:
|
||||
@@ -1910,11 +1725,6 @@ jobs:
|
||||
windows-msvc2019-ai-server-binary/*
|
||||
windows-binary-no-python/*
|
||||
windows-binary/*
|
||||
fdroid-android-trial/android-debug.apk
|
||||
nordictrack-treadmill-android-trial/android-debug-nordictrack-treadmill.apk
|
||||
nordictrack-bike-android-trial/android-debug-nordictrack-bike.apk
|
||||
nordictrack-rower-android-trial/android-debug-nordictrack-rower.apk
|
||||
peloton-bike-plus-android-trial/android-debug-peloton-bike-plus.apk
|
||||
peloton-bike-android-trial/android-debug-peloton-bike.apk
|
||||
peloton-v1-grupetto/*
|
||||
raspberry-pi-binary/qdomyos-zwift-32bit
|
||||
raspberry-pi-binary/qdomyos-zwift-64bit
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -5,6 +5,10 @@
|
||||
path = src/smtpclient
|
||||
url = https://github.com/cagnulein/SmtpClient-for-Qt.git
|
||||
branch = cagnulein-patch-2
|
||||
[submodule "src/qmdnsengine"]
|
||||
path = src/qmdnsengine
|
||||
url = https://github.com/cagnulein/qmdnsengine.git
|
||||
branch = zwift
|
||||
[submodule "tst/googletest"]
|
||||
path = tst/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
|
||||
@@ -370,5 +370,7 @@ The ProForm 995i implementation serves as the reference example:
|
||||
|
||||
## Additional Memories
|
||||
|
||||
- When adding a new setting in QML (setting-tiles.qml), you must:
|
||||
* Add the property at the END of the properties list
|
||||
- When adding a new setting in QML (settings.qml), you must:
|
||||
* Add the property at the END of the properties list (before the closing brace)
|
||||
* NEVER add properties in the middle of the properties list
|
||||
* This applies to ALL QML settings properties, not just setting-tiles.qml
|
||||
@@ -304,6 +304,7 @@
|
||||
87646C2227B5065100F82131 /* moc_bhfitnesselliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87646C2127B5065100F82131 /* moc_bhfitnesselliptical.cpp */; };
|
||||
8767CA552DA3C1FD0003001F /* elitesquarecontroller.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767CA532DA3C1FD0003001F /* elitesquarecontroller.cpp */; };
|
||||
8767CA562DA3C1FD0003001F /* moc_elitesquarecontroller.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767CA542DA3C1FD0003001F /* moc_elitesquarecontroller.cpp */; };
|
||||
8767CA5D2DA7F5170003001F /* ios_wahookickrsnapbike.mm in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767CA5C2DA7F5170003001F /* ios_wahookickrsnapbike.mm */; };
|
||||
8767CA602DA800590003001F /* ios_zwiftclickremote.mm in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767CA5F2DA800590003001F /* ios_zwiftclickremote.mm */; };
|
||||
8767EF1E29448D6700810C0F /* characteristicwriteprocessor.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8767EF1D29448D6700810C0F /* characteristicwriteprocessor.cpp */; };
|
||||
8768C8BA2BBC11C80099DBE1 /* file_sync_client.c in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8768C89C2BBC11C70099DBE1 /* file_sync_client.c */; };
|
||||
@@ -374,6 +375,7 @@
|
||||
8772A0E625E43ADB0080718C /* trxappgateusbbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8772A0E525E43ADA0080718C /* trxappgateusbbike.cpp */; };
|
||||
8772A0E825E43AE70080718C /* moc_trxappgateusbbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8772A0E725E43AE70080718C /* moc_trxappgateusbbike.cpp */; };
|
||||
8772B7F42CB55E80004AB8E9 /* moc_deerruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8772B7F32CB55E80004AB8E9 /* moc_deerruntreadmill.cpp */; };
|
||||
8772B7F72CB55E98004AB8E9 /* deerruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8772B7F62CB55E98004AB8E9 /* deerruntreadmill.cpp */; };
|
||||
877350F72D1C08E60070CBD8 /* SmartControl.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 877350F62D1C08E50070CBD8 /* SmartControl.cpp */; };
|
||||
8775008329E876F8008E48B7 /* iconceptelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8775008129E876F7008E48B7 /* iconceptelliptical.cpp */; };
|
||||
8775008529E87713008E48B7 /* moc_iconceptelliptical.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8775008429E87712008E48B7 /* moc_iconceptelliptical.cpp */; };
|
||||
@@ -401,6 +403,7 @@
|
||||
8785D5432B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8785D5412B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp */; };
|
||||
8785D5442B3DD105005A2EB7 /* moc_zwift_client_auth.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 8785D5422B3DD105005A2EB7 /* moc_zwift_client_auth.cpp */; };
|
||||
87873AEE2D09A8AA005F86B4 /* moc_sportsplusrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87873AED2D09A8AA005F86B4 /* moc_sportsplusrower.cpp */; };
|
||||
87873AF12D09A8CE005F86B4 /* sportsplusrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87873AF02D09A8CE005F86B4 /* sportsplusrower.cpp */; };
|
||||
878895DB2DD48AB100BF5162 /* moc_inclinationresistancetable.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878895DA2DD48AB100BF5162 /* moc_inclinationresistancetable.cpp */; };
|
||||
878A331A25AB4FF800BD13E1 /* yesoulbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878A331725AB4FF800BD13E1 /* yesoulbike.cpp */; };
|
||||
878A331D25AB50C300BD13E1 /* moc_yesoulbike.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 878A331B25AB50C200BD13E1 /* moc_yesoulbike.cpp */; };
|
||||
@@ -459,8 +462,6 @@
|
||||
87A4B76125AF27CB0027EF3C /* metric.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A4B75F25AF27CB0027EF3C /* metric.cpp */; };
|
||||
87A6825A2CE3AB3100586A2A /* moc_sramAXSController.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A682592CE3AB3100586A2A /* moc_sramAXSController.cpp */; };
|
||||
87A6825D2CE3AB4000586A2A /* sramAXSController.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A6825C2CE3AB4000586A2A /* sramAXSController.cpp */; };
|
||||
87A892562F0C12EB00811D95 /* deerruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A892552F0C12EB00811D95 /* deerruntreadmill.cpp */; };
|
||||
87A892582F0C173600811D95 /* sportsplusrower.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87A892572F0C173600811D95 /* sportsplusrower.cpp */; };
|
||||
87ACBE9E2E250F7D00F1B6EA /* moc_androidstatusbar.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87ACBE9D2E250F7D00F1B6EA /* moc_androidstatusbar.cpp */; };
|
||||
87ACBE9F2E250F7D00F1B6EA /* androidstatusbar.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87ACBE9C2E250F7D00F1B6EA /* androidstatusbar.cpp */; };
|
||||
87ADD2BB27634C1500B7A0AB /* technogymmyruntreadmill.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 87ADD2B927634C1400B7A0AB /* technogymmyruntreadmill.cpp */; };
|
||||
@@ -1272,6 +1273,8 @@
|
||||
8767CA522DA3C1FD0003001F /* elitesquarecontroller.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = elitesquarecontroller.h; path = ../src/devices/elitesquarecontroller/elitesquarecontroller.h; sourceTree = SOURCE_ROOT; };
|
||||
8767CA532DA3C1FD0003001F /* elitesquarecontroller.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = elitesquarecontroller.cpp; path = ../src/devices/elitesquarecontroller/elitesquarecontroller.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8767CA542DA3C1FD0003001F /* moc_elitesquarecontroller.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_elitesquarecontroller.cpp; sourceTree = "<group>"; };
|
||||
8767CA5B2DA7F5170003001F /* ios_wahookickrsnapbike.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ios_wahookickrsnapbike.h; path = ../src/ios/ios_wahookickrsnapbike.h; sourceTree = SOURCE_ROOT; };
|
||||
8767CA5C2DA7F5170003001F /* ios_wahookickrsnapbike.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_wahookickrsnapbike.mm; path = ../src/ios/ios_wahookickrsnapbike.mm; sourceTree = SOURCE_ROOT; };
|
||||
8767CA5E2DA800590003001F /* ios_zwiftclickremote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ios_zwiftclickremote.h; path = ../src/ios/ios_zwiftclickremote.h; sourceTree = SOURCE_ROOT; };
|
||||
8767CA5F2DA800590003001F /* ios_zwiftclickremote.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_zwiftclickremote.mm; path = ../src/ios/ios_zwiftclickremote.mm; sourceTree = SOURCE_ROOT; };
|
||||
8767EF1D29448D6700810C0F /* characteristicwriteprocessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = characteristicwriteprocessor.cpp; path = ../src/characteristics/characteristicwriteprocessor.cpp; sourceTree = "<group>"; };
|
||||
@@ -1385,6 +1388,7 @@
|
||||
8772A0E525E43ADA0080718C /* trxappgateusbbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = trxappgateusbbike.cpp; path = ../src/devices/trxappgateusbbike/trxappgateusbbike.cpp; sourceTree = "<group>"; };
|
||||
8772A0E725E43AE70080718C /* moc_trxappgateusbbike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_trxappgateusbbike.cpp; sourceTree = "<group>"; };
|
||||
8772B7F32CB55E80004AB8E9 /* moc_deerruntreadmill.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_deerruntreadmill.cpp; sourceTree = "<group>"; };
|
||||
8772B7F62CB55E98004AB8E9 /* deerruntreadmill.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = deerruntreadmill.cpp; sourceTree = "<group>"; };
|
||||
8772B7F92CB5603A004AB8E9 /* deerruntreadmill.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = deerruntreadmill.h; path = ../src/devices/deeruntreadmill/deerruntreadmill.h; sourceTree = SOURCE_ROOT; };
|
||||
877350F52D1C08E50070CBD8 /* SmartControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SmartControl.h; path = ../src/devices/kineticinroadbike/SmartControl.h; sourceTree = SOURCE_ROOT; };
|
||||
877350F62D1C08E50070CBD8 /* SmartControl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SmartControl.cpp; path = ../src/devices/kineticinroadbike/SmartControl.cpp; sourceTree = SOURCE_ROOT; };
|
||||
@@ -1426,6 +1430,7 @@
|
||||
8785D5412B3DD105005A2EB7 /* moc_PlayerStateWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_PlayerStateWrapper.cpp; sourceTree = "<group>"; };
|
||||
8785D5422B3DD105005A2EB7 /* moc_zwift_client_auth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moc_zwift_client_auth.cpp; sourceTree = "<group>"; };
|
||||
87873AED2D09A8AA005F86B4 /* moc_sportsplusrower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_sportsplusrower.cpp; sourceTree = "<group>"; };
|
||||
87873AF02D09A8CE005F86B4 /* sportsplusrower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sportsplusrower.cpp; sourceTree = "<group>"; };
|
||||
87873AF22D09AADF005F86B4 /* sportsplusrower.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sportsplusrower.h; path = ../src/devices/sportsplusrower/sportsplusrower.h; sourceTree = SOURCE_ROOT; };
|
||||
878895D92DD48AB100BF5162 /* inclinationresistancetable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = inclinationresistancetable.h; path = ../src/inclinationresistancetable.h; sourceTree = SOURCE_ROOT; };
|
||||
878895DA2DD48AB100BF5162 /* moc_inclinationresistancetable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_inclinationresistancetable.cpp; sourceTree = "<group>"; };
|
||||
@@ -1520,8 +1525,6 @@
|
||||
87A682592CE3AB3100586A2A /* moc_sramAXSController.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_sramAXSController.cpp; sourceTree = "<group>"; };
|
||||
87A6825B2CE3AB4000586A2A /* sramAXSController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sramAXSController.h; path = ../src/devices/sramAXSController/sramAXSController.h; sourceTree = SOURCE_ROOT; };
|
||||
87A6825C2CE3AB4000586A2A /* sramAXSController.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = sramAXSController.cpp; path = ../src/devices/sramAXSController/sramAXSController.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87A892552F0C12EB00811D95 /* deerruntreadmill.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = deerruntreadmill.cpp; path = ../src/devices/deeruntreadmill/deerruntreadmill.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87A892572F0C173600811D95 /* sportsplusrower.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = sportsplusrower.cpp; path = ../src/devices/sportsplusrower/sportsplusrower.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87ACBE9B2E250F7D00F1B6EA /* androidstatusbar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = androidstatusbar.h; path = ../src/androidstatusbar.h; sourceTree = SOURCE_ROOT; };
|
||||
87ACBE9C2E250F7D00F1B6EA /* androidstatusbar.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = androidstatusbar.cpp; path = ../src/androidstatusbar.cpp; sourceTree = SOURCE_ROOT; };
|
||||
87ACBE9D2E250F7D00F1B6EA /* moc_androidstatusbar.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moc_androidstatusbar.cpp; sourceTree = "<group>"; };
|
||||
@@ -2335,8 +2338,6 @@
|
||||
2EB56BE3C2D93CDAB0C52E67 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
87A892572F0C173600811D95 /* sportsplusrower.cpp */,
|
||||
87A892552F0C12EB00811D95 /* deerruntreadmill.cpp */,
|
||||
87CBCF0F2EFAA2F8004F5ECE /* garminconnect.h */,
|
||||
87CBCF102EFAA2F8004F5ECE /* garminconnect.cpp */,
|
||||
87CBCF112EFAA2F8004F5ECE /* moc_garminconnect.cpp */,
|
||||
@@ -2373,6 +2374,8 @@
|
||||
87F1BD702DC0D59600416506 /* moc_coresensor.cpp */,
|
||||
8767CA5E2DA800590003001F /* ios_zwiftclickremote.h */,
|
||||
8767CA5F2DA800590003001F /* ios_zwiftclickremote.mm */,
|
||||
8767CA5B2DA7F5170003001F /* ios_wahookickrsnapbike.h */,
|
||||
8767CA5C2DA7F5170003001F /* ios_wahookickrsnapbike.mm */,
|
||||
87BFEA2D2CEDDEEE00BDD759 /* ios_echelonconnectsport.h */,
|
||||
87BFEA2E2CEDDEEE00BDD759 /* ios_echelonconnectsport.mm */,
|
||||
8767CA522DA3C1FD0003001F /* elitesquarecontroller.h */,
|
||||
@@ -2415,6 +2418,7 @@
|
||||
875CA9482D0C742500667EE6 /* kineticinroadbike.cpp */,
|
||||
875CA9452D0C740000667EE6 /* moc_kineticinroadbike.cpp */,
|
||||
87873AF22D09AADF005F86B4 /* sportsplusrower.h */,
|
||||
87873AF02D09A8CE005F86B4 /* sportsplusrower.cpp */,
|
||||
87873AED2D09A8AA005F86B4 /* moc_sportsplusrower.cpp */,
|
||||
870A5DB42CEF8FD200839641 /* technogymbike.cpp */,
|
||||
870A5DB22CEF8FB100839641 /* moc_technogymbike.cpp */,
|
||||
@@ -2460,6 +2464,7 @@
|
||||
87A682592CE3AB3100586A2A /* moc_sramAXSController.cpp */,
|
||||
87A083062C73361C00567A4E /* characteristicnotifier2ad9.h */,
|
||||
8772B7F92CB5603A004AB8E9 /* deerruntreadmill.h */,
|
||||
8772B7F62CB55E98004AB8E9 /* deerruntreadmill.cpp */,
|
||||
8772B7F32CB55E80004AB8E9 /* moc_deerruntreadmill.cpp */,
|
||||
877758B52C98629B00BB1697 /* sportstechelliptical.cpp */,
|
||||
877758B42C98629B00BB1697 /* sportstechelliptical.h */,
|
||||
@@ -3737,6 +3742,7 @@
|
||||
871235BF26B297670012D0F2 /* kingsmithr1protreadmill.cpp in Compile Sources */,
|
||||
87DA62AA2D2305F5008ADA0F /* characteristicwriteprocessor0003.cpp in Compile Sources */,
|
||||
87DA62AB2D2305F5008ADA0F /* characteristicnotifier0002.cpp in Compile Sources */,
|
||||
8772B7F72CB55E98004AB8E9 /* deerruntreadmill.cpp in Compile Sources */,
|
||||
20A50533946A39CBD2C89104 /* bluetoothdevice.cpp in Compile Sources */,
|
||||
87C5F0D126285E7E0067A1B5 /* moc_stagesbike.cpp in Compile Sources */,
|
||||
873824E927E647A8004F1B46 /* mdns.cpp in Compile Sources */,
|
||||
@@ -3846,6 +3852,7 @@
|
||||
87BE6FDE272D2A3E00C35795 /* moc_horizongr7bike.cpp in Compile Sources */,
|
||||
A4BD6DF51CFFF867B7B5AED4 /* fit_developer_field_definition.cpp in Compile Sources */,
|
||||
87EB918B27EE5FE7002535E1 /* moc_inappproductqmltype.cpp in Compile Sources */,
|
||||
87873AF12D09A8CE005F86B4 /* sportsplusrower.cpp in Compile Sources */,
|
||||
8762D5132601F89500F6F049 /* scanrecordresult.cpp in Compile Sources */,
|
||||
3015F9B9FF4CA6D653D46CCA /* fit_developer_field_description.cpp in Compile Sources */,
|
||||
878521D42E44B26600922796 /* moc_nordictrackifitadbrower.cpp in Compile Sources */,
|
||||
@@ -3861,7 +3868,6 @@
|
||||
87DC27F32D9BDC43007A1B9D /* moc_moxy5sensor.cpp in Compile Sources */,
|
||||
87DC27F42D9BDC43007A1B9D /* moxy5sensor.cpp in Compile Sources */,
|
||||
87EB918C27EE5FE7002535E1 /* moc_inappproduct.cpp in Compile Sources */,
|
||||
87A892582F0C173600811D95 /* sportsplusrower.cpp in Compile Sources */,
|
||||
87E34C2D2886F99A00CEDE4B /* moc_octanetreadmill.cpp in Compile Sources */,
|
||||
87D91F9A2800B9970026D43C /* proformwifibike.cpp in Compile Sources */,
|
||||
873CD22327EF8E18000131BC /* inappstoreqmltype.cpp in Compile Sources */,
|
||||
@@ -3889,6 +3895,7 @@
|
||||
87EFB56E25BD703D0039DD5A /* proformtreadmill.cpp in Compile Sources */,
|
||||
87DA8465284933D200B550E9 /* fakeelliptical.cpp in Compile Sources */,
|
||||
876E50F52B701C050080FAAF /* moc_zwiftclickremote.cpp in Compile Sources */,
|
||||
8767CA5D2DA7F5170003001F /* ios_wahookickrsnapbike.mm in Compile Sources */,
|
||||
87FE5BAF2692F3130056EFC8 /* tacxneo2.cpp in Compile Sources */,
|
||||
8718CBAC263063CE004BF4EE /* moc_tcpclientinfosender.cpp in Compile Sources */,
|
||||
873824B527E64707004F1B46 /* moc_provider_p.cpp in Compile Sources */,
|
||||
@@ -4143,7 +4150,6 @@
|
||||
E8B499F921FB0AB55C7A8A8B /* moc_gpx.cpp in Compile Sources */,
|
||||
87E6A85825B5C88E00371D28 /* moc_flywheelbike.cpp in Compile Sources */,
|
||||
8754D24C27F786F0003D7054 /* virtualrower.swift in Compile Sources */,
|
||||
87A892562F0C12EB00811D95 /* deerruntreadmill.cpp in Compile Sources */,
|
||||
878D83742A1F33C600D7F004 /* bkoolbike.cpp in Compile Sources */,
|
||||
873824B727E64707004F1B46 /* moc_characteristicwriteprocessor.cpp in Compile Sources */,
|
||||
87310B1F266FBB59008BA0D6 /* homefitnessbuddy.cpp in Compile Sources */,
|
||||
@@ -4573,7 +4579,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -4774,7 +4780,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
@@ -5011,7 +5017,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -5107,7 +5113,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -5199,7 +5205,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -5315,7 +5321,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -5425,7 +5431,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = QZWidgetExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
@@ -5516,7 +5522,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = QZWidgetExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1274;
|
||||
CURRENT_PROJECT_VERSION = 1241;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
|
||||
@@ -10,6 +10,6 @@ INCLUDEPATH += $$PWD/src/qmdnsengine/src/include
|
||||
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/src/android
|
||||
|
||||
ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64
|
||||
ANDROID_ABIS = arm64-v8a
|
||||
|
||||
#QMAKE_CXXFLAGS += -Werror=suggest-override
|
||||
|
||||
BIN
src/ConnectIQ/iOS/ConnectIQ.xcframework/ios-arm64/ConnectIQ.framework/ConnectIQ
Executable file → Normal file
BIN
src/ConnectIQ/iOS/ConnectIQ.xcframework/ios-arm64/ConnectIQ.framework/ConnectIQ
Executable file → Normal file
Binary file not shown.
@@ -6,10 +6,9 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "IQConstants.h"
|
||||
#import "IQDevice.h"
|
||||
#import "IQApp.h"
|
||||
#import <ConnectIQ/IQConstants.h>
|
||||
#import <ConnectIQ/IQDevice.h>
|
||||
#import <ConnectIQ/IQApp.h>
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
#pragma mark - PUBLIC TYPES
|
||||
@@ -50,22 +49,9 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
/// @brief Called by the ConnectIQ SDK when an IQDevice's connection status has
|
||||
/// changed.
|
||||
///
|
||||
/// When the device status is updated to ``IQDeviceStatus.IQDeviceStatus_Connected``
|
||||
/// it does not mean the device services and characteristics have been discovered yet. To wait
|
||||
/// till the services and characteristics to be discovered the client app has to wait on the delegate call
|
||||
/// ``deviceCharacteristicsDiscovered:(IQDevice *)``. After that the client
|
||||
/// app can start communicating with the device. The method ``deviceCharacteristicsDiscovered:``
|
||||
/// was added to keep backwards compatibility for ``IQDeviceStatus``.
|
||||
///
|
||||
/// @param device The IQDevice whose status changed.
|
||||
/// @param status The new status of the device.
|
||||
- (void)deviceStatusChanged:(IQDevice *)device status:(IQDeviceStatus)status;
|
||||
|
||||
/// @brief Called by the ConnectIQ SDK when an IQDevice's charactersitics are discovered.
|
||||
/// When this method is called the device is ready for communication with the client app.
|
||||
///
|
||||
/// @param device The IQDevice whose characteristics are discovered.
|
||||
- (void)deviceCharacteristicsDiscovered:(IQDevice *)device;
|
||||
@end
|
||||
|
||||
/// @brief Conforming to the IQAppMessageDelegate protocol indicates that an
|
||||
@@ -102,11 +88,8 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
#pragma mark - INITIALIZATION
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with a URL Scheme. See also
|
||||
/// - (void)initializeWithUrlScheme:(NSString *)urlScheme
|
||||
/// uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
/// stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
/// for comparison.
|
||||
/// @brief Initializes the ConnectIQ SDK with startup parameters necessary for
|
||||
/// its operation.
|
||||
///
|
||||
/// @param urlScheme The URL scheme for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
@@ -116,60 +99,6 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
- (void)initializeWithUrlScheme:(NSString *)urlScheme uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate;
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with a URL Scheme.
|
||||
///
|
||||
/// @param urlScheme The URL scheme for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
/// launching a URL with this scheme.
|
||||
/// @param delegate The delegate that the SDK will use for notifying the
|
||||
/// companion app about events that require user input. If this
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
/// @param restorationIdentifier The string which will be used as the value for
|
||||
/// CBCentralManagerOptionRestoreIdentifierKey for the internal CBCentralManager.
|
||||
/// The benefit of adding this identifier is that it allows the app to relaunch in the background
|
||||
/// when BLE activity is detected on associated devices after being suspended by iOS. The SDK
|
||||
/// does not currently handle the resulting call to willRestoreState because most CIQ companion apps
|
||||
/// will reconnect to devices they are interested in during app launch.
|
||||
- (void)initializeWithUrlScheme:(NSString *)urlScheme
|
||||
uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with Universal links. See also
|
||||
/// - (void)initializeWithUniversalLinks:(NSString *)urlHost
|
||||
/// uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
/// stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
/// for comparison.
|
||||
///
|
||||
/// @param urlHost The URL host for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
/// launching a URL with this host. The host URL shall be added
|
||||
/// to associated domains list and shall have an entry in apple-app-site-association
|
||||
/// JSON file hosted on the same domain to be able to launch the companion app
|
||||
/// @param delegate The delegate that the SDK will use for notifying the
|
||||
/// companion app about events that require user input. If this
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
- (void)initializeWithUniversalLinks:(NSString *)urlHost uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate;
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with Universal links.
|
||||
///
|
||||
/// @param urlHost The URL host for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
/// launching a URL with this host. The host URL shall be added
|
||||
/// to associated domains list and shall have an entry in apple-app-site-association
|
||||
/// JSON file hosted on the same domain to be able to launch the companion app
|
||||
/// @param delegate The delegate that the SDK will use for notifying the
|
||||
/// companion app about events that require user input. If this
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
/// @param restorationIdentifier The string which will be used as the value for
|
||||
/// CBCentralManagerOptionRestoreIdentifierKey for the internal CBCentralManager.
|
||||
/// The benefit of adding this identifier is that it allows the app to relaunch in the background
|
||||
/// when BLE activity is detected on associated devices after being suspended by iOS. The SDK
|
||||
/// does not currently handle the resulting call to willRestoreState because most CIQ companion apps
|
||||
/// will reconnect to devices they are interested in during app launch.
|
||||
- (void)initializeWithUniversalLinks:(NSString *)urlHost
|
||||
uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
#pragma mark - EXTERNAL LAUNCHING
|
||||
// --------------------------------------------------------------------------------
|
||||
@@ -295,21 +224,6 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
/// message operation is complete.
|
||||
- (void)sendMessage:(id)message toApp:(IQApp *)app progress:(IQSendMessageProgress)progress completion:(IQSendMessageCompletion)completion;
|
||||
|
||||
/// @brief Begins sending a message to an app while allowing the message to be marked as transient. This method returns immediately.
|
||||
///
|
||||
/// @param message The message to send to the app. This message must be one of
|
||||
/// the following types: NSString, NSNumber, NSNull, NSArray,
|
||||
/// or NSDictionary. Arrays and dictionaries may be nested.
|
||||
/// @param app The app to send the message to.
|
||||
/// @param progress A progress block that will be triggered periodically
|
||||
/// throughout the transfer. This is guaranteed to be triggered
|
||||
/// at least once.
|
||||
/// @param completion A completion block that will be triggered when the send
|
||||
/// message operation is complete.
|
||||
/// @param isTransient Flag to mark the message as transient.
|
||||
- (void)sendMessage:(id)message toApp:(IQApp *)app progress:(IQSendMessageProgress)progress
|
||||
completion:(IQSendMessageCompletion)completion isTransient:(BOOL)isTransient;
|
||||
|
||||
/// @brief Sends an open app request message request to the device. This method returns immediately.
|
||||
///
|
||||
/// @param app The app to open.
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "IQDevice.h"
|
||||
#import "IQAppStatus.h"
|
||||
#import <ConnectIQ/IQDevice.h>
|
||||
#import <ConnectIQ/IQAppStatus.h>
|
||||
|
||||
/// @brief Represents an instance of a ConnectIQ app that is installed on a
|
||||
/// Garmin device.
|
||||
|
||||
@@ -42,9 +42,6 @@ typedef NS_ENUM(NSInteger, IQDeviceStatus){
|
||||
/// Garmin Connect Mobile.
|
||||
@property (nonatomic, readonly) NSString *friendlyName;
|
||||
|
||||
/// @brief The part number of the device per the Garmin catalog of devices.
|
||||
@property (nonatomic, readonly) NSString *partNumber;
|
||||
|
||||
/// @brief Creates a new device instance.
|
||||
///
|
||||
/// @param uuid The UUID of the device to create.
|
||||
@@ -54,17 +51,6 @@ typedef NS_ENUM(NSInteger, IQDeviceStatus){
|
||||
/// @return A new IQDevice instance with the appropriate values set.
|
||||
+ (IQDevice *)deviceWithId:(NSUUID *)uuid modelName:(NSString *)modelName friendlyName:(NSString *)friendlyName;
|
||||
|
||||
/// @brief Creates a new device instance with part number included.
|
||||
///
|
||||
/// @param uuid The UUID of the device to create.
|
||||
/// @param modelName The model name of the device to create.
|
||||
/// @param friendlyName The friendly name of the device to create.
|
||||
/// @param partNumber The part number of the device to create.
|
||||
///
|
||||
/// @return A new IQDevice instance with the appropriate values set.
|
||||
+ (IQDevice *)deviceWithId:(NSUUID *)uuid modelName:(NSString *)modelName friendlyName:(NSString *)friendlyName
|
||||
partNumber:(NSString *)partNumber;
|
||||
|
||||
/// @brief Creates a new device instance by copying another device's values.
|
||||
///
|
||||
/// @param device The device to copy values from.
|
||||
|
||||
Binary file not shown.
BIN
src/ConnectIQ/iOS/ConnectIQ.xcframework/ios-arm64_x86_64-simulator/ConnectIQ.framework/ConnectIQ
Executable file → Normal file
BIN
src/ConnectIQ/iOS/ConnectIQ.xcframework/ios-arm64_x86_64-simulator/ConnectIQ.framework/ConnectIQ
Executable file → Normal file
Binary file not shown.
@@ -6,10 +6,9 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "IQConstants.h"
|
||||
#import "IQDevice.h"
|
||||
#import "IQApp.h"
|
||||
#import <ConnectIQ/IQConstants.h>
|
||||
#import <ConnectIQ/IQDevice.h>
|
||||
#import <ConnectIQ/IQApp.h>
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
#pragma mark - PUBLIC TYPES
|
||||
@@ -50,22 +49,9 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
/// @brief Called by the ConnectIQ SDK when an IQDevice's connection status has
|
||||
/// changed.
|
||||
///
|
||||
/// When the device status is updated to ``IQDeviceStatus.IQDeviceStatus_Connected``
|
||||
/// it does not mean the device services and characteristics have been discovered yet. To wait
|
||||
/// till the services and characteristics to be discovered the client app has to wait on the delegate call
|
||||
/// ``deviceCharacteristicsDiscovered:(IQDevice *)``. After that the client
|
||||
/// app can start communicating with the device. The method ``deviceCharacteristicsDiscovered:``
|
||||
/// was added to keep backwards compatibility for ``IQDeviceStatus``.
|
||||
///
|
||||
/// @param device The IQDevice whose status changed.
|
||||
/// @param status The new status of the device.
|
||||
- (void)deviceStatusChanged:(IQDevice *)device status:(IQDeviceStatus)status;
|
||||
|
||||
/// @brief Called by the ConnectIQ SDK when an IQDevice's charactersitics are discovered.
|
||||
/// When this method is called the device is ready for communication with the client app.
|
||||
///
|
||||
/// @param device The IQDevice whose characteristics are discovered.
|
||||
- (void)deviceCharacteristicsDiscovered:(IQDevice *)device;
|
||||
@end
|
||||
|
||||
/// @brief Conforming to the IQAppMessageDelegate protocol indicates that an
|
||||
@@ -102,11 +88,8 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
#pragma mark - INITIALIZATION
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with a URL Scheme. See also
|
||||
/// - (void)initializeWithUrlScheme:(NSString *)urlScheme
|
||||
/// uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
/// stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
/// for comparison.
|
||||
/// @brief Initializes the ConnectIQ SDK with startup parameters necessary for
|
||||
/// its operation.
|
||||
///
|
||||
/// @param urlScheme The URL scheme for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
@@ -116,60 +99,6 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
- (void)initializeWithUrlScheme:(NSString *)urlScheme uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate;
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with a URL Scheme.
|
||||
///
|
||||
/// @param urlScheme The URL scheme for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
/// launching a URL with this scheme.
|
||||
/// @param delegate The delegate that the SDK will use for notifying the
|
||||
/// companion app about events that require user input. If this
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
/// @param restorationIdentifier The string which will be used as the value for
|
||||
/// CBCentralManagerOptionRestoreIdentifierKey for the internal CBCentralManager.
|
||||
/// The benefit of adding this identifier is that it allows the app to relaunch in the background
|
||||
/// when BLE activity is detected on associated devices after being suspended by iOS. The SDK
|
||||
/// does not currently handle the resulting call to willRestoreState because most CIQ companion apps
|
||||
/// will reconnect to devices they are interested in during app launch.
|
||||
- (void)initializeWithUrlScheme:(NSString *)urlScheme
|
||||
uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with Universal links. See also
|
||||
/// - (void)initializeWithUniversalLinks:(NSString *)urlHost
|
||||
/// uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
/// stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
/// for comparison.
|
||||
///
|
||||
/// @param urlHost The URL host for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
/// launching a URL with this host. The host URL shall be added
|
||||
/// to associated domains list and shall have an entry in apple-app-site-association
|
||||
/// JSON file hosted on the same domain to be able to launch the companion app
|
||||
/// @param delegate The delegate that the SDK will use for notifying the
|
||||
/// companion app about events that require user input. If this
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
- (void)initializeWithUniversalLinks:(NSString *)urlHost uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate;
|
||||
|
||||
/// @brief Initializes the ConnectIQ SDK for use with Universal links.
|
||||
///
|
||||
/// @param urlHost The URL host for this companion app. When Garmin Connect
|
||||
/// Mobile is launched, it will return to the companion app by
|
||||
/// launching a URL with this host. The host URL shall be added
|
||||
/// to associated domains list and shall have an entry in apple-app-site-association
|
||||
/// JSON file hosted on the same domain to be able to launch the companion app
|
||||
/// @param delegate The delegate that the SDK will use for notifying the
|
||||
/// companion app about events that require user input. If this
|
||||
/// is nil, the SDK's default UI will be used.
|
||||
/// @param restorationIdentifier The string which will be used as the value for
|
||||
/// CBCentralManagerOptionRestoreIdentifierKey for the internal CBCentralManager.
|
||||
/// The benefit of adding this identifier is that it allows the app to relaunch in the background
|
||||
/// when BLE activity is detected on associated devices after being suspended by iOS. The SDK
|
||||
/// does not currently handle the resulting call to willRestoreState because most CIQ companion apps
|
||||
/// will reconnect to devices they are interested in during app launch.
|
||||
- (void)initializeWithUniversalLinks:(NSString *)urlHost
|
||||
uiOverrideDelegate:(id<IQUIOverrideDelegate>)delegate
|
||||
stateRestorationIdentifier:(NSString *) restorationIdentifier;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
#pragma mark - EXTERNAL LAUNCHING
|
||||
// --------------------------------------------------------------------------------
|
||||
@@ -295,21 +224,6 @@ typedef void (^IQSendMessageCompletion)(IQSendMessageResult result);
|
||||
/// message operation is complete.
|
||||
- (void)sendMessage:(id)message toApp:(IQApp *)app progress:(IQSendMessageProgress)progress completion:(IQSendMessageCompletion)completion;
|
||||
|
||||
/// @brief Begins sending a message to an app while allowing the message to be marked as transient. This method returns immediately.
|
||||
///
|
||||
/// @param message The message to send to the app. This message must be one of
|
||||
/// the following types: NSString, NSNumber, NSNull, NSArray,
|
||||
/// or NSDictionary. Arrays and dictionaries may be nested.
|
||||
/// @param app The app to send the message to.
|
||||
/// @param progress A progress block that will be triggered periodically
|
||||
/// throughout the transfer. This is guaranteed to be triggered
|
||||
/// at least once.
|
||||
/// @param completion A completion block that will be triggered when the send
|
||||
/// message operation is complete.
|
||||
/// @param isTransient Flag to mark the message as transient.
|
||||
- (void)sendMessage:(id)message toApp:(IQApp *)app progress:(IQSendMessageProgress)progress
|
||||
completion:(IQSendMessageCompletion)completion isTransient:(BOOL)isTransient;
|
||||
|
||||
/// @brief Sends an open app request message request to the device. This method returns immediately.
|
||||
///
|
||||
/// @param app The app to open.
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "IQDevice.h"
|
||||
#import "IQAppStatus.h"
|
||||
#import <ConnectIQ/IQDevice.h>
|
||||
#import <ConnectIQ/IQAppStatus.h>
|
||||
|
||||
/// @brief Represents an instance of a ConnectIQ app that is installed on a
|
||||
/// Garmin device.
|
||||
|
||||
@@ -42,9 +42,6 @@ typedef NS_ENUM(NSInteger, IQDeviceStatus){
|
||||
/// Garmin Connect Mobile.
|
||||
@property (nonatomic, readonly) NSString *friendlyName;
|
||||
|
||||
/// @brief The part number of the device per the Garmin catalog of devices.
|
||||
@property (nonatomic, readonly) NSString *partNumber;
|
||||
|
||||
/// @brief Creates a new device instance.
|
||||
///
|
||||
/// @param uuid The UUID of the device to create.
|
||||
@@ -54,17 +51,6 @@ typedef NS_ENUM(NSInteger, IQDeviceStatus){
|
||||
/// @return A new IQDevice instance with the appropriate values set.
|
||||
+ (IQDevice *)deviceWithId:(NSUUID *)uuid modelName:(NSString *)modelName friendlyName:(NSString *)friendlyName;
|
||||
|
||||
/// @brief Creates a new device instance with part number included.
|
||||
///
|
||||
/// @param uuid The UUID of the device to create.
|
||||
/// @param modelName The model name of the device to create.
|
||||
/// @param friendlyName The friendly name of the device to create.
|
||||
/// @param partNumber The part number of the device to create.
|
||||
///
|
||||
/// @return A new IQDevice instance with the appropriate values set.
|
||||
+ (IQDevice *)deviceWithId:(NSUUID *)uuid modelName:(NSString *)modelName friendlyName:(NSString *)friendlyName
|
||||
partNumber:(NSString *)partNumber;
|
||||
|
||||
/// @brief Creates a new device instance by copying another device's values.
|
||||
///
|
||||
/// @param device The device to copy values from.
|
||||
|
||||
Binary file not shown.
@@ -6,11 +6,11 @@
|
||||
<dict>
|
||||
<key>Headers/ConnectIQ.h</key>
|
||||
<data>
|
||||
oktDCwqbdQQg6rdcptAN5TGhUZs=
|
||||
yih4e2KjbC/GqavxdCZ3xQ4mHmA=
|
||||
</data>
|
||||
<key>Headers/IQApp.h</key>
|
||||
<data>
|
||||
CMQ9wDp2PKaw9dRd8NBYpX9xkzE=
|
||||
NDlj8k5C84UPFmD+qEMz2WcZloY=
|
||||
</data>
|
||||
<key>Headers/IQAppStatus.h</key>
|
||||
<data>
|
||||
@@ -22,11 +22,11 @@
|
||||
</data>
|
||||
<key>Headers/IQDevice.h</key>
|
||||
<data>
|
||||
a4hkgIut7ETtkOJXPkn/nGElEYg=
|
||||
bl545C/cu0mw2KlRmzojKmHPom0=
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
LeO8CbXcC4FrKgyl2zDm7R7nOj0=
|
||||
YUOCJU/YBLc4CRWV1z8JHDjCx8M=
|
||||
</data>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<data>
|
||||
@@ -300,14 +300,14 @@
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
E2QDme6rWC+CJc/kKtxIVSpPzbE4ArUwNagnLG6Nxis=
|
||||
kAenemss8n98vVLi54JqBUtGwaL1/i+HSejFBZgawHA=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/IQApp.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
KhyZorkoK2Qipuzee5aE5ENCarHR+Ni21GdxCV3FQ0s=
|
||||
bSRRooQ0FKFr3BgrFolAnkU402889YFHrH+6EEca3cg=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/IQAppStatus.h</key>
|
||||
@@ -328,7 +328,7 @@
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Xx+4dhu0JD6w2pd9UMvLXukYVQfKzaLJhU0paDUQyls=
|
||||
4N4+64IHeb9iBwyziNxo0SMuCM75ez9Em4UfmtgtTHA=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/module.modulemap</key>
|
||||
|
||||
@@ -246,7 +246,7 @@ ColumnLayout {
|
||||
elevationGain = elevationGain + (pathController.geopath.coordinateAt(i).altitude - pathController.geopath.coordinateAt(i-1).altitude)
|
||||
lines[i] = pathController.geopath.coordinateAt(i)
|
||||
}
|
||||
distance.text = "Distance " + pathController.distance.toFixed(1) + " km Elevation Gain: " + elevationGain.toFixed(1) + " meters"
|
||||
distance.text = "Distance " + (pathController.geopath.length() / 1000.0).toFixed(1) + " km Elevation Gain: " + elevationGain.toFixed(1) + " meters"
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
91
src/GrupettoDisclaimer.qml
Normal file
91
src/GrupettoDisclaimer.qml
Normal file
@@ -0,0 +1,91 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.0
|
||||
|
||||
Dialog {
|
||||
id: disclaimerDialog
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Dialog.NoAutoClose
|
||||
|
||||
width: Math.min(parent.width * 0.9, 600)
|
||||
height: Math.min(parent.height * 0.8, 500)
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
property bool wasShown: settings.grupetto_disclaimer_shown || false
|
||||
|
||||
Material.theme: Material.Dark
|
||||
Material.accent: Material.Orange
|
||||
|
||||
header: Rectangle {
|
||||
height: 60
|
||||
color: Material.color(Material.Orange)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Legal Disclaimer - Grupetto Integration"
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
contentWidth: availableWidth
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
color: "white"
|
||||
font.pixelSize: 14
|
||||
lineHeight: 1.3
|
||||
|
||||
text: "IMPORTANT LEGAL NOTICE - THIRD-PARTY CODE DISCLAIMER\n\n" +
|
||||
"This application incorporates code derived from the Grupetto project " +
|
||||
"(https://github.com/spencerpayne/grupetto), which enables communication " +
|
||||
"with Peloton fitness equipment sensors.\n\n" +
|
||||
|
||||
"LIABILITY DISCLAIMER:\n\n" +
|
||||
"1. The Grupetto-derived code is provided \"AS IS\" without any warranties " +
|
||||
"of any kind, either expressed or implied.\n\n" +
|
||||
|
||||
"2. The author of QDomyos-Zwift DISCLAIMS ALL RESPONSIBILITY AND LIABILITY " +
|
||||
"for any damages, losses, or issues arising from the use of Grupetto-derived code, " +
|
||||
"including but not limited to:\n" +
|
||||
" • Equipment damage or malfunction\n" +
|
||||
" • Data loss or corruption\n" +
|
||||
" • Personal injury\n" +
|
||||
" • Software crashes or instability\n" +
|
||||
" • Unauthorized access to device systems\n\n" +
|
||||
|
||||
"3. Users assume full responsibility and risk when using features that rely " +
|
||||
"on Grupetto-derived code for Peloton sensor integration.\n\n" +
|
||||
|
||||
"4. This disclaimer does not affect the warranty or liability for other " +
|
||||
"parts of QDomyos-Zwift not derived from Grupetto.\n\n" +
|
||||
|
||||
"5. By clicking 'OK', you acknowledge that you have read, understood, " +
|
||||
"and agree to this disclaimer.\n\n" +
|
||||
|
||||
"ATTRIBUTION:\n" +
|
||||
"Portions of this software are derived from Grupetto, developed by Spencer Payne. " +
|
||||
"Original project: https://github.com/spencerpayne/grupetto"
|
||||
}
|
||||
}
|
||||
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
onAccepted: {
|
||||
settings.grupetto_disclaimer_shown = true
|
||||
close()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!wasShown) {
|
||||
open()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,27 +42,11 @@ class PathController : public QObject {
|
||||
|
||||
void centerChanged() W_SIGNAL(centerChanged)
|
||||
|
||||
double distance() const {
|
||||
return mDistance;
|
||||
}
|
||||
|
||||
void setDistance(double distance) {
|
||||
if (qFuzzyCompare(distance, mDistance)) {
|
||||
return;
|
||||
}
|
||||
mDistance = distance;
|
||||
emit distanceChanged();
|
||||
}
|
||||
|
||||
void distanceChanged() W_SIGNAL(distanceChanged)
|
||||
|
||||
private : QGeoPath mGeoPath;
|
||||
QGeoCoordinate mCenter;
|
||||
double mDistance = 0.0;
|
||||
|
||||
W_PROPERTY(QGeoPath, geopath READ geoPath WRITE setGeoPath NOTIFY geopathChanged)
|
||||
W_PROPERTY(QGeoCoordinate, center READ center WRITE setCenter NOTIFY centerChanged)
|
||||
W_PROPERTY(double, distance READ distance WRITE setDistance NOTIFY distanceChanged)
|
||||
};
|
||||
|
||||
#endif // APPLICATION_PATHCONTROLLER_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.20.23" android:versionCode="1264" android:installLocation="auto">
|
||||
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.20.21" android:versionCode="1240" android:installLocation="auto">
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
@@ -140,4 +140,8 @@
|
||||
<uses-permission android:name="android.permission.GET_TASKS" />
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<!-- Peloton sensor integration permissions (based on Grupetto analysis) -->
|
||||
<uses-permission android:name="onepeloton.permission.ACCESS_SENSOR_SERVICE" />
|
||||
<uses-permission android:name="onepeloton.permission.SUBSCRIPTION_TYPE_ACCESS" />
|
||||
</manifest>
|
||||
|
||||
21
src/android/assets/ca_cert.pem
Normal file
21
src/android/assets/ca_cert.pem
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDeTCCAmGgAwIBAgIUbbOvLluQ8WhwXEL54Z4s9/T3BO4wDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIz
|
||||
MTAyNTIxMDkzM1oXDTMzMTAyMjIxMDkzM1owVjELMAkGA1UEBhMCQVUxEzARBgNV
|
||||
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
|
||||
ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEAySGlxDrbSL9U1N65oYCNpnlXgFgY/uZViJ1wPN92xbsiYCKV5VBEKhA6fKh9
|
||||
K9+VvMqxNycXMpXhXj4YI2hP6MktnOGkz/7RA5lKQGu7fCY/1tutGECfKmKhudWn
|
||||
kvDgPJPxZr1mwqQjuFVSVcV0e763lGE/QdrdsndHjIjJOB5nZ1Q67Ga6tkXQYjtb
|
||||
A6fw0LiZ9xJB/dpZ90wVIfaP22tFVgBBkFvnb91+/fA9dNsjtCRVgzz/qdoQbWF0
|
||||
WMP8PE9jlA0x0cmd+yP6MIQaTqf1j3XSiLvPph/4DeWjcpA3R6Xh515iVRbAXrfO
|
||||
tl5p44mjQYUpOxcZmrl7szGOqwIDAQABoz8wPTAMBgNVHRMEBTADAQH/MA4GA1Ud
|
||||
DwEB/wQEAwICBDAdBgNVHQ4EFgQUpbZ5I+JmUaNH8Idzi8j4D9PiepkwDQYJKoZI
|
||||
hvcNAQELBQADggEBAK+9zI1R56gAXv1bHsb6lQrMHHkWdY/xtiDBrTGC9WssKcx3
|
||||
Lfzy9ajzb7T0tVwus2qfM1QUFD53WqusYpA969r3t17/J+7esIyld6193g3aPS5r
|
||||
STrCn8LOmJ+GDgMWU57a2KFNgi3LxtZQeXP1wP10bBWZ8TbYZ5Z5rKbLsnVdc7su
|
||||
gvdg/cH5XQol2jiA1QT076yiUereNkQHNnQW/XuPL30p11Lwzvm0mtBp7lohGZK3
|
||||
zshpXndf741pjdjkUU0OJ/ZhJJycZs6j9xBvElZcFiPiA7S3fuE9APSHaXiTb/AZ
|
||||
4ypwTg9TrqpWG/foB8OdtRe0nbpdOyVPZVC1kSk=
|
||||
-----END CERTIFICATE-----
|
||||
20
src/android/assets/client_cert.pem
Normal file
20
src/android/assets/client_cert.pem
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQTCCAimgAwIBAgIUXcT0gdvvszPRFgr0N1RpnEpZqkgwDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0
|
||||
MDcwMzE5MDc1NVoXDTM0MDcwMTE5MDc1NVowGzEZMBcGA1UEAwwQY29tLmlmaXQu
|
||||
ZXJpYWRvcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALP7690AqHha
|
||||
e2HQ+Adr6awIb9ebJd5g46Feu+WF4XAFOQGvVihXEOHANpriN1c6Rz6xuEPRTtZR
|
||||
B+Wt82ajHPi/tWwfImWGvQd0wdqOs+hcdR5Hxg15CxHHFGvGdFWZumO3gSm62mvo
|
||||
yBUlZX2RpZ0ZJYFuy8Z1GZQmiym4peQZpCNi8YLzKZQNefBXfqLra6/W9vwN35zt
|
||||
UW82jMT4VQEWRU7PgF7U1Svbu8fja4cK5mh8JX/vESXXkIUOlLAonjCNJ+0eh+5k
|
||||
+HEd3sxeKdb4bAkB4UixtUWSf4kzkqzRufwwC/0Mry3UE8byL8J+Bk5L4H5AT3Rl
|
||||
sBMGPeYeWzMCAwEAAaNCMEAwHQYDVR0OBBYEFOZ3xbUHLCiCbX//Qj87HlmYhbvL
|
||||
MB8GA1UdIwQYMBaAFKW2eSPiZlGjR/CHc4vI+A/T4nqZMA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQBXjaqAEgOtaGkmmQLus0sNItE6hJH7r58tmHF19iQGcXnOaMYxyF36i9M2
|
||||
rFBinybQUJ68A74Uz/R7YdOJxcOonSXC1A5/8mUJmlUAQmp+mkdgU68P/pZ1uxUV
|
||||
tyHd+u+J6CUN1qJfmeb0dq532cVJD0TUK8/NbmySpvhsKpVFCIEnUh4DQinkvgAk
|
||||
zheN/qabNwBYflUQOc9Ce5BPYYIGJM96KMofN0ZqbDjjtqgPqvq4SvDBAjvaof9y
|
||||
4Wjiz2TTJCWwmE1/MnRs0N56j147BvTX+9r5k90CESWUv3sCiHYtTN81LcL01DxS
|
||||
mBpnVS9EDui2Lm4FslkSkerCnTfa
|
||||
-----END CERTIFICATE-----
|
||||
28
src/android/assets/client_key.pem
Normal file
28
src/android/assets/client_key.pem
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCz++vdAKh4Wnth
|
||||
0PgHa+msCG/XmyXeYOOhXrvlheFwBTkBr1YoVxDhwDaa4jdXOkc+sbhD0U7WUQfl
|
||||
rfNmoxz4v7VsHyJlhr0HdMHajrPoXHUeR8YNeQsRxxRrxnRVmbpjt4Eputpr6MgV
|
||||
JWV9kaWdGSWBbsvGdRmUJospuKXkGaQjYvGC8ymUDXnwV36i62uv1vb8Dd+c7VFv
|
||||
NozE+FUBFkVOz4Be1NUr27vH42uHCuZofCV/7xEl15CFDpSwKJ4wjSftHofuZPhx
|
||||
Hd7MXinW+GwJAeFIsbVFkn+JM5Ks0bn8MAv9DK8t1BPG8i/CfgZOS+B+QE90ZbAT
|
||||
Bj3mHlszAgMBAAECggEAUN959Cw/hxThK+rCCFOtA+gmmTLVqT7QCcqPk2q9CaDP
|
||||
JLqsdCPrKgU8hAvx4fgF213v9kkuq45thf7Lx+qzMfKyiorS4dvRRHBqStKkdFxX
|
||||
I+wMSjGBj9NskaDy1SPmZLgoCaA0VRicDyRmni27xQNvnuEyH1Ku06seDPkzUXKS
|
||||
+7YtDdHjuh7rfZdN9phkwcM4qJ7ScElr+WP5DL42AhuL7e0bu8EYCZNrgdV826p+
|
||||
/I8eRu4LNYEZ/XnNhKt6I+Qovlq2dLgb0cyMFqOUjPp2CDRkJFIC9E2Llg3AUOnX
|
||||
jJCvBdNkXIh/PsUHx2C7pxg7cUuNvyqnUP/dyxSbgQKBgQDgpTxKEPnit412huRB
|
||||
6J7XbcQHJWypzm2634rIguAKdf+lPFmBcAAVQAJ0mkzX0K9a+6xAlyimrjrMFwVn
|
||||
WndFL9N8KKOsGPryDBMiUtCwROwYjZNQ4ToTMwtOB1Ih1+e6hWLqJWM38nlp1RW7
|
||||
R0qpcYeRoqnl+sirw08DOoh+6wKBgQDNGuX4J1wWs049Kmq4v0BPUacMqq5T7Y1S
|
||||
PgYn16A69lC2qW/cgAB2HAOoOBS+0i6GbQmF/tptN97XOD5an2c4vSQbKKqGkyYk
|
||||
oXl46uqACJBMgR0WaergrcBKuKvnfURVpVNlG08+wsnEGb5apCiyIK4H+g68R/Qr
|
||||
68jniWrS2QKBgQCv81u0W3WNiNzpICA6Kzv2Wgf23O4uVfwGKT6nbDKUnvV78zfb
|
||||
tOCrxDXoJE7Znp8qMQMql/qECuUMo19dIzNV4m7PyXjgu7QZzzFRafIAjgsp9AGV
|
||||
kMMO9KT/GabP0S60HfNql5wN3wIPzZE23VDyRHS9sd1Gv1Vbix8g1UDBvwKBgGBO
|
||||
sg88xBPwq9sysJwBSbw09gCPoH3OPJ6Seyd4K0ekYy/yDZF3FUBgVSNG+g7D+I6s
|
||||
Yl1l1sCUDHH4eebplHli7rJF/RRlwfJPVA+AFw55dvBFbBgbMevAClvLrQRsoIqq
|
||||
r6b5FNO+eSk4gVZkYKuLhsw+EW89RhzdgR+fOea5AoGAPNa30OpFIRY1ViyAu+Nm
|
||||
0bAKDHZXRajOSYzsSeJI7BjlNtRDNDJfcUjYtpJGk8SOFV2Y0IOIlN3GYCO1x/0V
|
||||
G7U6EDAYYun+mlP91d8IHRAWcvIiZNuqP8IO2MZRen1jEOhTF9GKsrAdN+1moeB5
|
||||
qziU9kATRT7PSCd0NhvhDXE=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -26,11 +26,35 @@ apply plugin: 'com.google.protobuf'
|
||||
def amazon = System.getenv('AMAZON')
|
||||
println(amazon)
|
||||
|
||||
// FIXED: Force resolution con versioni consistenti
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
// TUTTE le versioni devono essere consistenti
|
||||
force 'com.google.protobuf:protobuf-javalite:3.25.3'
|
||||
force 'io.grpc:grpc-okhttp:1.63.0'
|
||||
force 'io.grpc:grpc-protobuf-lite:1.63.0'
|
||||
force 'io.grpc:grpc-stub:1.63.0'
|
||||
force 'io.grpc:grpc-core:1.63.0'
|
||||
}
|
||||
// Exclude full protobuf from ALL dependencies
|
||||
exclude group: 'com.google.protobuf', module: 'protobuf-java'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.core:core:1.12.0"
|
||||
implementation "androidx.core:core-ktx:1.12.0"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0"
|
||||
implementation 'com.google.protobuf:protobuf-javalite:3.25.1'
|
||||
|
||||
// Peloton sensor integration uses Android system service binding
|
||||
// No additional external dependencies required beyond standard Android APIs
|
||||
|
||||
// FIXED: Una sola versione di protobuf-javalite
|
||||
implementation 'com.google.protobuf:protobuf-javalite:3.25.3'
|
||||
|
||||
implementation 'io.grpc:grpc-okhttp:1.63.0'
|
||||
implementation 'io.grpc:grpc-protobuf-lite:1.63.0'
|
||||
implementation 'io.grpc:grpc-stub:1.63.0'
|
||||
implementation 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
|
||||
if(amazon == "1") {
|
||||
// amazon app store
|
||||
@@ -47,12 +71,12 @@ dependencies {
|
||||
implementation "com.android.billingclient:billing:8.0.0"
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation files('libs/usb-serial-for-android-3.8.1.aar')
|
||||
androidTestImplementation "com.android.support:support-annotations:28.0.0"
|
||||
implementation 'com.google.android.gms:play-services-wearable:+'
|
||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation files('libs/usb-serial-for-android-3.8.1.aar')
|
||||
androidTestImplementation "com.android.support:support-annotations:28.0.0"
|
||||
implementation 'com.google.android.gms:play-services-wearable:+'
|
||||
|
||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.60'
|
||||
@@ -66,7 +90,12 @@ def archSuffix = Os.isFamily(Os.FAMILY_MAC) ? ':osx-x86_64' : ''
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = "com.google.protobuf:protoc:3.25.1$archSuffix"
|
||||
artifact = "com.google.protobuf:protoc:3.25.3$archSuffix"
|
||||
}
|
||||
plugins {
|
||||
grpc {
|
||||
artifact = "io.grpc:protoc-gen-grpc-java:1.63.0"
|
||||
}
|
||||
}
|
||||
generateProtoTasks {
|
||||
all().configureEach { task ->
|
||||
@@ -75,6 +104,11 @@ protobuf {
|
||||
option "lite"
|
||||
}
|
||||
}
|
||||
task.plugins {
|
||||
grpc {
|
||||
option "lite"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +151,7 @@ android {
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
checkReleaseBuilds false
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
|
||||
// Do not compress Qt binary resources file
|
||||
@@ -132,14 +166,43 @@ android {
|
||||
targetSdkVersion = 36
|
||||
}
|
||||
|
||||
tasks.all { task ->
|
||||
if (task.name == 'compileDebugJavaWithJavac' && amazon == "1") {
|
||||
task.dependsOn copyArm64Directory
|
||||
task.dependsOn copyArm32Directory
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXED: Packaging options ottimizzato per gestire i conflitti protobuf
|
||||
packagingOptions {
|
||||
// EXCLUDE problematic META-INF files instead of pickFirst to avoid collisions
|
||||
exclude 'META-INF/MANIFEST.MF'
|
||||
exclude 'META-INF/INDEX.LIST'
|
||||
exclude 'META-INF/io.netty.versions.properties'
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/LICENSE.txt'
|
||||
exclude 'META-INF/NOTICE'
|
||||
exclude 'META-INF/NOTICE.txt'
|
||||
exclude 'META-INF/AL2.0'
|
||||
exclude 'META-INF/LGPL2.1'
|
||||
|
||||
// Keep pickFirst only for files that are actually needed
|
||||
pickFirst '**/META-INF/okio.kotlin_module'
|
||||
pickFirst '**/META-INF/*.kotlin_module'
|
||||
|
||||
// CRITICAL: Handle duplicate protobuf classes - this is crucial for your error
|
||||
pickFirst '**/com/google/protobuf/**'
|
||||
|
||||
// Handle native libraries
|
||||
pickFirst '**/libprotobuf-lite.so'
|
||||
pickFirst '**/libprotoc.so'
|
||||
|
||||
// Additional common conflicts
|
||||
pickFirst '**/META-INF/services/**'
|
||||
pickFirst '**/kotlin/**'
|
||||
}
|
||||
|
||||
tasks.all { task ->
|
||||
if (task.name == 'compileDebugJavaWithJavac' && amazon == "1") {
|
||||
task.dependsOn copyArm64Directory
|
||||
task.dependsOn copyArm32Directory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyArm64Directory(type: Copy) {
|
||||
from "libs/arm64-v8a/"
|
||||
|
||||
0
src/android/proguard-rules.pro
vendored
Normal file
0
src/android/proguard-rules.pro
vendored
Normal file
239
src/android/src/BikeData.java
Normal file
239
src/android/src/BikeData.java
Normal file
@@ -0,0 +1,239 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class BikeData implements Parcelable {
|
||||
public static final Creator<BikeData> CREATOR = new Creator<BikeData>() {
|
||||
|
||||
@Override // android.os.Parcelable.Creator
|
||||
public BikeData createFromParcel(Parcel parcel) {
|
||||
return new BikeData(parcel);
|
||||
}
|
||||
|
||||
@Override // android.os.Parcelable.Creator
|
||||
public BikeData[] newArray(int i) {
|
||||
return new BikeData[i];
|
||||
}
|
||||
};
|
||||
private long mRPM;
|
||||
private long mPower;
|
||||
private int mTargetResistance;
|
||||
|
||||
// For full compatibility, include all fields from original
|
||||
private int mADValue;
|
||||
private int mAppliedPositionOffset;
|
||||
private String mBikeFrameSerial;
|
||||
private int mCalibrationState;
|
||||
private int mCurrentResistance;
|
||||
private int mDataWriteCycle;
|
||||
private String mDataWriteDate;
|
||||
private String mDataWriteTime;
|
||||
private int mEncoderAngle;
|
||||
private int mError1Code;
|
||||
private String mError1Time;
|
||||
private int mError2Code;
|
||||
private String mError2Time;
|
||||
private int mError3Code;
|
||||
private String mError3Time;
|
||||
private int mError4Code;
|
||||
private String mError4Time;
|
||||
private int mError5Code;
|
||||
private String mError5Time;
|
||||
private int mErrorIndex;
|
||||
private int[] mErrorMap;
|
||||
private String mFWVersionNumber;
|
||||
private String mHardwareVersion;
|
||||
private int mLoadCellCalSpan;
|
||||
private float mLoadCellOffset;
|
||||
private long mLoadCellReading;
|
||||
private String mLoadCellSerial;
|
||||
private String mLoadCellTable;
|
||||
private int mLoadCellTableCrc;
|
||||
private int mLoadCellTableStatus;
|
||||
private int mLoadCellTempCount;
|
||||
private String mLoadCellVersion;
|
||||
private int mLoadCellZeroData;
|
||||
private String mPSerial;
|
||||
private int mPZAFMaxResistanceSetPoint;
|
||||
private int mPZAFMinUpdateRPM;
|
||||
private int mPZAFRampDownRate;
|
||||
private int mPZAFRampUpRate;
|
||||
private byte[] mPacketData;
|
||||
private String mPacketTime;
|
||||
private int mPositionOffset;
|
||||
private int mPowerZoneAutoFollowEnabled;
|
||||
private int mPowerZoneAutoFollowPowerSetPoint;
|
||||
private int mPowerZoneAutoFollowStatus;
|
||||
private float mPowerZoneAutoFollowTargetResistance;
|
||||
private String mQSerial;
|
||||
private float mResistanceOffset;
|
||||
private int mStallThreshold;
|
||||
private int mStepperMotorEndPosition;
|
||||
private long mStepperMotorPosition;
|
||||
private int mStepperMotorStartPosition;
|
||||
private int mSystemState;
|
||||
private float mV1Resistance;
|
||||
|
||||
@Override // android.os.Parcelable
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getRPM() {
|
||||
return this.mRPM;
|
||||
}
|
||||
|
||||
public long getPower() {
|
||||
return this.mPower;
|
||||
}
|
||||
|
||||
public int getTargetResistance() {
|
||||
return this.mTargetResistance;
|
||||
}
|
||||
|
||||
public int getCurrentResistance() {
|
||||
return this.mCurrentResistance;
|
||||
}
|
||||
|
||||
public void setRPM(long rpm) {
|
||||
this.mRPM = rpm;
|
||||
}
|
||||
|
||||
public void setPower(long power) {
|
||||
this.mPower = power;
|
||||
}
|
||||
|
||||
public void setTargetResistance(int resistance) {
|
||||
this.mTargetResistance = resistance;
|
||||
}
|
||||
|
||||
public void setCurrentResistance(int resistance) {
|
||||
this.mCurrentResistance = resistance;
|
||||
}
|
||||
|
||||
private BikeData(Parcel parcel) {
|
||||
readFromParcel(parcel);
|
||||
}
|
||||
|
||||
@Override // android.os.Parcelable
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
parcel.writeLong(this.mRPM);
|
||||
parcel.writeLong(this.mPower);
|
||||
parcel.writeLong(this.mStepperMotorPosition);
|
||||
parcel.writeLong(this.mLoadCellReading);
|
||||
parcel.writeInt(this.mCurrentResistance);
|
||||
parcel.writeInt(this.mTargetResistance);
|
||||
parcel.writeString(this.mFWVersionNumber);
|
||||
parcel.writeByteArray(this.mPacketData);
|
||||
parcel.writeString(this.mPacketTime);
|
||||
parcel.writeInt(this.mStepperMotorStartPosition);
|
||||
parcel.writeInt(this.mStepperMotorEndPosition);
|
||||
parcel.writeInt(this.mCalibrationState);
|
||||
parcel.writeInt(this.mEncoderAngle);
|
||||
parcel.writeInt(this.mSystemState);
|
||||
parcel.writeInt(this.mErrorIndex);
|
||||
parcel.writeInt(this.mError1Code);
|
||||
parcel.writeString(this.mError1Time);
|
||||
parcel.writeInt(this.mError2Code);
|
||||
parcel.writeString(this.mError2Time);
|
||||
parcel.writeInt(this.mError3Code);
|
||||
parcel.writeString(this.mError3Time);
|
||||
parcel.writeInt(this.mError4Code);
|
||||
parcel.writeString(this.mError4Time);
|
||||
parcel.writeInt(this.mError5Code);
|
||||
parcel.writeString(this.mError5Time);
|
||||
parcel.writeIntArray(this.mErrorMap);
|
||||
parcel.writeString(this.mLoadCellTable);
|
||||
parcel.writeInt(this.mLoadCellTableCrc);
|
||||
parcel.writeString(this.mPSerial);
|
||||
parcel.writeString(this.mQSerial);
|
||||
parcel.writeString(this.mBikeFrameSerial);
|
||||
parcel.writeString(this.mLoadCellSerial);
|
||||
parcel.writeFloat(this.mLoadCellOffset);
|
||||
parcel.writeInt(this.mDataWriteCycle);
|
||||
parcel.writeString(this.mDataWriteDate);
|
||||
parcel.writeString(this.mDataWriteTime);
|
||||
parcel.writeInt(this.mLoadCellZeroData);
|
||||
parcel.writeInt(this.mLoadCellCalSpan);
|
||||
parcel.writeInt(this.mLoadCellTempCount);
|
||||
parcel.writeFloat(this.mResistanceOffset);
|
||||
parcel.writeInt(this.mPositionOffset);
|
||||
parcel.writeInt(this.mLoadCellTableStatus);
|
||||
parcel.writeFloat(this.mV1Resistance);
|
||||
parcel.writeString(this.mLoadCellVersion);
|
||||
parcel.writeInt(this.mAppliedPositionOffset);
|
||||
parcel.writeInt(this.mStallThreshold);
|
||||
parcel.writeString(this.mHardwareVersion);
|
||||
parcel.writeInt(this.mADValue);
|
||||
parcel.writeInt(this.mPowerZoneAutoFollowEnabled);
|
||||
parcel.writeInt(this.mPowerZoneAutoFollowPowerSetPoint);
|
||||
parcel.writeFloat(this.mPowerZoneAutoFollowTargetResistance);
|
||||
parcel.writeInt(this.mPowerZoneAutoFollowStatus);
|
||||
parcel.writeInt(this.mPZAFRampUpRate);
|
||||
parcel.writeInt(this.mPZAFRampDownRate);
|
||||
parcel.writeInt(this.mPZAFMaxResistanceSetPoint);
|
||||
parcel.writeInt(this.mPZAFMinUpdateRPM);
|
||||
}
|
||||
|
||||
private void readFromParcel(Parcel parcel) {
|
||||
this.mRPM = parcel.readLong();
|
||||
this.mPower = parcel.readLong();
|
||||
this.mStepperMotorPosition = parcel.readLong();
|
||||
this.mLoadCellReading = parcel.readLong();
|
||||
this.mCurrentResistance = parcel.readInt();
|
||||
this.mTargetResistance = parcel.readInt();
|
||||
this.mFWVersionNumber = parcel.readString();
|
||||
this.mPacketData = parcel.createByteArray();
|
||||
this.mPacketTime = parcel.readString();
|
||||
this.mStepperMotorStartPosition = parcel.readInt();
|
||||
this.mStepperMotorEndPosition = parcel.readInt();
|
||||
this.mCalibrationState = parcel.readInt();
|
||||
this.mEncoderAngle = parcel.readInt();
|
||||
this.mSystemState = parcel.readInt();
|
||||
this.mErrorIndex = parcel.readInt();
|
||||
this.mError1Code = parcel.readInt();
|
||||
this.mError1Time = parcel.readString();
|
||||
this.mError2Code = parcel.readInt();
|
||||
this.mError2Time = parcel.readString();
|
||||
this.mError3Code = parcel.readInt();
|
||||
this.mError3Time = parcel.readString();
|
||||
this.mError4Code = parcel.readInt();
|
||||
this.mError4Time = parcel.readString();
|
||||
this.mError5Code = parcel.readInt();
|
||||
this.mError5Time = parcel.readString();
|
||||
int[] iArr = new int[15];
|
||||
this.mErrorMap = iArr;
|
||||
parcel.readIntArray(iArr);
|
||||
this.mLoadCellTable = parcel.readString();
|
||||
this.mLoadCellTableCrc = parcel.readInt();
|
||||
this.mPSerial = parcel.readString();
|
||||
this.mQSerial = parcel.readString();
|
||||
this.mBikeFrameSerial = parcel.readString();
|
||||
this.mLoadCellSerial = parcel.readString();
|
||||
this.mLoadCellOffset = parcel.readFloat();
|
||||
this.mDataWriteCycle = parcel.readInt();
|
||||
this.mDataWriteDate = parcel.readString();
|
||||
this.mDataWriteTime = parcel.readString();
|
||||
this.mLoadCellZeroData = parcel.readInt();
|
||||
this.mLoadCellCalSpan = parcel.readInt();
|
||||
this.mLoadCellTempCount = parcel.readInt();
|
||||
this.mResistanceOffset = parcel.readFloat();
|
||||
this.mPositionOffset = parcel.readInt();
|
||||
this.mLoadCellTableStatus = parcel.readInt();
|
||||
this.mV1Resistance = parcel.readFloat();
|
||||
this.mLoadCellVersion = parcel.readString();
|
||||
this.mAppliedPositionOffset = parcel.readInt();
|
||||
this.mStallThreshold = parcel.readInt();
|
||||
this.mHardwareVersion = parcel.readString();
|
||||
this.mADValue = parcel.readInt();
|
||||
this.mPowerZoneAutoFollowEnabled = parcel.readInt();
|
||||
this.mPowerZoneAutoFollowPowerSetPoint = parcel.readInt();
|
||||
this.mPowerZoneAutoFollowTargetResistance = parcel.readFloat();
|
||||
this.mPowerZoneAutoFollowStatus = parcel.readInt();
|
||||
this.mPZAFRampUpRate = parcel.readInt();
|
||||
this.mPZAFRampDownRate = parcel.readInt();
|
||||
this.mPZAFMaxResistanceSetPoint = parcel.readInt();
|
||||
this.mPZAFMinUpdateRPM = parcel.readInt();
|
||||
}
|
||||
}
|
||||
@@ -73,12 +73,6 @@ public class Garmin {
|
||||
}
|
||||
|
||||
public static void init(Context c) {
|
||||
if (connectIqReady || connectIqInitializing) {
|
||||
QLog.d(TAG, "Garmin already initialized or initializing");
|
||||
return;
|
||||
}
|
||||
connectIqInitializing = true;
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -90,7 +84,6 @@ public class Garmin {
|
||||
@Override
|
||||
public void onInitializeError(ConnectIQ.IQSdkErrorStatus errStatus) {
|
||||
QLog.e(TAG, errStatus.toString());
|
||||
connectIqInitializing = false;
|
||||
connectIqReady = false;
|
||||
}
|
||||
|
||||
|
||||
863
src/android/src/GrpcTreadmillService.java
Normal file
863
src/android/src/GrpcTreadmillService.java
Normal file
@@ -0,0 +1,863 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.okhttp.OkHttpChannelBuilder;
|
||||
import io.grpc.stub.MetadataUtils;
|
||||
|
||||
import com.ifit.glassos.util.Empty;
|
||||
import com.ifit.glassos.workout.SpeedMetric;
|
||||
import com.ifit.glassos.workout.SpeedServiceGrpc;
|
||||
import com.ifit.glassos.workout.SpeedRequest;
|
||||
import com.ifit.glassos.workout.InclineMetric;
|
||||
import com.ifit.glassos.workout.InclineServiceGrpc;
|
||||
import com.ifit.glassos.workout.InclineRequest;
|
||||
import com.ifit.glassos.workout.WattsMetric;
|
||||
import com.ifit.glassos.workout.WattsServiceGrpc;
|
||||
import com.ifit.glassos.console.constantwatts.ConstantWattsMessage;
|
||||
import com.ifit.glassos.console.constantwatts.ConstantWattsServiceGrpc;
|
||||
import com.ifit.glassos.workout.ResistanceMetric;
|
||||
import com.ifit.glassos.workout.ResistanceServiceGrpc;
|
||||
import com.ifit.glassos.workout.ResistanceRequest;
|
||||
import com.ifit.glassos.workout.CadenceMetric;
|
||||
import com.ifit.glassos.workout.CadenceServiceGrpc;
|
||||
import com.ifit.glassos.workout.RpmMetric;
|
||||
import com.ifit.glassos.workout.RpmServiceGrpc;
|
||||
import com.ifit.glassos.settings.FanState;
|
||||
import com.ifit.glassos.settings.FanStateMessage;
|
||||
import com.ifit.glassos.settings.FanStateServiceGrpc;
|
||||
|
||||
import org.cagnulen.qdomyoszwift.QLog;
|
||||
|
||||
public class GrpcTreadmillService {
|
||||
|
||||
private static final String TAG = "GrpcTreadmillService";
|
||||
|
||||
// Singleton instance for static access
|
||||
private static GrpcTreadmillService instance = null;
|
||||
private static Context staticContext = null;
|
||||
private static String serverHost = "localhost";
|
||||
private static final int SERVER_PORT = 54321;
|
||||
private static final int UPDATE_INTERVAL_MS = 500;
|
||||
|
||||
// Threading components
|
||||
private Handler mainHandler;
|
||||
private ExecutorService executorService;
|
||||
private Runnable metricsUpdateRunnable;
|
||||
|
||||
// gRPC components
|
||||
private ManagedChannel channel;
|
||||
private SpeedServiceGrpc.SpeedServiceBlockingStub speedStub;
|
||||
private InclineServiceGrpc.InclineServiceBlockingStub inclineStub;
|
||||
private WattsServiceGrpc.WattsServiceBlockingStub wattsStub;
|
||||
private ConstantWattsServiceGrpc.ConstantWattsServiceBlockingStub constantWattsStub;
|
||||
private ResistanceServiceGrpc.ResistanceServiceBlockingStub resistanceStub;
|
||||
private CadenceServiceGrpc.CadenceServiceBlockingStub cadenceStub;
|
||||
private RpmServiceGrpc.RpmServiceBlockingStub rpmStub;
|
||||
private FanStateServiceGrpc.FanStateServiceBlockingStub fanStub;
|
||||
|
||||
// Control flags and current values
|
||||
private volatile boolean isUpdating = false;
|
||||
private volatile double currentSpeed = 0.0;
|
||||
private volatile double currentIncline = 0.0;
|
||||
private volatile double currentResistance = 0.0;
|
||||
private volatile double currentWatts = 0.0;
|
||||
private volatile double currentCadence = 0.0;
|
||||
private volatile double currentRpm = 0.0;
|
||||
private volatile int currentFanSpeed = 0;
|
||||
|
||||
// Context for accessing assets
|
||||
private Context context;
|
||||
|
||||
// Metrics listener interface
|
||||
public interface MetricsListener {
|
||||
void onSpeedUpdated(double speed);
|
||||
void onInclineUpdated(double incline);
|
||||
void onWattsUpdated(double watts);
|
||||
void onResistanceUpdated(double resistance);
|
||||
void onCadenceUpdated(double cadence);
|
||||
void onRpmUpdated(double rpm);
|
||||
void onFanSpeedUpdated(int fanSpeed);
|
||||
void onError(String metric, String error);
|
||||
}
|
||||
|
||||
private MetricsListener metricsListener;
|
||||
|
||||
public GrpcTreadmillService(Context context) {
|
||||
this.context = context;
|
||||
this.mainHandler = new Handler(Looper.getMainLooper());
|
||||
this.executorService = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
public void setMetricsListener(MetricsListener listener) {
|
||||
this.metricsListener = listener;
|
||||
}
|
||||
|
||||
private void initializeInstance() throws Exception {
|
||||
initializeGrpcConnection();
|
||||
}
|
||||
|
||||
private void startMetricsUpdatesInstance() {
|
||||
if (isUpdating) return;
|
||||
|
||||
isUpdating = true;
|
||||
|
||||
metricsUpdateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isUpdating) return;
|
||||
|
||||
executorService.execute(() -> {
|
||||
fetchAllMetricsFromServer();
|
||||
|
||||
if (isUpdating) {
|
||||
mainHandler.postDelayed(metricsUpdateRunnable, UPDATE_INTERVAL_MS);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
mainHandler.post(metricsUpdateRunnable);
|
||||
QLog.i(TAG, "Started periodic metrics updates");
|
||||
}
|
||||
|
||||
private void stopMetricsUpdatesInstance() {
|
||||
isUpdating = false;
|
||||
|
||||
if (metricsUpdateRunnable != null) {
|
||||
mainHandler.removeCallbacks(metricsUpdateRunnable);
|
||||
}
|
||||
|
||||
QLog.i(TAG, "Stopped periodic metrics updates");
|
||||
}
|
||||
|
||||
private void adjustSpeedInstance(double delta) {
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
double newSpeed = Math.max(0.0, currentSpeed + delta);
|
||||
|
||||
Metadata headers = createHeaders();
|
||||
SpeedServiceGrpc.SpeedServiceBlockingStub stubWithHeaders = speedStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
|
||||
SpeedRequest request = SpeedRequest.newBuilder().setKph(newSpeed).build();
|
||||
stubWithHeaders.setSpeed(request);
|
||||
|
||||
QLog.d(TAG, String.format("Set speed to %.1f km/h", newSpeed));
|
||||
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to set speed", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("speed", e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void adjustInclineInstance(double delta) {
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
double newIncline = Math.max(-50.0, currentIncline + delta);
|
||||
|
||||
Metadata headers = createHeaders();
|
||||
InclineServiceGrpc.InclineServiceBlockingStub stubWithHeaders = inclineStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
|
||||
InclineRequest request = InclineRequest.newBuilder().setPercent(newIncline).build();
|
||||
stubWithHeaders.setIncline(request);
|
||||
|
||||
QLog.d(TAG, String.format("Set incline to %.1f%%", newIncline));
|
||||
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to set incline", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("incline", e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void adjustResistanceInstance(double delta) {
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
double newResistance = Math.max(0.0, currentResistance + delta);
|
||||
|
||||
Metadata headers = createHeaders();
|
||||
ResistanceServiceGrpc.ResistanceServiceBlockingStub stubWithHeaders = resistanceStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
|
||||
ResistanceRequest request = ResistanceRequest.newBuilder().setResistance(newResistance).build();
|
||||
stubWithHeaders.setResistance(request);
|
||||
|
||||
QLog.d(TAG, String.format("Set resistance to %.0f level", newResistance));
|
||||
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to set resistance", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("resistance", e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setWattsInstance(double watts) {
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
Metadata headers = createHeaders();
|
||||
ConstantWattsServiceGrpc.ConstantWattsServiceBlockingStub stubWithHeaders = constantWattsStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
|
||||
if (watts <= 0) {
|
||||
// Disable constant watts mode when watts is 0 or negative
|
||||
stubWithHeaders.disable(Empty.newBuilder().build());
|
||||
QLog.d(TAG, "Disabled constant watts mode");
|
||||
} else {
|
||||
// Set target watts
|
||||
int targetWatts = (int) watts;
|
||||
ConstantWattsMessage request = ConstantWattsMessage.newBuilder().setWatts(targetWatts).build();
|
||||
stubWithHeaders.setConstantWatts(request);
|
||||
QLog.d(TAG, String.format("Set constant watts to %d", targetWatts));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to set watts", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("watts", e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void disableConstantWattsInstance() {
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
Metadata headers = createHeaders();
|
||||
ConstantWattsServiceGrpc.ConstantWattsServiceBlockingStub stubWithHeaders = constantWattsStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
|
||||
stubWithHeaders.disable(Empty.newBuilder().build());
|
||||
QLog.d(TAG, "Explicitly disabled constant watts mode");
|
||||
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to disable constant watts", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("watts", e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setFanSpeedInstance(int fanSpeed) {
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
Metadata headers = createHeaders();
|
||||
FanStateServiceGrpc.FanStateServiceBlockingStub stubWithHeaders = fanStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
|
||||
FanState fanState;
|
||||
switch (fanSpeed) {
|
||||
case 0:
|
||||
fanState = FanState.FAN_STATE_OFF;
|
||||
break;
|
||||
case 1:
|
||||
fanState = FanState.FAN_STATE_LOW;
|
||||
break;
|
||||
case 2:
|
||||
fanState = FanState.FAN_STATE_MEDIUM;
|
||||
break;
|
||||
case 3:
|
||||
fanState = FanState.FAN_STATE_HIGH;
|
||||
break;
|
||||
case 4:
|
||||
fanState = FanState.FAN_STATE_AUTO;
|
||||
break;
|
||||
default:
|
||||
fanState = FanState.FAN_STATE_OFF;
|
||||
break;
|
||||
}
|
||||
|
||||
FanStateMessage request = FanStateMessage.newBuilder().setState(fanState).build();
|
||||
stubWithHeaders.setFanState(request);
|
||||
|
||||
QLog.d(TAG, String.format("Set fan speed to %d (%s)", fanSpeed, fanState.name()));
|
||||
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to set fan speed", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("fan", e.getMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void shutdownInstance() {
|
||||
stopMetricsUpdates();
|
||||
|
||||
if (channel != null) {
|
||||
try {
|
||||
channel.shutdown();
|
||||
if (!channel.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
channel.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
QLog.e(TAG, "Error shutting down gRPC channel", e);
|
||||
channel.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
try {
|
||||
if (!executorService.awaitTermination(2, TimeUnit.SECONDS)) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
QLog.e(TAG, "Error shutting down executor service", e);
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeGrpcConnection() throws Exception {
|
||||
AssetManager assets = context.getAssets();
|
||||
|
||||
String[] requiredFiles = {"client_cert.pem", "client_key.pem"};
|
||||
for (String file : requiredFiles) {
|
||||
try {
|
||||
assets.open(file).close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Required certificate file missing: " + file +
|
||||
". Please add it to app/src/main/assets/");
|
||||
}
|
||||
}
|
||||
|
||||
InputStream caCertStream = null;
|
||||
try {
|
||||
caCertStream = assets.open("ca_cert.pem");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "ca_cert.pem not found, continuing with insecure mode");
|
||||
}
|
||||
InputStream clientCertStream = assets.open("client_cert.pem");
|
||||
InputStream clientKeyStream = assets.open("client_key.pem");
|
||||
|
||||
QLog.i(TAG, "Loading TLS certificates (insecure server validation mode)...");
|
||||
|
||||
SSLContext sslContext = createSSLContext(caCertStream, clientCertStream, clientKeyStream);
|
||||
|
||||
channel = OkHttpChannelBuilder.forAddress(serverHost, SERVER_PORT)
|
||||
.sslSocketFactory(sslContext.getSocketFactory())
|
||||
.build();
|
||||
|
||||
if (caCertStream != null) caCertStream.close();
|
||||
clientCertStream.close();
|
||||
clientKeyStream.close();
|
||||
|
||||
speedStub = SpeedServiceGrpc.newBlockingStub(channel);
|
||||
inclineStub = InclineServiceGrpc.newBlockingStub(channel);
|
||||
wattsStub = WattsServiceGrpc.newBlockingStub(channel);
|
||||
constantWattsStub = ConstantWattsServiceGrpc.newBlockingStub(channel);
|
||||
resistanceStub = ResistanceServiceGrpc.newBlockingStub(channel);
|
||||
cadenceStub = CadenceServiceGrpc.newBlockingStub(channel);
|
||||
rpmStub = RpmServiceGrpc.newBlockingStub(channel);
|
||||
fanStub = FanStateServiceGrpc.newBlockingStub(channel);
|
||||
|
||||
QLog.i(TAG, "gRPC connection initialized with client certificates");
|
||||
}
|
||||
|
||||
private SSLContext createSSLContext(InputStream caCertStream, InputStream clientCertStream,
|
||||
InputStream clientKeyStream) throws Exception {
|
||||
|
||||
QLog.d(TAG, "Creating SSL context with client certificates (insecure server validation)...");
|
||||
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate clientCert = (X509Certificate) cf.generateCertificate(clientCertStream);
|
||||
QLog.d(TAG, "Loaded client certificate: " + clientCert.getSubjectDN());
|
||||
|
||||
byte[] keyData = readAllBytesCompat(clientKeyStream);
|
||||
String keyString = new String(keyData, StandardCharsets.UTF_8);
|
||||
keyString = keyString.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replaceAll("\\s", "");
|
||||
|
||||
byte[] keyBytes = Base64.getDecoder().decode(keyString);
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
|
||||
QLog.d(TAG, "Loaded private key");
|
||||
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keyStore.load(null, null);
|
||||
keyStore.setKeyEntry("client", privateKey, "".toCharArray(), new Certificate[]{clientCert});
|
||||
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(keyStore, "".toCharArray());
|
||||
|
||||
javax.net.ssl.TrustManager[] insecureTrustManagers = new javax.net.ssl.TrustManager[] {
|
||||
new javax.net.ssl.X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
QLog.d(TAG, "Accepting server certificate without validation (insecure mode)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(kmf.getKeyManagers(), insecureTrustManagers, new SecureRandom());
|
||||
|
||||
QLog.i(TAG, "SSL context created with client authentication but insecure server validation");
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
private byte[] readAllBytesCompat(InputStream inputStream) throws Exception {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
|
||||
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
return output.toByteArray();
|
||||
}
|
||||
|
||||
private Metadata createHeaders() {
|
||||
Metadata headers = new Metadata();
|
||||
headers.put(Metadata.Key.of("client_id", Metadata.ASCII_STRING_MARSHALLER),
|
||||
"com.ifit.eriador");
|
||||
return headers;
|
||||
}
|
||||
|
||||
private void fetchAllMetricsFromServer() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
QLog.d(TAG, "Making gRPC calls for all metrics...");
|
||||
|
||||
long headersStartTime = System.currentTimeMillis();
|
||||
Metadata headers = createHeaders();
|
||||
Empty request = Empty.newBuilder().build();
|
||||
long headersEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Headers creation took: " + (headersEndTime - headersStartTime) + "ms");
|
||||
|
||||
// Fetch speed
|
||||
try {
|
||||
long speedStartTime = System.currentTimeMillis();
|
||||
SpeedServiceGrpc.SpeedServiceBlockingStub speedStubWithHeaders = speedStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
long speedInterceptorTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Speed interceptor setup took: " + (speedInterceptorTime - speedStartTime) + "ms");
|
||||
|
||||
SpeedMetric speedResponse = speedStubWithHeaders.getSpeed(request);
|
||||
long speedCallTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Speed gRPC call took: " + (speedCallTime - speedInterceptorTime) + "ms");
|
||||
|
||||
currentSpeed = speedResponse.getLastKph();
|
||||
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onSpeedUpdated(currentSpeed));
|
||||
}
|
||||
long speedEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Speed total processing took: " + (speedEndTime - speedStartTime) + "ms");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Failed to fetch speed", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("speed", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch inclination
|
||||
try {
|
||||
long inclineStartTime = System.currentTimeMillis();
|
||||
InclineServiceGrpc.InclineServiceBlockingStub inclineStubWithHeaders = inclineStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
long inclineInterceptorTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Incline interceptor setup took: " + (inclineInterceptorTime - inclineStartTime) + "ms");
|
||||
|
||||
InclineMetric inclineResponse = inclineStubWithHeaders.getIncline(request);
|
||||
long inclineCallTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Incline gRPC call took: " + (inclineCallTime - inclineInterceptorTime) + "ms");
|
||||
|
||||
currentIncline = inclineResponse.getLastInclinePercent();
|
||||
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onInclineUpdated(currentIncline));
|
||||
}
|
||||
long inclineEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Incline total processing took: " + (inclineEndTime - inclineStartTime) + "ms");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Failed to fetch inclination", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("inclination", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch watts
|
||||
try {
|
||||
long wattsStartTime = System.currentTimeMillis();
|
||||
WattsServiceGrpc.WattsServiceBlockingStub wattsStubWithHeaders = wattsStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
long wattsInterceptorTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Watts interceptor setup took: " + (wattsInterceptorTime - wattsStartTime) + "ms");
|
||||
|
||||
WattsMetric wattsResponse = wattsStubWithHeaders.getWatts(request);
|
||||
long wattsCallTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Watts gRPC call took: " + (wattsCallTime - wattsInterceptorTime) + "ms");
|
||||
|
||||
currentWatts = wattsResponse.getLastWatts();
|
||||
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onWattsUpdated(currentWatts));
|
||||
}
|
||||
long wattsEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Watts total processing took: " + (wattsEndTime - wattsStartTime) + "ms");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Failed to fetch watts", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("watts", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch resistance
|
||||
try {
|
||||
long resistanceStartTime = System.currentTimeMillis();
|
||||
ResistanceServiceGrpc.ResistanceServiceBlockingStub resistanceStubWithHeaders = resistanceStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
long resistanceInterceptorTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Resistance interceptor setup took: " + (resistanceInterceptorTime - resistanceStartTime) + "ms");
|
||||
|
||||
ResistanceMetric resistanceResponse = resistanceStubWithHeaders.getResistance(request);
|
||||
long resistanceCallTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Resistance gRPC call took: " + (resistanceCallTime - resistanceInterceptorTime) + "ms");
|
||||
|
||||
currentResistance = resistanceResponse.getLastResistance();
|
||||
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onResistanceUpdated(currentResistance));
|
||||
}
|
||||
long resistanceEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Resistance total processing took: " + (resistanceEndTime - resistanceStartTime) + "ms");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Failed to fetch resistance", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("resistance", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch RPM (for bikes)
|
||||
try {
|
||||
long rpmStartTime = System.currentTimeMillis();
|
||||
RpmServiceGrpc.RpmServiceBlockingStub rpmStubWithHeaders = rpmStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
long rpmInterceptorTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "RPM interceptor setup took: " + (rpmInterceptorTime - rpmStartTime) + "ms");
|
||||
|
||||
RpmMetric rpmResponse = rpmStubWithHeaders.getRpm(request);
|
||||
long rpmCallTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "RPM gRPC call took: " + (rpmCallTime - rpmInterceptorTime) + "ms");
|
||||
|
||||
currentRpm = rpmResponse.getLastRpm();
|
||||
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onRpmUpdated(currentRpm));
|
||||
}
|
||||
long rpmEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "RPM total processing took: " + (rpmEndTime - rpmStartTime) + "ms");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Failed to fetch RPM", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("rpm", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch cadence (for treadmills)
|
||||
try {
|
||||
long cadenceStartTime = System.currentTimeMillis();
|
||||
CadenceServiceGrpc.CadenceServiceBlockingStub cadenceStubWithHeaders = cadenceStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
long cadenceInterceptorTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Cadence interceptor setup took: " + (cadenceInterceptorTime - cadenceStartTime) + "ms");
|
||||
|
||||
CadenceMetric cadenceResponse = cadenceStubWithHeaders.getCadence(request);
|
||||
long cadenceCallTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Cadence gRPC call took: " + (cadenceCallTime - cadenceInterceptorTime) + "ms");
|
||||
|
||||
currentCadence = cadenceResponse.getLastStepsPerMinute();
|
||||
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onCadenceUpdated(currentCadence));
|
||||
}
|
||||
long cadenceEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Cadence total processing took: " + (cadenceEndTime - cadenceStartTime) + "ms");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Failed to fetch cadence", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("cadence", "Error"));
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch fan speed
|
||||
/*
|
||||
try {
|
||||
long fanStartTime = System.currentTimeMillis();
|
||||
FanStateServiceGrpc.FanStateServiceBlockingStub fanStubWithHeaders = fanStub.withInterceptors(
|
||||
MetadataUtils.newAttachHeadersInterceptor(headers)
|
||||
);
|
||||
long fanInterceptorTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Fan interceptor setup took: " + (fanInterceptorTime - fanStartTime) + "ms");
|
||||
|
||||
FanStateMessage fanResponse = fanStubWithHeaders.getFanState(request);
|
||||
long fanCallTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Fan gRPC call took: " + (fanCallTime - fanInterceptorTime) + "ms");
|
||||
|
||||
int fanSpeed;
|
||||
switch (fanResponse.getState()) {
|
||||
case FAN_STATE_OFF:
|
||||
fanSpeed = 0;
|
||||
break;
|
||||
case FAN_STATE_LOW:
|
||||
fanSpeed = 1;
|
||||
break;
|
||||
case FAN_STATE_MEDIUM:
|
||||
fanSpeed = 2;
|
||||
break;
|
||||
case FAN_STATE_HIGH:
|
||||
fanSpeed = 3;
|
||||
break;
|
||||
case FAN_STATE_AUTO:
|
||||
fanSpeed = 4;
|
||||
break;
|
||||
default:
|
||||
fanSpeed = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
currentFanSpeed = fanSpeed;
|
||||
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onFanSpeedUpdated(currentFanSpeed));
|
||||
}
|
||||
long fanEndTime = System.currentTimeMillis();
|
||||
QLog.d(TAG, "Fan total processing took: " + (fanEndTime - fanStartTime) + "ms");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Failed to fetch fan speed", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> metricsListener.onError("fan", "Error"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
long totalEndTime = System.currentTimeMillis();
|
||||
long totalTime = totalEndTime - startTime;
|
||||
QLog.d(TAG, "=== TIMING SUMMARY ===");
|
||||
QLog.d(TAG, "Total fetchAllMetricsFromServer execution time: " + totalTime + "ms");
|
||||
QLog.d(TAG, "Completed all metrics fetch");
|
||||
|
||||
} catch (Exception e) {
|
||||
long totalEndTime = System.currentTimeMillis();
|
||||
long totalTime = totalEndTime - startTime;
|
||||
QLog.e(TAG, "Failed to fetch metrics after " + totalTime + "ms", e);
|
||||
if (metricsListener != null) {
|
||||
mainHandler.post(() -> {
|
||||
metricsListener.onError("speed", "Error");
|
||||
metricsListener.onError("inclination", "Error");
|
||||
metricsListener.onError("watts", "Error");
|
||||
metricsListener.onError("resistance", "Error");
|
||||
metricsListener.onError("cadence", "Error");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Static wrapper methods for JNI calls
|
||||
public static void initialize() {
|
||||
initialize("localhost");
|
||||
}
|
||||
|
||||
public static void initialize(String host) {
|
||||
try {
|
||||
if (staticContext == null) {
|
||||
QLog.e(TAG, "Context not set. Call setContext() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
serverHost = host;
|
||||
|
||||
if (instance == null) {
|
||||
instance = new GrpcTreadmillService(staticContext);
|
||||
}
|
||||
|
||||
instance.initializeInstance();
|
||||
QLog.i(TAG, "Static initialize completed with host: " + host);
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Static initialize failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setContext(Context context) {
|
||||
staticContext = context;
|
||||
}
|
||||
|
||||
public static void startMetricsUpdates() {
|
||||
if (instance != null) {
|
||||
instance.startMetricsUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopMetricsUpdates() {
|
||||
if (instance != null) {
|
||||
instance.stopMetricsUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static double getCurrentSpeed() {
|
||||
if (instance != null) {
|
||||
return instance.currentSpeed;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public static double getCurrentIncline() {
|
||||
if (instance != null) {
|
||||
return instance.currentIncline;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public static double getCurrentWatts() {
|
||||
if (instance != null) {
|
||||
return instance.currentWatts;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public static double getCurrentCadence() {
|
||||
if (instance != null) {
|
||||
return instance.currentCadence;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public static double getCurrentRpm() {
|
||||
if (instance != null) {
|
||||
return instance.currentRpm;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public static int getCurrentFanSpeed() {
|
||||
if (instance != null) {
|
||||
return instance.currentFanSpeed;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static double getCurrentResistance() {
|
||||
if (instance != null) {
|
||||
return instance.currentResistance;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public static void adjustSpeed(double delta) {
|
||||
if (instance != null) {
|
||||
instance.adjustSpeedInstance(delta);
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void adjustIncline(double delta) {
|
||||
if (instance != null) {
|
||||
instance.adjustInclineInstance(delta);
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void adjustResistance(double delta) {
|
||||
if (instance != null) {
|
||||
instance.adjustResistanceInstance(delta);
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setWatts(double watts) {
|
||||
if (instance != null) {
|
||||
instance.setWattsInstance(watts);
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableConstantWatts() {
|
||||
if (instance != null) {
|
||||
instance.disableConstantWattsInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setFanSpeed(int fanSpeed) {
|
||||
if (instance != null) {
|
||||
instance.setFanSpeedInstance(fanSpeed);
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
if (instance != null) {
|
||||
instance.shutdownInstance();
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
295
src/android/src/PelotonCallbackSensor.java
Normal file
295
src/android/src/PelotonCallbackSensor.java
Normal file
@@ -0,0 +1,295 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import org.cagnulen.qdomyoszwift.QLog;
|
||||
|
||||
/**
|
||||
* Callback-based sensor implementation from Grupetto v1 develop
|
||||
* Based on: https://github.com/selalipop/grupetto/pull/10
|
||||
* More efficient than polling - receives data only when it changes
|
||||
*/
|
||||
public abstract class PelotonCallbackSensor {
|
||||
|
||||
private static final String TAG = "PelotonCallbackSensor";
|
||||
|
||||
// Transaction codes from Grupetto v1 CallbackSensor.kt
|
||||
private static final int TRANSACTION_REGISTER_CALLBACK = 1;
|
||||
private static final int TRANSACTION_UNREGISTER_CALLBACK = 2;
|
||||
|
||||
// Interface descriptors from Grupetto v1
|
||||
private static final String IV1_INTERFACE = "com.onepeloton.affernetservice.IV1Interface";
|
||||
private static final String IV1_CALLBACK_INTERFACE = "com.onepeloton.affernetservice.IV1Callback";
|
||||
|
||||
private IBinder binder;
|
||||
private boolean isRegistered = false;
|
||||
private PelotonCallbackBinder callbackBinder;
|
||||
|
||||
// Callback interface for receiving sensor data
|
||||
public interface SensorDataCallback {
|
||||
void onSensorDataReceived(float value);
|
||||
void onSensorError(long errorCode);
|
||||
}
|
||||
|
||||
private SensorDataCallback callback;
|
||||
|
||||
public PelotonCallbackSensor(IBinder binder) {
|
||||
this.binder = binder;
|
||||
this.callbackBinder = new PelotonCallbackBinder();
|
||||
}
|
||||
|
||||
public void setCallback(SensorDataCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public void start() throws RemoteException {
|
||||
if (isRegistered) {
|
||||
QLog.w(TAG, "Sensor already started");
|
||||
return;
|
||||
}
|
||||
|
||||
registerCallback();
|
||||
isRegistered = true;
|
||||
QLog.d(TAG, "Callback sensor started successfully");
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (!isRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
unregisterCallback();
|
||||
isRegistered = false;
|
||||
QLog.d(TAG, "Callback sensor stopped successfully");
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to stop callback sensor", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCallback() throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
try {
|
||||
data.writeInterfaceToken(IV1_INTERFACE);
|
||||
data.writeStrongBinder(callbackBinder);
|
||||
data.writeString("QDomyos-Zwift"); // Identifier like Grupetto
|
||||
|
||||
QLog.d(TAG, "Registering callback with interface: " + IV1_INTERFACE);
|
||||
boolean success = binder.transact(TRANSACTION_REGISTER_CALLBACK, data, reply, 0);
|
||||
if (success) {
|
||||
reply.readException();
|
||||
QLog.i(TAG, "Successfully registered callback");
|
||||
} else {
|
||||
throw new RemoteException("Failed to register callback");
|
||||
}
|
||||
} finally {
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterCallback() throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
try {
|
||||
data.writeInterfaceToken(IV1_INTERFACE);
|
||||
data.writeStrongBinder(callbackBinder);
|
||||
data.writeString("QDomyos-Zwift"); // Identifier like Grupetto
|
||||
|
||||
boolean success = binder.transact(TRANSACTION_UNREGISTER_CALLBACK, data, reply, 0);
|
||||
if (success) {
|
||||
reply.readException();
|
||||
QLog.d(TAG, "Successfully unregistered callback");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Error unregistering callback", e);
|
||||
} finally {
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the specific sensor value from BikeData
|
||||
* Override in subclasses for different sensor types
|
||||
*/
|
||||
protected abstract float extractValue(BikeData bikeData);
|
||||
|
||||
/**
|
||||
* Apply sensor-specific value mapping
|
||||
* Override in subclasses if needed
|
||||
*/
|
||||
protected float mapValue(float rawValue) {
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binder implementation for receiving callbacks from Peloton service
|
||||
*/
|
||||
private class PelotonCallbackBinder extends android.os.Binder {
|
||||
|
||||
@Override
|
||||
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
QLog.d(TAG, "Callback onTransact called with code: " + code);
|
||||
|
||||
switch (code) {
|
||||
case 1: // onSensorDataChange
|
||||
try {
|
||||
data.enforceInterface(IV1_CALLBACK_INTERFACE);
|
||||
QLog.d(TAG, "Interface enforced successfully");
|
||||
|
||||
int hasData = data.readInt();
|
||||
QLog.d(TAG, "Has data flag: " + hasData);
|
||||
|
||||
if (hasData != 0) {
|
||||
QLog.d(TAG, "Creating BikeData from parcel");
|
||||
BikeData bikeData = BikeData.CREATOR.createFromParcel(data);
|
||||
|
||||
float rawValue = extractValue(bikeData);
|
||||
float mappedValue = mapValue(rawValue);
|
||||
|
||||
if (callback != null) {
|
||||
callback.onSensorDataReceived(mappedValue);
|
||||
}
|
||||
|
||||
QLog.i(TAG, "Received sensor data: " + mappedValue);
|
||||
} else {
|
||||
QLog.d(TAG, "No bike data received");
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Error processing sensor data", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
case 2: // onSensorError
|
||||
try {
|
||||
data.enforceInterface(IV1_CALLBACK_INTERFACE);
|
||||
long errorCode = data.readLong();
|
||||
QLog.w(TAG, "Sensor error: " + errorCode);
|
||||
|
||||
if (callback != null) {
|
||||
callback.onSensorError(errorCode);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Error processing sensor error", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
case 3: // onCalibrationStatus
|
||||
try {
|
||||
data.enforceInterface(IV1_CALLBACK_INTERFACE);
|
||||
int status = data.readInt();
|
||||
boolean success = data.readInt() != 0;
|
||||
long errorCode = data.readLong();
|
||||
QLog.d(TAG, "Calibration status: status=" + status + " success=" + success + " error=" + errorCode);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Error processing calibration status", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
QLog.d(TAG, "Unknown transaction code: " + code + ", calling super");
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Power sensor implementation
|
||||
*/
|
||||
public static class PowerSensor extends PelotonCallbackSensor {
|
||||
|
||||
public PowerSensor(IBinder binder) {
|
||||
super(binder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float extractValue(BikeData bikeData) {
|
||||
return (float) bikeData.getPower();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float mapValue(float rawValue) {
|
||||
// From Grupetto v1: divide by 100 to normalize power values
|
||||
float normalizedValue = rawValue / 100.0f;
|
||||
|
||||
// Filter out spurious readings
|
||||
if (normalizedValue < 0 || normalizedValue > 1000) {
|
||||
QLog.w(TAG, "Filtering spurious power reading: " + normalizedValue);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return normalizedValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RPM sensor implementation
|
||||
*/
|
||||
public static class RpmSensor extends PelotonCallbackSensor {
|
||||
|
||||
public RpmSensor(IBinder binder) {
|
||||
super(binder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float extractValue(BikeData bikeData) {
|
||||
return (float) bikeData.getRPM();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resistance sensor implementation with moving window filtering
|
||||
*/
|
||||
public static class ResistanceSensor extends PelotonCallbackSensor {
|
||||
|
||||
// Moving window for resistance filtering (from Grupetto approach)
|
||||
private static final int FILTER_WINDOW_SIZE = 3;
|
||||
private float[] resistanceWindow = new float[FILTER_WINDOW_SIZE];
|
||||
private int windowIndex = 0;
|
||||
private boolean windowFilled = false;
|
||||
|
||||
public ResistanceSensor(IBinder binder) {
|
||||
super(binder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float extractValue(BikeData bikeData) {
|
||||
return (float) bikeData.getTargetResistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float mapValue(float rawValue) {
|
||||
// Add value to moving window
|
||||
resistanceWindow[windowIndex] = rawValue;
|
||||
windowIndex = (windowIndex + 1) % FILTER_WINDOW_SIZE;
|
||||
|
||||
if (!windowFilled && windowIndex == 0) {
|
||||
windowFilled = true;
|
||||
}
|
||||
|
||||
// If window not full yet, return current value
|
||||
if (!windowFilled) {
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
// Return minimum value from window (Grupetto strategy for spike filtering)
|
||||
float minValue = resistanceWindow[0];
|
||||
for (int i = 1; i < FILTER_WINDOW_SIZE; i++) {
|
||||
if (resistanceWindow[i] < minValue) {
|
||||
minValue = resistanceWindow[i];
|
||||
}
|
||||
}
|
||||
|
||||
return minValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/android/src/PelotonSensorBinder.java
Normal file
106
src/android/src/PelotonSensorBinder.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.cagnulen.qdomyoszwift.QLog;
|
||||
|
||||
/**
|
||||
* Service binder for connecting to Peloton Grupetto v1 callback-based service
|
||||
* Based on: https://github.com/selalipop/grupetto/pull/10
|
||||
* More efficient than polling - receives data only when it changes
|
||||
*/
|
||||
public class PelotonSensorBinder {
|
||||
|
||||
private static final String TAG = "PelotonSensorBinder";
|
||||
|
||||
// Peloton service constants (from Grupetto v1 develop - callback-based)
|
||||
private static final String SERVICE_ACTION = "com.onepeloton.affernetservice.IV1Interface";
|
||||
private static final String SERVICE_PACKAGE = "com.onepeloton.affernetservice";
|
||||
private static final String SERVICE_INTENT = "com.onepeloton.affernetservice.AffernetService";
|
||||
|
||||
// Using callback-based sensors from Grupetto v1 develop
|
||||
// No transaction codes needed here - handled by PelotonCallbackSensor
|
||||
|
||||
private Context context;
|
||||
private IBinder serviceBinder = null;
|
||||
private boolean isConnected = false;
|
||||
|
||||
public PelotonSensorBinder(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously connects to the Peloton sensor service
|
||||
* Based on Grupetto's v1 Binder.kt implementation
|
||||
*/
|
||||
public CompletableFuture<IBinder> getBinder() {
|
||||
CompletableFuture<IBinder> future = new CompletableFuture<>();
|
||||
CountDownLatch connectionLatch = new CountDownLatch(1);
|
||||
|
||||
ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
QLog.i(TAG, "V1 service connected: " + name.getClassName());
|
||||
serviceBinder = service;
|
||||
isConnected = true;
|
||||
future.complete(service);
|
||||
connectionLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
QLog.w(TAG, "V1 service disconnected: " + name.getClassName());
|
||||
serviceBinder = null;
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindingDied(ComponentName name) {
|
||||
QLog.e(TAG, "V1 service binding died: " + name.getClassName());
|
||||
serviceBinder = null;
|
||||
isConnected = false;
|
||||
if (!future.isDone()) {
|
||||
future.completeExceptionally(new RuntimeException("V1 service binding died"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNullBinding(ComponentName name) {
|
||||
QLog.i(TAG, "V1 service null binding: " + name.getClassName());
|
||||
if (!future.isDone()) {
|
||||
future.completeExceptionally(new RuntimeException("V1 service null binding"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Intent intent = new Intent(SERVICE_INTENT);
|
||||
intent.setAction(SERVICE_ACTION);
|
||||
intent.setPackage(SERVICE_PACKAGE);
|
||||
|
||||
boolean bound = context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
|
||||
if (!bound) {
|
||||
QLog.e(TAG, "Failed to bind to Peloton V1 sensor service");
|
||||
future.completeExceptionally(new RuntimeException("Failed to bind to V1 service"));
|
||||
return future;
|
||||
}
|
||||
|
||||
QLog.i(TAG, "Binding to Peloton V1 sensor service...");
|
||||
return future;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
public IBinder getServiceBinder() {
|
||||
return serviceBinder;
|
||||
}
|
||||
}
|
||||
279
src/android/src/PelotonSensorHelper.java
Normal file
279
src/android/src/PelotonSensorHelper.java
Normal file
@@ -0,0 +1,279 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.cagnulen.qdomyoszwift.QLog;
|
||||
import org.cagnulen.qdomyoszwift.PelotonSensorBinder;
|
||||
import org.cagnulen.qdomyoszwift.PelotonCallbackSensor;
|
||||
|
||||
/**
|
||||
* Peloton sensor helper class using callback-based approach from Grupetto v1 develop
|
||||
* Based on: https://github.com/selalipop/grupetto/pull/10
|
||||
* More efficient than polling - receives data only when it changes
|
||||
*/
|
||||
public class PelotonSensorHelper {
|
||||
|
||||
private static final String TAG = "PelotonSensorHelper";
|
||||
|
||||
// Singleton instance for static access
|
||||
private static PelotonSensorHelper instance = null;
|
||||
private static Context staticContext = null;
|
||||
|
||||
// Threading components (reduced need with callback approach)
|
||||
private Handler mainHandler;
|
||||
private ExecutorService executorService;
|
||||
|
||||
// Sensor components (callback-based from Grupetto v1)
|
||||
private PelotonSensorBinder sensorBinder;
|
||||
private PelotonCallbackSensor.PowerSensor powerSensor;
|
||||
private PelotonCallbackSensor.RpmSensor rpmSensor;
|
||||
private PelotonCallbackSensor.ResistanceSensor resistanceSensor;
|
||||
|
||||
// Control flags and current values
|
||||
private volatile boolean isInitialized = false;
|
||||
private volatile boolean isUpdating = false;
|
||||
private volatile float currentPower = 0.0f;
|
||||
private volatile float currentCadence = 0.0f;
|
||||
private volatile float currentResistance = 0.0f;
|
||||
private volatile float currentSpeed = 0.0f;
|
||||
|
||||
// Context for accessing system services
|
||||
private Context context;
|
||||
|
||||
public PelotonSensorHelper(Context context) {
|
||||
this.context = context;
|
||||
this.mainHandler = new Handler(Looper.getMainLooper());
|
||||
this.executorService = Executors.newSingleThreadExecutor();
|
||||
this.sensorBinder = new PelotonSensorBinder(context);
|
||||
}
|
||||
|
||||
private void initializeInstance() throws Exception {
|
||||
QLog.i(TAG, "Initializing Peloton sensor connection...");
|
||||
|
||||
// Get binder to Peloton service (async operation)
|
||||
IBinder serviceBinder = sensorBinder.getBinder().get(10, TimeUnit.SECONDS);
|
||||
|
||||
if (serviceBinder == null) {
|
||||
throw new Exception("Failed to get service binder");
|
||||
}
|
||||
|
||||
// Initialize individual callback-based sensors
|
||||
powerSensor = new PelotonCallbackSensor.PowerSensor(serviceBinder);
|
||||
rpmSensor = new PelotonCallbackSensor.RpmSensor(serviceBinder);
|
||||
resistanceSensor = new PelotonCallbackSensor.ResistanceSensor(serviceBinder);
|
||||
|
||||
// Set up callbacks to receive sensor data
|
||||
powerSensor.setCallback(new PelotonCallbackSensor.SensorDataCallback() {
|
||||
@Override
|
||||
public void onSensorDataReceived(float value) {
|
||||
currentPower = value;
|
||||
currentSpeed = calculateSpeedFromPelotonV1Power(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorError(long errorCode) {
|
||||
QLog.w(TAG, "Power sensor error: " + errorCode);
|
||||
}
|
||||
});
|
||||
|
||||
rpmSensor.setCallback(new PelotonCallbackSensor.SensorDataCallback() {
|
||||
@Override
|
||||
public void onSensorDataReceived(float value) {
|
||||
currentCadence = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorError(long errorCode) {
|
||||
QLog.w(TAG, "RPM sensor error: " + errorCode);
|
||||
}
|
||||
});
|
||||
|
||||
resistanceSensor.setCallback(new PelotonCallbackSensor.SensorDataCallback() {
|
||||
@Override
|
||||
public void onSensorDataReceived(float value) {
|
||||
currentResistance = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorError(long errorCode) {
|
||||
QLog.w(TAG, "Resistance sensor error: " + errorCode);
|
||||
}
|
||||
});
|
||||
|
||||
isInitialized = true;
|
||||
QLog.i(TAG, "Peloton sensor initialization completed");
|
||||
}
|
||||
|
||||
private void startSensorUpdatesInstance() {
|
||||
if (isUpdating || !isInitialized) {
|
||||
QLog.w(TAG, "Cannot start sensor updates - not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
isUpdating = true;
|
||||
|
||||
try {
|
||||
// Start callback-based sensors (no polling needed)
|
||||
if (powerSensor != null) powerSensor.start();
|
||||
if (rpmSensor != null) rpmSensor.start();
|
||||
if (resistanceSensor != null) resistanceSensor.start();
|
||||
|
||||
QLog.i(TAG, "Started callback-based sensor updates");
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to start sensor updates", e);
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopSensorUpdatesInstance() {
|
||||
isUpdating = false;
|
||||
|
||||
// Stop callback-based sensors
|
||||
if (powerSensor != null) powerSensor.stop();
|
||||
if (rpmSensor != null) rpmSensor.stop();
|
||||
if (resistanceSensor != null) resistanceSensor.stop();
|
||||
|
||||
QLog.i(TAG, "Stopped callback-based sensor updates");
|
||||
}
|
||||
|
||||
// Sensor values are now updated via callbacks - no polling needed
|
||||
|
||||
/**
|
||||
* Calculate speed from power using Peloton V1 bike formula
|
||||
* Based on Grupetto's SensorInterface.kt implementation
|
||||
*/
|
||||
private float calculateSpeedFromPelotonV1Power(float power) {
|
||||
if (power < 0.1f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Use exact formula from Grupetto Peloton.kt
|
||||
double pwrSqrt = Math.sqrt(power);
|
||||
if (power < 26f) {
|
||||
return (float)(0.057f - (0.172f * pwrSqrt) + (0.759f * Math.pow(pwrSqrt, 2)) - (0.079f * Math.pow(pwrSqrt, 3)));
|
||||
} else {
|
||||
return (float)(-1.635f + (2.325f * pwrSqrt) - (0.064f * Math.pow(pwrSqrt, 2)) + (0.001f * Math.pow(pwrSqrt, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownInstance() {
|
||||
stopSensorUpdates();
|
||||
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
try {
|
||||
if (!executorService.awaitTermination(2, TimeUnit.SECONDS)) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
QLog.e(TAG, "Error shutting down executor service", e);
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up sensors
|
||||
powerSensor = null;
|
||||
rpmSensor = null;
|
||||
resistanceSensor = null;
|
||||
sensorBinder = null;
|
||||
|
||||
isInitialized = false;
|
||||
}
|
||||
|
||||
// Static wrapper methods for JNI calls
|
||||
public static void initialize() {
|
||||
try {
|
||||
if (staticContext == null) {
|
||||
QLog.e(TAG, "Context not set. Call setContext() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance == null) {
|
||||
instance = new PelotonSensorHelper(staticContext);
|
||||
}
|
||||
|
||||
instance.initializeInstance();
|
||||
QLog.i(TAG, "Static initialize completed");
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Static initialize failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setContext(Context context) {
|
||||
staticContext = context;
|
||||
}
|
||||
|
||||
public static void startSensorUpdates() {
|
||||
if (instance != null) {
|
||||
instance.startSensorUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Helper not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopSensorUpdates() {
|
||||
if (instance != null) {
|
||||
instance.stopSensorUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Helper not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
// Getter methods for current sensor values
|
||||
public static float getCurrentPower() {
|
||||
if (instance != null) {
|
||||
return instance.currentPower;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentCadence() {
|
||||
if (instance != null) {
|
||||
return instance.currentCadence;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentResistance() {
|
||||
if (instance != null) {
|
||||
return instance.currentResistance;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentSpeed() {
|
||||
if (instance != null) {
|
||||
return instance.currentSpeed;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static boolean isConnected() {
|
||||
if (instance != null && instance.sensorBinder != null) {
|
||||
return instance.sensorBinder.isConnected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isInitialized() {
|
||||
if (instance != null) {
|
||||
return instance.isInitialized;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
if (instance != null) {
|
||||
instance.shutdownInstance();
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
286
src/android/src/PelotonSensorHelperV1.java
Normal file
286
src/android/src/PelotonSensorHelperV1.java
Normal file
@@ -0,0 +1,286 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.cagnulen.qdomyoszwift.QLog;
|
||||
import org.cagnulen.qdomyoszwift.PelotonSensorBinder;
|
||||
import org.cagnulen.qdomyoszwift.PelotonCallbackSensor;
|
||||
|
||||
/**
|
||||
* Peloton sensor helper class using callback-based approach from Grupetto v1 develop
|
||||
* Based on: https://github.com/selalipop/grupetto/pull/10
|
||||
* More efficient than polling - receives data only when it changes
|
||||
*/
|
||||
public class PelotonSensorHelperV1 {
|
||||
|
||||
private static final String TAG = "PelotonSensorHelperV1";
|
||||
|
||||
// Singleton instance for static access
|
||||
private static PelotonSensorHelperV1 instance = null;
|
||||
private static Context staticContext = null;
|
||||
|
||||
// Threading components (reduced need with callback approach)
|
||||
private Handler mainHandler;
|
||||
private ExecutorService executorService;
|
||||
|
||||
// Sensor components (callback-based from Grupetto v1)
|
||||
private PelotonSensorBinder sensorBinder;
|
||||
private PelotonCallbackSensor.PowerSensor powerSensor;
|
||||
private PelotonCallbackSensor.RpmSensor rpmSensor;
|
||||
private PelotonCallbackSensor.ResistanceSensor resistanceSensor;
|
||||
|
||||
// Control flags and current values
|
||||
private volatile boolean isInitialized = false;
|
||||
private volatile boolean isUpdating = false;
|
||||
private volatile float currentPower = 0.0f;
|
||||
private volatile float currentCadence = 0.0f;
|
||||
private volatile float currentResistance = 0.0f;
|
||||
private volatile float currentSpeed = 0.0f;
|
||||
|
||||
// Context for accessing system services
|
||||
private Context context;
|
||||
|
||||
public PelotonSensorHelperV1(Context context) {
|
||||
this.context = context;
|
||||
this.mainHandler = new Handler(Looper.getMainLooper());
|
||||
this.executorService = Executors.newSingleThreadExecutor();
|
||||
this.sensorBinder = new PelotonSensorBinder(context);
|
||||
}
|
||||
|
||||
private void initializeInstance() throws Exception {
|
||||
QLog.i(TAG, "Initializing Peloton V1 callback sensor connection...");
|
||||
|
||||
// Get binder to Peloton service (async operation)
|
||||
IBinder serviceBinder = sensorBinder.getBinder().get(10, TimeUnit.SECONDS);
|
||||
|
||||
if (serviceBinder == null) {
|
||||
throw new Exception("Failed to get service binder");
|
||||
}
|
||||
|
||||
// Initialize individual callback-based sensors
|
||||
powerSensor = new PelotonCallbackSensor.PowerSensor(serviceBinder);
|
||||
rpmSensor = new PelotonCallbackSensor.RpmSensor(serviceBinder);
|
||||
resistanceSensor = new PelotonCallbackSensor.ResistanceSensor(serviceBinder);
|
||||
|
||||
// Set up callbacks to receive sensor data
|
||||
powerSensor.setCallback(new PelotonCallbackSensor.SensorDataCallback() {
|
||||
@Override
|
||||
public void onSensorDataReceived(float value) {
|
||||
currentPower = value;
|
||||
currentSpeed = calculateSpeedFromPelotonV1Power(value);
|
||||
QLog.d(TAG, "Power updated: " + value + "W, Speed: " + currentSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorError(long errorCode) {
|
||||
QLog.w(TAG, "Power sensor error: " + errorCode);
|
||||
}
|
||||
});
|
||||
|
||||
rpmSensor.setCallback(new PelotonCallbackSensor.SensorDataCallback() {
|
||||
@Override
|
||||
public void onSensorDataReceived(float value) {
|
||||
currentCadence = value;
|
||||
QLog.d(TAG, "Cadence updated: " + value + " RPM");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorError(long errorCode) {
|
||||
QLog.w(TAG, "RPM sensor error: " + errorCode);
|
||||
}
|
||||
});
|
||||
|
||||
resistanceSensor.setCallback(new PelotonCallbackSensor.SensorDataCallback() {
|
||||
@Override
|
||||
public void onSensorDataReceived(float value) {
|
||||
currentResistance = value;
|
||||
QLog.d(TAG, "Resistance updated: " + value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorError(long errorCode) {
|
||||
QLog.w(TAG, "Resistance sensor error: " + errorCode);
|
||||
}
|
||||
});
|
||||
|
||||
isInitialized = true;
|
||||
QLog.i(TAG, "Peloton V1 callback sensor initialization completed");
|
||||
}
|
||||
|
||||
private void startSensorUpdatesInstance() {
|
||||
if (isUpdating || !isInitialized) {
|
||||
QLog.w(TAG, "Cannot start sensor updates - not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
isUpdating = true;
|
||||
|
||||
try {
|
||||
// Start callback-based sensors (no polling needed)
|
||||
if (powerSensor != null) powerSensor.start();
|
||||
if (rpmSensor != null) rpmSensor.start();
|
||||
if (resistanceSensor != null) resistanceSensor.start();
|
||||
|
||||
QLog.i(TAG, "Started callback-based sensor updates");
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to start sensor updates", e);
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopSensorUpdatesInstance() {
|
||||
isUpdating = false;
|
||||
|
||||
// Stop callback-based sensors
|
||||
if (powerSensor != null) powerSensor.stop();
|
||||
if (rpmSensor != null) rpmSensor.stop();
|
||||
if (resistanceSensor != null) resistanceSensor.stop();
|
||||
|
||||
QLog.i(TAG, "Stopped callback-based sensor updates");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate speed from power using Peloton V1 bike formula
|
||||
* Based on Grupetto's SensorInterface.kt implementation
|
||||
*/
|
||||
private float calculateSpeedFromPelotonV1Power(float power) {
|
||||
if (power < 0.1f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Use exact formula from Grupetto Peloton.kt
|
||||
double pwrSqrt = Math.sqrt(power);
|
||||
if (power < 26f) {
|
||||
return (float)(0.057f - (0.172f * pwrSqrt) + (0.759f * Math.pow(pwrSqrt, 2)) - (0.079f * Math.pow(pwrSqrt, 3)));
|
||||
} else {
|
||||
return (float)(-1.635f + (2.325f * pwrSqrt) - (0.064f * Math.pow(pwrSqrt, 2)) + (0.001f * Math.pow(pwrSqrt, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownInstance() {
|
||||
stopSensorUpdates();
|
||||
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
try {
|
||||
if (!executorService.awaitTermination(2, TimeUnit.SECONDS)) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
QLog.e(TAG, "Error shutting down executor service", e);
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up sensors
|
||||
powerSensor = null;
|
||||
rpmSensor = null;
|
||||
resistanceSensor = null;
|
||||
sensorBinder = null;
|
||||
|
||||
isInitialized = false;
|
||||
}
|
||||
|
||||
// Static wrapper methods for JNI calls
|
||||
public static void initialize() {
|
||||
try {
|
||||
if (staticContext == null) {
|
||||
QLog.e(TAG, "Context not set. Call setContext() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance == null) {
|
||||
instance = new PelotonSensorHelperV1(staticContext);
|
||||
}
|
||||
|
||||
instance.initializeInstance();
|
||||
QLog.i(TAG, "Static V1 initialize completed");
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Peloton V1 service not available - continuing without sensor integration: " + e.getMessage());
|
||||
// Create instance anyway to provide fallback behavior
|
||||
if (instance == null) {
|
||||
instance = new PelotonSensorHelperV1(staticContext);
|
||||
}
|
||||
// Mark as not initialized but don't crash the app
|
||||
instance.isInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setContext(Context context) {
|
||||
staticContext = context;
|
||||
}
|
||||
|
||||
public static void startSensorUpdates() {
|
||||
if (instance != null) {
|
||||
instance.startSensorUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Helper not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopSensorUpdates() {
|
||||
if (instance != null) {
|
||||
instance.stopSensorUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Helper not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
// Getter methods for current sensor values
|
||||
public static float getCurrentPower() {
|
||||
if (instance != null) {
|
||||
return instance.currentPower;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentCadence() {
|
||||
if (instance != null) {
|
||||
return instance.currentCadence;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentResistance() {
|
||||
if (instance != null) {
|
||||
return instance.currentResistance;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentSpeed() {
|
||||
if (instance != null) {
|
||||
return instance.currentSpeed;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static boolean isConnected() {
|
||||
if (instance != null && instance.sensorBinder != null) {
|
||||
return instance.sensorBinder.isConnected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isInitialized() {
|
||||
if (instance != null) {
|
||||
return instance.isInitialized;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
if (instance != null) {
|
||||
instance.shutdownInstance();
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
379
src/android/src/PelotonSensorService.java
Normal file
379
src/android/src/PelotonSensorService.java
Normal file
@@ -0,0 +1,379 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.ComponentName;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.cagnulen.qdomyoszwift.QLog;
|
||||
|
||||
public class PelotonSensorService {
|
||||
|
||||
private static final String TAG = "PelotonSensorService";
|
||||
|
||||
// Singleton instance for static access
|
||||
private static PelotonSensorService instance = null;
|
||||
private static Context staticContext = null;
|
||||
|
||||
// Peloton service action and permissions
|
||||
private static final String PELOTON_SENSOR_ACTION = "android.intent.action.peloton.SensorData";
|
||||
private static final String PELOTON_SENSOR_PERMISSION = "onepeloton.permission.ACCESS_SENSOR_SERVICE";
|
||||
|
||||
// Update interval for sensor reading
|
||||
private static final int SENSOR_UPDATE_INTERVAL_MS = 200;
|
||||
|
||||
// Threading components
|
||||
private Handler mainHandler;
|
||||
private ExecutorService executorService;
|
||||
private Runnable sensorUpdateRunnable;
|
||||
|
||||
// Service connection components
|
||||
private IBinder sensorBinder = null;
|
||||
private boolean isServiceConnected = false;
|
||||
private boolean isUpdating = false;
|
||||
|
||||
// Sensor components (similar to Grupetto's implementation)
|
||||
private PelotonPowerSensor powerSensor;
|
||||
private PelotonRpmSensor rpmSensor;
|
||||
private PelotonResistanceSensor resistanceSensor;
|
||||
|
||||
// Current sensor values
|
||||
private volatile float currentPower = 0.0f;
|
||||
private volatile float currentCadence = 0.0f;
|
||||
private volatile float currentResistance = 0.0f;
|
||||
private volatile float currentSpeed = 0.0f;
|
||||
|
||||
// Context for service binding
|
||||
private Context context;
|
||||
|
||||
public PelotonSensorService(Context context) {
|
||||
this.context = context;
|
||||
this.mainHandler = new Handler(Looper.getMainLooper());
|
||||
this.executorService = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
private void initializeInstance() throws Exception {
|
||||
QLog.i(TAG, "Initializing Peloton sensor service connection...");
|
||||
|
||||
// Check if required permission is available
|
||||
if (context.checkSelfPermission(PELOTON_SENSOR_PERMISSION) !=
|
||||
android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
throw new Exception("Missing required permission: " + PELOTON_SENSOR_PERMISSION);
|
||||
}
|
||||
|
||||
// Connect to Peloton sensor service
|
||||
connectToSensorService();
|
||||
}
|
||||
|
||||
private void connectToSensorService() throws Exception {
|
||||
QLog.i(TAG, "Attempting to connect to Peloton sensor service...");
|
||||
|
||||
CompletableFuture<IBinder> binderFuture = new CompletableFuture<>();
|
||||
CountDownLatch connectionLatch = new CountDownLatch(1);
|
||||
|
||||
ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
QLog.i(TAG, "Connected to Peloton sensor service");
|
||||
sensorBinder = service;
|
||||
isServiceConnected = true;
|
||||
binderFuture.complete(service);
|
||||
connectionLatch.countDown();
|
||||
|
||||
// Initialize sensor components
|
||||
try {
|
||||
initializeSensors();
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Failed to initialize sensors", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
QLog.w(TAG, "Disconnected from Peloton sensor service");
|
||||
sensorBinder = null;
|
||||
isServiceConnected = false;
|
||||
isUpdating = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindingDied(ComponentName name) {
|
||||
QLog.e(TAG, "Peloton sensor service binding died");
|
||||
sensorBinder = null;
|
||||
isServiceConnected = false;
|
||||
isUpdating = false;
|
||||
}
|
||||
};
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(PELOTON_SENSOR_ACTION);
|
||||
|
||||
boolean bound = context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
|
||||
if (!bound) {
|
||||
throw new Exception("Failed to bind to Peloton sensor service");
|
||||
}
|
||||
|
||||
// Wait for connection with timeout
|
||||
try {
|
||||
if (!connectionLatch.await(10, TimeUnit.SECONDS)) {
|
||||
throw new Exception("Timeout waiting for Peloton sensor service connection");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new Exception("Interrupted while waiting for service connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeSensors() throws Exception {
|
||||
if (sensorBinder == null) {
|
||||
throw new Exception("Service binder not available");
|
||||
}
|
||||
|
||||
// Initialize individual sensor components (similar to Grupetto approach)
|
||||
powerSensor = new PelotonPowerSensor(sensorBinder);
|
||||
rpmSensor = new PelotonRpmSensor(sensorBinder);
|
||||
resistanceSensor = new PelotonResistanceSensor(sensorBinder);
|
||||
|
||||
QLog.i(TAG, "All sensors initialized successfully");
|
||||
}
|
||||
|
||||
private void startSensorUpdatesInstance() {
|
||||
if (isUpdating || !isServiceConnected) {
|
||||
QLog.w(TAG, "Cannot start sensor updates - service not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
isUpdating = true;
|
||||
|
||||
sensorUpdateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isUpdating || !isServiceConnected) return;
|
||||
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
// Read all sensor values
|
||||
if (powerSensor != null) {
|
||||
currentPower = powerSensor.readValue();
|
||||
}
|
||||
if (rpmSensor != null) {
|
||||
currentCadence = rpmSensor.readValue();
|
||||
}
|
||||
if (resistanceSensor != null) {
|
||||
currentResistance = resistanceSensor.readValue();
|
||||
}
|
||||
|
||||
// Calculate speed from power (similar to Grupetto approach)
|
||||
currentSpeed = calculateSpeedFromPower(currentPower);
|
||||
|
||||
} catch (Exception e) {
|
||||
QLog.w(TAG, "Error reading sensor values", e);
|
||||
}
|
||||
|
||||
if (isUpdating && isServiceConnected) {
|
||||
mainHandler.postDelayed(sensorUpdateRunnable, SENSOR_UPDATE_INTERVAL_MS);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
mainHandler.post(sensorUpdateRunnable);
|
||||
QLog.i(TAG, "Started periodic sensor updates");
|
||||
}
|
||||
|
||||
private void stopSensorUpdatesInstance() {
|
||||
isUpdating = false;
|
||||
|
||||
if (sensorUpdateRunnable != null) {
|
||||
mainHandler.removeCallbacks(sensorUpdateRunnable);
|
||||
}
|
||||
|
||||
QLog.i(TAG, "Stopped periodic sensor updates");
|
||||
}
|
||||
|
||||
private float calculateSpeedFromPower(float power) {
|
||||
if (power < 0.1f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Use exact formula from Grupetto Peloton.kt
|
||||
double pwrSqrt = Math.sqrt(power);
|
||||
if (power < 26f) {
|
||||
return (float)(0.057f - (0.172f * pwrSqrt) + (0.759f * Math.pow(pwrSqrt, 2)) - (0.079f * Math.pow(pwrSqrt, 3)));
|
||||
} else {
|
||||
return (float)(-1.635f + (2.325f * pwrSqrt) - (0.064f * Math.pow(pwrSqrt, 2)) + (0.001f * Math.pow(pwrSqrt, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownInstance() {
|
||||
stopSensorUpdates();
|
||||
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
try {
|
||||
if (!executorService.awaitTermination(2, TimeUnit.SECONDS)) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
QLog.e(TAG, "Error shutting down executor service", e);
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup sensors
|
||||
powerSensor = null;
|
||||
rpmSensor = null;
|
||||
resistanceSensor = null;
|
||||
|
||||
// Unbind from service
|
||||
if (isServiceConnected && context != null) {
|
||||
try {
|
||||
// Note: In real implementation, we'd need to properly unbind
|
||||
// context.unbindService(serviceConnection);
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Error unbinding service", e);
|
||||
}
|
||||
}
|
||||
|
||||
isServiceConnected = false;
|
||||
sensorBinder = null;
|
||||
}
|
||||
|
||||
// Static wrapper methods for JNI calls (similar to GrpcTreadmillService)
|
||||
public static void initialize() {
|
||||
try {
|
||||
if (staticContext == null) {
|
||||
QLog.e(TAG, "Context not set. Call setContext() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance == null) {
|
||||
instance = new PelotonSensorService(staticContext);
|
||||
}
|
||||
|
||||
instance.initializeInstance();
|
||||
QLog.i(TAG, "Static initialize completed");
|
||||
} catch (Exception e) {
|
||||
QLog.e(TAG, "Static initialize failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setContext(Context context) {
|
||||
staticContext = context;
|
||||
}
|
||||
|
||||
public static void startSensorUpdates() {
|
||||
if (instance != null) {
|
||||
instance.startSensorUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopSensorUpdates() {
|
||||
if (instance != null) {
|
||||
instance.stopSensorUpdatesInstance();
|
||||
} else {
|
||||
QLog.e(TAG, "Service not initialized. Call initialize() first.");
|
||||
}
|
||||
}
|
||||
|
||||
// Getter methods for current sensor values
|
||||
public static float getCurrentPower() {
|
||||
if (instance != null) {
|
||||
return instance.currentPower;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentCadence() {
|
||||
if (instance != null) {
|
||||
return instance.currentCadence;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentResistance() {
|
||||
if (instance != null) {
|
||||
return instance.currentResistance;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static float getCurrentSpeed() {
|
||||
if (instance != null) {
|
||||
return instance.currentSpeed;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public static boolean isConnected() {
|
||||
if (instance != null) {
|
||||
return instance.isServiceConnected;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
if (instance != null) {
|
||||
instance.shutdownInstance();
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Inner classes for individual sensors (simplified versions based on Grupetto)
|
||||
private static class PelotonPowerSensor {
|
||||
private IBinder binder;
|
||||
|
||||
public PelotonPowerSensor(IBinder binder) {
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
public float readValue() throws RemoteException {
|
||||
// Implementation would call into Peloton service via binder
|
||||
// This is a simplified version - actual implementation would need
|
||||
// proper AIDL interface definitions
|
||||
|
||||
// For now, return mock data or attempt basic binder calls
|
||||
// In real implementation, this would use proper service calls
|
||||
return 0.0f; // Placeholder
|
||||
}
|
||||
}
|
||||
|
||||
private static class PelotonRpmSensor {
|
||||
private IBinder binder;
|
||||
|
||||
public PelotonRpmSensor(IBinder binder) {
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
public float readValue() throws RemoteException {
|
||||
// Implementation would call into Peloton service via binder
|
||||
return 0.0f; // Placeholder
|
||||
}
|
||||
}
|
||||
|
||||
private static class PelotonResistanceSensor {
|
||||
private IBinder binder;
|
||||
|
||||
public PelotonResistanceSensor(IBinder binder) {
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
public float readValue() throws RemoteException {
|
||||
// Implementation would call into Peloton service via binder
|
||||
return 0.0f; // Placeholder
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,13 +208,14 @@ public class SDMChannelController {
|
||||
byte[] payload = new byte[8];
|
||||
|
||||
payload[0] = (byte) 0x01;
|
||||
payload[1] = (byte) ((lastTime % 1000) / 5); // time fractional: 0-199 in 1/200 sec units
|
||||
payload[2] = (byte) ((lastTime / 1000) % 256); // time integer: seconds mod 256
|
||||
payload[1] = (byte) (((lastTime % 256000) / 5) & 0xFF);
|
||||
payload[2] = (byte) ((lastTime % 256000) / 1000);
|
||||
payload[3] = (byte) 0x00;
|
||||
payload[4] = (byte) ((int)speedM_s & 0x0F); // speed integer in lower 4 bits only
|
||||
payload[5] = (byte) Math.round((speedM_s - (double)((int)speedM_s)) * 256.0);
|
||||
payload[6] = (byte) stride_count++;
|
||||
payload[7] = (byte) 0; // update latency: no delay in real-time system
|
||||
int speedFixed = (int) Math.round(speedM_s * 256.0);
|
||||
payload[4] = (byte) (speedFixed & 0xFF); // low byte
|
||||
payload[5] = (byte) ((speedFixed >> 8) & 0xFF); // high byte
|
||||
payload[6] = (byte) stride_count++; // bad but it works on zwift
|
||||
payload[7] = (byte) ((double)deltaTime * 0.03125);
|
||||
|
||||
if (mIsOpen) {
|
||||
try {
|
||||
@@ -257,13 +258,13 @@ public class SDMChannelController {
|
||||
byte[] payload = new byte[8];
|
||||
|
||||
payload[0] = (byte) 0x01;
|
||||
payload[1] = (byte) ((lastTime % 1000) / 5); // time fractional: 0-199 in 1/200 sec units
|
||||
payload[2] = (byte) ((lastTime / 1000) % 256); // time integer: seconds mod 256
|
||||
payload[1] = (byte) (((lastTime % 256000) / 5) & 0xFF);
|
||||
payload[2] = (byte) ((lastTime % 256000) / 1000);
|
||||
payload[3] = (byte) 0x00;
|
||||
payload[4] = (byte) ((int)speedM_s & 0x0F); // speed integer in lower 4 bits only
|
||||
payload[5] = (byte) Math.round((speedM_s - (double)((int)speedM_s)) * 256.0);
|
||||
payload[6] = (byte) stride_count++;
|
||||
payload[7] = (byte) 0; // update latency: no delay in real-time system
|
||||
payload[4] = (byte) speedM_s;
|
||||
payload[5] = (byte) ((speedM_s - (double)((int)speedM_s)) / (1.0/256.0));
|
||||
payload[6] = (byte) stride_count;
|
||||
payload[7] = (byte) ((double)deltaTime * 0.03125);
|
||||
|
||||
if (mIsOpen) {
|
||||
try {
|
||||
|
||||
20
src/android/src/main/proto/activitylog/ActivityLog.proto
Normal file
20
src/android/src/main/proto/activitylog/ActivityLog.proto
Normal file
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "activitylog/ActivityLogStats.proto";
|
||||
import "activitylog/ActivityLogSummary.proto";
|
||||
import "activitylog/ActivityLogMetadata.proto";
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ActivityLog {
|
||||
ActivityLogMetadata metadata = 1;
|
||||
string id = 2;
|
||||
int32 softwareNumber = 3;
|
||||
int64 startMsSinceEpoch = 4;
|
||||
int64 endMsSinceEpoch = 5;
|
||||
int32 durationMs = 6;
|
||||
ActivityLogStats stats = 7;
|
||||
ActivityLogSummary summary = 8;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogErrorProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum ActivityLogErrorCode {
|
||||
ACTIVITY_LOG_UNKNOWN_ERROR = 0;
|
||||
ACTIVITY_LOG_NOT_FOUND_ERROR = 1;
|
||||
ACTIVITY_LOG_INVALID_TYPE_ERROR = 2;
|
||||
ACTIVITY_LOG_INVALID_DURATION_ERROR = 3;
|
||||
}
|
||||
|
||||
message ActivityLogError {
|
||||
ActivityLogErrorCode errorCode = 1;
|
||||
string message = 2;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogEventProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum ActivityLogEventType {
|
||||
ACTIVITY_LOG_EVENT_WORKOUT_STARTED = 0;
|
||||
ACTIVITY_LOG_EVENT_WORKOUT_MINIMUMS_REACHED = 1;
|
||||
ACTIVITY_LOG_EVENT_UPLOAD_STARTED = 2;
|
||||
ACTIVITY_LOG_EVENT_UPLOAD_SUCCESSFUL = 3;
|
||||
ACTIVITY_LOG_EVENT_WORKOUT_COMPLETED_NOT_UPLOADING = 4;
|
||||
ACTIVITY_LOG_EVENT_UPLOAD_RECOVERABLE_ERROR = 5;
|
||||
ACTIVITY_LOG_EVENT_UPLOAD_TERMINAL_ERROR = 6;
|
||||
ACTIVITY_LOG_EVENT_UNDER_MINIMUM_DURATION_ERROR = 7;
|
||||
ACTIVITY_LOG_EVENT_UNDER_MINIMUM_DISTANCE_ERROR = 8;
|
||||
ACTIVITY_LOG_EVENT_METADATA_UPDATED = 9;
|
||||
ACTIVITY_LOG_EVENT_WORKOUT_COMPLETED_ANONYMOUSLY_NOT_UPLOADING = 10;
|
||||
}
|
||||
|
||||
message ActivityLogEvent {
|
||||
ActivityLogEventType eventType = 1;
|
||||
string workoutID = 2;
|
||||
string contentID = 3;
|
||||
bool shouldUploadLog = 4;
|
||||
string activityLogID = 5;
|
||||
string errorCode = 6;
|
||||
int32 minimumDurationSeconds = 7;
|
||||
int32 workoutDurationSeconds = 8;
|
||||
int32 minimumDistanceMeters = 9;
|
||||
int32 workoutDistanceMeters = 10;
|
||||
string workoutDriverFQN = 11;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "activitylog/ActivityLogUtils.proto";
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogMetadataProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ActivityLogMetadata {
|
||||
string workoutId = 1;
|
||||
bool shouldUploadLog = 2;
|
||||
string contentId = 3;
|
||||
string title = 4;
|
||||
string heroImageUrl = 5;
|
||||
string socialImageUrl = 6;
|
||||
string programId = 7;
|
||||
string videoId = 8;
|
||||
string listWorkoutId = 9;
|
||||
string liveWorkoutId = 10;
|
||||
string liveWorkoutScheduleId = 11;
|
||||
ActivityLogOrigin origin = 12;
|
||||
ActivityLogContext context = 13;
|
||||
ActivityLogType type = 14;
|
||||
string typeDetail = 15;
|
||||
string externalType = 16;
|
||||
repeated string completedMovements = 17;
|
||||
bool redundant = 18;
|
||||
int32 sleepScore = 19;
|
||||
string seriesId = 20;
|
||||
string challengeId = 21;
|
||||
string workoutDriverFQN = 22;
|
||||
string thirdPartyContentId = 23;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
import "activitylog/ActivityLog.proto";
|
||||
import "activitylog/ActivityLogEvent.proto";
|
||||
import "activitylog/ActivityLogMetadata.proto";
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ActivityLogResult {
|
||||
oneof errorOrToken {
|
||||
IFitError error = 1;
|
||||
ActivityLog activityLog = 2;
|
||||
ActivityLogMetadata metadata = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message ActivityLogID {
|
||||
string id = 1;
|
||||
}
|
||||
message ExternalUploadRequest {
|
||||
ActivityLog log = 1;
|
||||
string userId = 2;
|
||||
}
|
||||
|
||||
message ContentID {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ActivityLogUploading {
|
||||
bool isUploading = 1;
|
||||
}
|
||||
|
||||
service ActivityLogService {
|
||||
rpc HasUnprocessedUploadEventsSubscription(Empty) returns (stream BooleanResponse) {}
|
||||
rpc PopMostRecentUploadEvent(Empty) returns (ActivityLogEvent) {}
|
||||
|
||||
rpc ActivityLogEventSubscription(Empty) returns (stream ActivityLogEvent) {}
|
||||
rpc ActivityLogUploadingSubscription(Empty) returns (stream ActivityLogUploading) {}
|
||||
|
||||
rpc GetActivityLogMetadataByWorkoutId(WorkoutID) returns (ActivityLogResult) {}
|
||||
rpc ChangeActivityLogMetadata(ActivityLogMetadata) returns (ActivityLogResult) {}
|
||||
|
||||
rpc GetActivityLogByWorkoutId(WorkoutID) returns (ActivityLogResult) {}
|
||||
rpc GetLatestActivityLogByContentId(ContentID) returns (ActivityLogResult) {}
|
||||
rpc GetActivityLogByActivityLogId(ActivityLogID) returns (ActivityLogResult) {}
|
||||
rpc DeleteActivityLogByActivityLogId(ActivityLogID) returns (ActivityLogResult) {}
|
||||
rpc UploadActivityLogFromExternalSource(ExternalUploadRequest) returns (ActivityLogResult) {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "activitylog/ActivityLogUtils.proto";
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogStatsProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ActivityLogStats {
|
||||
repeated ActivityOffsetValue bpm = 1;
|
||||
repeated ActivityOffsetValue calories = 2;
|
||||
repeated ActivityOffsetValue elevation = 3;
|
||||
repeated ActivityOffsetValue fiveHundredSplit = 4;
|
||||
repeated ActivityOffsetValue incline = 5;
|
||||
repeated ActivityOffsetValue meters = 6;
|
||||
repeated ActivityOffsetValue mps = 7;
|
||||
repeated ActivityOffsetValue resistance = 8;
|
||||
repeated ActivityOffsetValue rpm = 9;
|
||||
repeated ActivityOffsetValue watts = 10;
|
||||
repeated ActivityOffsetValue cadence = 11;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogSummaryProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ActivityLogSummary {
|
||||
int32 averageBpm = 1;
|
||||
int32 averageFiveHundredSplit = 2;
|
||||
int32 averageResistance = 3;
|
||||
int32 averageRespiration = 4;
|
||||
int32 averageSpm = 5;
|
||||
double averageWatts = 6;
|
||||
int32 maxBpm = 7;
|
||||
int32 maxFiveHundredSplit = 8;
|
||||
int32 maxSpm = 9;
|
||||
int32 maxWatts = 10;
|
||||
int32 minFiveHundredSplit = 11;
|
||||
float totalCalories = 12;
|
||||
float totalElevationGain = 13;
|
||||
float totalMeters = 14;
|
||||
int32 totalMovements = 15;
|
||||
int32 totalSteps = 16;
|
||||
int32 averageCadence = 17;
|
||||
int32 maxCadence = 18;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.activitylog";
|
||||
option java_outer_classname = "ActivityLogUtilsProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum ActivityLogContext {
|
||||
ACT_LOG_CONTEXT_ON_DEMAND = 0;
|
||||
ACT_LOG_CONTEXT_SCHEDULED_LIVE = 1;
|
||||
ACT_LOG_CONTEXT_SCHEDULED_PRE = 2;
|
||||
}
|
||||
|
||||
enum ActivityLogOrigin {
|
||||
ACT_LOG_ORIGIN_BIKE = 0;
|
||||
ACT_LOG_ORIGIN_DAILY = 1;
|
||||
ACT_LOG_ORIGIN_ELLIPTICAL = 2;
|
||||
ACT_LOG_ORIGIN_FUSION = 3;
|
||||
ACT_LOG_ORIGIN_GARMIN = 4;
|
||||
ACT_LOG_ORIGIN_GOOGLEFIT = 5;
|
||||
ACT_LOG_ORIGIN_HEALTHKIT = 6;
|
||||
ACT_LOG_ORIGIN_IFITAPP = 7;
|
||||
ACT_LOG_ORIGIN_ROWER = 8;
|
||||
ACT_LOG_ORIGIN_SLEEPSENSOR = 9;
|
||||
ACT_LOG_ORIGIN_STATIONARYBIKE = 10;
|
||||
ACT_LOG_ORIGIN_STRAVA = 11;
|
||||
ACT_LOG_ORIGIN_STRIDER = 12;
|
||||
ACT_LOG_ORIGIN_THIRDPARTY = 13;
|
||||
ACT_LOG_ORIGIN_TREADMILL = 14;
|
||||
ACT_LOG_ORIGIN_WEARABLE = 15;
|
||||
ACT_LOG_ORIGIN_WEBSITE = 16;
|
||||
ACT_LOG_ORIGIN_VALINOR = 17;
|
||||
}
|
||||
|
||||
enum ActivityLogType {
|
||||
ACT_LOG_TYPE_CARDIO = 0;
|
||||
ACT_LOG_TYPE_CYCLE = 1;
|
||||
ACT_LOG_TYPE_RUN = 2;
|
||||
ACT_LOG_TYPE_PULLEY = 3;
|
||||
ACT_LOG_TYPE_FUSION = 4;
|
||||
ACT_LOG_TYPE_ROW = 5;
|
||||
ACT_LOG_TYPE_DAILY_VIDEO = 6;
|
||||
ACT_LOG_TYPE_STRENGTH = 7;
|
||||
}
|
||||
|
||||
message ActivityOffsetValue {
|
||||
float offset = 1;
|
||||
float value = 2;
|
||||
}
|
||||
23
src/android/src/main/proto/antplus/AntPlusDevice.proto
Normal file
23
src/android/src/main/proto/antplus/AntPlusDevice.proto
Normal file
@@ -0,0 +1,23 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.antplus";
|
||||
option java_outer_classname = "AntPlusDeviceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message AntPlusDevice {
|
||||
int32 deviceType = 1;
|
||||
int32 deviceNumber = 2;
|
||||
int32 signalStrength = 3;
|
||||
int32 manufacturerID = 4;
|
||||
int32 serialNumberLSB = 5;
|
||||
int32 serialNumberMSB = 6;
|
||||
int32 hardwareVersion = 7;
|
||||
int32 softwareVersion = 8;
|
||||
int32 modelNumber = 9;
|
||||
int32 serialNumberCalculated = 10;
|
||||
}
|
||||
|
||||
message AntPlusDeviceList {
|
||||
repeated AntPlusDevice devices = 1;
|
||||
}
|
||||
18
src/android/src/main/proto/antplus/AntPlusService.proto
Normal file
18
src/android/src/main/proto/antplus/AntPlusService.proto
Normal file
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.antplus";
|
||||
option java_outer_classname = "AntPlusServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "antplus/AntPlusDevice.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
message AntPlusScanDurationMessage {
|
||||
int32 durationSeconds = 1;
|
||||
}
|
||||
|
||||
service AntPlusService {
|
||||
rpc ScanForDuration(AntPlusScanDurationMessage) returns (Empty) {}
|
||||
rpc FoundAntPlusDevicesSubscription(Empty) returns (stream AntPlusDeviceList) {}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
import "appnavigation/ForegroundFqns.proto";
|
||||
import "appnavigation/TouchEvent.proto";
|
||||
import "appnavigation/ForegroundClasses.proto";
|
||||
import "appnavigation/ForegroundRequest.proto";
|
||||
option java_package = "com.ifit.glassos.appnavigation";
|
||||
option java_outer_classname = "AppNavigationServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
service AppNavigationService {
|
||||
rpc ForegroundFQNsFlowSubscription(Empty) returns (stream ForegroundFqns) {}
|
||||
rpc TouchFlowSubscription(Empty) returns (stream TouchEvent) {}
|
||||
rpc EnabledSubscription(Empty) returns (stream BooleanResponse) {}
|
||||
rpc KeyboardVisibleFlowSubscription(Empty) returns (stream BooleanResponse) {}
|
||||
rpc ForegroundClassNameFlowSubscription(Empty) returns (stream ListStringResponse) {}
|
||||
rpc ForegroundClassesFlowSubscription(Empty) returns (stream ForegroundClasses) {}
|
||||
|
||||
rpc PerformBackButton(Empty) returns (Empty) {}
|
||||
rpc GetForegroundFqns(Empty) returns (ForegroundFqns) {}
|
||||
rpc SetCurrentForegroundFQN(ForegroundFqnRequest) returns (Empty) {}
|
||||
rpc RemoveCurrentForegroundFQN(ForegroundFqnRequest) returns (Empty) {}
|
||||
rpc RemoveForegroundFQNFromHistory(ForegroundFqnRequest) returns (Empty) {}
|
||||
rpc SetCurrentForegroundClass(ForegroundClassNameRequest) returns (Empty) {}
|
||||
rpc NavigatedToThirdParty(ForegroundFqnRequest) returns (Empty) {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appnavigation";
|
||||
option java_outer_classname = "ForegroundClassesProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ForegroundClasses {
|
||||
ForegroundClass currentlyForegrounded = 1;
|
||||
repeated ForegroundClass foregroundHistory = 2;
|
||||
}
|
||||
|
||||
message ForegroundClass {
|
||||
string className = 1;
|
||||
int64 timestamp = 2;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appnavigation";
|
||||
option java_outer_classname = "ForegroundFqnsProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ForegroundFqns {
|
||||
string currentFqn = 1;
|
||||
repeated string historyFqns = 2;
|
||||
ForegroundFqn currentlyForegrounded = 3;
|
||||
repeated ForegroundFqn foregroundHistory = 4;
|
||||
}
|
||||
|
||||
message ForegroundFqn {
|
||||
string fqn = 1;
|
||||
int64 timestamp = 2;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appnavigation";
|
||||
option java_outer_classname = "ForegroundRequestProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ForegroundFqnRequest {
|
||||
string fqn = 1;
|
||||
}
|
||||
|
||||
message ForegroundClassNameRequest {
|
||||
string className = 1;
|
||||
}
|
||||
10
src/android/src/main/proto/appnavigation/TouchEvent.proto
Normal file
10
src/android/src/main/proto/appnavigation/TouchEvent.proto
Normal file
@@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appnavigation";
|
||||
option java_outer_classname = "TouchEventProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message TouchEvent {
|
||||
int64 timestamp = 1;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appstore";
|
||||
option java_outer_classname = "AppStoreActionRequestProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message AppStoreActionRequest {
|
||||
string fqn = 1;
|
||||
}
|
||||
21
src/android/src/main/proto/appstore/AppStoreApp.proto
Normal file
21
src/android/src/main/proto/appstore/AppStoreApp.proto
Normal file
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "appstore/AppStoreAppStatus.proto";
|
||||
option java_package = "com.ifit.glassos.appstore";
|
||||
option java_outer_classname = "AppStoreAppProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message AppStoreApp {
|
||||
string label = 1;
|
||||
string icon = 2;
|
||||
string fqn = 3;
|
||||
string category = 4;
|
||||
string version = 5;
|
||||
bool installed = 6;
|
||||
AppStoreAppStatus status = 7;
|
||||
}
|
||||
|
||||
message AppStoreAppList {
|
||||
repeated AppStoreApp appStoreApps = 1;
|
||||
}
|
||||
12
src/android/src/main/proto/appstore/AppStoreAppStatus.proto
Normal file
12
src/android/src/main/proto/appstore/AppStoreAppStatus.proto
Normal file
@@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appstore";
|
||||
option java_outer_classname = "AppStoreAppStatusProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum AppStoreAppStatus {
|
||||
NOT_INSTALLED = 0;
|
||||
INSTALLED = 1;
|
||||
PENDING = 2;
|
||||
}
|
||||
23
src/android/src/main/proto/appstore/AppStoreService.proto
Normal file
23
src/android/src/main/proto/appstore/AppStoreService.proto
Normal file
@@ -0,0 +1,23 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
import "appstore/StorageStats.proto";
|
||||
import "appstore/AppStoreApp.proto";
|
||||
import "appstore/AppStoreActionRequest.proto";
|
||||
import "appstore/AppStoreState.proto";
|
||||
option java_package = "com.ifit.glassos.appstore";
|
||||
option java_outer_classname = "AppStoreServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
service AppStoreService {
|
||||
rpc AppStoreStateFlowSubscription(Empty) returns (stream AppStoreState) {}
|
||||
rpc AppsFlowSubscription(Empty) returns (stream AppStoreAppList) {}
|
||||
|
||||
rpc RequestAppInstall(AppStoreActionRequest) returns (Empty) {}
|
||||
rpc RequestAppUninstall(AppStoreActionRequest) returns (Empty) {}
|
||||
rpc GetApps(BooleanRequest) returns (Empty) {}
|
||||
rpc GoIdle(Empty) returns (Empty) {}
|
||||
|
||||
rpc GetStorageStats(Empty) returns (StorageStats) {}
|
||||
}
|
||||
43
src/android/src/main/proto/appstore/AppStoreState.proto
Normal file
43
src/android/src/main/proto/appstore/AppStoreState.proto
Normal file
@@ -0,0 +1,43 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appstore";
|
||||
option java_outer_classname = "AppStoreStateProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message AppStoreState {
|
||||
oneof state {
|
||||
Idle idle = 1;
|
||||
Loading loading = 2;
|
||||
Checking checking = 3;
|
||||
Uninstalling uninstalling = 4;
|
||||
Error error = 5;
|
||||
Downloading downloading = 6;
|
||||
Installing installing = 7;
|
||||
}
|
||||
}
|
||||
|
||||
message Idle {}
|
||||
|
||||
message Loading {}
|
||||
|
||||
message Checking {}
|
||||
|
||||
message Uninstalling {
|
||||
string fqn = 1;
|
||||
}
|
||||
|
||||
message Error {
|
||||
int32 errorCode = 1;
|
||||
optional string fqn = 2;
|
||||
}
|
||||
|
||||
message Downloading {
|
||||
string fqn = 1;
|
||||
float progress = 2;
|
||||
}
|
||||
|
||||
message Installing {
|
||||
string fqn = 1;
|
||||
float progress = 2;
|
||||
}
|
||||
12
src/android/src/main/proto/appstore/StorageStats.proto
Normal file
12
src/android/src/main/proto/appstore/StorageStats.proto
Normal file
@@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.appstore";
|
||||
option java_outer_classname = "StorageStatsProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message StorageStats {
|
||||
int64 totalBytes = 1;
|
||||
int64 allocatableBytes = 2;
|
||||
int64 reservedBytes = 3;
|
||||
}
|
||||
17
src/android/src/main/proto/auth/AuthError.proto
Normal file
17
src/android/src/main/proto/auth/AuthError.proto
Normal file
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.auth";
|
||||
option java_outer_classname = "AuthErrorCodeProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum AuthErrorCode {
|
||||
AUTH_FAILURE = 0;
|
||||
AUTH_LOGIN_REQUIRED = 1;
|
||||
AUTH_NETWORK_ERROR = 2;
|
||||
}
|
||||
|
||||
message AuthError {
|
||||
AuthErrorCode errorCode = 1;
|
||||
string message = 2;
|
||||
}
|
||||
116
src/android/src/main/proto/auth/AuthService.proto
Normal file
116
src/android/src/main/proto/auth/AuthService.proto
Normal file
@@ -0,0 +1,116 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.auth";
|
||||
option java_outer_classname = "AuthServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message Username {
|
||||
string username = 1;
|
||||
}
|
||||
|
||||
message UserCredentials {
|
||||
string username = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message AuthToken {
|
||||
string username = 1;
|
||||
string accessToken = 2;
|
||||
int64 validUntilTimestampMs = 3;
|
||||
}
|
||||
|
||||
message AuthCredentials {
|
||||
string username = 1;
|
||||
string accessToken = 2;
|
||||
string refreshToken = 3;
|
||||
int64 expiresIn = 4;
|
||||
}
|
||||
|
||||
message GetCurrentTokenRequest {
|
||||
bool forceRefresh = 1;
|
||||
}
|
||||
|
||||
message AuthResult {
|
||||
oneof errorOrToken {
|
||||
IFitError error = 1;
|
||||
AuthToken token = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message MachineToken {
|
||||
string accessToken = 1;
|
||||
int64 validUntilTimestampMs = 2;
|
||||
}
|
||||
|
||||
message MachineTokenResult {
|
||||
oneof errorOrToken {
|
||||
IFitError error = 1;
|
||||
MachineToken token = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message AuthTokenList {
|
||||
repeated AuthToken tokens = 1;
|
||||
}
|
||||
|
||||
message AuthQRCodeData {
|
||||
string deviceCode = 1;
|
||||
string userCode = 2;
|
||||
string verificationUri = 3;
|
||||
string verificationUriComplete = 4;
|
||||
int64 expiresIn = 5;
|
||||
}
|
||||
|
||||
message AuthQRCodeResult {
|
||||
oneof errorOrData {
|
||||
IFitError error = 1;
|
||||
AuthQRCodeData data = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message AuthQRCodePollingState {
|
||||
oneof state {
|
||||
AUTH_QR_CODE_POLLING_IDLE pollingIdle = 1;
|
||||
AUTH_QR_CODE_POLLING_ACTIVE pollingActive = 2;
|
||||
AUTH_QR_CODE_POLLING_EXPIRED pollingExpired = 3;
|
||||
AUTH_QR_CODE_POLLING_USER_AUTHED pollingUserAuthed = 4;
|
||||
AUTH_QR_CODE_POLLING_ERROR pollingError = 5;
|
||||
AUTH_QR_CODE_POLLING_AUTH_ERROR pollingAuthError = 6;
|
||||
}
|
||||
}
|
||||
|
||||
message AUTH_QR_CODE_POLLING_IDLE {}
|
||||
message AUTH_QR_CODE_POLLING_ACTIVE {}
|
||||
message AUTH_QR_CODE_POLLING_EXPIRED {}
|
||||
message AUTH_QR_CODE_POLLING_USER_AUTHED {
|
||||
AuthToken token = 1;
|
||||
}
|
||||
message AUTH_QR_CODE_POLLING_ERROR {
|
||||
int32 errorCode = 1;
|
||||
optional string errorMessage = 2;
|
||||
}
|
||||
message AUTH_QR_CODE_POLLING_AUTH_ERROR {
|
||||
int32 errorCode = 1;
|
||||
optional string errorMessage = 2;
|
||||
}
|
||||
|
||||
|
||||
service AuthService {
|
||||
rpc Login(UserCredentials) returns (AuthResult) {}
|
||||
rpc SwitchUser(Username) returns (AuthResult) {}
|
||||
rpc SetCredentials(AuthCredentials) returns (AuthResult) {}
|
||||
rpc Logout(Empty) returns (Empty) {}
|
||||
rpc GetQRCodeData(Empty) returns (AuthQRCodeResult) {}
|
||||
rpc StopPollingForQRAuthToken(Empty) returns (Empty) {}
|
||||
rpc QrCodePollingStateChanged(Empty) returns (stream AuthQRCodePollingState) {}
|
||||
|
||||
rpc TokenChanged(Empty) returns (stream AuthToken) {}
|
||||
rpc GetCurrentToken(GetCurrentTokenRequest) returns (AuthResult) {}
|
||||
rpc GetAllTokens(Empty) returns (AuthTokenList) {}
|
||||
|
||||
rpc MachineTokenChanged(Empty) returns (stream MachineToken) {}
|
||||
rpc GetMachineToken(Empty) returns (MachineTokenResult) {}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.bluetooth";
|
||||
option java_outer_classname = "BluetoothConnectionStateProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum BluetoothConnectionState {
|
||||
BLE_DEVICE_CONNECTED = 0;
|
||||
BLE_DEVICE_CONNECTING = 1;
|
||||
BLE_DEVICE_DISCONNECTED = 2;
|
||||
}
|
||||
38
src/android/src/main/proto/bluetooth/BluetoothDevice.proto
Normal file
38
src/android/src/main/proto/bluetooth/BluetoothDevice.proto
Normal file
@@ -0,0 +1,38 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "bluetooth/BluetoothDeviceType.proto";
|
||||
import "bluetooth/BluetoothConnectionState.proto";
|
||||
option java_package = "com.ifit.glassos.bluetooth";
|
||||
option java_outer_classname = "BluetoothDeviceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message DeviceStreamRequest {
|
||||
string macAddress = 1;
|
||||
}
|
||||
|
||||
message DeviceConnectionStateResult {
|
||||
BluetoothConnectionState connectionState = 1;
|
||||
}
|
||||
|
||||
message DeviceRssiResult {
|
||||
int32 rssi = 1;
|
||||
}
|
||||
|
||||
message DeviceBatteryLevelResult {
|
||||
int32 batteryLevel = 1;
|
||||
}
|
||||
|
||||
message BluetoothDevice {
|
||||
string deviceName = 1;
|
||||
string macAddress = 2;
|
||||
int32 rssi = 3;
|
||||
int32 batteryLevel = 4;
|
||||
BluetoothDeviceType deviceType = 5;
|
||||
BluetoothConnectionState connectionState = 6;
|
||||
string pairKey = 7;
|
||||
}
|
||||
|
||||
message BluetoothDeviceList {
|
||||
repeated BluetoothDevice devices = 1;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.bluetooth";
|
||||
option java_outer_classname = "BluetoothDeviceTypeProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum BluetoothDeviceType {
|
||||
BLE_IFIT_CONSOLE = 0;
|
||||
BLE_HEART_RATE = 1;
|
||||
BLE_HEADPHONE = 2;
|
||||
BLE_OTHER = 3;
|
||||
BLE_IFIT_VIRTUAL_CONSOLE = 4;
|
||||
BLE_SMART_WATCH = 5;
|
||||
ARCX_RING = 6;
|
||||
BLE_PHONE_TABLET = 7;
|
||||
}
|
||||
66
src/android/src/main/proto/bluetooth/BluetoothService.proto
Normal file
66
src/android/src/main/proto/bluetooth/BluetoothService.proto
Normal file
@@ -0,0 +1,66 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
import "bluetooth/BluetoothDevice.proto";
|
||||
import "bluetooth/BluetoothDeviceType.proto";
|
||||
option java_package = "com.ifit.glassos.bluetooth";
|
||||
option java_outer_classname = "BluetoothServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message BluetoothResult {
|
||||
oneof errorOrSuccess {
|
||||
IFitError error = 1;
|
||||
bool success = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message StartScanRequest {
|
||||
int32 scanTimeoutSeconds = 1;
|
||||
repeated BluetoothDeviceType deviceTypes = 2;
|
||||
}
|
||||
|
||||
message BluetoothScanState {
|
||||
bool scanning = 1;
|
||||
}
|
||||
|
||||
message BluetoothServiceState {
|
||||
repeated BluetoothDevice connectedDevices = 1;
|
||||
}
|
||||
|
||||
message MACAddressConnectionRequest {
|
||||
string macAddress = 1;
|
||||
BluetoothDeviceType deviceType = 2;
|
||||
}
|
||||
|
||||
message MACAddressConnectionResult {
|
||||
oneof emptyOrDevice {
|
||||
BluetoothDevice device = 1;
|
||||
Empty empty = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message DeviceIdentifierRequest {
|
||||
string deviceIdentifier = 1;
|
||||
}
|
||||
|
||||
service BluetoothService {
|
||||
rpc ScanStateChanged(Empty) returns (stream BluetoothScanState) {}
|
||||
rpc BluetoothServiceStateChanged(Empty) returns (stream BluetoothServiceState) {}
|
||||
rpc FoundDevicesChanged(Empty) returns (stream BluetoothDevice) {}
|
||||
rpc StartScan(StartScanRequest) returns (BluetoothResult) {}
|
||||
rpc StopScan(Empty) returns (BluetoothResult) {}
|
||||
|
||||
rpc ConnectDevice(BluetoothDevice) returns (BluetoothResult) {}
|
||||
rpc ConnectWithMACAddress(MACAddressConnectionRequest) returns (MACAddressConnectionResult) {}
|
||||
rpc DisconnectDevice(BluetoothDevice) returns (BluetoothResult) {}
|
||||
rpc ConnectToHRM(DeviceIdentifierRequest) returns (BluetoothResult) {}
|
||||
rpc ConnectToRing(DeviceIdentifierRequest) returns (BluetoothResult) {}
|
||||
|
||||
rpc GetPairedDevices(Empty) returns (BluetoothDeviceList) {}
|
||||
|
||||
rpc BluetoothDeviceBatteryLevelChanged(DeviceStreamRequest) returns (stream DeviceBatteryLevelResult) {}
|
||||
rpc BluetoothDeviceConnectionStateChanged(DeviceStreamRequest) returns (stream DeviceConnectionStateResult) {}
|
||||
rpc BluetoothDeviceRSSIChanged(DeviceStreamRequest) returns (stream DeviceRssiResult) {}
|
||||
}
|
||||
95
src/android/src/main/proto/client_analytics.proto
Normal file
95
src/android/src/main/proto/client_analytics.proto
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2021 Google LLC.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package firebase.transport;
|
||||
|
||||
option java_multiple_files = true;
|
||||
|
||||
// Top level metrics for all client analytics metrics.
|
||||
// These metrics should be sent as a part of every request that is uploaded to
|
||||
// FireLog server. In more detail, an additional LogRequest should be added to
|
||||
// the BatchedLogRequest, where the LogSource of the LogRequest should be
|
||||
// GDT_CLIENT_METRICS and the LogRequest should have a single LogEvent whose
|
||||
// payload is a ClientMetrics message.
|
||||
//
|
||||
// See go/firelog-client-analytics for more details.
|
||||
message ClientMetrics {
|
||||
// The window of time over which the metrics are evaluated.
|
||||
TimeWindow window = 1;
|
||||
|
||||
repeated LogSourceMetrics log_source_metrics = 2;
|
||||
|
||||
GlobalMetrics global_metrics = 3;
|
||||
|
||||
// The bundle ID on Apple platforms (e.g., iOS) or the package name on Android
|
||||
string app_namespace = 4;
|
||||
}
|
||||
|
||||
// Represents an arbitrary window of time.
|
||||
message TimeWindow {
|
||||
// The time that the window first starts.
|
||||
// start_ms is the number of milliseconds since the UNIX epoch
|
||||
// (January 1, 1970 00:00:00 UTC)
|
||||
int64 start_ms = 1;
|
||||
|
||||
// The time that the window ends.
|
||||
// end_ms is the number of milliseconds since the UNIX epoch
|
||||
// (January 1, 1970 00:00:00 UTC)
|
||||
int64 end_ms = 2;
|
||||
}
|
||||
|
||||
// Metrics per app, not per log source
|
||||
message GlobalMetrics {
|
||||
StorageMetrics storage_metrics = 1;
|
||||
}
|
||||
|
||||
message StorageMetrics {
|
||||
// The number of bytes of storage the event cache was consuming on the client
|
||||
// at the time the request was sent.
|
||||
int64 current_cache_size_bytes = 1;
|
||||
|
||||
// The maximum number of bytes to which the event cache is allowed to grow.
|
||||
int64 max_cache_size_bytes = 2;
|
||||
}
|
||||
|
||||
// Metrics per log source.
|
||||
message LogSourceMetrics {
|
||||
// A LogSource uniquely identifies a logging configuration. log_source should
|
||||
// contains a string value of the LogSource from
|
||||
// google3/wireless/android/play/playlog/proto/clientanalytics.proto
|
||||
string log_source = 1;
|
||||
|
||||
repeated LogEventDropped log_event_dropped = 2;
|
||||
}
|
||||
|
||||
message LogEventDropped {
|
||||
// A count of how many log event have been dropped on the client.
|
||||
int64 events_dropped_count = 1;
|
||||
|
||||
// The reason why log events have been dropped on the client.
|
||||
enum Reason {
|
||||
REASON_UNKNOWN = 0;
|
||||
MESSAGE_TOO_OLD = 1;
|
||||
CACHE_FULL = 2;
|
||||
PAYLOAD_TOO_BIG = 3;
|
||||
MAX_RETRIES_REACHED = 4;
|
||||
INVALID_PAYLOD = 5;
|
||||
SERVER_ERROR = 6;
|
||||
}
|
||||
|
||||
Reason reason = 3;
|
||||
}
|
||||
84
src/android/src/main/proto/club/ClubSettingsService.proto
Normal file
84
src/android/src/main/proto/club/ClubSettingsService.proto
Normal file
@@ -0,0 +1,84 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos.club;
|
||||
option java_package = "com.ifit.glassos.club";
|
||||
option java_outer_classname = "ClubSettingsServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/IFitError.proto";
|
||||
import "settings/SystemUnitsService.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
// Enum for UserRole
|
||||
enum UserRole {
|
||||
HOME_USER = 0;
|
||||
CLUB_ADMIN = 1;
|
||||
CLUB_GUEST = 2;
|
||||
CLUB_USER = 3;
|
||||
}
|
||||
|
||||
// Response message for getting club code
|
||||
message GetClubCodeResponse {
|
||||
oneof errorOrClubCode {
|
||||
IFitError error = 1;
|
||||
string clubCode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Response message for getting video screensaver setting
|
||||
message GetUseVideoScreensaverResponse {
|
||||
oneof errorOrUseVideoScreensaver {
|
||||
IFitError error = 1;
|
||||
bool useVideoScreensaver = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message UserRoleResponse {
|
||||
UserRole role = 1;
|
||||
}
|
||||
|
||||
// Request message for changing user role
|
||||
message ChangeUserRoleRequest {
|
||||
UserRole newRole = 1;
|
||||
}
|
||||
|
||||
// Request message for saving club code
|
||||
message SaveClubCodeRequest {
|
||||
string clubCode = 1;
|
||||
}
|
||||
|
||||
// Request message for saving video screensaver setting
|
||||
message SaveUseVideoScreensaverRequest {
|
||||
bool useVideoScreensaver = 1;
|
||||
}
|
||||
|
||||
// Request message for saving default language
|
||||
message SaveDefaultLanguageRequest {
|
||||
string language = 1;
|
||||
}
|
||||
|
||||
// Request message for saving default language
|
||||
message IsEgymEnabledRequest {
|
||||
bool featureFlagOnly = 1;
|
||||
bool adminOnly = 2;
|
||||
}
|
||||
|
||||
// Service definition for IFitClubSettingsService
|
||||
service IFitClubSettingsService {
|
||||
rpc ChangeUserRole(ChangeUserRoleRequest) returns (Empty) {}
|
||||
rpc CurrentUserRole(Empty) returns (stream UserRoleResponse) {}
|
||||
rpc RestoreClubOwnerDefaultSettings(Empty) returns (Empty) {}
|
||||
rpc GetClubCode(Empty) returns (GetClubCodeResponse) {}
|
||||
rpc SaveClubCode(SaveClubCodeRequest) returns (Empty) {}
|
||||
rpc GetUseVideoScreensaver(Empty) returns (GetUseVideoScreensaverResponse) {}
|
||||
rpc SaveUseVideoScreensaver(SaveUseVideoScreensaverRequest) returns (Empty) {}
|
||||
rpc SaveDefaultSystemUnits(SystemUnitsMessage) returns (Empty) {}
|
||||
rpc SaveDefaultLanguage(SaveDefaultLanguageRequest) returns (Empty) {}
|
||||
rpc GetCurrentUserRole(Empty) returns (UserRoleResponse) {}
|
||||
rpc SaveAdminEgymEnabledState(BooleanRequest) returns (Empty) {}
|
||||
rpc IsClub(Empty) returns (BooleanResponse) {}
|
||||
rpc IsClubUser(Empty) returns (BooleanResponse) {}
|
||||
rpc IsClubFreeUser(Empty) returns (BooleanResponse) {}
|
||||
rpc IsClubGuest(Empty) returns (BooleanResponse) {}
|
||||
rpc IsClubPremiumUser(Empty) returns (BooleanResponse) {}
|
||||
rpc IsEgymEnabled(IsEgymEnabledRequest) returns (BooleanResponse) {}
|
||||
}
|
||||
87
src/android/src/main/proto/compile_protos.bat
Normal file
87
src/android/src/main/proto/compile_protos.bat
Normal file
@@ -0,0 +1,87 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM Percorso al tuo protoc specifico
|
||||
set PROTOC_EXE=C:\Users\violarob\Downloads\protoc-3.25.8-windows-x86_64.exe
|
||||
|
||||
REM Verifica che protoc esista
|
||||
if not exist "%PROTOC_EXE%" (
|
||||
echo ERRORE: protoc non trovato in: %PROTOC_EXE%
|
||||
echo Verifica che il file esista e il percorso sia corretto.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Directory di output
|
||||
set OUTPUT_DIR=..\java
|
||||
|
||||
REM Crea directory di output
|
||||
if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%"
|
||||
|
||||
echo ===============================
|
||||
echo COMPILAZIONE PROTOBUF
|
||||
echo ===============================
|
||||
echo Protoc: %PROTOC_EXE%
|
||||
echo Directory corrente: %CD%
|
||||
echo Output in: %OUTPUT_DIR%
|
||||
echo.
|
||||
|
||||
REM Verifica versione protoc
|
||||
echo Versione protoc:
|
||||
"%PROTOC_EXE%" --version
|
||||
echo.
|
||||
|
||||
REM Contatori
|
||||
set /a success_count=0
|
||||
set /a error_count=0
|
||||
|
||||
REM Compila file .proto nella directory corrente
|
||||
for %%f in (*.proto) do (
|
||||
echo [INFO] Compilando: %%f
|
||||
"%PROTOC_EXE%" --java_out=lite:"%OUTPUT_DIR%" --proto_path=. "%%f"
|
||||
if errorlevel 1 (
|
||||
echo [ERRORE] Fallito: %%f
|
||||
set /a error_count+=1
|
||||
) else (
|
||||
echo [OK] Successo: %%f
|
||||
set /a success_count+=1
|
||||
)
|
||||
echo.
|
||||
)
|
||||
|
||||
REM Compila file .proto nelle sottocartelle
|
||||
for /d %%d in (*) do (
|
||||
if exist "%%d\*.proto" (
|
||||
echo [INFO] Sottocartella trovata: %%d
|
||||
for %%f in (%%d\*.proto) do (
|
||||
echo [INFO] Compilando: %%f
|
||||
"%PROTOC_EXE%" --java_out=lite:"%OUTPUT_DIR%" --proto_path=. "%%f"
|
||||
if errorlevel 1 (
|
||||
echo [ERRORE] Fallito: %%f
|
||||
set /a error_count+=1
|
||||
) else (
|
||||
echo [OK] Successo: %%f
|
||||
set /a success_count+=1
|
||||
)
|
||||
)
|
||||
echo.
|
||||
)
|
||||
)
|
||||
|
||||
REM Riepilogo finale
|
||||
echo ===============================
|
||||
echo RIEPILOGO COMPILAZIONE:
|
||||
echo File compilati con successo: %success_count%
|
||||
echo File con errori: %error_count%
|
||||
echo Directory output: %OUTPUT_DIR%
|
||||
echo ===============================
|
||||
|
||||
if %error_count% gtr 0 (
|
||||
echo ATTENZIONE: Compilazione completata con %error_count% errori!
|
||||
pause
|
||||
exit /b 1
|
||||
) else (
|
||||
echo SUCCESSO: Tutti i file compilati correttamente!
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
17
src/android/src/main/proto/console/ConsoleError.proto
Normal file
17
src/android/src/main/proto/console/ConsoleError.proto
Normal file
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console";
|
||||
option java_outer_classname = "ConsoleErrorCodeProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum ConsoleErrorCode {
|
||||
FITNESS_VALUE_UNSUPPORTED = 0;
|
||||
VIRTUAL_CONSOLE_REQUIRED = 1;
|
||||
NO_VALUE_SET = 2;
|
||||
}
|
||||
|
||||
message ConsoleError {
|
||||
ConsoleErrorCode errorCode = 1;
|
||||
string message = 2;
|
||||
}
|
||||
62
src/android/src/main/proto/console/ConsoleInfo.proto
Normal file
62
src/android/src/main/proto/console/ConsoleInfo.proto
Normal file
@@ -0,0 +1,62 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "console/ConsoleType.proto";
|
||||
import "settings/SystemUnitsService.proto";
|
||||
option java_package = "com.ifit.glassos.console";
|
||||
option java_outer_classname = "ConsoleInfoProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ConsoleInfo {
|
||||
int32 modelNumber = 1;
|
||||
int32 partNumber = 2;
|
||||
int32 softwareVersion = 3;
|
||||
int32 hardwareVersion = 4;
|
||||
string firmwareVersion = 5;
|
||||
int32 serialNumber = 6;
|
||||
ConsoleType machineType = 7;
|
||||
string name = 8;
|
||||
string brainboardSerialNumber = 9;
|
||||
int32 masterLibraryVersion = 10;
|
||||
int32 masterLibraryBuild = 11;
|
||||
SystemUnits systemUnits = 12;
|
||||
double maxKph = 13;
|
||||
double minKph = 14;
|
||||
double maxInclinePercent = 15;
|
||||
double minInclinePercent = 16;
|
||||
double minResistance = 17;
|
||||
double maxResistance = 18;
|
||||
int32 minGear = 19;
|
||||
int32 maxGear = 20;
|
||||
double maxWeightKg = 21;
|
||||
bool canSetSpeed = 22;
|
||||
bool canSetIncline = 23;
|
||||
bool canSetResistance = 24;
|
||||
bool canSetGear = 25;
|
||||
bool canSetActivationLock = 26;
|
||||
bool supportsVerticalGain = 27;
|
||||
bool supportsVerticalNet = 28;
|
||||
bool supportsStartRequested = 29;
|
||||
bool supportsRequireStartRequested = 30;
|
||||
bool supportsKeyPressObserved = 31;
|
||||
bool supportsPulse = 32;
|
||||
double totalTimeSeconds = 33;
|
||||
double warmUpTimeoutSeconds = 34;
|
||||
double coolDownTimeoutSeconds = 35;
|
||||
double pauseTimeoutSeconds = 36;
|
||||
double totalDistanceKm = 37;
|
||||
bool isClubUnit = 38;
|
||||
double weightKg = 39;
|
||||
bool supportsConstantWatts = 40;
|
||||
string antPlusBootloaderVersion = 41;
|
||||
string antPlusSerialNumber = 42;
|
||||
string antPlusDeviceNumber = 43;
|
||||
string antPlusRelaySoftwareVersion = 44;
|
||||
string productSerialNumber = 45;
|
||||
string controller1SoftwareVersion = 46;
|
||||
string controller1SoftwarePartNumber = 47;
|
||||
string controller4SoftwareVersion = 48;
|
||||
string controller4SoftwarePartNumber = 49;
|
||||
string controller40SoftwareVersion = 50;
|
||||
string controller40SoftwarePartNumber = 51;
|
||||
}
|
||||
35
src/android/src/main/proto/console/ConsoleService.proto
Normal file
35
src/android/src/main/proto/console/ConsoleService.proto
Normal file
@@ -0,0 +1,35 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
import "console/ConsoleState.proto";
|
||||
import "console/ConsoleInfo.proto";
|
||||
option java_package = "com.ifit.glassos.console";
|
||||
option java_outer_classname = "ConsoleServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message ConnectionResult {
|
||||
oneof errorOrState {
|
||||
IFitError error = 1;
|
||||
ConsoleState consoleState = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message ConsoleStateMessage {
|
||||
ConsoleState consoleState = 1;
|
||||
}
|
||||
|
||||
service ConsoleService {
|
||||
rpc Connect(Empty) returns (ConnectionResult) {}
|
||||
rpc Disconnect(Empty) returns (Empty) {}
|
||||
|
||||
rpc GetConsole(Empty) returns (ConsoleInfo) {}
|
||||
rpc ConsoleChanged(Empty) returns (stream ConsoleInfo) {}
|
||||
|
||||
rpc GetConsoleState(Empty) returns (ConsoleStateMessage) {}
|
||||
rpc ConsoleStateChanged(Empty) returns (stream ConsoleStateMessage) {}
|
||||
|
||||
rpc GetKnownConsoleInfo(Empty) returns (ConsoleInfo) {}
|
||||
rpc RefreshKnownConsoleInfo(Empty) returns (ConsoleInfo) {}
|
||||
}
|
||||
23
src/android/src/main/proto/console/ConsoleState.proto
Normal file
23
src/android/src/main/proto/console/ConsoleState.proto
Normal file
@@ -0,0 +1,23 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console";
|
||||
option java_outer_classname = "ConsoleStateProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum ConsoleState {
|
||||
DISCONNECTED = 0;
|
||||
CONSOLE_STATE_UNKNOWN = 1;
|
||||
IDLE = 2;
|
||||
WORKOUT = 3;
|
||||
PAUSED = 4;
|
||||
WORKOUT_RESULTS = 5;
|
||||
SAFETY_KEY_REMOVED = 6;
|
||||
WARM_UP = 7;
|
||||
COOL_DOWN = 8;
|
||||
RESUME = 9;
|
||||
LOCKED = 10;
|
||||
DEMO = 11;
|
||||
SLEEP = 12;
|
||||
ERROR = 13;
|
||||
}
|
||||
22
src/android/src/main/proto/console/ConsoleType.proto
Normal file
22
src/android/src/main/proto/console/ConsoleType.proto
Normal file
@@ -0,0 +1,22 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console";
|
||||
option java_outer_classname = "ConsoleTypeProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum ConsoleType {
|
||||
CONSOLE_TYPE_UNKNOWN = 0;
|
||||
TREADMILL = 1;
|
||||
INCLINE_TRAINER = 2;
|
||||
ELLIPTICAL = 3;
|
||||
BIKE = 4;
|
||||
STRIDER = 5;
|
||||
FREE_STRIDER = 6;
|
||||
VERTICAL_ELLIPTICAL = 7;
|
||||
SPIN_BIKE = 8;
|
||||
ROWER = 9;
|
||||
EQUIPMENTLESS = 10;
|
||||
MIRROR = 11;
|
||||
VIBRATION = 12;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.calibration";
|
||||
option java_outer_classname = "InclineCalibrationProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/IFitError.proto";
|
||||
|
||||
enum CalibrateInclineState {
|
||||
CALIBRATE_INCLINE_STATE_DONE = 0;
|
||||
CALIBRATE_INCLINE_STATE_FAILED = 1;
|
||||
CALIBRATE_INCLINE_STATE_IN_PROGRESS = 2;
|
||||
CALIBRATE_INCLINE_STATE_WAITING = 3;
|
||||
}
|
||||
|
||||
message InclineCalibrationStateResult {
|
||||
CalibrateInclineState state = 1;
|
||||
}
|
||||
|
||||
message InclineCalibrationStartedResult {
|
||||
oneof errorOrBool {
|
||||
IFitError error = 1;
|
||||
bool calibrationStarted = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.calibration";
|
||||
option java_outer_classname = "InclineCalibrationServiceProto";
|
||||
option java_multiple_files = true;
|
||||
import "console/calibration/InclineCalibration.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
service InclineCalibrationService {
|
||||
rpc CalibrateIncline(Empty) returns (Empty) {}
|
||||
rpc InclineCalibrationStateChanged(Empty) returns (stream InclineCalibrationStateResult) {}
|
||||
rpc InclineCalibrationStartedChanged(Empty) returns (stream InclineCalibrationStartedResult) {}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.calibration";
|
||||
option java_outer_classname = "ThrottleCalibrationProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/IFitError.proto";
|
||||
|
||||
enum ThrottleCalibrationState {
|
||||
CALIBRATE_THROTTLE_STATE_IDLE = 0;
|
||||
CALIBRATE_THROTTLE_STATE_FAILED = 1;
|
||||
CALIBRATE_THROTTLE_STATE_WAITING_FOR_NEUTRAL = 2;
|
||||
CALIBRATE_THROTTLE_STATE_WAITING_FOR_GRADE_FORWARD = 3;
|
||||
CALIBRATE_THROTTLE_STATE_WAITING_FOR_GRADE_BACKWARD = 4;
|
||||
CALIBRATE_THROTTLE_STATE_WAITING_FOR_SPEED_FORWARD = 5;
|
||||
CALIBRATE_THROTTLE_STATE_WAITING_FOR_SPEED_BACKWARD = 6;
|
||||
CALIBRATE_THROTTLE_STATE_DONE = 7;
|
||||
}
|
||||
|
||||
message ThrottleCalibrationValues {
|
||||
int32 rawGradeReading = 1;
|
||||
int32 rawSpeedReading = 2;
|
||||
int32 gradeTopThreshold = 3;
|
||||
int32 gradeHighThreshold = 4;
|
||||
int32 gradeLowThreshold = 5;
|
||||
int32 gradeBottomThreshold = 6;
|
||||
int32 gradeFilterConstant = 7;
|
||||
int32 speedTopThreshold = 8;
|
||||
int32 speedHighThreshold = 9;
|
||||
int32 speedLowThreshold = 10;
|
||||
int32 speedBottomThreshold = 11;
|
||||
int32 speedFilterConstant = 12;
|
||||
}
|
||||
|
||||
message ThrottleCalibrationStateResult {
|
||||
ThrottleCalibrationState state = 1;
|
||||
optional string errorMessage = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.calibration";
|
||||
option java_outer_classname = "ThrottleCalibrationServiceProto";
|
||||
option java_multiple_files = true;
|
||||
import "util/Util.proto";
|
||||
import "console/calibration/ThrottleCalibration.proto";
|
||||
|
||||
service ThrottleCalibrationService {
|
||||
rpc IsThrottleCalibrationAvailable(Empty) returns (AvailabilityResponse) {}
|
||||
|
||||
rpc CalibrateThrottles(Empty) returns (Empty) {}
|
||||
rpc ConfirmThrottleState(Empty) returns (Empty) {}
|
||||
rpc AbortCalibrateThrottles(Empty) returns (Empty) {}
|
||||
|
||||
rpc ThrottleCalibrationStateChanged(Empty) returns (stream ThrottleCalibrationStateResult) {}
|
||||
|
||||
rpc GetThrottleCalibrationValues(Empty) returns (ThrottleCalibrationValues) {}
|
||||
rpc ThrottleCalibrationValuesChanged(Empty) returns (stream ThrottleCalibrationValues) {}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.constantwatts";
|
||||
option java_outer_classname = "ConstantWattsServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/Util.proto";
|
||||
import "util/IFitError.proto";
|
||||
|
||||
message ConstantWattsMessage {
|
||||
int32 watts = 1;
|
||||
}
|
||||
|
||||
enum ConstantWattsState {
|
||||
CONSTANT_WATTS_STATE_DISABLED = 0;
|
||||
CONSTANT_WATTS_STATE_ENABLED = 1;
|
||||
CONSTANT_WATTS_STATE_PAUSED = 2;
|
||||
}
|
||||
|
||||
message ConstantWattsStateMessage {
|
||||
oneof errorOrState {
|
||||
IFitError error = 1;
|
||||
ConstantWattsState state = 2;
|
||||
}
|
||||
}
|
||||
|
||||
service ConstantWattsService {
|
||||
rpc CanRead(Empty) returns (AvailabilityResponse) {}
|
||||
rpc CanWrite(Empty) returns (AvailabilityResponse) {}
|
||||
rpc IsSupported(Empty) returns (AvailabilityResponse) {}
|
||||
rpc GetConstantWatts(Empty) returns (ConstantWattsMessage) {}
|
||||
rpc SetConstantWatts(ConstantWattsMessage) returns (AvailabilityResponse) {}
|
||||
rpc GetState(Empty) returns (ConstantWattsStateMessage){}
|
||||
rpc IsEquipmentSupported(Empty) returns (AvailabilityResponse){}
|
||||
rpc IsWorkoutSupported(Empty) returns (AvailabilityResponse){}
|
||||
rpc IsUserSupported(Empty) returns (AvailabilityResponse){}
|
||||
rpc Pause(Empty) returns (Empty){}
|
||||
rpc Resume(Empty) returns (Empty){}
|
||||
rpc Enable(Empty) returns (Empty){}
|
||||
rpc Disable(Empty) returns (Empty){}
|
||||
rpc Increment(Empty) returns (Empty){}
|
||||
rpc Decrement(Empty) returns (Empty){}
|
||||
rpc OnStateChanged(Empty) returns (stream ConstantWattsStateMessage) {}
|
||||
rpc ConstantWattsSubscription(Empty) returns (stream ConstantWattsMessage) {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.idlelockout";
|
||||
option java_outer_classname = "IdleModeLockoutProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/IFitError.proto";
|
||||
|
||||
enum IdleModeLockoutState {
|
||||
LOCK_STATE_UNKNOWN = 0;
|
||||
LOCK_STATE_UNLOCKED = 1;
|
||||
LOCK_STATE_LOCKED = 2;
|
||||
}
|
||||
|
||||
message IdleModeLockoutMessage {
|
||||
IdleModeLockoutState state = 1;
|
||||
}
|
||||
|
||||
message IdleModeLockoutResult {
|
||||
oneof errorOrIdleModeLockoutState {
|
||||
IFitError error = 1;
|
||||
IdleModeLockoutState idleModeLockoutState = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.idlelockout";
|
||||
option java_outer_classname = "IdleModeLockoutServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "console/idlelockout/IdleModeLockout.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
service IdleModeLockoutService {
|
||||
rpc CanRead(Empty) returns (AvailabilityResponse) {}
|
||||
rpc CanWrite(Empty) returns (AvailabilityResponse) {}
|
||||
rpc GetIdleModeLockout(Empty) returns (IdleModeLockoutResult) {}
|
||||
rpc SetIdleModeLockout(IdleModeLockoutMessage) returns (IdleModeLockoutResult) {}
|
||||
rpc IdleModeLockoutSubscription(Empty) returns (stream IdleModeLockoutMessage) {}
|
||||
}
|
||||
297
src/android/src/main/proto/console/keypress/KeyCode.proto
Normal file
297
src/android/src/main/proto/console/keypress/KeyCode.proto
Normal file
@@ -0,0 +1,297 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.keypress";
|
||||
option java_outer_classname = "KeyCodeProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum KeyCode {
|
||||
NO_KEY = 0;
|
||||
STOP = 1;
|
||||
START = 2;
|
||||
SPEED_UP = 3;
|
||||
SPEED_DOWN = 4;
|
||||
INCLINE_UP = 5;
|
||||
INCLINE_DOWN = 6;
|
||||
RESISTANCE_UP = 7;
|
||||
RESISTANCE_DOWN = 8;
|
||||
GEAR_UP = 9;
|
||||
GEAR_DOWN = 10;
|
||||
WEIGHT_UP = 11;
|
||||
WEIGHT_DOWN = 12;
|
||||
AGE_UP = 13;
|
||||
AGE_DOWN = 14;
|
||||
SPEED_RESUME = 15;
|
||||
INCLINE_RESUME = 16;
|
||||
BLE_KEY = 17;
|
||||
ON_RESET = 18;
|
||||
PRIORITY_DISPLAY = 19;
|
||||
BURN_RATE_UP = 20;
|
||||
BURN_RATE_DOWN = 21;
|
||||
RECOVERY = 22;
|
||||
WORK = 23;
|
||||
START_STOP = 24;
|
||||
POWER_ON_OFF = 25;
|
||||
FAN_UP = 50;
|
||||
FAN_DOWN = 51;
|
||||
FAN_OFF = 52;
|
||||
FAN_MANUAL = 53;
|
||||
FAN_AUTO = 54;
|
||||
FAN_1 = 55;
|
||||
FAN_2 = 56;
|
||||
FAN_3 = 57;
|
||||
FAN_4 = 58;
|
||||
FAN_5 = 59;
|
||||
PC_BACK = 100;
|
||||
PC_MENU = 101;
|
||||
PC_HOME = 102;
|
||||
KEYPAD = 103;
|
||||
DISPLAY = 104;
|
||||
ENTER = 105;
|
||||
UP = 106;
|
||||
DOWN = 107;
|
||||
LEFT = 108;
|
||||
RIGHT = 109;
|
||||
TV_POWER = 120;
|
||||
TV_CHANNEL_UP = 121;
|
||||
TV_CHANNEL_DOWN = 122;
|
||||
TV_RECALL = 123;
|
||||
TV_MENU = 124;
|
||||
TV_SOURCE = 125;
|
||||
TV_SEEK = 126;
|
||||
TV_CLOSE_CAPTION = 127;
|
||||
TV_VOLUME_UP = 128;
|
||||
TV_VOLUME_DOWN = 129;
|
||||
TV_MUTE = 130;
|
||||
RIGHT_GEAR_UP = 150;
|
||||
RIGHT_GEAR_DOWN = 151;
|
||||
LEFT_GEAR_UP = 152;
|
||||
LEFT_GEAR_DOWN = 153;
|
||||
AUDIO_VOLUME_UP = 200;
|
||||
AUDIO_VOLUME_DOWN = 201;
|
||||
AUDIO_MUTE = 202;
|
||||
AUDIO_EQUALIZER = 203;
|
||||
AUDIO_SOURCE = 204;
|
||||
NUMBER_PAD_0 = 300;
|
||||
NUMBER_PAD_1 = 301;
|
||||
NUMBER_PAD_2 = 302;
|
||||
NUMBER_PAD_3 = 303;
|
||||
NUMBER_PAD_4 = 304;
|
||||
NUMBER_PAD_5 = 305;
|
||||
NUMBER_PAD_6 = 306;
|
||||
NUMBER_PAD_7 = 307;
|
||||
NUMBER_PAD_8 = 308;
|
||||
NUMBER_PAD_9 = 309;
|
||||
NUMBER_PAD_STAR = 310;
|
||||
NUMBER_PAD_DOT = 311;
|
||||
NUMBER_PAD_HASH = 312;
|
||||
NUMBER_PAD_OK = 313;
|
||||
NUMBER_PAD_ENTER = 314;
|
||||
ERGOFIT_TILT_FORWARD = 400;
|
||||
ERGOFIT_TILT_BACK = 401;
|
||||
ERGOFIT_UPRIGHT_UP = 402;
|
||||
ERGOFIT_UPRIGHT_DOWN = 403;
|
||||
ERGOFIT_MEMORY = 404;
|
||||
ERGOFIT_USER_1 = 405;
|
||||
ERGOFIT_USER_2 = 406;
|
||||
ERGOFIT_USER_3 = 407;
|
||||
ERGOFIT_USER_4 = 408;
|
||||
SET_TO_SHIP = 500;
|
||||
DEBUG_MODE = 501;
|
||||
LOG_MODE = 502;
|
||||
SETTINGS = 503;
|
||||
INCLINE_DISPLAY = 600;
|
||||
PULSE_DISPLAY = 601;
|
||||
WATTS_DISPLAY = 602;
|
||||
SPEED_DISPLAY = 603;
|
||||
TIME_DISPLAY = 604;
|
||||
PACE_DISPLAY = 605;
|
||||
CALORIES_DISPLAY = 606;
|
||||
DISTANCE_DISPLAY = 607;
|
||||
SCAN_DISPLAY = 608;
|
||||
MPH_1 = 1000;
|
||||
MPH_2 = 1001;
|
||||
MPH_3 = 1002;
|
||||
MPH_4 = 1003;
|
||||
MPH_5 = 1004;
|
||||
MPH_6 = 1005;
|
||||
MPH_7 = 1006;
|
||||
MPH_8 = 1007;
|
||||
MPH_9 = 1008;
|
||||
MPH_10 = 1009;
|
||||
MPH_11 = 1010;
|
||||
MPH_12 = 1011;
|
||||
MPH_13 = 1012;
|
||||
MPH_14 = 1013;
|
||||
MPH_15 = 1014;
|
||||
KPH_1 = 1100;
|
||||
KPH_2 = 1101;
|
||||
KPH_3 = 1102;
|
||||
KPH_4 = 1103;
|
||||
KPH_5 = 1104;
|
||||
KPH_6 = 1105;
|
||||
KPH_7 = 1106;
|
||||
KPH_8 = 1107;
|
||||
KPH_9 = 1108;
|
||||
KPH_10 = 1109;
|
||||
KPH_11 = 1110;
|
||||
KPH_12 = 1111;
|
||||
KPH_13 = 1112;
|
||||
KPH_14 = 1113;
|
||||
KPH_15 = 1114;
|
||||
KPH_16 = 1115;
|
||||
KPH_17 = 1116;
|
||||
KPH_18 = 1117;
|
||||
KPH_19 = 1118;
|
||||
KPH_20 = 1119;
|
||||
KPH_21 = 1120;
|
||||
KPH_22 = 1121;
|
||||
KPH_23 = 1122;
|
||||
KPH_24 = 1123;
|
||||
INCLINE_NEG_30 = 1200;
|
||||
INCLINE_NEG_29 = 1201;
|
||||
INCLINE_NEG_28 = 1202;
|
||||
INCLINE_NEG_27 = 1203;
|
||||
INCLINE_NEG_26 = 1204;
|
||||
INCLINE_NEG_25 = 1205;
|
||||
INCLINE_NEG_24 = 1206;
|
||||
INCLINE_NEG_23 = 1207;
|
||||
INCLINE_NEG_22 = 1208;
|
||||
INCLINE_NEG_21 = 1209;
|
||||
INCLINE_NEG_20 = 1210;
|
||||
INCLINE_NEG_19 = 1211;
|
||||
INCLINE_NEG_18 = 1212;
|
||||
INCLINE_NEG_17 = 1213;
|
||||
INCLINE_NEG_16 = 1214;
|
||||
INCLINE_NEG_15 = 1215;
|
||||
INCLINE_NEG_14 = 1216;
|
||||
INCLINE_NEG_13 = 1217;
|
||||
INCLINE_NEG_12 = 1218;
|
||||
INCLINE_NEG_11 = 1219;
|
||||
INCLINE_NEG_10 = 1220;
|
||||
INCLINE_NEG_9 = 1221;
|
||||
INCLINE_NEG_8 = 1222;
|
||||
INCLINE_NEG_7 = 1223;
|
||||
INCLINE_NEG_6 = 1224;
|
||||
INCLINE_NEG_5 = 1225;
|
||||
INCLINE_NEG_4 = 1226;
|
||||
INCLINE_NEG_3 = 1227;
|
||||
INCLINE_NEG_2 = 1228;
|
||||
INCLINE_NEG_1 = 1229;
|
||||
INCLINE_0 = 1230;
|
||||
INCLINE_1 = 1231;
|
||||
INCLINE_2 = 1232;
|
||||
INCLINE_3 = 1233;
|
||||
INCLINE_4 = 1234;
|
||||
INCLINE_5 = 1235;
|
||||
INCLINE_6 = 1236;
|
||||
INCLINE_7 = 1237;
|
||||
INCLINE_8 = 1238;
|
||||
INCLINE_9 = 1239;
|
||||
INCLINE_10 = 1240;
|
||||
INCLINE_11 = 1241;
|
||||
INCLINE_12 = 1242;
|
||||
INCLINE_13 = 1243;
|
||||
INCLINE_14 = 1244;
|
||||
INCLINE_15 = 1245;
|
||||
INCLINE_16 = 1246;
|
||||
INCLINE_17 = 1247;
|
||||
INCLINE_18 = 1248;
|
||||
INCLINE_19 = 1249;
|
||||
INCLINE_20 = 1250;
|
||||
INCLINE_21 = 1251;
|
||||
INCLINE_22 = 1252;
|
||||
INCLINE_23 = 1253;
|
||||
INCLINE_24 = 1254;
|
||||
INCLINE_25 = 1255;
|
||||
INCLINE_26 = 1256;
|
||||
INCLINE_27 = 1257;
|
||||
INCLINE_28 = 1258;
|
||||
INCLINE_29 = 1259;
|
||||
INCLINE_30 = 1260;
|
||||
INCLINE_31 = 1261;
|
||||
INCLINE_32 = 1262;
|
||||
INCLINE_33 = 1263;
|
||||
INCLINE_34 = 1264;
|
||||
INCLINE_35 = 1265;
|
||||
INCLINE_36 = 1266;
|
||||
INCLINE_37 = 1267;
|
||||
INCLINE_38 = 1268;
|
||||
INCLINE_39 = 1269;
|
||||
INCLINE_40 = 1270;
|
||||
INCLINE_41 = 1271;
|
||||
INCLINE_42 = 1272;
|
||||
INCLINE_43 = 1273;
|
||||
INCLINE_44 = 1274;
|
||||
INCLINE_45 = 1275;
|
||||
INCLINE_46 = 1276;
|
||||
INCLINE_47 = 1277;
|
||||
INCLINE_48 = 1278;
|
||||
INCLINE_49 = 1279;
|
||||
INCLINE_50 = 1280;
|
||||
RESISTANCE_0 = 1300;
|
||||
RESISTANCE_1 = 1301;
|
||||
RESISTANCE_2 = 1302;
|
||||
RESISTANCE_3 = 1303;
|
||||
RESISTANCE_4 = 1304;
|
||||
RESISTANCE_5 = 1305;
|
||||
RESISTANCE_6 = 1306;
|
||||
RESISTANCE_7 = 1307;
|
||||
RESISTANCE_8 = 1308;
|
||||
RESISTANCE_9 = 1309;
|
||||
RESISTANCE_10 = 1310;
|
||||
RESISTANCE_11 = 1311;
|
||||
RESISTANCE_12 = 1312;
|
||||
RESISTANCE_13 = 1313;
|
||||
RESISTANCE_14 = 1314;
|
||||
RESISTANCE_15 = 1315;
|
||||
RESISTANCE_16 = 1316;
|
||||
RESISTANCE_17 = 1317;
|
||||
RESISTANCE_18 = 1318;
|
||||
RESISTANCE_19 = 1319;
|
||||
RESISTANCE_20 = 1320;
|
||||
RESISTANCE_21 = 1321;
|
||||
RESISTANCE_22 = 1322;
|
||||
RESISTANCE_23 = 1323;
|
||||
RESISTANCE_24 = 1324;
|
||||
RESISTANCE_25 = 1325;
|
||||
RESISTANCE_26 = 1326;
|
||||
RESISTANCE_27 = 1327;
|
||||
RESISTANCE_28 = 1328;
|
||||
RESISTANCE_29 = 1329;
|
||||
RESISTANCE_30 = 1330;
|
||||
MANUAL_WORKOUT = 11000;
|
||||
MAP_WORKOUT = 11001;
|
||||
TRAIN_WORKOUT = 11002;
|
||||
COMPETE_WORKOUT = 11003;
|
||||
TRACK_WORKOUT = 11004;
|
||||
SET_A_GOAL_WORKOUT = 11005;
|
||||
VIDEO_WORKOUT = 11006;
|
||||
LOSE_WT_WORKOUT = 11007;
|
||||
CALORIES_WORKOUT = 11008;
|
||||
INTENSITY_WORKOUT = 11009;
|
||||
INCLINE_WORKOUT = 11010;
|
||||
SPEED_WORKOUT = 11011;
|
||||
PULSE_WORKOUT = 11012;
|
||||
PERFORMANCE_WORKOUT = 11013;
|
||||
DAY_WORKOUT = 11014;
|
||||
WEEK_WORKOUT = 11015;
|
||||
MONTH_WORKOUT = 11016;
|
||||
INTERVAL_WORKOUT = 11017;
|
||||
TEMP_WORKOUT = 11018;
|
||||
DUMMY_WORKOUT_1 = 11100;
|
||||
DUMMY_WORKOUT_2 = 11101;
|
||||
DUMMY_WORKOUT_3 = 11102;
|
||||
DUMMY_WORKOUT_4 = 11103;
|
||||
DUMMY_WORKOUT_5 = 11104;
|
||||
DUMMY_WORKOUT_6 = 11105;
|
||||
DUMMY_WORKOUT_7 = 11106;
|
||||
DUMMY_WORKOUT_8 = 11107;
|
||||
DUMMY_WORKOUT_9 = 11108;
|
||||
DUMMY_WORKOUT_10 = 11109;
|
||||
CALORIES_WORKOUT_0 = 12000;
|
||||
CALORIES_WORKOUT_999 = 12999;
|
||||
TIME_WORKOUT_0 = 13000;
|
||||
TIME_WORKOUT_99 = 13099;
|
||||
DUMMY = 9999;
|
||||
}
|
||||
21
src/android/src/main/proto/console/keypress/KeyPress.proto
Normal file
21
src/android/src/main/proto/console/keypress/KeyPress.proto
Normal file
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.keypress";
|
||||
option java_outer_classname = "KeyPressProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "console/keypress/KeyCode.proto";
|
||||
import "util/IFitError.proto";
|
||||
|
||||
message KeyPress {
|
||||
KeyCode code = 1;
|
||||
int32 timePressed = 2;
|
||||
int32 durationHeld = 3;
|
||||
}
|
||||
|
||||
message KeyPressResult {
|
||||
oneof errorOrKeyPress {
|
||||
IFitError error = 1;
|
||||
KeyPress keyPress = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.keypress";
|
||||
option java_outer_classname = "KeyPressServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "console/keypress/KeyPress.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
service KeyPressService {
|
||||
rpc CanRead(Empty) returns (AvailabilityResponse) {}
|
||||
rpc CanWriteVirtual(Empty) returns (AvailabilityResponse) {}
|
||||
rpc GetKeyPress(Empty) returns (KeyPressResult) {}
|
||||
rpc KeyPressSubscription(Empty) returns (stream KeyPress) {}
|
||||
rpc SetVirtualKeyPress(KeyPress) returns (KeyPressResult) {}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.console.proximity";
|
||||
option java_outer_classname = "ProximitySensingServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
service ProximitySensingService {
|
||||
|
||||
// check whether Movement Detection (PIR) is Readable
|
||||
rpc CanReadMovementDetect(Empty) returns (AvailabilityResponse) {}
|
||||
|
||||
// check whether User Distance (LIDAR) is Readable
|
||||
rpc CanReadUserDistance(Empty) returns (AvailabilityResponse) {}
|
||||
|
||||
// get the current Movement Detection (PIR) state
|
||||
rpc GetMovementDetect(Empty) returns (BooleanResponse) {}
|
||||
|
||||
// subscribe to Movement Detection (PIR) updates
|
||||
rpc MovementDetectSubscription(Empty) returns (stream BooleanResponse) {}
|
||||
|
||||
// get the current User Distance (LIDAR) in centimeters
|
||||
rpc GetUserDistance(Empty) returns (FloatResponse) {}
|
||||
|
||||
// subscribe to User Distance (LIDAR) updates
|
||||
rpc UserDistanceSubscription(Empty) returns (stream FloatResponse) {}
|
||||
}
|
||||
25
src/android/src/main/proto/console/sleep/SleepState.proto
Normal file
25
src/android/src/main/proto/console/sleep/SleepState.proto
Normal file
@@ -0,0 +1,25 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.sleep";
|
||||
option java_outer_classname = "SleepStateProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/IFitError.proto";
|
||||
|
||||
enum SleepState {
|
||||
SLEEP_STATE_UNKNOWN = 0;
|
||||
SLEEP_STATE_AWAKE = 1;
|
||||
SLEEP_STATE_INITIATE_SLEEP = 2;
|
||||
SLEEP_STATE_SLEEPING = 3;
|
||||
}
|
||||
|
||||
message SleepStateMessage {
|
||||
SleepState state = 1;
|
||||
}
|
||||
|
||||
message SleepStateResult {
|
||||
oneof errorOrSleepState {
|
||||
IFitError error = 1;
|
||||
SleepState sleepState = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.sleep";
|
||||
option java_outer_classname = "SleepStateServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "console/sleep/SleepState.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
service SleepStateService {
|
||||
rpc CanRead(Empty) returns (AvailabilityResponse) {}
|
||||
rpc CanWrite(Empty) returns (AvailabilityResponse) {}
|
||||
rpc GetSleepState(Empty) returns (SleepStateResult) {}
|
||||
rpc SetSleepState(SleepStateMessage) returns (SleepStateResult) {}
|
||||
rpc SleepStateSubscription(Empty) returns (stream SleepStateMessage) {}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "console/spoofing/SpoofPartNumberResult.proto";
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.console.spoofing";
|
||||
option java_outer_classname = "ConsoleSpoofingServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
service ConsoleSpoofingService {
|
||||
rpc SetSpoofedPartNumber(IntRequest) returns (SpoofPartNumberResult) {}
|
||||
rpc GetSpoofedPartNumber(Empty) returns (SpoofPartNumberResult) {}
|
||||
rpc ClearSpoofedPartNumber(Empty) returns (SpoofPartNumberResult) {}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
option java_package = "com.ifit.glassos.console.spoofing";
|
||||
option java_outer_classname = "SpoofPartNumberResultProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message SpoofPartNumberResult {
|
||||
oneof errorOrPartNumber {
|
||||
IFitError error = 1;
|
||||
int32 partNumber = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
option java_package = "com.ifit.glassos.console.tdf";
|
||||
option java_outer_classname = "TDFChainRingConfigProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum TDFChainRingConfig {
|
||||
INVALID_CHAIN_RING = 0;
|
||||
COMPACT_34_50 = 1;
|
||||
SUB_COMPACT_36_52 = 2;
|
||||
STANDARD_39_53 = 3;
|
||||
TRIPLE_30_39_53 = 4;
|
||||
}
|
||||
|
||||
message TDFChainRingConfigList {
|
||||
repeated TDFChainRingConfig chainRingConfigs = 1;
|
||||
}
|
||||
|
||||
message TDFChainRingConfigsResult {
|
||||
oneof errorOrChainRingConfigs {
|
||||
IFitError error = 1;
|
||||
TDFChainRingConfigList chainRingConfigs = 2;
|
||||
}
|
||||
}
|
||||
19
src/android/src/main/proto/console/tdf/TDFGear.proto
Normal file
19
src/android/src/main/proto/console/tdf/TDFGear.proto
Normal file
@@ -0,0 +1,19 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
option java_package = "com.ifit.glassos.console.tdf";
|
||||
option java_outer_classname = "TDFGearProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message TDFGear {
|
||||
int32 frontGear = 1;
|
||||
int32 rearGear = 2;
|
||||
}
|
||||
|
||||
message TDFGearResult {
|
||||
oneof errorOrGear {
|
||||
IFitError error = 1;
|
||||
TDFGear gear = 2;
|
||||
}
|
||||
}
|
||||
21
src/android/src/main/proto/console/tdf/TDFGearConfig.proto
Normal file
21
src/android/src/main/proto/console/tdf/TDFGearConfig.proto
Normal file
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "console/tdf/TDFChainRingConfig.proto";
|
||||
import "console/tdf/TDFRearCassetteConfig.proto";
|
||||
import "util/IFitError.proto";
|
||||
option java_package = "com.ifit.glassos.console.tdf";
|
||||
option java_outer_classname = "TDFGearConfigProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message TDFGearConfig {
|
||||
TDFChainRingConfig frontGearConfig = 1;
|
||||
TDFRearCassetteConfig rearGearConfig = 2;
|
||||
}
|
||||
|
||||
message TDFGearConfigResult {
|
||||
oneof errorOrGearConfig {
|
||||
IFitError error = 1;
|
||||
TDFGearConfig gearConfig = 2;
|
||||
}
|
||||
}
|
||||
29
src/android/src/main/proto/console/tdf/TDFGearService.proto
Normal file
29
src/android/src/main/proto/console/tdf/TDFGearService.proto
Normal file
@@ -0,0 +1,29 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "console/tdf/TDFChainRingConfig.proto";
|
||||
import "console/tdf/TDFGearConfig.proto";
|
||||
import "console/tdf/TDFGear.proto";
|
||||
import "console/tdf/TDFRearCassetteConfig.proto";
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.console.tdf";
|
||||
option java_outer_classname = "TDFGearServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
service TDFGearService {
|
||||
rpc IsSupported(Empty) returns (BooleanResponse) {}
|
||||
rpc ResetGearConfig(Empty) returns (TDFGearConfigResult) {}
|
||||
rpc ResetGearAndGearConfig(Empty) returns (BooleanResponse) {}
|
||||
rpc SetGearConfig(TDFGearConfig) returns (TDFGearConfigResult) {}
|
||||
rpc GetGearConfig(Empty) returns (TDFGearConfigResult) {}
|
||||
rpc ListFrontGearConfigs(Empty) returns (TDFChainRingConfigsResult) {}
|
||||
rpc ListRearGearConfigs(Empty) returns (TDFRearCassetteConfigsResult) {}
|
||||
rpc GearConfigChangedSubscription(Empty) returns (stream TDFGearConfig) {}
|
||||
|
||||
rpc SetGear(TDFGear) returns (TDFGearResult) {}
|
||||
rpc GetCurrentGear(Empty) returns (TDFGearResult) {}
|
||||
rpc GearChangedSubscription(Empty) returns (stream TDFGear) {}
|
||||
|
||||
rpc GetGearRatio(Empty) returns (FloatResponse) {}
|
||||
rpc GearRatioChangedSubscription(Empty) returns (stream FloatResponse) {}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
option java_package = "com.ifit.glassos.console.tdf";
|
||||
option java_outer_classname = "TDFRearCassetteConfigProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message TDFRearCassetteConfig {
|
||||
int32 minTeeth = 1;
|
||||
int32 maxTeeth = 2;
|
||||
int32 speeds = 3;
|
||||
repeated int32 teethAtGear = 4;
|
||||
}
|
||||
|
||||
message TDFRearCassetteConfigList {
|
||||
repeated TDFRearCassetteConfig cassetteConfigs = 1;
|
||||
}
|
||||
|
||||
message TDFRearCassetteConfigsResult {
|
||||
oneof errorOrCassetteConfigs {
|
||||
IFitError error = 1;
|
||||
TDFRearCassetteConfigList cassetteConfigs = 2;
|
||||
}
|
||||
}
|
||||
14
src/android/src/main/proto/console/update/FirmwareType.proto
Normal file
14
src/android/src/main/proto/console/update/FirmwareType.proto
Normal file
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.update";
|
||||
option java_outer_classname = "FirmwareTypeProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum FirmwareType {
|
||||
FIRMWARE_TYPE_UNKNOWN = 0;
|
||||
FIRMWARE_TYPE_BRAINBOARD = 1;
|
||||
FIRMWARE_TYPE_ANT_PLUS_APPLICATION = 2;
|
||||
FIRMWARE_TYPE_ANT_PLUS_BOOTLOADER = 3;
|
||||
FIRMWARE_TYPE_MOTOR_CONTROLLER = 4;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.update";
|
||||
option java_outer_classname = "FirmwareUpdateFileProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "console/update/FirmwareType.proto";
|
||||
|
||||
message FirmwareUpdateFile {
|
||||
FirmwareType updateType = 1;
|
||||
string filePath = 2;
|
||||
string fileName = 3;
|
||||
string version = 4;
|
||||
int32 partNumber = 5;
|
||||
bool forceUpdate = 6;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.update";
|
||||
option java_outer_classname = "FirmwareUpdateServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "console/update/FirmwareUpdateFile.proto";
|
||||
import "console/update/FirmwareUpdateState.proto";
|
||||
import "console/update/FirmwareUpdateStatus.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
service FirmwareUpdateService {
|
||||
rpc GetFirmwareUpdateStatus(Empty) returns (FirmwareUpdateStatus) {}
|
||||
rpc FirmwareUpdateStatusChangedSubscription(Empty) returns (stream FirmwareUpdateStatus) {}
|
||||
rpc StartFirmwareUpdate(FirmwareUpdateFile) returns (FirmwareUpdateStatus) {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.update";
|
||||
option java_outer_classname = "FirmwareUpdateStateProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum FirmwareUpdateState {
|
||||
FIRMWARE_UPDATE_STATE_UNKNOWN = 0;
|
||||
FIRMWARE_UPDATE_STATE_IDLE = 1;
|
||||
FIRMWARE_UPDATE_STATE_PREPARING = 2;
|
||||
FIRMWARE_UPDATE_STATE_UPDATING = 3;
|
||||
FIRMWARE_UPDATE_STATE_VERIFYING = 4;
|
||||
FIRMWARE_UPDATE_STATE_SUCCESSFUL = 5;
|
||||
FIRMWARE_UPDATE_STATE_FAILED = 6;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.update";
|
||||
option java_outer_classname = "FirmwareUpdateStatusProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "console/update/FirmwareUpdateFile.proto";
|
||||
import "console/update/FirmwareUpdateState.proto";
|
||||
|
||||
message FirmwareUpdateStatus {
|
||||
FirmwareUpdateState state = 1;
|
||||
string updateSessionId = 2;
|
||||
FirmwareUpdateFile updateFile = 3;
|
||||
google.protobuf.Timestamp startTime = 4;
|
||||
google.protobuf.Timestamp endTime = 5;
|
||||
string resultMessage = 6;
|
||||
float percentComplete = 7;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.useractivity";
|
||||
option java_outer_classname = "UserActivityProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/IFitError.proto";
|
||||
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
message DurationResult {
|
||||
google.protobuf.Duration duration = 1;
|
||||
}
|
||||
|
||||
message UserActivityOverrideMessage {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message SetDurationRequest {
|
||||
google.protobuf.Duration duration = 1;
|
||||
}
|
||||
|
||||
message UserActivityServiceResult {
|
||||
oneof errorOrSuccess {
|
||||
IFitError error = 1;
|
||||
bool success = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.useractivity";
|
||||
option java_outer_classname = "UserActivityServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "console/useractivity/UserActivity.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
service UserActivityService {
|
||||
rpc DurationSinceLastScreenTap(Empty) returns (stream DurationResult) {}
|
||||
rpc StartUserActivityOverride(Empty) returns (UserActivityOverrideMessage) {}
|
||||
rpc CompleteUserActivityOverride(UserActivityOverrideMessage) returns (UserActivityServiceResult) {}
|
||||
rpc SetDurationSinceLastScreenTap(SetDurationRequest) returns (UserActivityServiceResult) {}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.console.virtualdmk";
|
||||
option java_outer_classname = "VirtualDMKServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
service VirtualDMKService {
|
||||
rpc GetDMKOverride(Empty) returns (BooleanResponse) {}
|
||||
rpc SetDMKOverride(BooleanRequest) returns (IFitError) {}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "user/UserTier.proto";
|
||||
import "console/ConsoleType.proto";
|
||||
import "club/ClubSettingsService.proto";
|
||||
option java_package = "com.ifit.glassos.featuregates";
|
||||
option java_outer_classname = "FeatureGateFacetProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum FeatureGateFacet {
|
||||
FEATURE_FACET_UNKNOWN = 0;
|
||||
FEATURE_FACET_CHINA = 1;
|
||||
FEATURE_FACET_CLUB_CONSOLE = 2;
|
||||
FEATURE_FACET_CLUB_USER_ROLE = 3;
|
||||
FEATURE_FACET_DEMO_MODE = 4;
|
||||
FEATURE_FACET_ENTIRE_FEATURE = 5;
|
||||
FEATURE_FACET_MOBILE = 6;
|
||||
FEATURE_FACET_MOBILE_FORM_FACTOR = 7;
|
||||
FEATURE_FACET_MODALITY = 8;
|
||||
FEATURE_FACET_SOFTWARE_NUMBER = 9;
|
||||
FEATURE_FACET_USER_TIER = 10;
|
||||
}
|
||||
|
||||
message FacetMessage {
|
||||
FeatureGateFacet featureGateFacet = 1;
|
||||
oneof payload {
|
||||
BooleanFacetPayload booleanFacetPayload = 2;
|
||||
EnumeratedUserRoleFacetMessage enumeratedUserRoleFacetMessage = 3;
|
||||
EnumeratedStringFacetMessage enumeratedStringFacetMessage = 4;
|
||||
EnumeratedConsoleTypeFacetMessage enumeratedConsoleTypeFacetMessage = 5;
|
||||
EnumeratedIntFacetMessage enumeratedIntFacetMessage = 6;
|
||||
EnumeratedUserTierFacetMessage enumeratedUserTierFacetMessage = 7;
|
||||
}
|
||||
}
|
||||
|
||||
message BooleanFacetPayload {
|
||||
bool enabled = 1;
|
||||
}
|
||||
|
||||
message EnumeratedUserRoleFacetMessage {
|
||||
repeated club.UserRole allowedValues = 1;
|
||||
repeated club.UserRole disallowedValues = 2;
|
||||
}
|
||||
|
||||
message EnumeratedStringFacetMessage {
|
||||
repeated string allowedValues = 1;
|
||||
repeated string disallowedValues = 2;
|
||||
}
|
||||
|
||||
message EnumeratedConsoleTypeFacetMessage {
|
||||
repeated ConsoleType allowedValues = 1;
|
||||
repeated ConsoleType disallowedValues = 2;
|
||||
}
|
||||
|
||||
message EnumeratedIntFacetMessage {
|
||||
repeated int32 allowedValues = 1;
|
||||
repeated int32 disallowedValues = 2;
|
||||
}
|
||||
|
||||
message EnumeratedUserTierFacetMessage {
|
||||
repeated UserTier allowedValues = 1;
|
||||
repeated UserTier disallowedValues = 2;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "featuregates/FeatureGateFacet.proto";
|
||||
import "featuregates/GatedFeature.proto";
|
||||
import "club/ClubSettingsService.proto";
|
||||
import "console/ConsoleType.proto";
|
||||
import "user/UserTier.proto";
|
||||
option java_package = "com.ifit.glassos.featuregates";
|
||||
option java_outer_classname = "FeatureGateResultProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message FeatureGateResult {
|
||||
GatedFeature gatedFeature = 1;
|
||||
bool enabled = 2;
|
||||
bool incomplete = 3;
|
||||
FacetMessage featureGateFacet = 4;
|
||||
oneof value {
|
||||
bool booleanValue = 5;
|
||||
club.UserRole userRole = 6;
|
||||
string stringValue = 7;
|
||||
int32 intValue = 8;
|
||||
ConsoleType consoleType = 9;
|
||||
UserTier userTier = 10;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "featuregates/FeatureGateFacet.proto";
|
||||
import "featuregates/GatedFeature.proto";
|
||||
option java_package = "com.ifit.glassos.featuregates";
|
||||
option java_outer_classname = "FeatureGateRuleProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message FeatureGateRule {
|
||||
GatedFeature gatedFeature = 1;
|
||||
bool incomplete = 2;
|
||||
repeated FacetMessage featureGateFacet = 3;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user