mirror of
https://github.com/cagnulein/qdomyos-zwift.git
synced 2026-02-18 00:17:41 +01:00
Compare commits
37 Commits
android36
...
raspberry-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9cfe24358 | ||
|
|
8cc1982dd4 | ||
|
|
f82e099795 | ||
|
|
705eb57414 | ||
|
|
9c446bcaf6 | ||
|
|
86118c04e2 | ||
|
|
081d9d4e24 | ||
|
|
4ffc0867e3 | ||
|
|
3bfecadd1f | ||
|
|
06aa01d755 | ||
|
|
e432df9f6b | ||
|
|
e3f4384014 | ||
|
|
563ced3de1 | ||
|
|
e48c6525ea | ||
|
|
ca34e99277 | ||
|
|
446f5200ba | ||
|
|
edcb7ab359 | ||
|
|
3844808b60 | ||
|
|
8e1ddc502f | ||
|
|
e633f0f671 | ||
|
|
93a38a7b79 | ||
|
|
d2f8ed8c01 | ||
|
|
60a9d7cb0f | ||
|
|
4c0793c785 | ||
|
|
5fc377f648 | ||
|
|
0d6f207991 | ||
|
|
051f296913 | ||
|
|
45a4d6d0b1 | ||
|
|
d2612ad03f | ||
|
|
6bb4d99f29 | ||
|
|
c3dbce9ea8 | ||
|
|
989315fb5e | ||
|
|
ce3782f80b | ||
|
|
4ee77b392e | ||
|
|
03896d7384 | ||
|
|
9258bf6af2 | ||
|
|
7a0a990eb8 |
194
.github/workflows/main.yml
vendored
194
.github/workflows/main.yml
vendored
@@ -647,11 +647,56 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fdroid-android-trial
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug.apk
|
||||
|
||||
android-emulator-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: android-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
api-level: [24, 26, 28, 29, 30, 31, 33, 34, 35, 36]
|
||||
include:
|
||||
- api-level: 24
|
||||
target: default
|
||||
arch: x86
|
||||
android-version: "Android 7.0"
|
||||
- api-level: 26
|
||||
target: default
|
||||
arch: x86
|
||||
android-version: "Android 8.0"
|
||||
- api-level: 28
|
||||
target: default
|
||||
arch: x86
|
||||
android-version: "Android 9.0"
|
||||
- api-level: 29
|
||||
target: default
|
||||
arch: x86
|
||||
android-version: "Android 10"
|
||||
- api-level: 30
|
||||
target: google_apis
|
||||
arch: x86
|
||||
android-version: "Android 11"
|
||||
- api-level: 31
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
android-version: "Android 12"
|
||||
- api-level: 33
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
android-version: "Android 13"
|
||||
- api-level: 34
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
android-version: "Android 14"
|
||||
- api-level: 35
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
android-version: "Android 15"
|
||||
- api-level: 36
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
android-version: "Android 16"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -677,12 +722,12 @@ jobs:
|
||||
java-version: '17'
|
||||
|
||||
# Use a smaller emulator configuration
|
||||
- name: Run tests on emulator
|
||||
- name: Run tests on emulator (${{ matrix.android-version }})
|
||||
uses: ReactiveCircus/android-emulator-runner@v2
|
||||
with:
|
||||
target: default # Use default instead of Google APIs
|
||||
arch: x86
|
||||
api-level: 29
|
||||
target: ${{ matrix.target }}
|
||||
arch: ${{ matrix.arch }}
|
||||
api-level: ${{ matrix.api-level }}
|
||||
profile: Nexus 6
|
||||
disable-animations: true
|
||||
script: |
|
||||
@@ -705,15 +750,29 @@ jobs:
|
||||
adb shell pm grant org.cagnulen.qdomyoszwift android.permission.WRITE_EXTERNAL_STORAGE || true
|
||||
|
||||
# Start the main activity
|
||||
adb shell am start -n org.cagnulen.qdomyoszwift/org.qtproject.qt5.android.bindings.QtActivity
|
||||
adb shell am start -n org.cagnulen.qdomyoszwift/org.cagnulen.qdomyoszwift.CustomQtActivity
|
||||
|
||||
# Wait for app to start
|
||||
sleep 40
|
||||
sleep 60
|
||||
|
||||
# Verify the app is running
|
||||
echo "Checking if app is running..."
|
||||
adb shell "ps -A" > process_list.txt
|
||||
# Use different ps commands for different Android versions
|
||||
adb shell "ps -A 2>/dev/null || ps" > process_list.txt
|
||||
|
||||
# Debug: show all processes to understand the format
|
||||
echo "=== All running processes ==="
|
||||
cat process_list.txt | head -20
|
||||
echo "=== Looking for our app ==="
|
||||
|
||||
grep -q "qdomyos" process_list.txt || (echo "App process not found in process list" && echo "TEST FAILED: App process not running" && exit 1)
|
||||
adb shell pm list packages | grep org.cagnulen.qdomyoszwift
|
||||
echo "=== Checking app info ==="
|
||||
adb shell dumpsys package org.cagnulen.qdomyoszwift | grep -A5 -B5 "state"
|
||||
echo "=== Logcat output for debugging ==="
|
||||
adb logcat -d | grep -i "qdomyos\|crash\|error\|exception\|fatal" | tail -n 50
|
||||
echo "=== Full recent logcat ==="
|
||||
adb logcat -d | tail -n 100
|
||||
echo "App is running successfully"
|
||||
|
||||
# Take a screenshot for verification
|
||||
@@ -723,17 +782,21 @@ jobs:
|
||||
# Check if the package is installed
|
||||
adb shell pm list packages | grep org.cagnulen.qdomyoszwift
|
||||
|
||||
# Display logcat output for debugging (just the last 100 lines)
|
||||
adb logcat -d | grep -i qdomyos | tail -n 100
|
||||
# Save logcat for debugging
|
||||
echo "Saving logcat for analysis..."
|
||||
adb logcat -d > full_logcat.txt
|
||||
adb logcat -d | grep -i qdomyos > qdomyos_logcat.txt
|
||||
|
||||
- name: Upload test evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: android-emulator-test-evidence
|
||||
name: android-emulator-test-evidence-api${{ matrix.api-level }}
|
||||
path: |
|
||||
screenshot.png
|
||||
process_list.txt
|
||||
full_logcat.txt
|
||||
qdomyos_logcat.txt
|
||||
if-no-files-found: warn
|
||||
|
||||
ios-build:
|
||||
@@ -1469,11 +1532,114 @@ jobs:
|
||||
name: windows-msvc2022-binary-no-python
|
||||
path: windows-msvc2022-binary-no-python.zip
|
||||
if: ${{ ! matrix.config.python }}
|
||||
|
||||
nordictrack-build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name == 'schedule'
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- name: Xvfb install and run
|
||||
run: |
|
||||
sudo apt-get install -y xvfb
|
||||
Xvfb -ac ${{ env.DISPLAY }} -screen 0 1280x780x24 &
|
||||
|
||||
- name: Checkout PR code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: refs/pull/3478/head
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
submodules: 'false' # Prima disattiva il checkout automatico dei submodule
|
||||
|
||||
- name: Checkout submodules with specific branches
|
||||
run: |
|
||||
git submodule init
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Fix qmdnsengine submodule
|
||||
run: |
|
||||
cd src/qmdnsengine
|
||||
git fetch
|
||||
git checkout 602da51dc43c55bd9aa8a83c47ea3594a9b01b98
|
||||
|
||||
- name: Install packages required to run QZ inside workflow
|
||||
run: sudo apt update -y && sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
|
||||
|
||||
- name: Install Qt Android
|
||||
uses: jdpurcell/install-qt-action@v5
|
||||
with:
|
||||
version: '5.15.0'
|
||||
host: 'linux'
|
||||
target: 'android'
|
||||
arch: 'android'
|
||||
modules: 'qtcharts qtnetworkauth'
|
||||
dir: '${{ github.workspace }}/output/android/'
|
||||
cache: 'true'
|
||||
cache-key-prefix: 'install-qt-action-android'
|
||||
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '11.0.23+9'
|
||||
|
||||
- name: patching qt for bluetooth
|
||||
run: cp qt-patches/android/5.15.0/jar/*.* ${{ github.workspace }}/output/android/Qt/5.15.0/android/jar/
|
||||
|
||||
- name: download 3rd party files for qthttpserver
|
||||
run: cp qHttpServerBin/5.15.2/headers/* src/qthttpserver/src/3rdparty/http-parser/
|
||||
|
||||
- name: Set Android NDK 21 && build
|
||||
run: |
|
||||
# Install NDK 21 after GitHub update
|
||||
# https://github.com/actions/virtual-environments/issues/5595
|
||||
ANDROID_ROOT="/usr/local/lib/android"
|
||||
ANDROID_SDK_ROOT="${ANDROID_ROOT}/sdk"
|
||||
SDKMANAGER="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager"
|
||||
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
|
||||
export ANDROID_NDK="${ANDROID_SDK_ROOT}/ndk-bundle"
|
||||
export ANDROID_NDK_ROOT="${ANDROID_NDK}"
|
||||
cd src
|
||||
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
|
||||
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
|
||||
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
|
||||
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
|
||||
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
|
||||
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
|
||||
echo "#define LICENSE" >> secret.h
|
||||
cd ..
|
||||
|
||||
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK
|
||||
rm -rf /usr/local/lib/android/sdk/ndk/25.1.8937393
|
||||
|
||||
# QTHTTPSERVER must use the same NDK
|
||||
cd src/qthttpserver
|
||||
qmake
|
||||
make -j8
|
||||
make install
|
||||
cd ../..
|
||||
|
||||
qmake -spec android-clang 'ANDROID_ABIS=armeabi-v7a arm64-v8a x86 x86_64' 'ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/21.4.7075529' && make -j4 && make INSTALL_ROOT=${{ github.workspace }}/output/android/ install
|
||||
cp src/android-qdomyos-zwift-deployment-settings.json src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
sed -i '1s|{|{\n "android-extra-libs": "${{ github.workspace }}/android_openssl/no-asm/latest/arm/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libssl_1_1.so",|' src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
sed -i 's/"android-debug"/"android-nordictrack"/g' src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
sed -i 's/android-debug\.apk/android-debug-nordictrack.apk/g' src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
cat src/android-qdomyos-zwift-nordictrack-deployment-settings.json
|
||||
|
||||
- name: Build APK (not usable for production due to unpatched QT library)
|
||||
run: cd src; androiddeployqt --input android-qdomyos-zwift-nordictrack-deployment-settings.json --output ${{ github.workspace }}/output/android/ --android-platform android-31 --gradle --aab
|
||||
|
||||
- name: Archive nordictrack binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nordictrack-android-trial
|
||||
path: ${{ github.workspace }}/output/android/build/outputs/apk/debug/android-debug-nordictrack.apk
|
||||
|
||||
upload_to_release:
|
||||
permissions: write-all
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name == 'schedule'
|
||||
#if: github.event_name == 'schedule'
|
||||
needs: [linux-x86-build, window-msvc2019-build, window-msvc2022-build, ios-build, window-build, android-build, raspberry-pi-build]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -1506,6 +1672,7 @@ jobs:
|
||||
|
||||
## Other Platforms:
|
||||
- **fdroid-android-trial**: Android build
|
||||
- **nordictrack-android-trial**: Nordictrack build for iFIT2 Tablets
|
||||
- **raspberry-pi-binary**: Raspberry Pi build
|
||||
|
||||
__Please help us improve QZ by reporting any issues you encounter!__ :wink:
|
||||
@@ -1517,6 +1684,7 @@ jobs:
|
||||
windows-msvc2019-ai-server-binary/*
|
||||
windows-binary-no-python/*
|
||||
windows-binary/*
|
||||
fdroid-android-trial/*
|
||||
fdroid-android-trial/android-debug.apk
|
||||
nordictrack-android-trial/android-debug-nordictrack.apk
|
||||
raspberry-pi-binary/qdomyos-zwift-32bit
|
||||
raspberry-pi-binary/qdomyos-zwift-64bit
|
||||
|
||||
@@ -4381,7 +4381,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1110;
|
||||
CURRENT_PROJECT_VERSION = 1121;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "ADB_HOST=1";
|
||||
@@ -4463,7 +4463,7 @@
|
||||
/Users/cagnulein/Qt/5.15.2/ios/plugins/audio,
|
||||
"/Users/cagnulein/qdomyos-zwift/src/ios/adb",
|
||||
);
|
||||
MARKETING_VERSION = 2.19;
|
||||
MARKETING_VERSION = 2.20;
|
||||
OTHER_CFLAGS = (
|
||||
"-pipe",
|
||||
"-g",
|
||||
@@ -4575,7 +4575,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = "../src/ios/qdomyos-zwift.entitlements";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1110;
|
||||
CURRENT_PROJECT_VERSION = 1121;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -4659,7 +4659,7 @@
|
||||
/Users/cagnulein/Qt/5.15.2/ios/plugins/audio,
|
||||
"/Users/cagnulein/qdomyos-zwift/src/ios/adb",
|
||||
);
|
||||
MARKETING_VERSION = 2.19;
|
||||
MARKETING_VERSION = 2.20;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-pipe",
|
||||
@@ -4805,7 +4805,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1110;
|
||||
CURRENT_PROJECT_VERSION = 1121;
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -4830,7 +4830,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.19;
|
||||
MARKETING_VERSION = 2.20;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";
|
||||
@@ -4901,7 +4901,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1110;
|
||||
CURRENT_PROJECT_VERSION = 1121;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 6335M7T29D;
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -4922,7 +4922,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.19;
|
||||
MARKETING_VERSION = 2.20;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";
|
||||
@@ -4993,7 +4993,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1110;
|
||||
CURRENT_PROJECT_VERSION = 1121;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -5038,7 +5038,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.19;
|
||||
MARKETING_VERSION = 2.20;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";
|
||||
@@ -5109,7 +5109,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "watchkit Extension/WatchKit Extension.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1110;
|
||||
CURRENT_PROJECT_VERSION = 1121;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"watchkit Extension/Preview Content\"";
|
||||
ENABLE_BITCODE = YES;
|
||||
@@ -5150,7 +5150,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.19;
|
||||
MARKETING_VERSION = 2.20;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CODE_SIGN_FLAGS = "--generate-entitlement-der";
|
||||
|
||||
4
src/CLAUDE.md
Normal file
4
src/CLAUDE.md
Normal file
@@ -0,0 +1,4 @@
|
||||
when you add a setting remember:
|
||||
- you have to add always as the last settings declared in the settings.qml
|
||||
- if you have to add a setting also on another qml file, you need also to declare it there always putting as the last one
|
||||
- in the qzsettings.cpp there is a allsettingscount that must be updated if you add a setting
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.19.2" android:versionCode="1114" android:installLocation="auto">
|
||||
<manifest package="org.cagnulen.qdomyoszwift" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionName="2.20.0" android:versionCode="1121" android:installLocation="auto">
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
<application android:hardwareAccelerated="true" android:debuggable="false" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="qdomyos-zwift" android:extractNativeLibs="true" android:icon="@drawable/icon" android:usesCleartextTraffic="true">
|
||||
<activity android:theme="@style/Theme.AppCompat" android:exported="true" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="QZ" android:launchMode="singleTop">
|
||||
<activity android:theme="@style/Theme.AppCompat" android:exported="true" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.cagnulen.qdomyoszwift.CustomQtActivity" android:label="QZ" android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
||||
34
src/android/src/CustomQtActivity.java
Normal file
34
src/android/src/CustomQtActivity.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.cagnulen.qdomyoszwift;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsController;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
|
||||
public class CustomQtActivity extends QtActivity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Handle Android 16 API 36 WindowInsetsController for fullscreen support
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Android 11 (API 30) and above - use WindowInsetsController
|
||||
getWindow().setDecorFitsSystemWindows(false);
|
||||
WindowInsetsController controller = getWindow().getDecorView().getWindowInsetsController();
|
||||
if (controller != null) {
|
||||
controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
|
||||
controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||
}
|
||||
} else {
|
||||
// Fallback for older Android versions (API < 30)
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ import com.android.billingclient.api.QueryProductDetailsResult;
|
||||
** Add Dependencies below to build.gradle file:
|
||||
|
||||
dependencies {
|
||||
def billing_version = "4.0.0"
|
||||
def billing_version = "8.0.0"
|
||||
implementation "com.android.billingclient:billing:$billing_version"
|
||||
}
|
||||
|
||||
@@ -152,18 +152,23 @@ public class InAppPurchase implements PurchasesUpdatedListener
|
||||
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
|
||||
|
||||
int responseCode = billingResult.getResponseCode();
|
||||
QLog.d(TAG, "onPurchasesUpdated called. Response code: " + responseCode + ", Debug message: " + billingResult.getDebugMessage());
|
||||
|
||||
if (purchases == null) {
|
||||
QLog.e(TAG, "Purchase failed: Data missing from result (purchases is null)");
|
||||
purchaseFailed(purchaseRequestCode, FAILUREREASON_ERROR, "Data missing from result");
|
||||
return;
|
||||
}
|
||||
|
||||
if (billingResult.getResponseCode() == RESULT_OK) {
|
||||
QLog.d(TAG, "Purchase successful, handling " + purchases.size() + " purchases");
|
||||
handlePurchase(purchases);
|
||||
} else if (responseCode == RESULT_USER_CANCELED) {
|
||||
QLog.d(TAG, "Purchase cancelled by user");
|
||||
purchaseFailed(purchaseRequestCode, FAILUREREASON_USERCANCELED, "");
|
||||
} else {
|
||||
String errorString = getErrorString(responseCode);
|
||||
QLog.e(TAG, "Purchase failed with error: " + errorString + " (code: " + responseCode + ")");
|
||||
purchaseFailed(purchaseRequestCode, FAILUREREASON_ERROR, errorString);
|
||||
}
|
||||
}
|
||||
@@ -287,7 +292,7 @@ public class InAppPurchase implements PurchasesUpdatedListener
|
||||
List<ProductDetails> productDetailsList = productDetailsResult.getProductDetailsList();
|
||||
|
||||
if (billingResult.getResponseCode() != RESULT_OK) {
|
||||
QLog.e(TAG, "Unable to launch Google Play purchase screen");
|
||||
QLog.e(TAG, "Unable to launch Google Play purchase screen. Response code: " + billingResult.getResponseCode() + ", Debug message: " + billingResult.getDebugMessage());
|
||||
String errorString = getErrorString(requestCode);
|
||||
purchaseFailed(requestCode, FAILUREREASON_ERROR, errorString);
|
||||
return;
|
||||
@@ -298,9 +303,19 @@ public class InAppPurchase implements PurchasesUpdatedListener
|
||||
}
|
||||
|
||||
ProductDetails productDetails = productDetailsList.get(0);
|
||||
BillingFlowParams.ProductDetailsParams productDetailsParams = BillingFlowParams.ProductDetailsParams.newBuilder()
|
||||
.setProductDetails(productDetails)
|
||||
.build();
|
||||
BillingFlowParams.ProductDetailsParams.Builder productDetailsParamsBuilder = BillingFlowParams.ProductDetailsParams.newBuilder()
|
||||
.setProductDetails(productDetails);
|
||||
|
||||
// For subscriptions, we need to set the offer token
|
||||
if (productDetails.getSubscriptionOfferDetails() != null && !productDetails.getSubscriptionOfferDetails().isEmpty()) {
|
||||
String offerToken = productDetails.getSubscriptionOfferDetails().get(0).getOfferToken();
|
||||
QLog.d(TAG, "Setting offer token for subscription: " + offerToken);
|
||||
productDetailsParamsBuilder.setOfferToken(offerToken);
|
||||
} else {
|
||||
QLog.w(TAG, "No subscription offer details found for product: " + identifier);
|
||||
}
|
||||
|
||||
BillingFlowParams.ProductDetailsParams productDetailsParams = productDetailsParamsBuilder.build();
|
||||
|
||||
BillingFlowParams purchaseParams = BillingFlowParams.newBuilder()
|
||||
.setProductDetailsParamsList(java.util.Arrays.asList(productDetailsParams))
|
||||
|
||||
@@ -63,6 +63,12 @@ int CharacteristicWriteProcessor2AD9::writeProcess(quint16 uuid, const QByteArra
|
||||
reply.append((quint8)FTMS_RESPONSE_CODE);
|
||||
reply.append((quint8)FTMS_START_RESUME);
|
||||
reply.append((quint8)FTMS_SUCCESS);
|
||||
} else if (cmd == FTMS_STOP_PAUSE) {
|
||||
qDebug() << QStringLiteral("stop/pause simulation! ignoring it");
|
||||
|
||||
reply.append((quint8)FTMS_RESPONSE_CODE);
|
||||
reply.append((quint8)FTMS_STOP_PAUSE);
|
||||
reply.append((quint8)FTMS_SUCCESS);
|
||||
} else if (cmd == FTMS_REQUEST_CONTROL) {
|
||||
qDebug() << QStringLiteral("control requested");
|
||||
|
||||
|
||||
@@ -1049,7 +1049,8 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
}
|
||||
this->signalBluetoothDeviceConnected(ypooElliptical);
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral("NAUTILUS E")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("NAUTILUS M"))) &&
|
||||
b.name().toUpper().startsWith(QStringLiteral("NAUTILUS M")) ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("NAUTILUS 616"))) && // actually this is a bike that uses the same Bluetooth characteristics of the elliptical
|
||||
!nautilusElliptical && // NAUTILUS E616
|
||||
filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
@@ -1698,10 +1699,10 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
(b.name().toUpper().startsWith("LYDSTO")) ||
|
||||
(b.name().toUpper().startsWith("CYCLO_")) ||
|
||||
(b.name().toUpper().startsWith("SL010-")) ||
|
||||
(b.name().toUpper().startsWith("EXPERT-SX9")) ||
|
||||
(b.name().toUpper().startsWith("LCR")) ||
|
||||
(b.name().toUpper().startsWith("EXPERT-SX9")) ||
|
||||
(b.name().toUpper().startsWith("MRK-S26S-")) ||
|
||||
(b.name().toUpper().startsWith("ROBX")) ||
|
||||
(b.name().toUpper().startsWith("SPEEDMAGPRO")) ||
|
||||
(b.name().toUpper().startsWith("XCX-")) ||
|
||||
(b.name().toUpper().startsWith("NEO BIKE PLUS ")) ||
|
||||
(b.name().toUpper().startsWith(QStringLiteral("PM5")) && !b.name().toUpper().endsWith(QStringLiteral("SKI")) && !b.name().toUpper().endsWith(QStringLiteral("ROW"))) ||
|
||||
@@ -2462,6 +2463,7 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
keepBike->deviceDiscovered(b);
|
||||
this->signalBluetoothDeviceConnected(keepBike);
|
||||
} else if ((b.name().toUpper().startsWith(QStringLiteral("LCB")) ||
|
||||
b.name().toUpper().startsWith("LCR") ||
|
||||
b.name().toUpper().startsWith(QStringLiteral("R92"))) &&
|
||||
!soleBike && filter) {
|
||||
this->setLastBluetoothDevice(b);
|
||||
|
||||
@@ -980,8 +980,11 @@ void ftmsbike::characteristicChanged(const QLowEnergyCharacteristic &characteris
|
||||
1000.0;
|
||||
index += 3;
|
||||
} else {
|
||||
Distance += ((Speed.value() / 3600000.0) *
|
||||
((double)lastRefreshCharacteristicChanged2ACE.msecsTo(now)));
|
||||
// Only calculate distance if 2AD2 hasn't already done it recently (within 2000ms)
|
||||
if (lastRefreshCharacteristicChanged2AD2.msecsTo(now) > 2000) {
|
||||
Distance += ((Speed.value() / 3600000.0) *
|
||||
((double)lastRefreshCharacteristicChanged2ACE.msecsTo(now)));
|
||||
}
|
||||
}
|
||||
|
||||
emit debug(QStringLiteral("Current Distance: ") + QString::number(Distance.value()));
|
||||
@@ -1585,6 +1588,12 @@ void ftmsbike::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
} else if(device.name().toUpper().startsWith(QStringLiteral("THINK X")) || device.name().toUpper().startsWith(QStringLiteral("THINK-"))) {
|
||||
THINK_X = true;
|
||||
qDebug() << "THINK X workaround enabled!";
|
||||
} else if(device.name().toUpper().startsWith(QStringLiteral("WLT8828"))) {
|
||||
qDebug() << QStringLiteral("WLT8828 found");
|
||||
WLT8828 = true;
|
||||
max_resistance = 32;
|
||||
resistance_lvl_mode = true;
|
||||
ergModeSupported = false; // this bike doesn't have ERG mode natively
|
||||
}
|
||||
|
||||
if(settings.value(QZSettings::force_resistance_instead_inclination, QZSettings::default_force_resistance_instead_inclination).toBool()) {
|
||||
|
||||
@@ -150,6 +150,7 @@ class ftmsbike : public bike {
|
||||
bool EXPERT_SX9 = false;
|
||||
bool PM5 = false;
|
||||
bool THINK_X = false;
|
||||
bool WLT8828 = false;
|
||||
|
||||
int16_t T2_lastGear = 0;
|
||||
|
||||
|
||||
@@ -102,6 +102,22 @@ void stagesbike::update() {
|
||||
// updateDisplay(elapsed);
|
||||
}
|
||||
|
||||
if (requestInclination != -100 || ((VirtualBike() && VirtualBike()->ftmsDeviceConnected()) && lastGearValue != gears() && lastRawRequestedInclinationValue != -100)) {
|
||||
qDebug() << QStringLiteral("writing inclination ") << requestInclination << lastRawRequestedInclinationValue << gears();
|
||||
|
||||
if(eliteService != nullptr) {
|
||||
QByteArray a = setSimulationMode(
|
||||
lastRawRequestedInclinationValue + gears(), 0.005, 0.5, 0.0, 1.0); // since this bike doesn't have the concept of resistance,
|
||||
// i'm using the gears in the inclination
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(eliteService, eliteWriteCharacteristic, b, a.length(), "forceInclination", false, false);
|
||||
|
||||
requestInclination = -100; // reset the requestInclination to -100 so that it doesn't get written again
|
||||
requestResistance = -1; // reset the requestResistance so that it doesn't get written again
|
||||
}
|
||||
}
|
||||
|
||||
if (requestResistance != -1) {
|
||||
if (requestResistance > 100) {
|
||||
requestResistance = 100;
|
||||
@@ -109,21 +125,6 @@ void stagesbike::update() {
|
||||
requestResistance = 1;
|
||||
}
|
||||
|
||||
if (requestInclination != -100 || ((VirtualBike() && VirtualBike()->ftmsDeviceConnected()) && lastGearValue != gears() && lastRawRequestedInclinationValue != -100)) {
|
||||
qDebug() << QStringLiteral("writing inclination ") << requestInclination << lastRawRequestedInclinationValue << gears();
|
||||
|
||||
if(eliteService != nullptr) {
|
||||
QByteArray a = setSimulationMode(
|
||||
lastRawRequestedInclinationValue + gears(), 0.005, 0.5, 0.0, 1.0); // since this bike doesn't have the concept of resistance,
|
||||
// i'm using the gears in the inclination
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(eliteService, eliteWriteCharacteristic, b, a.length(), "forcePower", false, false);
|
||||
|
||||
requestInclination = -100; // reset the requestInclination to -100 so that it doesn't get written again
|
||||
requestResistance = -1; // reset the requestResistance so that it doesn't get written again
|
||||
}
|
||||
}
|
||||
if (requestResistance != currentResistance().value() || lastGearValue != gears()) {
|
||||
emit debug(QStringLiteral("writing resistance ") + QString::number(requestResistance));
|
||||
if(eliteService != nullptr) {
|
||||
@@ -566,6 +567,10 @@ void stagesbike::stateChanged(QLowEnergyService::ServiceState state) {
|
||||
// ********************************************************************************************************
|
||||
}
|
||||
|
||||
void stagesbike::inclinationChanged(double grade, double percentage) {
|
||||
changeInclination(grade, percentage);
|
||||
}
|
||||
|
||||
void stagesbike::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue) {
|
||||
emit debug(QStringLiteral("descriptorWritten ") + descriptor.name() + QStringLiteral(" ") + newValue.toHex(' '));
|
||||
|
||||
|
||||
@@ -104,6 +104,8 @@ class stagesbike : public bike {
|
||||
void stateChanged(QLowEnergyService::ServiceState state);
|
||||
void controllerStateChanged(QLowEnergyController::ControllerState state);
|
||||
|
||||
void inclinationChanged(double grade, double percentage);
|
||||
|
||||
void serviceDiscovered(const QBluetoothUuid &gatt);
|
||||
void serviceScanDone(void);
|
||||
void update();
|
||||
|
||||
@@ -199,9 +199,11 @@ void wahookickrsnapbike::update() {
|
||||
}
|
||||
#endif
|
||||
|
||||
QSettings settings;
|
||||
bool wahooWithoutWheelDiameter = settings.value(QZSettings::wahoo_without_wheel_diameter, QZSettings::default_wahoo_without_wheel_diameter).toBool();
|
||||
|
||||
if (initRequest) {
|
||||
lastCommandErgMode = false;
|
||||
QSettings settings;
|
||||
QByteArray a = unlockCommand();
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
@@ -218,16 +220,18 @@ void wahookickrsnapbike::update() {
|
||||
}
|
||||
QThread::msleep(700);
|
||||
|
||||
QByteArray d = setWheelCircumference(wheelCircumference::gearsToWheelDiameter(gears()));
|
||||
uint8_t e[20];
|
||||
setGears(settings.value(QZSettings::gears_current_value, QZSettings::default_gears_current_value).toDouble());
|
||||
memcpy(e, d.constData(), d.length());
|
||||
writeCharacteristic(e, d.length(), "setWheelCircumference", false, true);
|
||||
if (!wahooWithoutWheelDiameter) {
|
||||
QByteArray d = setWheelCircumference(wheelCircumference::gearsToWheelDiameter(gears()));
|
||||
uint8_t e[20];
|
||||
setGears(settings.value(QZSettings::gears_current_value, QZSettings::default_gears_current_value).toDouble());
|
||||
memcpy(e, d.constData(), d.length());
|
||||
writeCharacteristic(e, d.length(), "setWheelCircumference", false, true);
|
||||
}
|
||||
|
||||
// required to the SS2K only one time
|
||||
Resistance = 0;
|
||||
emit resistanceRead(Resistance.value());
|
||||
initRequest = false;
|
||||
initRequest = false;
|
||||
} else if (
|
||||
#ifndef Q_OS_IOS
|
||||
bluetoothDevice.isValid() &&
|
||||
@@ -264,54 +268,93 @@ void wahookickrsnapbike::update() {
|
||||
requestResistance = -1;
|
||||
}
|
||||
|
||||
if (KICKR_BIKE) {
|
||||
if(requestInclination != -100) {
|
||||
debug("writing inclination request " + QString::number(requestInclination));
|
||||
inclinationChanged(requestInclination, requestInclination);
|
||||
Inclination = requestInclination; // the bike is not sending back the inclination?
|
||||
requestInclination = -100;
|
||||
}
|
||||
} else if (requestResistance != -1 && KICKR_BIKE == false) {
|
||||
if (requestResistance > 100) {
|
||||
requestResistance = 100;
|
||||
} else if (requestResistance == 0) {
|
||||
requestResistance = 1;
|
||||
if (!wahooWithoutWheelDiameter) {
|
||||
if (KICKR_BIKE) {
|
||||
if(requestInclination != -100) {
|
||||
debug("writing inclination request " + QString::number(requestInclination));
|
||||
inclinationChanged(requestInclination, requestInclination);
|
||||
Inclination = requestInclination; // the bike is not sending back the inclination?
|
||||
requestInclination = -100;
|
||||
}
|
||||
} else if (requestResistance != -1 && KICKR_BIKE == false) {
|
||||
if (requestResistance > 100) {
|
||||
requestResistance = 100;
|
||||
} else if (requestResistance == 0) {
|
||||
requestResistance = 1;
|
||||
}
|
||||
|
||||
auto virtualBike = this->VirtualBike();
|
||||
if (requestResistance != currentResistance().value() &&
|
||||
((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike)) {
|
||||
emit debug(QStringLiteral("writing resistance ") + QString::number(requestResistance));
|
||||
lastForcedResistance = requestResistance;
|
||||
QByteArray a = setResistanceMode(((double)requestResistance) / 100.0);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setResistance", false, false);
|
||||
} else if (requestResistance != currentResistance().value() && ((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike)) {
|
||||
emit debug(QStringLiteral("writing resistance ") + QString::number(lastForcedResistance));
|
||||
QByteArray a = setResistanceMode(((double)lastForcedResistance) / 100.0);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setResistance", false, false);
|
||||
}
|
||||
requestResistance = -1;
|
||||
}
|
||||
|
||||
auto virtualBike = this->VirtualBike();
|
||||
if (requestResistance != currentResistance().value() &&
|
||||
((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike)) {
|
||||
emit debug(QStringLiteral("writing resistance ") + QString::number(requestResistance));
|
||||
lastForcedResistance = requestResistance;
|
||||
QByteArray a = setResistanceMode(((double)requestResistance) / 100.0);
|
||||
if (lastGearValue != gears()) {
|
||||
if(KICKR_SNAP) {
|
||||
inclinationChanged(lastGrade, lastGrade);
|
||||
} else {
|
||||
QByteArray a = setWheelCircumference(wheelCircumference::gearsToWheelDiameter(gears()));
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setWheelCircumference", false, false);
|
||||
lastGrade = 999; // to force a change
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (KICKR_BIKE) {
|
||||
if(requestInclination != -100) {
|
||||
debug("writing inclination request " + QString::number(requestInclination));
|
||||
inclinationChanged(requestInclination, requestInclination);
|
||||
Inclination = requestInclination; // the bike is not sending back the inclination?
|
||||
requestInclination = -100;
|
||||
} else if (lastGearValue != gears()) {
|
||||
inclinationChanged(lastGrade, lastGrade);
|
||||
}
|
||||
} else if (requestResistance != -1 && KICKR_BIKE == false) {
|
||||
if (requestResistance > 100) {
|
||||
requestResistance = 100;
|
||||
} else if (requestResistance == 0) {
|
||||
requestResistance = 1;
|
||||
}
|
||||
|
||||
auto virtualBike = this->VirtualBike();
|
||||
if (requestResistance != currentResistance().value() &&
|
||||
((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike)) {
|
||||
emit debug(QStringLiteral("writing resistance ") + QString::number(requestResistance));
|
||||
lastForcedResistance = requestResistance;
|
||||
QByteArray a = setResistanceMode(((double)requestResistance) / 100.0);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setResistance", false, false);
|
||||
} else if (requestResistance != currentResistance().value() &&
|
||||
((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike) && lastGearValue != gears()) {
|
||||
emit debug(QStringLiteral("writing resistance due to gears changed ") + QString::number(lastForcedResistance));
|
||||
QByteArray a = setResistanceMode(((double)lastForcedResistance + (gears() - lastGearValue)) / 100.0);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setResistance", false, false);
|
||||
} else if (requestResistance != currentResistance().value() &&
|
||||
((virtualBike && !virtualBike->ftmsDeviceConnected()) || !virtualBike)) {
|
||||
emit debug(QStringLiteral("writing resistance ") + QString::number(lastForcedResistance));
|
||||
QByteArray a = setResistanceMode(((double)lastForcedResistance) / 100.0);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setResistance", false, false);
|
||||
}
|
||||
requestResistance = -1;
|
||||
}
|
||||
|
||||
if (lastGearValue != gears()) {
|
||||
if(KICKR_SNAP) {
|
||||
inclinationChanged(lastGrade, lastGrade);
|
||||
} else {
|
||||
QByteArray a = setWheelCircumference(wheelCircumference::gearsToWheelDiameter(gears()));
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setWheelCircumference", false, false);
|
||||
lastGrade = 999; // to force a change
|
||||
} else if (virtualBike && virtualBike->ftmsDeviceConnected() && lastGearValue != gears()) {
|
||||
inclinationChanged(lastGrade, lastGrade);
|
||||
}
|
||||
requestResistance = -1;
|
||||
}
|
||||
}
|
||||
|
||||
lastGearValue = gears();
|
||||
|
||||
if (requestStart != -1) {
|
||||
emit debug(QStringLiteral("starting..."));
|
||||
|
||||
@@ -802,7 +845,7 @@ void wahookickrsnapbike::serviceScanDone(void) {
|
||||
QSettings settings;
|
||||
settings.setValue(QZSettings::ftms_bike, bluetoothDevice.name());
|
||||
settings.sync();
|
||||
if(homeform::singleton())
|
||||
if(homeform::singleton())
|
||||
homeform::singleton()->setToastRequested("Zwift Hub device found, please restart the app to enjoy virtual gearing!");
|
||||
return;
|
||||
}
|
||||
@@ -823,13 +866,13 @@ void wahookickrsnapbike::error(QLowEnergyController::Error err) {
|
||||
void wahookickrsnapbike::deviceDiscovered(const QBluetoothDeviceInfo &device) {
|
||||
emit debug(QStringLiteral("Found new device: ") + device.name() + QStringLiteral(" (") +
|
||||
device.address().toString() + ')');
|
||||
|
||||
if (device.name().toUpper().startsWith("WAHOO KICKR")) {
|
||||
WAHOO_KICKR = true;
|
||||
qDebug() << "WAHOO KICKR workaround activated";
|
||||
} else if(device.name().toUpper().startsWith("KICKR BIKE")) {
|
||||
KICKR_BIKE = true;
|
||||
qDebug() << "KICKR BIKE workaround activated";
|
||||
|
||||
if (device.name().toUpper().startsWith("WAHOO KICKR")) {
|
||||
WAHOO_KICKR = true;
|
||||
qDebug() << "WAHOO KICKR workaround activated";
|
||||
} else if(device.name().toUpper().startsWith("KICKR BIKE")) {
|
||||
KICKR_BIKE = true;
|
||||
qDebug() << "KICKR BIKE workaround activated";
|
||||
} else if(device.name().toUpper().startsWith("KICKR SNAP")) {
|
||||
KICKR_SNAP = true;
|
||||
qDebug() << "KICKR SNAP workaround activated";
|
||||
@@ -915,30 +958,42 @@ void wahookickrsnapbike::controllerStateChanged(QLowEnergyController::Controller
|
||||
|
||||
void wahookickrsnapbike::inclinationChanged(double grade, double percentage) {
|
||||
Q_UNUSED(percentage);
|
||||
if(lastCommandErgMode) {
|
||||
lastGrade = grade + 1; // to force a refresh
|
||||
initRequest = true;
|
||||
qDebug() << "avoid sending this command, since I have first to restore the setSimGrade";
|
||||
return;
|
||||
}
|
||||
if(lastGrade == grade) {
|
||||
qDebug() << "grade is already set to " << grade << "skipping";
|
||||
return;
|
||||
}
|
||||
lastGrade = grade;
|
||||
Inclination = grade;
|
||||
emit debug(QStringLiteral("writing inclination ") + QString::number(grade));
|
||||
QSettings settings;
|
||||
double g = grade;
|
||||
if(KICKR_SNAP) {
|
||||
g += gears() * 0.5;
|
||||
qDebug() << "adding gear offset so " << g;
|
||||
|
||||
if (settings.value(QZSettings::wahoo_without_wheel_diameter, QZSettings::default_wahoo_without_wheel_diameter).toBool()) {
|
||||
lastGrade = grade;
|
||||
emit debug(QStringLiteral("writing inclination ") + QString::number(grade));
|
||||
double g = grade;
|
||||
g += gears();
|
||||
QByteArray a = setSimGrade(g);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setSimGrade", false, false);
|
||||
} else {
|
||||
if(lastCommandErgMode) {
|
||||
lastGrade = grade + 1; // to force a refresh
|
||||
initRequest = true;
|
||||
qDebug() << "avoid sending this command, since I have first to restore the setSimGrade";
|
||||
return;
|
||||
}
|
||||
if(lastGrade == grade) {
|
||||
qDebug() << "grade is already set to " << grade << "skipping";
|
||||
return;
|
||||
}
|
||||
lastGrade = grade;
|
||||
Inclination = grade;
|
||||
emit debug(QStringLiteral("writing inclination ") + QString::number(grade));
|
||||
double g = grade;
|
||||
if(KICKR_SNAP) {
|
||||
g += gears() * 0.5;
|
||||
qDebug() << "adding gear offset so " << g;
|
||||
}
|
||||
QByteArray a = setSimGrade(g);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setSimGrade", false, false);
|
||||
lastCommandErgMode = false;
|
||||
}
|
||||
QByteArray a = setSimGrade(g);
|
||||
uint8_t b[20];
|
||||
memcpy(b, a.constData(), a.length());
|
||||
writeCharacteristic(b, a.length(), "setSimGrade", false, false);
|
||||
lastCommandErgMode = false;
|
||||
}
|
||||
|
||||
bool wahookickrsnapbike::inclinationAvailableByHardware() {
|
||||
@@ -946,10 +1001,18 @@ bool wahookickrsnapbike::inclinationAvailableByHardware() {
|
||||
}
|
||||
|
||||
double wahookickrsnapbike::maxGears() {
|
||||
QSettings settings;
|
||||
if (settings.value(QZSettings::wahoo_without_wheel_diameter, QZSettings::default_wahoo_without_wheel_diameter).toBool()) {
|
||||
return bike::maxGears(); // Use base class behavior
|
||||
}
|
||||
wheelCircumference::GearTable g;
|
||||
return g.maxGears;
|
||||
return g.maxGears; // Use gear table when wheel diameter mode is disabled
|
||||
}
|
||||
|
||||
double wahookickrsnapbike::minGears() {
|
||||
return 1;
|
||||
}
|
||||
QSettings settings;
|
||||
if (settings.value(QZSettings::wahoo_without_wheel_diameter, QZSettings::default_wahoo_without_wheel_diameter).toBool()) {
|
||||
return bike::minGears(); // Use base class behavior
|
||||
}
|
||||
return 1; // Use gear minimum when wheel diameter mode is disabled
|
||||
}
|
||||
@@ -46,6 +46,7 @@ class wahookickrsnapbike : public bike {
|
||||
double maxGears() override;
|
||||
double minGears() override;
|
||||
|
||||
|
||||
enum OperationCode : uint8_t {
|
||||
_unlock = 32,
|
||||
_setResistanceMode = 64,
|
||||
@@ -62,7 +63,7 @@ class wahookickrsnapbike : public bike {
|
||||
// Variabili per iOS (pubbliche per permettere all'implementazione iOS di impostarle)
|
||||
bool zwift_found = false;
|
||||
bool wahoo_found = false;
|
||||
|
||||
|
||||
// Wrapper per characteristicChanged che accetta direttamente QBluetoothUuid
|
||||
void handleCharacteristicValueChanged(const QBluetoothUuid &uuid, const QByteArray &newValue);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ ScrollView {
|
||||
property int gear_cog_size: 14
|
||||
property string gear_wheel_size: "700 x 18C"
|
||||
property real gear_circumference: 2070
|
||||
property bool wahoo_without_wheel_diameter: false
|
||||
}
|
||||
|
||||
property int selectedCranksetSize: settings.gear_crankset_size
|
||||
@@ -323,6 +324,38 @@ ScrollView {
|
||||
spacing: 20
|
||||
id: chainringColumn
|
||||
|
||||
// Wahoo Options
|
||||
GroupBox {
|
||||
title: "Wahoo Options"
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
IndicatorOnlySwitch {
|
||||
id: wahooWithoutWheelDiameterDelegate
|
||||
text: qsTr("Without Wheel Diameter Protocol")
|
||||
spacing: 0
|
||||
bottomPadding: 0
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
leftPadding: 0
|
||||
clip: false
|
||||
checked: settings.wahoo_without_wheel_diameter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
onClicked: settings.wahoo_without_wheel_diameter = checked
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Enable this for simplified Wahoo protocol that adds gears directly to grade instead of using wheel diameter changes. Default is false.")
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: chainringColumn.width - 20
|
||||
font.pixelSize: Qt.application.font.pixelSize - 2
|
||||
color: Material.accent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crankset Size
|
||||
GroupBox {
|
||||
title: "Chainring Size"
|
||||
|
||||
148
src/homeform.cpp
148
src/homeform.cpp
@@ -781,9 +781,35 @@ homeform::homeform(QQmlApplicationEngine *engine, bluetooth *bl) {
|
||||
QtAndroid::androidContext().object());
|
||||
#endif
|
||||
|
||||
// Initialize GPIO gear worker if enabled
|
||||
gpioGearsEnabled = settings.value(QZSettings::gpio_gears_enabled, QZSettings::default_gpio_gears_enabled).toBool();
|
||||
if (gpioGearsEnabled) {
|
||||
gpioGearWorker = new GPIOGearWorkerThread(this);
|
||||
connect(gpioGearWorker, &GPIOGearWorkerThread::gearUpPressed, this, &homeform::onGPIOGearUpPressed);
|
||||
connect(gpioGearWorker, &GPIOGearWorkerThread::gearDownPressed, this, &homeform::onGPIOGearDownPressed);
|
||||
gpioGearWorker->start();
|
||||
qDebug() << "GPIO Gear Worker initialized for Raspberry Pi";
|
||||
}
|
||||
|
||||
bluetoothManager->homeformLoaded = true;
|
||||
}
|
||||
|
||||
homeform::~homeform() {
|
||||
|
||||
gpx_save_clicked();
|
||||
fit_save_clicked();
|
||||
|
||||
// Cleanup GPIO gear worker
|
||||
if (gpioGearWorker) {
|
||||
gpioGearWorker->stop();
|
||||
gpioGearWorker->quit();
|
||||
gpioGearWorker->wait(1000);
|
||||
delete gpioGearWorker;
|
||||
gpioGearWorker = nullptr;
|
||||
qDebug() << "GPIO Gear Worker cleaned up";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
extern "C" {
|
||||
JNIEXPORT void JNICALL
|
||||
@@ -1141,11 +1167,6 @@ void homeform::refresh_bluetooth_devices_clicked() {
|
||||
bluetoothManager->restart();
|
||||
}
|
||||
|
||||
homeform::~homeform() {
|
||||
gpx_save_clicked();
|
||||
fit_save_clicked();
|
||||
}
|
||||
|
||||
void homeform::aboutToQuit() {
|
||||
qDebug() << "homeform::aboutToQuit()";
|
||||
|
||||
@@ -4162,9 +4183,12 @@ void homeform::Plus(const QString &name) {
|
||||
double perc = ((elliptical *)bluetoothManager->device())->currentInclination().value() + step;
|
||||
((elliptical *)bluetoothManager->device())->changeInclination(perc, perc);
|
||||
} else if (bluetoothManager->device()->deviceType() == bluetoothdevice::BIKE) {
|
||||
double step =
|
||||
settings.value(QZSettings::treadmill_step_incline, QZSettings::default_treadmill_step_incline)
|
||||
.toDouble();
|
||||
((bike *)bluetoothManager->device())
|
||||
->changeInclination(((bike *)bluetoothManager->device())->currentInclination().value() + 0.5,
|
||||
((bike *)bluetoothManager->device())->currentInclination().value() + 0.5);
|
||||
->changeInclination(((bike *)bluetoothManager->device())->currentInclination().value() + step,
|
||||
((bike *)bluetoothManager->device())->currentInclination().value() + step);
|
||||
}
|
||||
}
|
||||
} else if (name.contains(QStringLiteral("pid_hr"))) {
|
||||
@@ -4424,9 +4448,12 @@ void homeform::Minus(const QString &name) {
|
||||
double perc = ((elliptical *)bluetoothManager->device())->currentInclination().value() - step;
|
||||
((elliptical *)bluetoothManager->device())->changeInclination(perc, perc);
|
||||
} else if (bluetoothManager->device()->deviceType() == bluetoothdevice::BIKE) {
|
||||
double step =
|
||||
settings.value(QZSettings::treadmill_step_incline, QZSettings::default_treadmill_step_incline)
|
||||
.toDouble();
|
||||
((bike *)bluetoothManager->device())
|
||||
->changeInclination(((bike *)bluetoothManager->device())->currentInclination().value() - 0.5,
|
||||
((bike *)bluetoothManager->device())->currentInclination().value() - 0.5);
|
||||
->changeInclination(((bike *)bluetoothManager->device())->currentInclination().value() - step,
|
||||
((bike *)bluetoothManager->device())->currentInclination().value() - step);
|
||||
}
|
||||
}
|
||||
} else if (name.contains(QStringLiteral("pid_hr"))) {
|
||||
@@ -7589,7 +7616,7 @@ void homeform::onStravaGranted() {
|
||||
settings.setValue(QZSettings::strava_accesstoken, strava->token());
|
||||
settings.setValue(QZSettings::strava_refreshtoken, strava->refreshToken());
|
||||
settings.setValue(QZSettings::strava_lastrefresh, QDateTime::currentDateTime());
|
||||
qDebug() << QStringLiteral("strava authenticathed") << strava->token() << strava->refreshToken();
|
||||
qDebug() << QStringLiteral("strava authenticated successfully");
|
||||
strava_refreshtoken();
|
||||
setGeneralPopupVisible(true);
|
||||
}
|
||||
@@ -7627,8 +7654,7 @@ void homeform::replyDataReceived(const QByteArray &v) {
|
||||
settings.setValue(QZSettings::strava_refreshtoken, jsonResponse[QStringLiteral("refresh_token")]);
|
||||
settings.setValue(QZSettings::strava_expires, jsonResponse[QStringLiteral("expires_at")]);
|
||||
|
||||
qDebug() << jsonResponse[QStringLiteral("access_token")] << jsonResponse[QStringLiteral("refresh_token")]
|
||||
<< jsonResponse[QStringLiteral("expires_at")];
|
||||
qDebug() << "Strava tokens received successfully, expires at:" << jsonResponse[QStringLiteral("expires_at")];
|
||||
|
||||
QString urlstr = QStringLiteral("https://www.strava.com/oauth/token?");
|
||||
QUrlQuery params;
|
||||
@@ -7691,7 +7717,7 @@ void homeform::networkRequestFinished(QNetworkReply *reply) {
|
||||
settings.setValue(QZSettings::strava_refreshtoken, refresh_token);
|
||||
settings.setValue(QZSettings::strava_lastrefresh, QDateTime::currentDateTime());
|
||||
|
||||
qDebug() << access_token << refresh_token;
|
||||
qDebug() << "Strava tokens refreshed successfully";
|
||||
|
||||
} else {
|
||||
|
||||
@@ -8563,3 +8589,99 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// GPIO Gear Worker Thread Implementation
|
||||
GPIOGearWorkerThread::GPIOGearWorkerThread(QObject *parent)
|
||||
: QThread(parent), m_running(false) {
|
||||
#if __has_include(<wiringPi.h>)
|
||||
if (wiringPiSetup() == -1) {
|
||||
qDebug() << "wiringPiSetup failed for GPIO Gear Worker";
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup GPIO pins as inputs with pull-up resistors
|
||||
pinMode(GPIO_GEAR_UP, INPUT);
|
||||
pinMode(GPIO_GEAR_DOWN, INPUT);
|
||||
pullUpDnControl(GPIO_GEAR_UP, PUD_UP);
|
||||
pullUpDnControl(GPIO_GEAR_DOWN, PUD_UP);
|
||||
|
||||
qDebug() << "GPIO Gear Worker: Pins" << GPIO_GEAR_UP << "and" << GPIO_GEAR_DOWN << "initialized";
|
||||
#endif
|
||||
}
|
||||
|
||||
GPIOGearWorkerThread::~GPIOGearWorkerThread() {
|
||||
stop();
|
||||
if (isRunning()) {
|
||||
quit();
|
||||
wait(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void GPIOGearWorkerThread::run() {
|
||||
m_running = true;
|
||||
m_lastUpState = HIGH;
|
||||
m_lastDownState = HIGH;
|
||||
m_lastTriggerTime = QDateTime::currentDateTime().addMSecs(-GPIO_DEBOUNCE_MS);
|
||||
|
||||
qDebug() << "GPIO Gear Worker thread started";
|
||||
|
||||
while (m_running) {
|
||||
#if __has_include(<wiringPi.h>)
|
||||
int upState = digitalRead(GPIO_GEAR_UP);
|
||||
int downState = digitalRead(GPIO_GEAR_DOWN);
|
||||
QDateTime currentTime = QDateTime::currentDateTime();
|
||||
|
||||
// Check for gear up (GPIO 17) - falling edge (button pressed)
|
||||
if (m_lastUpState == HIGH && upState == LOW) {
|
||||
if (m_lastTriggerTime.msecsTo(currentTime) >= GPIO_DEBOUNCE_MS) {
|
||||
emit gearUpPressed();
|
||||
m_lastTriggerTime = currentTime;
|
||||
qDebug() << "GPIO Gear Up pressed";
|
||||
}
|
||||
}
|
||||
|
||||
// Check for gear down (GPIO 27) - falling edge (button pressed)
|
||||
if (m_lastDownState == HIGH && downState == LOW) {
|
||||
if (m_lastTriggerTime.msecsTo(currentTime) >= GPIO_DEBOUNCE_MS) {
|
||||
emit gearDownPressed();
|
||||
m_lastTriggerTime = currentTime;
|
||||
qDebug() << "GPIO Gear Down pressed";
|
||||
}
|
||||
}
|
||||
|
||||
m_lastUpState = upState;
|
||||
m_lastDownState = downState;
|
||||
#endif
|
||||
|
||||
msleep(GPIO_POLL_MS);
|
||||
}
|
||||
|
||||
qDebug() << "GPIO Gear Worker thread stopped";
|
||||
}
|
||||
|
||||
void GPIOGearWorkerThread::stop() {
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
// GPIO Gear Control Methods in homeform
|
||||
void homeform::onGPIOGearUpPressed() {
|
||||
QDateTime currentTime = QDateTime::currentDateTime();
|
||||
if (lastGearGpioTime.msecsTo(currentTime) < 100) {
|
||||
return; // Additional debouncing protection
|
||||
}
|
||||
lastGearGpioTime = currentTime;
|
||||
|
||||
qDebug() << "GPIO Gear Up processed";
|
||||
Plus(QStringLiteral("gears"));
|
||||
}
|
||||
|
||||
void homeform::onGPIOGearDownPressed() {
|
||||
QDateTime currentTime = QDateTime::currentDateTime();
|
||||
if (lastGearGpioTime.msecsTo(currentTime) < 100) {
|
||||
return; // Additional debouncing protection
|
||||
}
|
||||
lastGearGpioTime = currentTime;
|
||||
|
||||
qDebug() << "GPIO Gear Down processed";
|
||||
Minus(QStringLiteral("gears"));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,21 @@
|
||||
#include <QQuickItem>
|
||||
#include <QQuickItemGrabResult>
|
||||
#include <QTextToSpeech>
|
||||
#include <QThread>
|
||||
#include <QDateTime>
|
||||
|
||||
#if __has_include(<wiringPi.h>)
|
||||
#include <wiringPi.h>
|
||||
#else
|
||||
#define INPUT 0
|
||||
#define HIGH 1
|
||||
#define LOW 0
|
||||
#define PUD_UP 2
|
||||
inline int digitalRead(int pin) { Q_UNUSED(pin); return HIGH; }
|
||||
inline void pinMode(int pin, int mode) { Q_UNUSED(pin); Q_UNUSED(mode); }
|
||||
inline void pullUpDnControl(int pin, int pud) { Q_UNUSED(pin); Q_UNUSED(pud); }
|
||||
inline int wiringPiSetup() { return 0; }
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
|
||||
@@ -42,6 +57,31 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class GPIOGearWorkerThread : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GPIOGearWorkerThread(QObject *parent = nullptr);
|
||||
~GPIOGearWorkerThread();
|
||||
void run() override;
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
void gearUpPressed();
|
||||
void gearDownPressed();
|
||||
|
||||
private:
|
||||
static const uint8_t GPIO_GEAR_UP = 17; // GPIO 17 (Physical pin 11)
|
||||
static const uint8_t GPIO_GEAR_DOWN = 27; // GPIO 27 (Physical pin 13)
|
||||
static const uint16_t GPIO_DEBOUNCE_MS = 100;
|
||||
static const uint16_t GPIO_POLL_MS = 50;
|
||||
|
||||
bool m_running = false;
|
||||
int m_lastUpState = HIGH;
|
||||
int m_lastDownState = HIGH;
|
||||
QDateTime m_lastTriggerTime;
|
||||
};
|
||||
|
||||
class DataObject : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
@@ -709,6 +749,9 @@ class homeform : public QObject {
|
||||
static homeform *m_singleton;
|
||||
TemplateInfoSenderBuilder *userTemplateManager = nullptr;
|
||||
TemplateInfoSenderBuilder *innerTemplateManager = nullptr;
|
||||
GPIOGearWorkerThread *gpioGearWorker = nullptr;
|
||||
QDateTime lastGearGpioTime;
|
||||
bool gpioGearsEnabled = false;
|
||||
QList<QObject *> dataList;
|
||||
QList<SessionLine> Session;
|
||||
QQmlApplicationEngine *engine;
|
||||
@@ -888,6 +931,8 @@ class homeform : public QObject {
|
||||
void sortTilesTimeout();
|
||||
void gearUp();
|
||||
void gearDown();
|
||||
void onGPIOGearUpPressed();
|
||||
void onGPIOGearDownPressed();
|
||||
void changeTimestamp(QTime source, QTime actual);
|
||||
void pelotonOffset_Plus();
|
||||
void pelotonOffset_Minus();
|
||||
|
||||
@@ -844,7 +844,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
text: "version 2.19.2"
|
||||
text: "version 2.20.0"
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
|
||||
@@ -1025,14 +1025,10 @@ void peloton::ride_onfinish(QNetworkReply *reply) {
|
||||
}
|
||||
qDebug() << row.duration << "power" << row.power << row.rampDuration << row.rampElapsed;
|
||||
trainrows.append(row);
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
} else {
|
||||
r.duration = QTime(0, len / 60, len % 60, 0);
|
||||
r.power = -1;
|
||||
if (r.power != -1) {
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
trainrows.append(r);
|
||||
}
|
||||
} else if (!zone.toUpper().compare(QStringLiteral("DESCENDING RECOVERY"))) {
|
||||
@@ -1054,38 +1050,25 @@ void peloton::ride_onfinish(QNetworkReply *reply) {
|
||||
}
|
||||
qDebug() << row.duration << "power" << row.power << row.rampDuration << row.rampElapsed;
|
||||
trainrows.append(row);
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
} else if (!zone.toUpper().compare(QStringLiteral("RECOVERY"))) {
|
||||
r.duration = QTime(0, len / 60, len % 60, 0);
|
||||
r.power = settings.value(QZSettings::ftp, QZSettings::default_ftp).toDouble() * 0.45;
|
||||
if (r.power != -1) {
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
trainrows.append(r);
|
||||
qDebug() << r.duration << "power" << r.power;
|
||||
} else if (!zone.toUpper().compare(QStringLiteral("FLAT ROAD"))) {
|
||||
r.duration = QTime(0, len / 60, len % 60, 0);
|
||||
r.power = settings.value(QZSettings::ftp, QZSettings::default_ftp).toDouble() * 0.50;
|
||||
if (r.power != -1) {
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
trainrows.append(r);
|
||||
qDebug() << r.duration << "power" << r.power;
|
||||
} else if (!zone.toUpper().compare(QStringLiteral("SWEET SPOT"))) {
|
||||
r.duration = QTime(0, len / 60, len % 60, 0);
|
||||
r.power = settings.value(QZSettings::ftp, QZSettings::default_ftp).toDouble() * 0.91;
|
||||
if (r.power != -1) {
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
trainrows.append(r);
|
||||
qDebug() << r.duration << "power" << r.power;
|
||||
} else if (!zone.toUpper().compare(QStringLiteral("INTERVALS"))) {
|
||||
r.duration = QTime(0, len / 60, len % 60, 0);
|
||||
r.power = settings.value(QZSettings::ftp, QZSettings::default_ftp).toDouble() * 0.75;
|
||||
if (r.power != -1) {
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
trainrows.append(r);
|
||||
qDebug() << r.duration << "power" << r.power;
|
||||
} else if (!zone.toUpper().compare(QStringLiteral("ZONE 1"))) {
|
||||
@@ -1148,9 +1131,6 @@ void peloton::ride_onfinish(QNetworkReply *reply) {
|
||||
if(len > 0 && atLeastOnePower) {
|
||||
r.duration = QTime(0, len / 60, len % 60, 0);
|
||||
r.power = -1;
|
||||
if (r.power != -1) {
|
||||
atLeastOnePower = true;
|
||||
}
|
||||
qDebug() << "ERROR not handled!" << zone;
|
||||
trainrows.append(r);
|
||||
}
|
||||
@@ -1167,7 +1147,7 @@ void peloton::ride_onfinish(QNetworkReply *reply) {
|
||||
QJsonArray pace_intensities_list = target_metrics_data_list[QStringLiteral("pace_intensities")].toArray();
|
||||
|
||||
int pace_count = 0;
|
||||
rower_pace_offset = 0;
|
||||
rower_pace_offset = -1;
|
||||
|
||||
foreach (QJsonValue o, pace_intensities_list) {
|
||||
if(o["value"].toInt() < 0) {
|
||||
@@ -2203,8 +2183,7 @@ void peloton::replyDataReceived(const QByteArray &v) {
|
||||
tempAccessToken = jsonResponse[QStringLiteral("access_token")].toString();
|
||||
tempRefreshToken = jsonResponse[QStringLiteral("refresh_token")].toString();
|
||||
|
||||
qDebug() << jsonResponse[QStringLiteral("access_token")] << jsonResponse[QStringLiteral("refresh_token")]
|
||||
<< jsonResponse[QStringLiteral("expires_at")];
|
||||
qDebug() << "Peloton tokens received successfully, expires at:" << jsonResponse[QStringLiteral("expires_at")];
|
||||
|
||||
QString urlstr = QStringLiteral("https://www.peloton.com/oauth/token?");
|
||||
QUrlQuery params;
|
||||
@@ -2268,7 +2247,7 @@ void peloton::networkRequestFinished(QNetworkReply *reply) {
|
||||
tempRefreshToken = refresh_token;
|
||||
tempExpiresAt = QDateTime::currentDateTime();
|
||||
|
||||
qDebug() << access_token << refresh_token;
|
||||
qDebug() << "Peloton tokens refreshed successfully";
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
@@ -980,4 +980,4 @@ INCLUDEPATH += purchasing/inapp
|
||||
|
||||
WINRT_MANIFEST = AppxManifest.xml
|
||||
|
||||
VERSION = 2.19.2
|
||||
VERSION = 2.20.0
|
||||
|
||||
@@ -442,6 +442,7 @@ const QString QZSettings::default_horizon_treadmill_profile_user5 = QStringLiter
|
||||
const QString QZSettings::nordictrack_gx_2_7 = QStringLiteral("nordictrack_gx_2_7");
|
||||
const QString QZSettings::rolling_resistance = QStringLiteral("rolling_resistance");
|
||||
const QString QZSettings::wahoo_rgt_dircon = QStringLiteral("wahoo_rgt_dircon");
|
||||
const QString QZSettings::wahoo_without_wheel_diameter = QStringLiteral("wahoo_without_wheel_diameter");
|
||||
const QString QZSettings::tts_description_enabled = QStringLiteral("tts_description_enabled");
|
||||
const QString QZSettings::tile_preset_resistance_1_enabled = QStringLiteral("tile_preset_resistance_1_enabled");
|
||||
const QString QZSettings::tile_preset_resistance_1_order = QStringLiteral("tile_preset_resistance_1_order");
|
||||
@@ -942,9 +943,10 @@ const QString QZSettings::inclinationResistancePoints = QStringLiteral("inclinat
|
||||
const QString QZSettings::default_inclinationResistancePoints = QStringLiteral("");
|
||||
|
||||
const QString QZSettings::floatingwindow_type = QStringLiteral("floatingwindow_type");
|
||||
const QString QZSettings::gpio_gears_enabled = QStringLiteral("gpio_gears_enabled");
|
||||
|
||||
|
||||
const uint32_t allSettingsCount = 771;
|
||||
const uint32_t allSettingsCount = 772;
|
||||
|
||||
QVariant allSettings[allSettingsCount][2] = {
|
||||
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
|
||||
@@ -1736,6 +1738,7 @@ QVariant allSettings[allSettingsCount][2] = {
|
||||
{QZSettings::nordictrack_elite_800, QZSettings::default_nordictrack_elite_800},
|
||||
{QZSettings::inclinationResistancePoints, QZSettings::default_inclinationResistancePoints},
|
||||
{QZSettings::floatingwindow_type, QZSettings::default_floatingwindow_type},
|
||||
{QZSettings::gpio_gears_enabled, QZSettings::default_gpio_gears_enabled},
|
||||
{QZSettings::rogue_echo_bike, QZSettings::default_rogue_echo_bike},
|
||||
};
|
||||
|
||||
|
||||
@@ -1343,6 +1343,9 @@ class QZSettings {
|
||||
static const QString wahoo_rgt_dircon;
|
||||
static constexpr bool default_wahoo_rgt_dircon = false;
|
||||
|
||||
static const QString wahoo_without_wheel_diameter;
|
||||
static constexpr bool default_wahoo_without_wheel_diameter = false;
|
||||
|
||||
static const QString tts_description_enabled;
|
||||
static constexpr bool default_tts_description_enabled = true;
|
||||
|
||||
@@ -2513,6 +2516,9 @@ class QZSettings {
|
||||
static const QString floatingwindow_type;
|
||||
static constexpr int default_floatingwindow_type = 0;
|
||||
|
||||
static const QString gpio_gears_enabled;
|
||||
static constexpr bool default_gpio_gears_enabled = false;
|
||||
|
||||
/**
|
||||
* @brief Write the QSettings values using the constants from this namespace.
|
||||
* @param showDefaults Optionally indicates if the default should be shown with the key.
|
||||
|
||||
125
src/settings.qml
125
src/settings.qml
@@ -489,7 +489,7 @@ import Qt.labs.platform 1.1
|
||||
property bool eslinker_ypoo: false
|
||||
|
||||
// from version 2.11.69
|
||||
property bool wahoo_rgt_dircon: false
|
||||
property bool wahoo_rgt_dircon: false
|
||||
|
||||
// from version 2.11.73
|
||||
property bool tts_description_enabled: true
|
||||
@@ -1158,10 +1158,16 @@ import Qt.labs.platform 1.1
|
||||
property int tile_heat_time_in_zone_4_order: 71
|
||||
|
||||
property bool proform_treadmill_carbon_tls: false
|
||||
|
||||
// 2.19.1
|
||||
property bool proform_treadmill_995i: false
|
||||
property bool rogue_echo_bike: false
|
||||
property int fit_file_garmin_device_training_effect_device: 3122
|
||||
property int fit_file_garmin_device_training_effect_device: 3122
|
||||
|
||||
// 2.19.2
|
||||
property bool tile_hr_time_in_zone_individual_mode: false
|
||||
property bool wahoo_without_wheel_diameter: false
|
||||
property bool gpio_gears_enabled: false
|
||||
}
|
||||
|
||||
function paddingZeros(text, limit) {
|
||||
@@ -5353,7 +5359,7 @@ import Qt.labs.platform 1.1
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
}
|
||||
|
||||
IndicatorOnlySwitch {
|
||||
text: qsTr("Zwift Treadmill Auto Inclination")
|
||||
@@ -6368,43 +6374,6 @@ import Qt.labs.platform 1.1
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
Label {
|
||||
id: labelTreadmillStepInclination
|
||||
text: qsTr("Inclination Step:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: treadmillInclinationStepTextField
|
||||
text: settings.treadmill_step_incline
|
||||
horizontalAlignment: Text.AlignRight
|
||||
Layout.fillHeight: false
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
//inputMethodHints: Qt.ImhDigitsOnly
|
||||
onAccepted: settings.treadmill_step_incline = text
|
||||
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
|
||||
}
|
||||
Button {
|
||||
id: okTreadmillInclinationStepButton
|
||||
text: "OK"
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
onClicked: { settings.treadmill_step_incline = treadmillInclinationStepTextField.text; toast.show("Setting saved!"); }
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("(Incline Tile) This controls the amount of the increase or decrease in the inclination when you press the plus or minus button in the Incline Tile. Default is 0.5.")
|
||||
font.bold: true
|
||||
font.italic: true
|
||||
font.pixelSize: Qt.application.font.pixelSize - 2
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
@@ -9327,6 +9296,43 @@ import Qt.labs.platform 1.1
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
Label {
|
||||
id: labelInclinationStep
|
||||
text: qsTr("Inclination Step:")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TextField {
|
||||
id: inclinationStepTextField
|
||||
text: settings.treadmill_step_incline
|
||||
horizontalAlignment: Text.AlignRight
|
||||
Layout.fillHeight: false
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
//inputMethodHints: Qt.ImhDigitsOnly
|
||||
onAccepted: settings.treadmill_step_incline = text
|
||||
onActiveFocusChanged: if(this.focus) this.cursorPosition = this.text.length
|
||||
}
|
||||
Button {
|
||||
id: okInclinationStepButton
|
||||
text: "OK"
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
onClicked: { settings.treadmill_step_incline = inclinationStepTextField.text; toast.show("Setting saved!"); }
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("(Incline Tile) This controls the amount of the increase or decrease in the inclination when you press the plus or minus button in the Incline Tile for both treadmills and bikes. Default is 0.5.")
|
||||
font.bold: true
|
||||
font.italic: true
|
||||
font.pixelSize: Qt.application.font.pixelSize - 2
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
|
||||
IndicatorOnlySwitch {
|
||||
text: qsTr("Send real inclination to virtual bridge")
|
||||
@@ -12054,6 +12060,45 @@ import Qt.labs.platform 1.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AccordionElement {
|
||||
id: hardwareControlAccordion
|
||||
title: qsTr("Hardware Control")
|
||||
indicatRectColor: Material.color(Material.Grey)
|
||||
textColor: Material.color(Material.Yellow)
|
||||
color: Material.backgroundColor
|
||||
accordionContent: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
IndicatorOnlySwitch {
|
||||
id: gpioGearsEnabledDelegate
|
||||
text: qsTr("GPIO Gears Control")
|
||||
spacing: 0
|
||||
bottomPadding: 0
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
leftPadding: 0
|
||||
clip: false
|
||||
checked: settings.gpio_gears_enabled
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
onClicked: { settings.gpio_gears_enabled = checked; window.settings_restart_to_apply = true; }
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Enable GPIO-based gear shifting on Raspberry Pi (GPIO 17 for up, GPIO 27 for down)")
|
||||
font.bold: true
|
||||
font.italic: true
|
||||
font.pixelSize: Qt.application.font.pixelSize - 2
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
color: Material.color(Material.Lime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*##^##
|
||||
Designer {
|
||||
|
||||
@@ -100,7 +100,7 @@ private slots:
|
||||
access_token_expiration = now.toMSecsSinceEpoch() + (expires_in - 5) * 1000;
|
||||
refresh_token_expiration = now.toMSecsSinceEpoch() + (refresh_expires_in - 5) * 1000;
|
||||
|
||||
qDebug() << "Access Token: " << access_token;
|
||||
qDebug() << "Access Token received successfully";
|
||||
} else {
|
||||
qDebug() << "Error fetching token: " << reply->errorString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user