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 |
3712
.github/workflows/main.yml
vendored
3712
.github/workflows/main.yml
vendored
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "featuregates/GatedFeature.proto";
|
||||
import "featuregates/FeatureGateResult.proto";
|
||||
import "featuregates/FeatureGateRule.proto";
|
||||
option java_package = "com.ifit.glassos.featuregates";
|
||||
option java_outer_classname = "FeatureGateServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message GatedFeatureMessage {
|
||||
GatedFeature gatedFeature = 1;
|
||||
}
|
||||
|
||||
service FeatureGateService {
|
||||
rpc CheckFeatureGateStatus(GatedFeatureMessage) returns (FeatureGateResult) {}
|
||||
rpc GetFeatureGateRule(GatedFeatureMessage) returns (FeatureGateRule) {}
|
||||
}
|
||||
13
src/android/src/main/proto/featuregates/GatedFeature.proto
Normal file
13
src/android/src/main/proto/featuregates/GatedFeature.proto
Normal file
@@ -0,0 +1,13 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.featuregates";
|
||||
option java_outer_classname = "GatedFeatureProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum GatedFeature {
|
||||
GATED_FEATURE_UNKNOWN = 0;
|
||||
GATED_FEATURE_UPSELL = 1;
|
||||
GATED_FEATURE_CHALLENGES = 2;
|
||||
GATED_FEATURE_WORKOUT_CREATOR = 3;
|
||||
}
|
||||
442
src/android/src/main/proto/firebase/perf/v1/perf_metric.proto
Normal file
442
src/android/src/main/proto/firebase/perf/v1/perf_metric.proto
Normal file
@@ -0,0 +1,442 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
// Protos for logging firebase performance monitoring metrics collected by the
|
||||
// Firebase performance sdk and transmitted to the backend using Firebase data transport.
|
||||
//
|
||||
// PRD: go/fireperf-prd
|
||||
// SDK Data Model: go/fireperf-data-model
|
||||
syntax = "proto2";
|
||||
|
||||
package firebase.perf.v1;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "FirebasePerfMetricProto";
|
||||
option java_package = "com.google.firebase.perf.v1";
|
||||
|
||||
// Firebase Perf Reporting Message
|
||||
option objc_class_prefix = "FPRMSG";
|
||||
|
||||
// Single unit of performance data collected from firebase integrated 3P apps by the firebase
|
||||
// performance sdk. This will be an an extension to GWSLogEntryProto and will correspond to one
|
||||
// record in the RecordIO logs generated by firebase data transport. Every firebase performance
|
||||
// related event logged to firebase data transport by the sdk will encapsulate one instance of this
|
||||
// object.
|
||||
//
|
||||
// Next tag: 6
|
||||
message PerfMetric {
|
||||
|
||||
// Additional metadata about an application and its state (including state of
|
||||
// the device at runtime) that is not provided by firebase data transport.
|
||||
optional ApplicationInfo application_info = 1;
|
||||
|
||||
// A metric which represents the performance statistics collected within an
|
||||
// instrumented trace.
|
||||
optional TraceMetric trace_metric = 2;
|
||||
|
||||
// A metric which represents the network latency, bandwidth and network
|
||||
// connection info about a network request captured by the firebase sdk.
|
||||
optional NetworkRequestMetric network_request_metric = 3;
|
||||
|
||||
// A metric which represents session gauges, such as cpu, memory, battery,
|
||||
// within a session.
|
||||
optional GaugeMetric gauge_metric = 4;
|
||||
|
||||
// A metric which represents the transport related information.
|
||||
// When transport_info field is empty, it means the message is from Cct.
|
||||
optional TransportInfo transport_info = 5;
|
||||
}
|
||||
|
||||
// Metric which represents everything collected in the span of a trace. A trace
|
||||
// may be further divided into subtraces.
|
||||
// The trace can either be a default out of the box trace which is a
|
||||
// part of the default instrumentation provided by the firebase performance sdk
|
||||
// or a custom trace instrumented by the app developer using the sdk apis.
|
||||
//
|
||||
// Next tag: 10
|
||||
message TraceMetric {
|
||||
// The name of the trace. This could either be a user defined name for the
|
||||
// developer instrumented custom traces or a default for traces automatically
|
||||
// instrumented by the Firebase Performance SDK. The max length of 64
|
||||
// characters will be enforced by the sdk.
|
||||
optional string name = 1; // required.
|
||||
|
||||
// If true, then this is considered to be a trace automatically instrumented
|
||||
// by the performance sdk. Otherwise, it is considered to be a custom trace
|
||||
// instrumented by the developer using firebase perf sdk apis.
|
||||
optional bool is_auto = 2;
|
||||
|
||||
// The timestamp in microseconds since epoch when the trace was started. This
|
||||
// time is recorded using the device clock.
|
||||
optional int64 client_start_time_us = 4; // required.
|
||||
|
||||
// The duration of the trace in microseconds.
|
||||
optional int64 duration_us = 5; // required.
|
||||
|
||||
// A map of custom or default counter names to values.
|
||||
map<string, int64> counters = 6;
|
||||
|
||||
// The metrics for subtraces within the trace.
|
||||
|
||||
// The following restrictions are currently enforced by the sdk on subtraces:
|
||||
// Subtraces should only have 1 level of nesting.
|
||||
// Subtraces should be non overlapping.
|
||||
// Subtraces should be continuous, i.e no gaps between consecutive subtraces.
|
||||
repeated TraceMetric subtraces = 7;
|
||||
|
||||
// A map of trace-level custom attribute names to values.
|
||||
map<string, string> custom_attributes = 8;
|
||||
|
||||
// Sessions across which the trace spanned. A session lasts from one
|
||||
// change in the app state (foreground/background) to the next. Basically
|
||||
// every foreground and background session gets it's own session id. A trace
|
||||
// may span across multiple such sessions. So we need a list to identify which
|
||||
// sessions it spanned across.
|
||||
repeated PerfSession perf_sessions = 9;
|
||||
}
|
||||
|
||||
// Metric which represents the latency, bandwidth consumption and other details
|
||||
// about a network request captured by the firebase sdk.
|
||||
//
|
||||
// Next tag: 14
|
||||
message NetworkRequestMetric {
|
||||
// The parameterless url to which the network request was made. The sdk will
|
||||
// redact the unnecessary components of the URL and only log the components
|
||||
// which are useful. For a url of the form
|
||||
// scheme://host[:port]/path[?params][#fragment], the sdk should only log
|
||||
// scheme://host[:port]/path
|
||||
// Example:
|
||||
// Captured Url: https://wwww.google.com/maps/cities#seattle?id=123
|
||||
// Logged Url: https://wwww.google.com/maps/cities
|
||||
optional string url = 1; // required.
|
||||
|
||||
// Supported HTTP methods for aggregating network requests. All network
|
||||
// requests that can not be classified into the 9 methods below should be set
|
||||
// to HTTP_METHOD_UNKNOWN.
|
||||
enum HttpMethod {
|
||||
HTTP_METHOD_UNKNOWN = 0;
|
||||
GET = 1;
|
||||
PUT = 2;
|
||||
POST = 3;
|
||||
DELETE = 4;
|
||||
HEAD = 5;
|
||||
PATCH = 6;
|
||||
OPTIONS = 7;
|
||||
TRACE = 8;
|
||||
CONNECT = 9;
|
||||
}
|
||||
|
||||
// The HTTP verb for the network request. Common values include GET,
|
||||
// PUT, POST and DELETE
|
||||
optional HttpMethod http_method = 2; // required.
|
||||
|
||||
// The size of the payload in the request.
|
||||
optional int64 request_payload_bytes = 3;
|
||||
|
||||
// The size of the payload in the response.
|
||||
optional int64 response_payload_bytes = 4;
|
||||
|
||||
// Info about the type of client error during network call.
|
||||
enum NetworkClientErrorReason {
|
||||
// Unspecified Network Client Error Reason.
|
||||
NETWORK_CLIENT_ERROR_REASON_UNKNOWN = 0;
|
||||
// No attempt made to classify the error.
|
||||
GENERIC_CLIENT_ERROR = 1;
|
||||
// Add the specific client error types below.
|
||||
}
|
||||
|
||||
// The client error received from the networking library.
|
||||
// Do not record a client error if we have HTTP response code available.
|
||||
optional NetworkClientErrorReason network_client_error_reason = 11;
|
||||
|
||||
// The Http response code received from the server.
|
||||
optional int32 http_response_code = 5; // required.
|
||||
|
||||
// The value of the content type header in the response.
|
||||
optional string response_content_type = 6;
|
||||
|
||||
// The timestamp in microseconds since epoch when the network request was
|
||||
// initiated. This time is recorded using the device clock.
|
||||
optional int64 client_start_time_us = 7; // required.
|
||||
|
||||
// The time in microseconds since the start of the network request and the
|
||||
// upload of the last request byte.
|
||||
optional int64 time_to_request_completed_us = 8;
|
||||
|
||||
// The time in microseconds between the start of the network request and the
|
||||
// receipt of the first byte of the response headers.
|
||||
optional int64 time_to_response_initiated_us = 9;
|
||||
|
||||
// The time in microseconds between the start of the network request and the
|
||||
// receipt of the last response byte.
|
||||
optional int64 time_to_response_completed_us = 10; // required.
|
||||
|
||||
// A map of network-level custom attribute names to values.
|
||||
map<string, string> custom_attributes = 12;
|
||||
|
||||
// Sessions across which the network request spanned. A session lasts
|
||||
// from one change in the app state (foreground/background) to the next.
|
||||
// Basically every foreground and background session gets it's own session id.
|
||||
// A network request may span across multiple such sessions. So we need a list
|
||||
// to identify which sessions it spanned across.
|
||||
repeated PerfSession perf_sessions = 13;
|
||||
}
|
||||
|
||||
// Metadata about a session and the amount of detail information it contains.
|
||||
// See go/what-is-a-perf-session
|
||||
message PerfSession {
|
||||
// The id of a session.
|
||||
optional string session_id = 1;
|
||||
|
||||
// The level of amount of detailed information that this session captures.
|
||||
repeated SessionVerbosity session_verbosity = 2;
|
||||
}
|
||||
|
||||
// Metric which represents gauges collected during the span of a session,
|
||||
// including cpu, memory, battery, etc.
|
||||
// The gauges will be collected by our own sdk and be purely numeric readings,
|
||||
// user cannot pass any information here, so cannot contain PIIs.
|
||||
//
|
||||
// Next tag: 6
|
||||
message GaugeMetric {
|
||||
// Identifier of the session in which this gauge reading takes place.
|
||||
// A session_id is specific to a device instance, and is used to tie gauge
|
||||
// metrics to other peer traces and network requests that occurs during
|
||||
// the session.
|
||||
optional string session_id = 1;
|
||||
|
||||
// Metadata of gauge metrics whose value stay constant throughout the session.
|
||||
optional GaugeMetadata gauge_metadata = 3;
|
||||
|
||||
// List of cpu gauge readings recorded in the session.
|
||||
repeated CpuMetricReading cpu_metric_readings = 2;
|
||||
|
||||
// List of Android memory readings recorded, absent for iOS apps.
|
||||
repeated AndroidMemoryReading android_memory_readings = 4;
|
||||
}
|
||||
|
||||
// One reading of cpu gauge metric.
|
||||
// See go/fireperf-cpu-gauge-metric.
|
||||
//
|
||||
// Next tag: 4
|
||||
message CpuMetricReading {
|
||||
// The timestamp in microseconds since epoch when this snapshot took place.
|
||||
// This time is recorded using the device clock.
|
||||
optional int64 client_time_us = 1;
|
||||
// The total user cpu time since process started in microseconds.
|
||||
optional int64 user_time_us = 2;
|
||||
// The total system cpu time since process started in microseconds.
|
||||
optional int64 system_time_us = 3;
|
||||
}
|
||||
|
||||
// One reading of Android memory gauge metric.
|
||||
// Note that this is cheap-to-capture memory reading, which is different from
|
||||
// application's summary of memory usage (expensive to capture). Summary of
|
||||
// memory usage will be captured at a much lower frequency in a different proto.
|
||||
// See go/fireperf-sessions-memory.
|
||||
//
|
||||
// Next tag: 3
|
||||
message AndroidMemoryReading {
|
||||
// The timestamp in microseconds since epoch when this snapshot took place.
|
||||
// This time is recorded using the device clock.
|
||||
optional int64 client_time_us = 1;
|
||||
|
||||
// The amount of java heap memory that the app is using, in kilobytes.
|
||||
optional int32 used_app_java_heap_memory_kb = 2;
|
||||
}
|
||||
|
||||
// Metadata about gauges of a session.
|
||||
// These are the gauge values that stay constant throughout the entire session.
|
||||
// Examples include maxAppJavaHeapMemory (max memory allowed for the app) and
|
||||
// cpuFrequency (frequency of cpu of the device that the app is running on).
|
||||
// As long as one GaugeMetadata is sent for a session, these metadata will be
|
||||
// available for all elements of the session. If multiple GaugeMetadata are sent
|
||||
// for the same session, they are expected to be identical.
|
||||
//
|
||||
// Next tag: 7
|
||||
message GaugeMetadata {
|
||||
// Deprecated on 09/2022.
|
||||
optional string process_name = 1 [deprecated = true];
|
||||
|
||||
// Clock rate of the cpu of the device, in kHz.
|
||||
optional int32 cpu_clock_rate_khz = 2;
|
||||
|
||||
// The number of cpu cores that the device has.
|
||||
optional int32 cpu_processor_count = 6;
|
||||
|
||||
// Size of RAM of the device, in kilobytes.
|
||||
optional int32 device_ram_size_kb = 3;
|
||||
|
||||
// Maximum amount of memory the app can use before an OutOfMemoryException
|
||||
// is triggered, in kilobytes.
|
||||
// Only present for Android apps.
|
||||
optional int32 max_app_java_heap_memory_kb = 4;
|
||||
|
||||
// The maximum amount of memory the app is encouraged to use to be properly
|
||||
// respectful of the limits of the client device.
|
||||
// Only present for Android apps.
|
||||
optional int32 max_encouraged_app_java_heap_memory_kb = 5;
|
||||
}
|
||||
|
||||
// Additional metadata about an application and its state (including state of
|
||||
// the device at runtime) that is not provided by firebase data transport.
|
||||
//
|
||||
// Next tag: 8
|
||||
message ApplicationInfo {
|
||||
// Identifier for the application that has been registered with firebase.
|
||||
// Contains pantheon project number, platform and the hash of the (package
|
||||
// name or bundle id) fields in hex.
|
||||
// [Version]:[Project Number]:[Platform]:[Hash(package_name/bundle_id)]
|
||||
// The app id contains Pantheon project number which is a GAIA ID that
|
||||
// identifies a particular organization or a customer.
|
||||
optional string google_app_id = 1; // required.
|
||||
|
||||
// The App Instance Id which is used to compute the distinct users for which
|
||||
// the metrics are recorded.
|
||||
// Look at go/iid-features for more details about the instance id.
|
||||
optional string app_instance_id = 2; // required.
|
||||
|
||||
// One of android_app_info, ios_app_info, and web_app_info is required.
|
||||
// Additional information specific to an android app.
|
||||
optional AndroidApplicationInfo android_app_info = 3;
|
||||
|
||||
// State of the application process during metric collection.
|
||||
optional ApplicationProcessState application_process_state = 5; // required.
|
||||
|
||||
// A map of global-level custom attribute names to values.
|
||||
map<string, string> custom_attributes = 6;
|
||||
}
|
||||
|
||||
// Additional metadata about an android application that is not provided by
|
||||
// firebase data transport.
|
||||
//
|
||||
// Next tag: 4
|
||||
message AndroidApplicationInfo {
|
||||
// The package name of the android application.
|
||||
// e.g com.google.android.apps.maps
|
||||
optional string package_name = 1; // required.
|
||||
|
||||
// The sdk version of the firebase perf android sdk.
|
||||
optional string sdk_version = 2; // required.
|
||||
|
||||
// The versionName of the android application as shown on the play store.
|
||||
// Firebase data transport logs the versionCode in the GWSLogEntryProto field:
|
||||
// PlayExtension.client_info.android_client_info.application_build
|
||||
// This field is necessary till the data transport supports logging version_name by
|
||||
// default: b/32584283
|
||||
optional string version_name = 3;
|
||||
}
|
||||
|
||||
// To describe the network connectivity of the client.
|
||||
// Copied from go/clientanalytics.proto
|
||||
// Next tag: 3
|
||||
message NetworkConnectionInfo {
|
||||
enum NetworkType {
|
||||
NONE = -1;
|
||||
MOBILE = 0;
|
||||
WIFI = 1;
|
||||
MOBILE_MMS = 2;
|
||||
MOBILE_SUPL = 3;
|
||||
MOBILE_DUN = 4;
|
||||
MOBILE_HIPRI = 5;
|
||||
WIMAX = 6;
|
||||
BLUETOOTH = 7;
|
||||
DUMMY = 8;
|
||||
ETHERNET = 9;
|
||||
MOBILE_FOTA = 10;
|
||||
MOBILE_IMS = 11;
|
||||
MOBILE_CBS = 12;
|
||||
WIFI_P2P = 13;
|
||||
MOBILE_IA = 14;
|
||||
MOBILE_EMERGENCY = 15;
|
||||
PROXY = 16;
|
||||
VPN = 17;
|
||||
}
|
||||
|
||||
enum MobileSubtype {
|
||||
UNKNOWN_MOBILE_SUBTYPE = 0;
|
||||
GPRS = 1;
|
||||
EDGE = 2;
|
||||
UMTS = 3;
|
||||
CDMA = 4;
|
||||
EVDO_0 = 5;
|
||||
EVDO_A = 6;
|
||||
RTT = 7;
|
||||
HSDPA = 8;
|
||||
HSUPA = 9;
|
||||
HSPA = 10;
|
||||
IDEN = 11;
|
||||
EVDO_B = 12;
|
||||
LTE = 13;
|
||||
EHRPD = 14;
|
||||
HSPAP = 15;
|
||||
GSM = 16;
|
||||
TD_SCDMA = 17;
|
||||
IWLAN = 18;
|
||||
LTE_CA = 19;
|
||||
|
||||
// COMBINED has value -1 in NetworkIdentity.java, but is given the value
|
||||
// 100 here to save (disk) space. The value -1 takes up the full 10 bytes in
|
||||
// a varint for enums, but the value 100 only takes up 1 byte.
|
||||
COMBINED = 100;
|
||||
}
|
||||
|
||||
// The current network connectivity type when the event was logged in the
|
||||
// client
|
||||
optional NetworkType network_type = 1 [default = NONE];
|
||||
|
||||
// The current mobile connectivity subtype when the event was logged in the
|
||||
// client
|
||||
optional MobileSubtype mobile_subtype = 2 [default = UNKNOWN_MOBILE_SUBTYPE];
|
||||
}
|
||||
|
||||
// Transport related metadata info.
|
||||
// Next tag: 2
|
||||
message TransportInfo {
|
||||
// Dispatch destination for the event.
|
||||
enum DispatchDestination {
|
||||
SOURCE_UNKNOWN = 0; // Reserved
|
||||
FL_LEGACY_V1 = 1; // Flg legacy endpoint
|
||||
}
|
||||
|
||||
// Destination to which the events are sent.
|
||||
optional DispatchDestination dispatch_destination = 1;
|
||||
}
|
||||
|
||||
// Metadata about the state of application process during metrics collection.
|
||||
//
|
||||
enum ApplicationProcessState {
|
||||
// Unspecified application process state.
|
||||
APPLICATION_PROCESS_STATE_UNKNOWN = 0;
|
||||
// Application process was in foreground
|
||||
FOREGROUND = 1;
|
||||
// Application process was in background
|
||||
BACKGROUND = 2;
|
||||
// Application process was both in foreground and background for the duration
|
||||
// of metrics collection.
|
||||
FOREGROUND_BACKGROUND = 3;
|
||||
}
|
||||
|
||||
// The level of detailed information that is captured in a Perf Session, known
|
||||
// as a session's verbosity. For different session we collect different levels
|
||||
// of detailed information (or none at all) to avoid penalizing the same device
|
||||
// constantly.
|
||||
enum SessionVerbosity {
|
||||
// Session doesn't have detailed information.
|
||||
SESSION_VERBOSITY_NONE = 0;
|
||||
// Session has gauges and system events information.
|
||||
// See go/fireperf-samples-metrics-all for the list of gauges and
|
||||
// system events that are captured within a session.
|
||||
GAUGES_AND_SYSTEM_EVENTS = 1;
|
||||
}
|
||||
26
src/android/src/main/proto/settings/BrightnessService.proto
Normal file
26
src/android/src/main/proto/settings/BrightnessService.proto
Normal file
@@ -0,0 +1,26 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "BrightnessServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
message Brightness {
|
||||
float brightnessPercent = 1;
|
||||
}
|
||||
|
||||
message BrightnessResult {
|
||||
oneof errorOrBrightness {
|
||||
IFitError error = 1;
|
||||
Brightness brightness = 2;
|
||||
}
|
||||
}
|
||||
|
||||
service BrightnessService {
|
||||
rpc BrightnessChanged(Empty) returns (stream Brightness) {}
|
||||
rpc SetBrightness(Brightness) returns (BrightnessResult) {}
|
||||
rpc GetBrightness(Empty) returns (BrightnessResult) {}
|
||||
}
|
||||
39
src/android/src/main/proto/settings/DemoModeService.proto
Normal file
39
src/android/src/main/proto/settings/DemoModeService.proto
Normal file
@@ -0,0 +1,39 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "DemoModeServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
message DemoMode {
|
||||
bool demoModeEnabled = 1;
|
||||
}
|
||||
|
||||
message DemoModeResult {
|
||||
oneof errorOrDemoMode {
|
||||
IFitError error = 1;
|
||||
DemoMode demoMode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message DemoModeScreensaverVolume {
|
||||
float volume = 1;
|
||||
}
|
||||
|
||||
message DemoModeScreensaverVolumeResult {
|
||||
oneof errorOrDemoModeScreensaverVolume {
|
||||
IFitError error = 1;
|
||||
DemoModeScreensaverVolume demoModeScreensaverVolume = 2;
|
||||
}
|
||||
}
|
||||
|
||||
service DemoModeService {
|
||||
rpc DemoModeChanged(Empty) returns (stream DemoMode) {}
|
||||
rpc SetDemoMode(DemoMode) returns (DemoModeResult) {}
|
||||
rpc GetDemoMode(Empty) returns (DemoModeResult) {}
|
||||
rpc SetDemoModeScreensaverVolume(DemoModeScreensaverVolume) returns (DemoModeScreensaverVolumeResult) {}
|
||||
rpc GetDemoModeScreensaverVolume(Empty) returns (DemoModeScreensaverVolumeResult) {}
|
||||
}
|
||||
29
src/android/src/main/proto/settings/FanStateService.proto
Normal file
29
src/android/src/main/proto/settings/FanStateService.proto
Normal file
@@ -0,0 +1,29 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "FanStateServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum FanState {
|
||||
FAN_STATE_OFF = 0;
|
||||
FAN_STATE_LOW = 1;
|
||||
FAN_STATE_MEDIUM = 2;
|
||||
FAN_STATE_HIGH = 3;
|
||||
FAN_STATE_AUTO = 4;
|
||||
FAN_STATE_UNKNOWN = 5;
|
||||
}
|
||||
|
||||
message FanStateMessage {
|
||||
FanState state = 1;
|
||||
}
|
||||
|
||||
service FanStateService {
|
||||
rpc CanRead(Empty) returns (AvailabilityResponse) {}
|
||||
rpc CanWrite(Empty) returns (AvailabilityResponse) {}
|
||||
rpc GetFanState(Empty) returns (FanStateMessage) {}
|
||||
rpc FanStateChanged(Empty) returns (stream FanStateMessage) {}
|
||||
rpc SetFanState(FanStateMessage) returns (Empty) {}
|
||||
rpc IsAutoFanStateSupported(Empty) returns (AvailabilityResponse) {}
|
||||
}
|
||||
20
src/android/src/main/proto/settings/MaxSpeedService.proto
Normal file
20
src/android/src/main/proto/settings/MaxSpeedService.proto
Normal file
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "MaxSpeedServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message MaxSpeedMessage {
|
||||
double maxSpeedKph = 1;
|
||||
}
|
||||
|
||||
service MaxSpeedService {
|
||||
// sets the Max Speed for scaling workouts, we will not auto scale to a control set that would
|
||||
// go above this value
|
||||
rpc SetMaxSpeed(MaxSpeedMessage) returns (Empty) {}
|
||||
|
||||
// subscribe to active pulse state updates
|
||||
rpc MaxSpeedSubscription(Empty) returns (stream MaxSpeedMessage) {}
|
||||
}
|
||||
12
src/android/src/main/proto/settings/Settings.proto
Normal file
12
src/android/src/main/proto/settings/Settings.proto
Normal file
@@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "SettingsProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "settings/VideoQualityService.proto";
|
||||
|
||||
message Settings {
|
||||
VideoQuality videoQuality = 1;
|
||||
}
|
||||
32
src/android/src/main/proto/settings/SystemUnitsService.proto
Normal file
32
src/android/src/main/proto/settings/SystemUnitsService.proto
Normal file
@@ -0,0 +1,32 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "SystemUnitsServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum SystemUnits {
|
||||
METRIC = 0;
|
||||
STANDARD = 1;
|
||||
}
|
||||
|
||||
message SystemUnitsMessage {
|
||||
SystemUnits units = 1;
|
||||
}
|
||||
|
||||
message SystemUnitsResult {
|
||||
oneof errorOrSystemUnits {
|
||||
IFitError error = 1;
|
||||
SystemUnits systemUnits = 2;
|
||||
}
|
||||
}
|
||||
|
||||
service SystemUnitsService {
|
||||
rpc CanRead(Empty) returns (AvailabilityResponse) {}
|
||||
rpc CanWrite(Empty) returns (AvailabilityResponse) {}
|
||||
rpc GetSystemUnits(Empty) returns (SystemUnitsMessage) {}
|
||||
rpc SystemUnitsChanged(Empty) returns (stream SystemUnitsMessage) {}
|
||||
rpc SetSystemUnits(SystemUnitsMessage) returns (Empty) {}
|
||||
}
|
||||
34
src/android/src/main/proto/settings/TimeZoneService.proto
Normal file
34
src/android/src/main/proto/settings/TimeZoneService.proto
Normal file
@@ -0,0 +1,34 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "TimeZoneServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
message TimeZone {
|
||||
string id = 1;
|
||||
string offsetString = 2;
|
||||
int32 utcOffset = 3;
|
||||
string name = 4;
|
||||
}
|
||||
|
||||
message TimeZoneResult {
|
||||
oneof errorOrTimeZone {
|
||||
IFitError error = 1;
|
||||
TimeZone timeZone = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message TimeZoneList {
|
||||
repeated TimeZone timeZones = 1;
|
||||
}
|
||||
|
||||
service TimeZoneService {
|
||||
rpc GetAllTimeZones(Empty) returns (TimeZoneList) {}
|
||||
rpc SetTimeZone(TimeZone) returns (TimeZoneResult) {}
|
||||
rpc GetCurrentTimeZone(Empty) returns (TimeZoneResult) {}
|
||||
rpc TimeZoneChanged(Empty) returns (stream TimeZone) {}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "VideoQualityServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
import "util/IFitError.proto";
|
||||
import "util/Util.proto";
|
||||
|
||||
enum VideoQualityLevel {
|
||||
OPTIMIZED = 0;
|
||||
HIGHEST = 1;
|
||||
HIGH = 2;
|
||||
MEDIUM = 3;
|
||||
LOW = 4;
|
||||
LOWEST = 5;
|
||||
}
|
||||
|
||||
message VideoQuality {
|
||||
VideoQualityLevel value = 1;
|
||||
}
|
||||
|
||||
message VideoQualityResult {
|
||||
oneof errorOrVideoQuality {
|
||||
IFitError error = 1;
|
||||
VideoQuality videoQuality = 2;
|
||||
}
|
||||
}
|
||||
|
||||
service VideoQualityService {
|
||||
rpc SetVideoQuality(VideoQuality) returns (VideoQualityResult) {}
|
||||
rpc GetVideoQuality(Empty) returns (VideoQualityResult) {}
|
||||
rpc VideoQualityChanged(Empty) returns (stream VideoQuality) {}
|
||||
}
|
||||
21
src/android/src/main/proto/settings/VolumeService.proto
Normal file
21
src/android/src/main/proto/settings/VolumeService.proto
Normal file
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.settings";
|
||||
option java_outer_classname = "VolumeServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message VolumeMessage {
|
||||
double percentage = 1;
|
||||
}
|
||||
|
||||
service VolumeService {
|
||||
rpc CanRead(Empty) returns (AvailabilityResponse) {}
|
||||
rpc CanWrite(Empty) returns (AvailabilityResponse) {}
|
||||
rpc GetVolume(Empty) returns (VolumeMessage) {}
|
||||
rpc IncreaseVolume(Empty) returns (Empty) {}
|
||||
rpc DecreaseVolume(Empty) returns (Empty) {}
|
||||
rpc VolumeChanged(Empty) returns (stream VolumeMessage) {}
|
||||
rpc SetVolume(VolumeMessage) returns (Empty) {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
option java_package = "com.ifit.glassos.settings.hdmi";
|
||||
option java_outer_classname = "HdmiSoundServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum AqProfile {
|
||||
AQ_PROFILE_DIALOG = 0;
|
||||
AQ_PROFILE_MUSIC = 1;
|
||||
AQ_PROFILE_MOVIE = 2;
|
||||
AQ_PROFILE_CUSTOM = 3;
|
||||
}
|
||||
|
||||
enum AqLevel {
|
||||
AQ_LEVEL_LOWEST = 0;
|
||||
AQ_LEVEL_LOW = 1;
|
||||
AQ_LEVEL_MEDIUM = 2;
|
||||
AQ_LEVEL_HIGH = 3;
|
||||
AQ_LEVEL_HIGHEST = 4;
|
||||
AQ_LEVEL_NOT_SET = 5;
|
||||
}
|
||||
|
||||
message AqProfileMessage {
|
||||
AqProfile profile = 1;
|
||||
}
|
||||
|
||||
message AqLevelsMessage {
|
||||
optional AqLevel bassLevel = 1;
|
||||
optional AqLevel trebleLevel = 2;
|
||||
optional AqLevel surroundLevel = 3;
|
||||
optional AqLevel dialogLevel = 4;
|
||||
}
|
||||
|
||||
message HdmiSoundStateMessage {
|
||||
AqProfile profile = 1;
|
||||
bool muted = 2;
|
||||
AqLevel bassLevel = 3;
|
||||
AqLevel trebleLevel = 4;
|
||||
AqLevel surroundLevel = 5;
|
||||
AqLevel dialogLevel = 6;
|
||||
}
|
||||
|
||||
service HdmiSoundService {
|
||||
rpc IsEnabled(Empty) returns (AvailabilityResponse) {}
|
||||
rpc IsEnabledFlow(Empty) returns (stream AvailabilityResponse) {}
|
||||
rpc SetMute(BooleanRequest) returns (Empty) {}
|
||||
rpc SetAqProfile(AqProfileMessage) returns (Empty) {}
|
||||
rpc SetAqLevels(AqLevelsMessage) returns (Empty) {}
|
||||
rpc GetHdmiSoundState(Empty) returns (HdmiSoundStateMessage) {}
|
||||
rpc HdmiSoundStateChanged(Empty) returns (stream HdmiSoundStateMessage) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "settings/heartrate/HeartRateZone.proto";
|
||||
option java_package = "com.ifit.glassos.settings.heartrate";
|
||||
option java_outer_classname = "HeartRateSettingsProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message HeartRateSettings {
|
||||
int32 restingHeartRate = 1;
|
||||
int32 maxHeartRate = 2;
|
||||
int32 heartRateReserve = 3;
|
||||
HeartRateZone zone0 = 4;
|
||||
HeartRateZone zone1 = 5;
|
||||
HeartRateZone zone2 = 6;
|
||||
HeartRateZone zone3 = 7;
|
||||
HeartRateZone zone4 = 8;
|
||||
HeartRateZone zone5 = 9;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "util/Util.proto";
|
||||
import "settings/heartrate/HeartRateSettings.proto";
|
||||
option java_package = "com.ifit.glassos.settings.heartrate";
|
||||
option java_outer_classname = "HeartRateSettingsServiceProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message HeartRateSettingsUpdate {
|
||||
int32 restingHeartRate = 1;
|
||||
int32 maxHeartRate = 2;
|
||||
}
|
||||
|
||||
service HeartRateSettingsService {
|
||||
// subscribe to heart rate settings changes
|
||||
rpc HeartRateSettingsSubscription(Empty) returns (stream HeartRateSettings) {}
|
||||
|
||||
// reset heart rate settings to default values
|
||||
rpc ResetHeartRateSettingsToDefault(Empty) returns (HeartRateSettings) {}
|
||||
|
||||
// updates heart rate settings
|
||||
rpc UpdateHeartRateSettings(HeartRateSettingsUpdate) returns (HeartRateSettings) {}
|
||||
|
||||
// get the current heart rate settings
|
||||
rpc GetHeartRateSettings(Empty) returns (HeartRateSettings) {}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
import "settings/heartrate/HeartRateZoneNumber.proto";
|
||||
option java_package = "com.ifit.glassos.settings.heartrate";
|
||||
option java_outer_classname = "HeartRateZoneProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
message HeartRateZone {
|
||||
HeartRateZoneNumber zoneNumber = 1;
|
||||
int32 minBpm = 2;
|
||||
int32 maxBpm = 3;
|
||||
int32 minPercentage = 4;
|
||||
int32 maxPercentage = 5;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
package com.ifit.glassos;
|
||||
option java_package = "com.ifit.glassos.settings.heartrate";
|
||||
option java_outer_classname = "HeartRateZoneNumberProto";
|
||||
option java_multiple_files = true;
|
||||
option swift_prefix = "IFit";
|
||||
|
||||
enum HeartRateZoneNumber {
|
||||
ZONE_0 = 0;
|
||||
ZONE_1 = 1;
|
||||
ZONE_2 = 2;
|
||||
ZONE_3 = 3;
|
||||
ZONE_4 = 4;
|
||||
ZONE_5 = 5;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user