Compare commits

...

3 Commits

Author SHA1 Message Date
Roberto Viola
777ccefc5e Add Android 16 API 36 compatibility with WindowInsetsController
- Create CustomQtActivity extending QtActivity for Android 16 support
- Replace deprecated setSystemUiVisibility with WindowInsetsController
- Maintain backward compatibility with older Android versions
- Fix header toolbar visibility issues on Android 16

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-02 19:50:13 +02:00
Roberto Viola
17a4c838b7 Merge branch 'master' into workout-history 2025-07-02 14:37:45 +02:00
Roberto Viola
aecb0c97df Add individual mode for HR time-in-zone tiles
Introduces a new setting to toggle between cumulative and individual time display for heart rate zone tiles. Updates the UI and logic in homeform.cpp to reflect the selected mode, adds the setting to QZSettings and QML, and documents the change.
2025-07-02 14:37:35 +02:00
8 changed files with 126 additions and 15 deletions

View File

@@ -228,6 +228,8 @@ static constexpr bool default_proform_treadmill_newmodel = false;
const QString QZSettings::proform_treadmill_newmodel = QStringLiteral("proform_treadmill_newmodel");
```
* Update the `allSettingsCount` in `qzsettings.cpp`
#### 4. Update QML Settings UI
**In `src/settings.qml`:**
@@ -364,4 +366,9 @@ The ProForm 995i implementation serves as the reference example:
- Device implementations should follow existing patterns for consistency
- Add comprehensive logging using the project's logging framework
- Test device detection thoroughly using the existing test infrastructure
- Consider platform differences when adding new features
- Consider platform differences when adding new features
## Additional Memories
- When adding a new setting in QML (setting-tiles.qml), you must:
* Add the property at the END of the properties list

View File

@@ -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"/>

View 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
);
}
}
}

View File

@@ -4941,19 +4941,50 @@ void homeform::update() {
uint32_t seconds_zone4 = bluetoothManager->device()->secondsForHeartZone(3);
uint32_t seconds_zone5 = bluetoothManager->device()->secondsForHeartZone(4);
// Calculate cumulative times (time in this zone or higher)
uint32_t seconds_zone1_plus = seconds_zone1 + seconds_zone2 + seconds_zone3 + seconds_zone4 + seconds_zone5;
uint32_t seconds_zone2_plus = seconds_zone2 + seconds_zone3 + seconds_zone4 + seconds_zone5;
uint32_t seconds_zone3_plus = seconds_zone3 + seconds_zone4 + seconds_zone5;
uint32_t seconds_zone4_plus = seconds_zone4 + seconds_zone5;
uint32_t seconds_zone5_plus = seconds_zone5; // Zone 5 is already just zone 5
// Check if individual mode is enabled
bool individual_mode = settings.value(QZSettings::tile_hr_time_in_zone_individual_mode, QZSettings::default_tile_hr_time_in_zone_individual_mode).toBool();
// Update the UI for each tile
tile_hr_time_in_zone_1->setValue(QTime(0, 0, 0).addSecs(seconds_zone1_plus).toString("h:mm:ss"));
tile_hr_time_in_zone_2->setValue(QTime(0, 0, 0).addSecs(seconds_zone2_plus).toString("h:mm:ss"));
tile_hr_time_in_zone_3->setValue(QTime(0, 0, 0).addSecs(seconds_zone3_plus).toString("h:mm:ss"));
tile_hr_time_in_zone_4->setValue(QTime(0, 0, 0).addSecs(seconds_zone4_plus).toString("h:mm:ss"));
tile_hr_time_in_zone_5->setValue(QTime(0, 0, 0).addSecs(seconds_zone5_plus).toString("h:mm:ss"));
uint32_t display_zone1, display_zone2, display_zone3, display_zone4, display_zone5;
if (individual_mode) {
// Individual mode: show only time in specific zone
display_zone1 = seconds_zone1;
display_zone2 = seconds_zone2;
display_zone3 = seconds_zone3;
display_zone4 = seconds_zone4;
display_zone5 = seconds_zone5;
} else {
// Progressive mode: show cumulative time (time in this zone or higher)
display_zone1 = seconds_zone1 + seconds_zone2 + seconds_zone3 + seconds_zone4 + seconds_zone5;
display_zone2 = seconds_zone2 + seconds_zone3 + seconds_zone4 + seconds_zone5;
display_zone3 = seconds_zone3 + seconds_zone4 + seconds_zone5;
display_zone4 = seconds_zone4 + seconds_zone5;
display_zone5 = seconds_zone5;
}
// Update labels based on mode
if (individual_mode) {
// Individual mode: show specific zone labels
tile_hr_time_in_zone_1->setName(QStringLiteral("HR Zone 1"));
tile_hr_time_in_zone_2->setName(QStringLiteral("HR Zone 2"));
tile_hr_time_in_zone_3->setName(QStringLiteral("HR Zone 3"));
tile_hr_time_in_zone_4->setName(QStringLiteral("HR Zone 4"));
tile_hr_time_in_zone_5->setName(QStringLiteral("HR Zone 5"));
} else {
// Progressive mode: show cumulative zone labels
tile_hr_time_in_zone_1->setName(QStringLiteral("HR Zone 1+"));
tile_hr_time_in_zone_2->setName(QStringLiteral("HR Zone 2+"));
tile_hr_time_in_zone_3->setName(QStringLiteral("HR Zone 3+"));
tile_hr_time_in_zone_4->setName(QStringLiteral("HR Zone 4+"));
tile_hr_time_in_zone_5->setName(QStringLiteral("HR Zone 5"));
}
// Update the UI for each tile
tile_hr_time_in_zone_1->setValue(QTime(0, 0, 0).addSecs(display_zone1).toString("h:mm:ss"));
tile_hr_time_in_zone_2->setValue(QTime(0, 0, 0).addSecs(display_zone2).toString("h:mm:ss"));
tile_hr_time_in_zone_3->setValue(QTime(0, 0, 0).addSecs(display_zone3).toString("h:mm:ss"));
tile_hr_time_in_zone_4->setValue(QTime(0, 0, 0).addSecs(display_zone4).toString("h:mm:ss"));
tile_hr_time_in_zone_5->setValue(QTime(0, 0, 0).addSecs(display_zone5).toString("h:mm:ss"));
// Set colors based on the zone
tile_hr_time_in_zone_1->setValueFontColor(QStringLiteral("lightsteelblue"));

View File

@@ -931,6 +931,8 @@ const QString QZSettings::tacxneo2_disable_negative_inclination = QStringLiteral
const QString QZSettings::tile_coretemperature_enabled = QStringLiteral("tile_coretemperature_enabled");
const QString QZSettings::tile_coretemperature_order = QStringLiteral("tile_coretemperature_order");
const QString QZSettings::tile_hr_time_in_zone_individual_mode = QStringLiteral("tile_hr_time_in_zone_individual_mode");
const QString QZSettings::nordictrack_t65s_treadmill_81_miles = QStringLiteral("nordictrack_t65s_treadmill_81_miles");
const QString QZSettings::nordictrack_elite_800 = QStringLiteral("nordictrack_elite_800");
@@ -942,7 +944,7 @@ const QString QZSettings::default_inclinationResistancePoints = QStringLiteral("
const QString QZSettings::floatingwindow_type = QStringLiteral("floatingwindow_type");
const uint32_t allSettingsCount = 770;
const uint32_t allSettingsCount = 771;
QVariant allSettings[allSettingsCount][2] = {
{QZSettings::cryptoKeySettingsProfiles, QZSettings::default_cryptoKeySettingsProfiles},
@@ -1728,6 +1730,8 @@ QVariant allSettings[allSettingsCount][2] = {
{QZSettings::tile_coretemperature_enabled, QZSettings::default_tile_coretemperature_enabled},
{QZSettings::tile_coretemperature_order, QZSettings::default_tile_coretemperature_order},
{QZSettings::tile_hr_time_in_zone_individual_mode, QZSettings::default_tile_hr_time_in_zone_individual_mode},
{QZSettings::nordictrack_t65s_treadmill_81_miles, QZSettings::default_nordictrack_t65s_treadmill_81_miles},
{QZSettings::nordictrack_elite_800, QZSettings::default_nordictrack_elite_800},
{QZSettings::inclinationResistancePoints, QZSettings::default_inclinationResistancePoints},

View File

@@ -2493,6 +2493,9 @@ class QZSettings {
static const QString tile_coretemperature_order;
static constexpr int default_tile_coretemperature_order = 67;
static const QString tile_hr_time_in_zone_individual_mode;
static constexpr bool default_tile_hr_time_in_zone_individual_mode = false;
static const QString nordictrack_t65s_treadmill_81_miles;
static constexpr bool default_nordictrack_t65s_treadmill_81_miles = false;

View File

@@ -261,6 +261,8 @@ ScrollView {
property int tile_heat_time_in_zone_3_order: 70
property bool tile_heat_time_in_zone_4_enabled: false
property int tile_heat_time_in_zone_4_order: 71
property bool tile_hr_time_in_zone_individual_mode: false
}
@@ -5020,6 +5022,35 @@ ScrollView {
color: Material.color(Material.Lime)
}
RowLayout {
spacing: 10
Layout.fillWidth: true
CheckBox {
id: hrTimeInZoneIndividualModeCheckBox
text: qsTr("Show individual zone times (instead of cumulative)")
Layout.fillWidth: true
checked: settings.tile_hr_time_in_zone_individual_mode
onClicked: {
settings.tile_hr_time_in_zone_individual_mode = checked
toast.show("Setting saved!")
}
}
}
Label {
text: qsTr("When enabled, each zone shows only the time spent in that specific zone. When disabled (default), each zone shows cumulative time spent in that zone or higher.")
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.Orange)
}
AccordionCheckElement {
id: coreTemperatureAccordion
title: qsTr("Core Temperature")

View File

@@ -1161,6 +1161,7 @@ import Qt.labs.platform 1.1
property bool proform_treadmill_995i: false
property bool rogue_echo_bike: false
property int fit_file_garmin_device_training_effect_device: 3122
property bool tile_hr_time_in_zone_individual_mode: false
}
function paddingZeros(text, limit) {