enh(cpn): Cloudbuild support - phase 1 (#4856)
This commit is contained in:
@@ -157,7 +157,6 @@ void AppPreferencesDialog::accept()
|
||||
profile.externalModuleSize(ui->externalModuleSizeCB->currentData().toInt());
|
||||
profile.channelOrder(ui->channelorderCB->currentIndex());
|
||||
profile.defaultMode(ui->stickmodeCB->currentIndex());
|
||||
profile.renameFwFiles(ui->renameFirmware->isChecked());
|
||||
profile.burnFirmware(ui->burnFirmware->isChecked());
|
||||
profile.sdPath(ui->sdPath->text());
|
||||
profile.pBackupDir(ui->profilebackupPath->text());
|
||||
@@ -307,7 +306,6 @@ void AppPreferencesDialog::initSettings()
|
||||
ui->externalModuleSizeCB->setCurrentIndex(ui->externalModuleSizeCB->findData(profile.externalModuleSize()));
|
||||
ui->channelorderCB->setCurrentIndex(profile.channelOrder());
|
||||
ui->stickmodeCB->setCurrentIndex(profile.defaultMode());
|
||||
ui->renameFirmware->setChecked(profile.renameFwFiles());
|
||||
ui->sdPath->setText(profile.sdPath());
|
||||
if (!profile.pBackupDir().isEmpty()) {
|
||||
if (QDir(profile.pBackupDir()).exists()) {
|
||||
|
||||
@@ -45,13 +45,32 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="profileTab">
|
||||
<attribute name="title">
|
||||
<string>Radio Profile</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0,0,0,0">
|
||||
<item row="12" column="2">
|
||||
<widget class="QLabel" name="lblGeneralSettings">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">General Settings Label</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="profileNameLE"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="sizePolicy">
|
||||
@@ -68,6 +87,250 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="5">
|
||||
<widget class="Line" name="line_8">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="2">
|
||||
<widget class="QCheckBox" name="burnFirmware">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Prompt to write firmware to radio after update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="2">
|
||||
<widget class="QLineEdit" name="profilebackupPath">
|
||||
<property name="toolTip">
|
||||
<string>If set it will override the application general setting</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QComboBox" name="langCombo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>General Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QWidget" name="optionsWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="optionsLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="5">
|
||||
<widget class="QWidget" name="widget_splashImage" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="splashLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Splash Screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="SplashFileName">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="SplashSelect">
|
||||
<property name="text">
|
||||
<string>Select Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="imageLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>212</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="clearImageButton">
|
||||
<property name="text">
|
||||
<string>Clear Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="3" colspan="2">
|
||||
<widget class="QPushButton" name="ProfilebackupPathButton">
|
||||
<property name="toolTip">
|
||||
<string>The profile specific folder, if set, will override general Backup folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QComboBox" name="boardCB">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="2">
|
||||
<widget class="QComboBox" name="externalModuleSizeCB"/>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>External Module</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="2">
|
||||
<widget class="QCheckBox" name="chkPromptSDSync">
|
||||
<property name="text">
|
||||
<string>Prompt to run SD Sync after update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>Default Int. Module</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="sizePolicy">
|
||||
@@ -90,32 +353,38 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="2">
|
||||
<widget class="QLineEdit" name="sdPath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="5">
|
||||
<widget class="Line" name="splashSeparator">
|
||||
<item row="8" column="0" colspan="5">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Other Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="2">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="16" column="2">
|
||||
<widget class="QComboBox" name="channelorderCB">
|
||||
<property name="sizePolicy">
|
||||
@@ -252,238 +521,6 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>Default Int. Module</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="3" colspan="2">
|
||||
<widget class="QPushButton" name="sdPathButton">
|
||||
<property name="text">
|
||||
<string>Select Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="sdPathLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SD Structure path</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="2">
|
||||
<widget class="QLabel" name="lblGeneralSettings">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">General Settings Label</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="2">
|
||||
<widget class="QCheckBox" name="chkPromptSDSync">
|
||||
<property name="text">
|
||||
<string>Prompt to run SD Sync after update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default Channel Order</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="2">
|
||||
<widget class="QComboBox" name="defaultInternalModuleCB"/>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QComboBox" name="langCombo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="5">
|
||||
<widget class="QWidget" name="widget_splashImage" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="splashLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Splash Screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="SplashFileName">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="SplashSelect">
|
||||
<property name="text">
|
||||
<string>Select Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="imageLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>212</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="clearImageButton">
|
||||
<property name="text">
|
||||
<string>Clear Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="3" colspan="2">
|
||||
<widget class="QPushButton" name="ProfilebackupPathButton">
|
||||
<property name="toolTip">
|
||||
<string>The profile specific folder, if set, will override general Backup folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>General Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="2">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="15" column="2">
|
||||
<widget class="QComboBox" name="stickmodeCB">
|
||||
<property name="maximumSize">
|
||||
@@ -538,46 +575,19 @@ Mode 4:
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QWidget" name="optionsWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="optionsLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
<item row="13" column="2">
|
||||
<widget class="QComboBox" name="defaultInternalModuleCB"/>
|
||||
</item>
|
||||
<item row="15" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<item row="9" column="2">
|
||||
<widget class="QLineEdit" name="sdPath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default Stick Mode</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -613,8 +623,76 @@ Mode 4:
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="5">
|
||||
<widget class="Line" name="line_8">
|
||||
<item row="15" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default Stick Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="3" colspan="2">
|
||||
<widget class="QPushButton" name="sdPathButton">
|
||||
<property name="text">
|
||||
<string>Select Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="sdPathLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SD Structure path</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="profilebackupPathLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The profile specific folder, if set, will override general Backup folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Backup folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default Channel Order</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="5">
|
||||
<widget class="Line" name="splashSeparator">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -626,38 +704,6 @@ Mode 4:
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="5">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Other Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="2">
|
||||
<widget class="QCheckBox" name="burnFirmware">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Prompt to write firmware to radio after update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
@@ -677,65 +723,6 @@ Mode 4:
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QComboBox" name="boardCB">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="profileNameLE"/>
|
||||
</item>
|
||||
<item row="17" column="2">
|
||||
<widget class="QCheckBox" name="renameFirmware">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Append version number to firmware file name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="2">
|
||||
<widget class="QLineEdit" name="profilebackupPath">
|
||||
<property name="toolTip">
|
||||
<string>If set it will override the application general setting</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="profilebackupPathLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The profile specific folder, if set, will override general Backup folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Backup folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>External Module</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="2">
|
||||
<widget class="QComboBox" name="externalModuleSizeCB"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="applicationTab">
|
||||
@@ -1642,7 +1629,6 @@ Mode 4:
|
||||
<tabstop>pbackupEnable</tabstop>
|
||||
<tabstop>stickmodeCB</tabstop>
|
||||
<tabstop>channelorderCB</tabstop>
|
||||
<tabstop>renameFirmware</tabstop>
|
||||
<tabstop>burnFirmware</tabstop>
|
||||
<tabstop>splashincludeCB</tabstop>
|
||||
<tabstop>snapshotPath</tabstop>
|
||||
|
||||
@@ -898,6 +898,20 @@ bool SemanticVersion::isEmpty()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SemanticVersion::isPreRelease(const QString vers)
|
||||
{
|
||||
fromString(vers);
|
||||
return isPreRelease();
|
||||
}
|
||||
|
||||
bool SemanticVersion::isPreRelease()
|
||||
{
|
||||
if (version.preReleaseType != PR_NONE)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int SemanticVersion::compare(const SemanticVersion& other)
|
||||
{
|
||||
if (version.major != other.version.major) {
|
||||
|
||||
@@ -263,6 +263,8 @@ class SemanticVersion
|
||||
bool fromInt(const unsigned int val);
|
||||
bool isEmpty(const QString vers);
|
||||
bool isEmpty();
|
||||
bool isPreRelease(const QString vers);
|
||||
bool isPreRelease();
|
||||
|
||||
SemanticVersion& operator=(const SemanticVersion& rhs);
|
||||
|
||||
|
||||
@@ -1250,6 +1250,7 @@ void MainWindow::onCurrentProfileChanged()
|
||||
g.moveCurrentProfileToTop();
|
||||
Firmware::setCurrentVariant(Firmware::getFirmwareForId(g.currentProfile().fwType()));
|
||||
emit firmwareChanged();
|
||||
updateFactories->radioProfileChanged();
|
||||
QApplication::clipboard()->clear();
|
||||
updateMenus();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
@@ -27,7 +28,8 @@
|
||||
ProgressDialog::ProgressDialog(QWidget *parent, const QString &title, const QIcon &icon, bool forceOpen):
|
||||
QDialog(parent),
|
||||
ui(new Ui::ProgressDialog),
|
||||
locked(false)
|
||||
locked(false),
|
||||
keepOpen(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowTitle(title);
|
||||
@@ -55,7 +57,8 @@ void ProgressDialog::on_closeButton_clicked()
|
||||
if (!locked) {
|
||||
ui->outputProgress->stop();
|
||||
emit rejected();
|
||||
close();
|
||||
if (!keepOpen)
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,3 +92,9 @@ void ProgressDialog::setProcessStopped()
|
||||
{
|
||||
ui->closeButton->setText(tr("Close"));
|
||||
}
|
||||
|
||||
void ProgressDialog::on_outputProgress_keepOpen(bool val)
|
||||
{
|
||||
keepOpen = val;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
@@ -18,8 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _PROGRESSDIALOG_H_
|
||||
#define _PROGRESSDIALOG_H_
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
@@ -34,26 +34,26 @@ class ProgressDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ProgressDialog(QWidget *parent, const QString &label, const QIcon &icon, bool forceOpen=false);
|
||||
~ProgressDialog();
|
||||
public:
|
||||
ProgressDialog(QWidget *parent, const QString &label, const QIcon &icon, bool forceOpen=false);
|
||||
~ProgressDialog();
|
||||
|
||||
ProgressWidget * progress();
|
||||
bool isEmpty() const;
|
||||
ProgressWidget * progress();
|
||||
bool isEmpty() const;
|
||||
|
||||
public slots:
|
||||
void setProcessStarted();
|
||||
void setProcessStopped();
|
||||
public slots:
|
||||
void setProcessStarted();
|
||||
void setProcessStopped();
|
||||
|
||||
private slots:
|
||||
void on_closeButton_clicked();
|
||||
void on_outputProgress_detailsToggled();
|
||||
void on_outputProgress_locked(bool);
|
||||
void shrink();
|
||||
private slots:
|
||||
void on_closeButton_clicked();
|
||||
void on_outputProgress_detailsToggled();
|
||||
void on_outputProgress_locked(bool);
|
||||
void on_outputProgress_keepOpen(bool);
|
||||
void shrink();
|
||||
|
||||
private:
|
||||
Ui::ProgressDialog *ui;
|
||||
bool locked;
|
||||
private:
|
||||
Ui::ProgressDialog *ui;
|
||||
bool locked;
|
||||
bool keepOpen;
|
||||
};
|
||||
|
||||
#endif // _PROGRESSDIALOG_H_
|
||||
|
||||
@@ -134,10 +134,10 @@ void ProgressWidget::addMessage(const QString & text, const int & type, bool ric
|
||||
color = "dimgrey"; // not important messages, may be filtered out
|
||||
break;
|
||||
case QtWarningMsg: // use warning level as emphasis
|
||||
color = "darkblue";
|
||||
color = "orangered";
|
||||
break;
|
||||
case QtCriticalMsg: // use critical as a warning
|
||||
color = "#ff7900";
|
||||
color = "darkred";
|
||||
break;
|
||||
case QtFatalMsg: // fatal for hard errors
|
||||
color = "red";
|
||||
@@ -206,3 +206,8 @@ void ProgressWidget::lock(bool lock)
|
||||
{
|
||||
emit locked(lock);
|
||||
}
|
||||
|
||||
void ProgressWidget::forceKeepOpen(bool value)
|
||||
{
|
||||
emit keepOpen(value);
|
||||
}
|
||||
|
||||
@@ -51,11 +51,13 @@ class ProgressWidget : public QWidget
|
||||
void forceOpen();
|
||||
void stop();
|
||||
void clearDetails() const;
|
||||
void forceKeepOpen(bool value);
|
||||
|
||||
signals:
|
||||
void detailsToggled();
|
||||
void locked(bool);
|
||||
void stopped();
|
||||
void keepOpen(bool);
|
||||
|
||||
protected slots:
|
||||
void toggleDetails();
|
||||
|
||||
@@ -492,7 +492,6 @@ class Profile: public CompStoreObj
|
||||
PROPERTY4(int, defaultMode, "default_mode", 1)
|
||||
PROPERTY (int, volumeGain, 10)
|
||||
|
||||
PROPERTY4(bool, renameFwFiles, "rename_firmware_files", false)
|
||||
PROPERTY (bool, burnFirmware, false)
|
||||
PROPERTY (bool, penableBackup, false)
|
||||
PROPERTY (bool, runSDSync, false)
|
||||
|
||||
@@ -3,12 +3,14 @@ project(updates)
|
||||
|
||||
set(${PROJECT_NAME}_NAMES
|
||||
chooserdialog
|
||||
downloaddialog
|
||||
repo
|
||||
repoassets
|
||||
repobuild
|
||||
repodatamodels
|
||||
repogithub
|
||||
repometadata
|
||||
reporeleases
|
||||
updatecloudbuild
|
||||
updatecompanion
|
||||
updatefactories
|
||||
updatefirmware
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "downloaddialog.h"
|
||||
#include "ui_downloaddialog.h"
|
||||
#include "constants.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include <QTime>
|
||||
|
||||
DownloadDialog::DownloadDialog(QWidget *parent, QString src, QString tgt, QString contentType, QString title):
|
||||
QDialog(parent),
|
||||
ui(new Ui::DownloadDialog),
|
||||
reply(nullptr),
|
||||
file(nullptr),
|
||||
aborted(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowIcon(CompanionIcon("download.png"));
|
||||
ui->progressBar->setValue(0);
|
||||
ui->progressBar->setMinimum(0);
|
||||
ui->progressBar->setMaximum(100);
|
||||
|
||||
if (tgt.isEmpty()) {
|
||||
setWindowTitle(src);
|
||||
return; // just show wait dialog.
|
||||
}
|
||||
|
||||
setWindowTitle(windowTitle() % title);
|
||||
|
||||
file = new QFile(tgt);
|
||||
if (!file->open(QIODevice::WriteOnly)) {
|
||||
QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open the download file %1 for writing.\nError: %2").arg(tgt).arg(file->errorString()));
|
||||
QTimer::singleShot(0, this, SLOT(fileError()));
|
||||
}
|
||||
else {
|
||||
url.setUrl(src);
|
||||
qDebug() << "url:" << url.url();
|
||||
request.setUrl(url);
|
||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
|
||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
|
||||
request.setRawHeader(QByteArray("Accept"), contentType.toUtf8());
|
||||
|
||||
reply = qnam.get(request);
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
|
||||
connect(reply, SIGNAL(readyRead()), this, SLOT(httpReadyRead()));
|
||||
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(updateDataReadProgress(qint64, qint64)));
|
||||
}
|
||||
}
|
||||
|
||||
DownloadDialog::~DownloadDialog()
|
||||
{
|
||||
delete ui;
|
||||
delete file;
|
||||
}
|
||||
|
||||
void DownloadDialog::reject()
|
||||
{
|
||||
if (reply && reply->isRunning()) {
|
||||
aborted = true;
|
||||
reply->abort(); // this will call QNetworkReply::finished()
|
||||
return;
|
||||
}
|
||||
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void DownloadDialog::httpFinished()
|
||||
{
|
||||
file->flush();
|
||||
file->close();
|
||||
|
||||
const bool ok = !(reply->error() || aborted);
|
||||
|
||||
if (!ok) {
|
||||
file->remove();
|
||||
if (!aborted)
|
||||
QMessageBox::information(this, CPN_STR_APP_NAME, tr("Download failed: %1.").arg(reply->errorString()));
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
reply = nullptr;
|
||||
file->deleteLater();
|
||||
file = nullptr;
|
||||
|
||||
if (ok)
|
||||
accept();
|
||||
else
|
||||
reject();
|
||||
}
|
||||
|
||||
void DownloadDialog::httpReadyRead()
|
||||
{
|
||||
if (file) {
|
||||
file->write(reply->readAll());
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadDialog::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
|
||||
{
|
||||
ui->progressBar->setMaximum(totalBytes);
|
||||
ui->progressBar->setValue(bytesRead);
|
||||
}
|
||||
|
||||
void DownloadDialog::fileError()
|
||||
{
|
||||
delete file;
|
||||
file = nullptr;
|
||||
reject();
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QFile>
|
||||
|
||||
namespace Ui {
|
||||
class DownloadDialog;
|
||||
}
|
||||
|
||||
class DownloadDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DownloadDialog(QWidget *parent = 0, QString src = "", QString tgt = "", QString contentType = "", QString title = "");
|
||||
|
||||
~DownloadDialog();
|
||||
|
||||
public slots:
|
||||
virtual void reject() override;
|
||||
|
||||
private slots:
|
||||
void fileError();
|
||||
void httpFinished();
|
||||
void httpReadyRead();
|
||||
void updateDataReadProgress(qint64 bytesRead, qint64 totalBytes);
|
||||
|
||||
private:
|
||||
Ui::DownloadDialog *ui;
|
||||
QNetworkAccessManager qnam;
|
||||
QNetworkReply *reply;
|
||||
QFile *file;
|
||||
bool aborted;
|
||||
QNetworkRequest request;
|
||||
QUrl url;
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DownloadDialog</class>
|
||||
<widget class="QDialog" name="DownloadDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>562</width>
|
||||
<height>82</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Downloading: </string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="companion.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>DownloadDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>DownloadDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -21,12 +21,17 @@
|
||||
|
||||
#include "repo.h"
|
||||
|
||||
Repo::Repo(QObject * parent, UpdateStatus * status, UpdateNetwork * network) :
|
||||
Repo::Repo(QObject * parent, UpdateStatus * status, UpdateNetwork * network,
|
||||
const QString & path, const QString & nightly, const int resultsPerPage) :
|
||||
QObject(parent),
|
||||
m_status(status),
|
||||
m_network(network),
|
||||
m_releases(new RepoReleases(this, status, network)),
|
||||
m_assets(new RepoAssets(this, status, network))
|
||||
m_assets(new RepoAssets(this, status, network)),
|
||||
m_path(path),
|
||||
m_nightly(nightly),
|
||||
m_resultsPerPage(resultsPerPage),
|
||||
m_config(nullptr)
|
||||
{
|
||||
connect(m_releases, &RepoReleases::idChanged, [=](const int id) {
|
||||
m_assets->onReleaseIdChanged(id);
|
||||
@@ -37,11 +42,13 @@ Repo::~Repo()
|
||||
{
|
||||
delete m_assets;
|
||||
delete m_releases;
|
||||
if (m_config)
|
||||
delete m_config;
|
||||
}
|
||||
|
||||
bool Repo::getJson(const QString filename, QJsonDocument * json)
|
||||
{
|
||||
m_network->downloadJsonContent(m_releases->urlContent(filename), json);
|
||||
m_network->downloadJsonContent(urlContent(filename), json);
|
||||
|
||||
if (!m_network->isSuccess()) {
|
||||
m_status->reportProgress("Unable to download json data", QtDebugMsg);
|
||||
@@ -51,9 +58,10 @@ bool Repo::getJson(const QString filename, QJsonDocument * json)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Repo::init(const QString & repoPath, const QString & nightly, const int resultsPerPage)
|
||||
void Repo::setConfig(const QJsonObject & config)
|
||||
{
|
||||
m_path = repoPath;
|
||||
m_releases->init(repoPath, nightly, resultsPerPage);
|
||||
m_assets->init(repoPath, QString(), resultsPerPage);
|
||||
if (m_config)
|
||||
delete m_config;
|
||||
|
||||
m_config = new QJsonObject(config);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "repoassets.h"
|
||||
|
||||
#include <QtCore>
|
||||
#include <QJsonObject>
|
||||
|
||||
constexpr char GH_API_REPOS[] {"https://api.github.com/repos"};
|
||||
constexpr char GH_API_REPOS_EDGETX[] {"https://api.github.com/repos/EdgeTX"};
|
||||
@@ -36,14 +37,39 @@ class Repo : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Repo(QObject * parent, UpdateStatus * status, UpdateNetwork * network);
|
||||
enum RepoType {
|
||||
REPO_TYPE_GITHUB,
|
||||
REPO_TYPE_BUILD,
|
||||
};
|
||||
Q_ENUM(RepoType)
|
||||
|
||||
explicit Repo(QObject * parent, UpdateStatus * status, UpdateNetwork * network,
|
||||
const QString & repoPath, const QString & nightly, const int resultsPerPage);
|
||||
virtual ~Repo();
|
||||
|
||||
virtual const QString urlAsset(const int assetId) const = 0;
|
||||
virtual const QString urlAssets(const int releaseId) const = 0;
|
||||
virtual const QString urlContent(const QString & filename) const = 0;
|
||||
virtual const QString urlJobs() const = 0;
|
||||
virtual const QString urlReleases() const = 0;
|
||||
virtual const QString urlStatus() const = 0;
|
||||
|
||||
virtual const UpdateNetwork::DownloadDataType assetDownloadDataType() const { return UpdateNetwork::DDT_Unknown; }
|
||||
virtual const UpdateNetwork::DownloadDataType assetContentDataType() const { return UpdateNetwork::DDT_Unknown; }
|
||||
|
||||
virtual const RepoRawItemModel::MetaDataType assetMetaDataType() const { return RepoRawItemModel::MDT_Unknown; }
|
||||
virtual const RepoRawItemModel::MetaDataType assetsMetaDataType() const { return RepoRawItemModel::MDT_Unknown; }
|
||||
virtual const RepoRawItemModel::MetaDataType releasesMetaDataType() const { return RepoRawItemModel::MDT_Unknown; }
|
||||
|
||||
void setConfig(const QJsonObject & config);
|
||||
QJsonObject* config() { return m_config; }
|
||||
|
||||
RepoAssets* const assets() const { return m_assets; }
|
||||
bool getJson(const QString filename, QJsonDocument * json);
|
||||
void init(const QString & repoPath, const QString & nightly, const int resultsPerPage);
|
||||
const QString nightly() const { return m_nightly; }
|
||||
const QString path() const { return m_path; }
|
||||
RepoReleases* const releases() const { return m_releases; }
|
||||
const int resultsPerPage() const { return m_resultsPerPage; }
|
||||
|
||||
private:
|
||||
UpdateStatus* const m_status;
|
||||
@@ -51,4 +77,7 @@ class Repo : public QObject
|
||||
RepoReleases* const m_releases;
|
||||
RepoAssets* const m_assets;
|
||||
QString m_path;
|
||||
QString m_nightly;
|
||||
int m_resultsPerPage;
|
||||
QJsonObject *m_config;
|
||||
};
|
||||
|
||||
@@ -19,24 +19,63 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "repoassets.h"
|
||||
#include "repotypes.h"
|
||||
|
||||
/*
|
||||
AssetsRawItemModel
|
||||
*/
|
||||
|
||||
AssetsRawItemModel::AssetsRawItemModel() :
|
||||
RepoRawItemModel("Raw Assets")
|
||||
AssetsRawItemModel::AssetsRawItemModel(QObject * parentRepo) :
|
||||
RepoRawItemModel("Raw Assets"),
|
||||
m_parentRepo(parentRepo)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AssetsRawItemModel::parseJsonObject(const QJsonObject & obj)
|
||||
{
|
||||
//qDebug() << obj;
|
||||
RepoGitHub *rg = dynamic_cast<RepoGitHub*>(m_parentRepo);
|
||||
if (rg) {
|
||||
parseJsonObjectGitHub(obj);
|
||||
}
|
||||
else {
|
||||
RepoBuild *rb = dynamic_cast<RepoBuild*>(m_parentRepo);
|
||||
if (rb) {
|
||||
parseJsonObjectBuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetsRawItemModel::parseJsonObjectBuild()
|
||||
{
|
||||
Repo *repo = static_cast<Repo*>(m_parentRepo);
|
||||
|
||||
if (repo->config()->value("targets").isObject()) {
|
||||
const QJsonObject &targets = repo->config()->value("targets").toObject();
|
||||
QStringList list = targets.keys();
|
||||
int id = 0;
|
||||
|
||||
for (auto i = list.cbegin(), end = list.cend(); i != end; ++i) {
|
||||
QStandardItem * item = new QStandardItem();
|
||||
|
||||
item->setText(*i);
|
||||
item->setData(++id, RIMR_Id);
|
||||
item->setData(true, RIMR_Available);
|
||||
item->setData(0, RIMR_Flags);
|
||||
|
||||
appendRow(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetsRawItemModel::parseJsonObjectGitHub(const QJsonObject & obj)
|
||||
{
|
||||
QStandardItem * item = new QStandardItem();
|
||||
|
||||
if (!obj.value("name").isUndefined()) {
|
||||
item->setText(obj.value("name").toString());
|
||||
item->setData(obj.value("name").toString(), RIMR_DownloadName);
|
||||
}
|
||||
|
||||
if (!obj.value("id").isUndefined()) {
|
||||
@@ -57,12 +96,23 @@ void AssetsRawItemModel::parseJsonObject(const QJsonObject & obj)
|
||||
appendRow(item);
|
||||
}
|
||||
|
||||
bool AssetsRawItemModel::setDownloadUrl(const int id, const QString url)
|
||||
{
|
||||
return setValue(id, RIMR_DownloadUrl, QVariant(url));
|
||||
}
|
||||
|
||||
bool AssetsRawItemModel::setDownloadName(const int id, const QString name)
|
||||
{
|
||||
return setValue(id, RIMR_DownloadName, QVariant(name));
|
||||
}
|
||||
|
||||
/*
|
||||
AssetsFilteredItemModel
|
||||
*/
|
||||
|
||||
AssetsFilteredItemModel::AssetsFilteredItemModel(AssetsRawItemModel * assetsRawItemModel) :
|
||||
RepoFilteredItemModel("Filtered Assets")
|
||||
AssetsFilteredItemModel::AssetsFilteredItemModel(QObject * parentRepo, AssetsRawItemModel * assetsRawItemModel) :
|
||||
RepoFilteredItemModel("Filtered Assets"),
|
||||
m_parentRepo(parentRepo)
|
||||
{
|
||||
setSourceModel(assetsRawItemModel);
|
||||
}
|
||||
@@ -77,6 +127,16 @@ const QString AssetsFilteredItemModel::copyFilter(const int id) const
|
||||
return value(id, RIMR_CopyFilter).toString();
|
||||
}
|
||||
|
||||
const QString AssetsFilteredItemModel::downloadUrl(const int id) const
|
||||
{
|
||||
return value(id, RIMR_DownloadUrl).toString();
|
||||
}
|
||||
|
||||
const QString AssetsFilteredItemModel::downloadName(const int id) const
|
||||
{
|
||||
return value(id, RIMR_DownloadName).toString();
|
||||
}
|
||||
|
||||
bool AssetsFilteredItemModel::setCopyFilter(const int id, const QString filter)
|
||||
{
|
||||
return setValue(id, RIMR_CopyFilter, QVariant(filter));
|
||||
@@ -96,15 +156,23 @@ const QString AssetsFilteredItemModel::subDirectory(const int id) const
|
||||
RepoAssets
|
||||
*/
|
||||
|
||||
RepoAssets::RepoAssets(QObject * parent, UpdateStatus * status, UpdateNetwork * network) :
|
||||
QObject(parent),
|
||||
RepoAssets::RepoAssets(QObject * parentRepo, UpdateStatus * status, UpdateNetwork * network) :
|
||||
QObject(parentRepo),
|
||||
RepoMetaData(status, network),
|
||||
m_rawItemModel(new AssetsRawItemModel()),
|
||||
m_filteredItemModel(new AssetsFilteredItemModel(m_rawItemModel))
|
||||
m_rawItemModel(new AssetsRawItemModel(parentRepo)),
|
||||
m_filteredItemModel(new AssetsFilteredItemModel(parentRepo, m_rawItemModel)),
|
||||
m_parentRepo(parentRepo)
|
||||
|
||||
{
|
||||
setModels(m_rawItemModel, m_filteredItemModel);
|
||||
}
|
||||
|
||||
bool RepoAssets::build(const int row)
|
||||
{
|
||||
status()->reportProgress(tr("Generic asset build not implemented"), QtDebugMsg);
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString RepoAssets::contentType() const
|
||||
{
|
||||
return m_filteredItemModel->contentType(id());
|
||||
@@ -117,21 +185,46 @@ const QString RepoAssets::copyFilter() const
|
||||
|
||||
bool RepoAssets::downloadToBuffer(const int id)
|
||||
{
|
||||
//progressMessage(tr("Download asset %1").arg(id));
|
||||
if (id > 0)
|
||||
setId(id);
|
||||
network()->downloadToBuffer(UpdateNetwork::DDT_Content, urlAsset(this->id()));
|
||||
|
||||
Repo *repo = static_cast<RepoGitHub*>(m_parentRepo);
|
||||
network()->downloadToBuffer(repo->assetDownloadDataType(), repo->urlAsset(this->id()));
|
||||
return network()->isSuccess();
|
||||
}
|
||||
|
||||
bool RepoAssets::downloadToFile(const int row, QString & downloadDir)
|
||||
{
|
||||
Repo *repo = static_cast<Repo*>(m_parentRepo);
|
||||
getSetId(row);
|
||||
QFileInfo f(QString("%1/A%2/%3").arg(downloadDir).arg(id()).arg(name()));
|
||||
network()->downloadToFile(urlAsset(id()), f.absoluteFilePath());
|
||||
QString fmt = QString("%1/A%2/%3").arg(downloadDir).arg(id()).arg(downloadName());
|
||||
QFileInfo f(fmt);
|
||||
network()->downloadToFile(repo->assetDownloadDataType(), repo->urlAsset(id()), f.absoluteFilePath());
|
||||
return network()->isSuccess();
|
||||
}
|
||||
|
||||
const QString RepoAssets::downloadUrl() const
|
||||
{
|
||||
return m_filteredItemModel->downloadUrl(id());
|
||||
}
|
||||
|
||||
const QString RepoAssets::downloadUrl(const int row)
|
||||
{
|
||||
getSetId(row);
|
||||
return downloadUrl();
|
||||
}
|
||||
|
||||
const QString RepoAssets::downloadName() const
|
||||
{
|
||||
return m_filteredItemModel->downloadName(id());
|
||||
}
|
||||
|
||||
const QString RepoAssets::downloadName(const int row)
|
||||
{
|
||||
getSetId(row);
|
||||
return downloadName();
|
||||
}
|
||||
|
||||
void RepoAssets::dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const
|
||||
{
|
||||
qDebug() << "Contents of model:" << modelName;
|
||||
@@ -153,7 +246,8 @@ void RepoAssets::dumpItemModel(const QString modelName, const QAbstractItemModel
|
||||
|
||||
bool RepoAssets::getJson(QJsonDocument * json)
|
||||
{
|
||||
network()->downloadJsonAsset(urlAsset(id()), json);
|
||||
Repo *repo = static_cast<Repo*>(m_parentRepo);
|
||||
network()->downloadJsonAsset(repo->urlAsset(id()), json);
|
||||
|
||||
if (!network()->isSuccess()) {
|
||||
status()->reportProgress("Unable to download json data", QtDebugMsg);
|
||||
@@ -171,9 +265,15 @@ bool RepoAssets::getJson(const QString assetName, QJsonDocument * json)
|
||||
return getJson(json);
|
||||
}
|
||||
|
||||
void RepoAssets::init(const QString & repoPath,const QString & nightly, const int resultsPerPage)
|
||||
const QString RepoAssets::name() const
|
||||
{
|
||||
RepoMetaData::init(repoPath, resultsPerPage);
|
||||
return m_filteredItemModel->name(id());
|
||||
}
|
||||
|
||||
const QString RepoAssets::name(const int row)
|
||||
{
|
||||
getSetId(row);
|
||||
return name();
|
||||
}
|
||||
|
||||
void RepoAssets::onReleaseIdChanged(const int id)
|
||||
@@ -184,13 +284,16 @@ void RepoAssets::onReleaseIdChanged(const int id)
|
||||
|
||||
bool RepoAssets::retrieveMetaDataAll()
|
||||
{
|
||||
return retrieveMetaData(RepoRawItemModel::MDT_Assets, urlAssets(m_releaseId));
|
||||
Repo *repo = static_cast<Repo*>(m_parentRepo);
|
||||
return retrieveMetaData(repo->assetsMetaDataType(), repo->urlAssets(m_releaseId));
|
||||
}
|
||||
|
||||
bool RepoAssets::retrieveMetaDataOne(const int id)
|
||||
{
|
||||
setId(id);
|
||||
return retrieveMetaData(RepoRawItemModel::MDT_Asset, urlAsset(id));
|
||||
|
||||
Repo *repo = static_cast<Repo*>(m_parentRepo);
|
||||
return retrieveMetaData(repo->assetMetaDataType(), repo->urlAsset(id));
|
||||
}
|
||||
|
||||
bool RepoAssets::setCopyFilter(const QString filter)
|
||||
@@ -207,3 +310,13 @@ const QString RepoAssets::subDirectory() const
|
||||
{
|
||||
return m_filteredItemModel->subDirectory(id());
|
||||
}
|
||||
|
||||
bool RepoAssets::setDownloadUrl(const QString url)
|
||||
{
|
||||
return m_rawItemModel->setDownloadUrl(id(), url);
|
||||
}
|
||||
|
||||
bool RepoAssets::setDownloadName(const QString name)
|
||||
{
|
||||
return m_rawItemModel->setDownloadName(id(), name);
|
||||
}
|
||||
|
||||
@@ -29,13 +29,22 @@ class AssetsRawItemModel : public RepoRawItemModel
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AssetsRawItemModel();
|
||||
explicit AssetsRawItemModel(QObject * parentRepo);
|
||||
virtual ~AssetsRawItemModel() {}
|
||||
|
||||
protected:
|
||||
friend class RepoAssets;
|
||||
|
||||
virtual void parseJsonObject(const QJsonObject & obj);
|
||||
|
||||
bool setDownloadUrl(const int id, const QString url);
|
||||
bool setDownloadName(const int id, const QString name);
|
||||
|
||||
private:
|
||||
QObject *m_parentRepo; // cannot use Repo class from repo.h as compile error due to cyclic headers
|
||||
|
||||
void parseJsonObjectGitHub(const QJsonObject & obj);
|
||||
void parseJsonObjectBuild();
|
||||
};
|
||||
|
||||
class AssetsFilteredItemModel : public RepoFilteredItemModel
|
||||
@@ -43,7 +52,7 @@ class AssetsFilteredItemModel : public RepoFilteredItemModel
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AssetsFilteredItemModel(AssetsRawItemModel * assetsRawItemModel);
|
||||
explicit AssetsFilteredItemModel(QObject * parentRepo, AssetsRawItemModel * assetsRawItemModel);
|
||||
virtual ~AssetsFilteredItemModel() {};
|
||||
|
||||
protected:
|
||||
@@ -54,6 +63,11 @@ class AssetsFilteredItemModel : public RepoFilteredItemModel
|
||||
bool setCopyFilter(const int id, const QString filter);
|
||||
bool setSubDirectory(const int id, const QString path);
|
||||
const QString subDirectory(const int id) const;
|
||||
const QString downloadUrl(const int id) const;
|
||||
const QString downloadName(const int id) const;
|
||||
|
||||
private:
|
||||
QObject *m_parentRepo; // cannot use Repo class from repo.h as compile error due to cyclic headers
|
||||
};
|
||||
|
||||
class RepoAssets : public QObject, public RepoMetaData
|
||||
@@ -67,19 +81,27 @@ class RepoAssets : public QObject, public RepoMetaData
|
||||
explicit RepoAssets(QObject * parent, UpdateStatus * status, UpdateNetwork * network);
|
||||
virtual ~RepoAssets() {};
|
||||
|
||||
virtual void init(const QString & repoPath, const QString & nightly, const int resultsPerPage);
|
||||
virtual bool retrieveMetaDataAll();
|
||||
virtual bool retrieveMetaDataOne(const int id);
|
||||
|
||||
bool build(const int row);
|
||||
const QString contentType() const;
|
||||
const QString copyFilter() const;
|
||||
bool downloadToBuffer(const int id = 0);
|
||||
bool downloadToFile(const int row, QString & downloadDir);
|
||||
bool getJson(QJsonDocument * json);
|
||||
bool getJson(const QString assetName, QJsonDocument * json);
|
||||
const QString name() const;
|
||||
const QString name(const int row);
|
||||
bool setCopyFilter(const QString filter);
|
||||
bool setSubDirectory(const QString path);
|
||||
const QString subDirectory() const;
|
||||
bool setDownloadUrl(const QString url);
|
||||
const QString downloadUrl() const;
|
||||
const QString downloadUrl(const int row);
|
||||
bool setDownloadName(const QString name);
|
||||
const QString downloadName() const;
|
||||
const QString downloadName(const int row);
|
||||
|
||||
signals:
|
||||
void idChanged(int id);
|
||||
@@ -87,6 +109,7 @@ class RepoAssets : public QObject, public RepoMetaData
|
||||
private:
|
||||
AssetsRawItemModel* const m_rawItemModel;
|
||||
AssetsFilteredItemModel* const m_filteredItemModel;
|
||||
QObject *m_parentRepo; // cannot use Repo class from repo.h as compile error due to cyclic headers
|
||||
int m_releaseId;
|
||||
|
||||
virtual void dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "repobuild.h"
|
||||
|
||||
RepoBuild::RepoBuild(QObject * parent, UpdateStatus * status, UpdateNetwork * network,
|
||||
const QString & path, const QString & nightly, const int resultsPerPage) :
|
||||
Repo(parent, status, network, path, nightly, resultsPerPage)
|
||||
{
|
||||
}
|
||||
|
||||
RepoBuild::~RepoBuild()
|
||||
{
|
||||
}
|
||||
|
||||
const QString RepoBuild::urlAsset(const int assetId) const
|
||||
{
|
||||
return QString(assets()->downloadUrl());
|
||||
}
|
||||
|
||||
const QString RepoBuild::urlJobs() const
|
||||
{
|
||||
return QString("%1/jobs").arg(path());
|
||||
}
|
||||
|
||||
const QString RepoBuild::urlReleases() const
|
||||
{
|
||||
return QString("%1/targets").arg(path());
|
||||
}
|
||||
|
||||
const QString RepoBuild::urlStatus() const
|
||||
{
|
||||
return QString("%1/status").arg(path());
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "repo.h"
|
||||
|
||||
class RepoBuild : public Repo
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RepoBuild(QObject * parent, UpdateStatus * status, UpdateNetwork * network,
|
||||
const QString & path, const QString & nightly, const int resultsPerPage);
|
||||
virtual ~RepoBuild();
|
||||
|
||||
const QString urlAsset(const int assetId) const;
|
||||
const QString urlAssets(const int releaseId) const { return ""; }
|
||||
const QString urlContent(const QString & filename) const { return ""; }
|
||||
const QString urlJobs() const;
|
||||
const QString urlReleases() const;
|
||||
const QString urlStatus() const;
|
||||
|
||||
const UpdateNetwork::DownloadDataType assetDownloadDataType() const override { return UpdateNetwork::DDT_Build_SaveToFile; }
|
||||
const UpdateNetwork::DownloadDataType assetContentDataType() const override { return UpdateNetwork::DDT_Unknown; }
|
||||
|
||||
const RepoRawItemModel::MetaDataType assetMetaDataType() const override { return RepoRawItemModel::MDT_Build_Asset; }
|
||||
const RepoRawItemModel::MetaDataType assetsMetaDataType() const override { return RepoRawItemModel::MDT_Build_Assets; }
|
||||
const RepoRawItemModel::MetaDataType releasesMetaDataType() const override { return RepoRawItemModel::MDT_Build_Releases; }
|
||||
|
||||
private:
|
||||
};
|
||||
@@ -59,12 +59,15 @@ void RepoRawItemModel::parseAll(QJsonDocument * json)
|
||||
void RepoRawItemModel::parseMetaData(const int mdt, QJsonDocument * json)
|
||||
{
|
||||
switch (mdt) {
|
||||
case MDT_Asset:
|
||||
case MDT_Release:
|
||||
case MDT_Build_Asset:
|
||||
case MDT_Build_Assets:
|
||||
case MDT_Build_Release:
|
||||
case MDT_Build_Releases:
|
||||
case MDT_GitHub_Asset:
|
||||
parseOne(json);
|
||||
break;
|
||||
case MDT_Assets:
|
||||
case MDT_Releases:
|
||||
case MDT_GitHub_Assets:
|
||||
case MDT_GitHub_Releases:
|
||||
parseAll(json);
|
||||
break;
|
||||
default:
|
||||
@@ -84,6 +87,17 @@ void RepoRawItemModel::parseOne(QJsonDocument * json)
|
||||
}
|
||||
}
|
||||
|
||||
bool RepoRawItemModel::setValue(const int id, const int role, const QVariant value)
|
||||
{
|
||||
//qDebug() << "id:" << id << "role:" << role << "value:" << value;
|
||||
const QModelIndexList idxlst = match(index(0, 0), RIMR_Id, QVariant(id), 1, Qt::MatchExactly);
|
||||
|
||||
if (!idxlst.isEmpty())
|
||||
return setData(idxlst.at(0), value, role);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
QString RepoRawItemModel::typeToString(const int val)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,9 @@ class RepoItemModelBase
|
||||
RIMR_Available,
|
||||
RIMR_Flags,
|
||||
RIMR_CopyFilter,
|
||||
RIMR_SubDirectory
|
||||
RIMR_SubDirectory,
|
||||
RIMR_DownloadUrl,
|
||||
RIMR_DownloadName,
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -60,10 +62,19 @@ class RepoRawItemModel : public QStandardItemModel, public RepoItemModelBase
|
||||
virtual ~RepoRawItemModel() {}
|
||||
|
||||
enum MetaDataType {
|
||||
MDT_Asset,
|
||||
MDT_Assets,
|
||||
MDT_Release,
|
||||
MDT_Releases
|
||||
MDT_Unknown,
|
||||
MDT_GitHub_Asset,
|
||||
MDT_GitHub_First = MDT_GitHub_Asset,
|
||||
MDT_GitHub_Assets,
|
||||
MDT_GitHub_Release,
|
||||
MDT_GitHub_Releases,
|
||||
MDT_GitHub_Last = MDT_GitHub_Releases,
|
||||
MDT_Build_Asset,
|
||||
MDT_Build_First = MDT_Build_Asset,
|
||||
MDT_Build_Assets,
|
||||
MDT_Build_Release,
|
||||
MDT_Build_Releases,
|
||||
MDT_Build_Last = MDT_Build_Releases,
|
||||
};
|
||||
|
||||
static QString typeToString(const int val);
|
||||
@@ -83,6 +94,7 @@ class RepoRawItemModel : public QStandardItemModel, public RepoItemModelBase
|
||||
void parseMetaData(const int mdt, QJsonDocument * json);
|
||||
void parseOne(QJsonDocument * json);
|
||||
void setRefreshRequired(const bool val) { m_refreshRequired = val; }
|
||||
bool setValue(const int id, const int role, const QVariant value);
|
||||
void update();
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "repogithub.h"
|
||||
|
||||
RepoGitHub::RepoGitHub(QObject * parent, UpdateStatus * status, UpdateNetwork * network,
|
||||
const QString & path, const QString & nightly, const int resultsPerPage) :
|
||||
Repo(parent, status, network, path, nightly, resultsPerPage)
|
||||
{
|
||||
}
|
||||
|
||||
RepoGitHub::~RepoGitHub()
|
||||
{
|
||||
}
|
||||
|
||||
const QString RepoGitHub::urlAsset(const int assetId) const
|
||||
{
|
||||
return QString("%1/assets/%2").arg(urlReleases()).arg(assetId);
|
||||
}
|
||||
|
||||
const QString RepoGitHub::urlAssets(const int releaseId) const
|
||||
{
|
||||
return QString("%1/%2/assets").arg(urlReleases()).arg(releaseId) % (resultsPerPage() > -1 ?
|
||||
QString("\?per_page=%1").arg(resultsPerPage()) : "");
|
||||
}
|
||||
|
||||
const QString RepoGitHub::urlContent(const QString & filename) const
|
||||
{
|
||||
return QString("%1/contents/%2").arg(path()).arg(filename);
|
||||
}
|
||||
|
||||
const QString RepoGitHub::urlReleases() const
|
||||
{
|
||||
return QString("%1/releases").arg(path());
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "repo.h"
|
||||
|
||||
class RepoGitHub : public Repo
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RepoGitHub(QObject * parent, UpdateStatus * status, UpdateNetwork * network,
|
||||
const QString & path, const QString & nightly, const int resultsPerPage);
|
||||
virtual ~RepoGitHub();
|
||||
|
||||
const QString urlAsset(const int assetId) const;
|
||||
const QString urlAssets(const int releaseId) const;
|
||||
const QString urlContent(const QString & filename) const;
|
||||
const QString urlJobs() const { return ""; }
|
||||
const QString urlReleases() const;
|
||||
const QString urlStatus() const { return ""; }
|
||||
|
||||
const UpdateNetwork::DownloadDataType assetDownloadDataType() const override { return UpdateNetwork::DDT_GitHub_SaveToFile; }
|
||||
const UpdateNetwork::DownloadDataType assetContentDataType() const override { return UpdateNetwork::DDT_GitHub_Content; }
|
||||
|
||||
const RepoRawItemModel::MetaDataType assetMetaDataType() const override { return RepoRawItemModel::MDT_GitHub_Asset; }
|
||||
const RepoRawItemModel::MetaDataType assetsMetaDataType() const override { return RepoRawItemModel::MDT_GitHub_Assets; }
|
||||
const RepoRawItemModel::MetaDataType releasesMetaDataType() const override { return RepoRawItemModel::MDT_GitHub_Releases; }
|
||||
|
||||
private:
|
||||
};
|
||||
@@ -76,12 +76,6 @@ const int RepoMetaData::id() const
|
||||
return m_id;
|
||||
}
|
||||
|
||||
void RepoMetaData::init(const QString & repoPath, const int resultsPerPage)
|
||||
{
|
||||
m_repoPath = repoPath;
|
||||
m_resultsPerPage = resultsPerPage;
|
||||
}
|
||||
|
||||
void RepoMetaData::invalidate()
|
||||
{
|
||||
setId(0);
|
||||
@@ -113,16 +107,6 @@ UpdateNetwork* const RepoMetaData::network() const
|
||||
return m_network;
|
||||
}
|
||||
|
||||
const QString RepoMetaData::repoPath() const
|
||||
{
|
||||
return m_repoPath;
|
||||
}
|
||||
|
||||
const int RepoMetaData::resultsPerPage() const
|
||||
{
|
||||
return m_resultsPerPage;
|
||||
}
|
||||
|
||||
bool RepoMetaData::retrieveMetaData(const int mdt, const QString url)
|
||||
{
|
||||
if (!m_rawItemModel->isRefreshRequired())
|
||||
@@ -130,12 +114,21 @@ bool RepoMetaData::retrieveMetaData(const int mdt, const QString url)
|
||||
|
||||
QJsonDocument *json = new QJsonDocument();
|
||||
|
||||
network()->downloadMetaData(url, json);
|
||||
if (!url.isEmpty()) {
|
||||
network()->downloadMetaData(mdt >= RepoRawItemModel::MDT_GitHub_First &&
|
||||
mdt <= RepoRawItemModel::MDT_GitHub_Last ? UpdateNetwork::DDT_GitHub_MetaData :
|
||||
UpdateNetwork::DDT_Build_MetaData,
|
||||
url,
|
||||
json);
|
||||
|
||||
if (!network()->isSuccess()) {
|
||||
status()->reportProgress("Unable to download release asset meta data", QtDebugMsg);
|
||||
invalidate();
|
||||
return false;
|
||||
if (!network()->isSuccess()) {
|
||||
status()->reportProgress("Unable to download release asset meta data", QtDebugMsg);
|
||||
invalidate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
json->setObject(QJsonObject()); // add a dummy object so later tests pass
|
||||
}
|
||||
|
||||
m_rawItemModel->parseMetaData(mdt, json);
|
||||
@@ -180,24 +173,3 @@ UpdateStatus* const RepoMetaData::status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
const QString RepoMetaData::urlAsset(const int assetId) const
|
||||
{
|
||||
return QString("%1/assets/%2").arg(urlReleases()).arg(assetId);
|
||||
}
|
||||
|
||||
const QString RepoMetaData::urlAssets(const int releaseId) const
|
||||
{
|
||||
return QString("%1/%2/assets").arg(urlReleases()).arg(releaseId) % (m_resultsPerPage > -1 ?
|
||||
QString("\?per_page=%1").arg(m_resultsPerPage) : "");
|
||||
}
|
||||
|
||||
const QString RepoMetaData::urlContent(const QString & filename) const
|
||||
{
|
||||
return QString("%1/contents/%2").arg(m_repoPath).arg(filename);
|
||||
}
|
||||
|
||||
const QString RepoMetaData::urlReleases() const
|
||||
{
|
||||
return QString("%1/releases").arg(m_repoPath);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ class RepoMetaData
|
||||
explicit RepoMetaData(UpdateStatus * status, UpdateNetwork * network);
|
||||
virtual ~RepoMetaData() {}
|
||||
|
||||
virtual void init(const QString & repoPath, const QString & nightly, const int resultsPerPage) = 0;
|
||||
virtual bool retrieveMetaDataAll() = 0;
|
||||
virtual bool retrieveMetaDataOne(const int id) = 0;
|
||||
virtual int setId(const int id);
|
||||
@@ -49,25 +48,18 @@ class RepoMetaData
|
||||
int getSetId(const int row);
|
||||
int getSetId(QVariant value, Qt::MatchFlags flags = Qt::MatchExactly, int role = Qt::DisplayRole);
|
||||
const int id() const;
|
||||
void init(const QString & repoPath, const int resultsPerPage);
|
||||
void invalidate();
|
||||
const int isEmpty() const;
|
||||
const bool isIdValid() const;
|
||||
const QStringList list() const;
|
||||
const QString name() const;
|
||||
UpdateNetwork* const network() const;
|
||||
const QString repoPath() const;
|
||||
const int resultsPerPage() const;
|
||||
bool retrieveMetaData(const int mdt, const QString url);
|
||||
bool setFlags(int flags);
|
||||
void setFilterFlags(int flags);
|
||||
void setFilterPattern(const QString & pattern);
|
||||
void setModels(RepoRawItemModel * repoRawItemModel, RepoFilteredItemModel * repoFilteredItemModel);
|
||||
UpdateStatus* const status() const;
|
||||
const QString urlAsset(const int assetId) const;
|
||||
const QString urlAssets(const int releaseId) const;
|
||||
const QString urlContent(const QString & filename) const;
|
||||
const QString urlReleases() const;
|
||||
|
||||
private:
|
||||
UpdateStatus* const m_status;
|
||||
@@ -75,8 +67,6 @@ class RepoMetaData
|
||||
RepoRawItemModel* m_rawItemModel;
|
||||
RepoFilteredItemModel* m_filteredItemModel;
|
||||
|
||||
QString m_repoPath;
|
||||
int m_resultsPerPage;
|
||||
int m_id;
|
||||
|
||||
virtual void dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const = 0;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "reporeleases.h"
|
||||
#include "repotypes.h"
|
||||
#include "appdata.h"
|
||||
#include "helpers.h"
|
||||
|
||||
@@ -55,15 +55,96 @@ const QString ReleasesItemModels::flagToString(const int val)
|
||||
ReleasesRawItemModel
|
||||
*/
|
||||
|
||||
ReleasesRawItemModel::ReleasesRawItemModel() :
|
||||
ReleasesRawItemModel::ReleasesRawItemModel(QObject * parentRepo) :
|
||||
RepoRawItemModel("Raw Releases"),
|
||||
m_nightlyName("")
|
||||
m_parentRepo(parentRepo)
|
||||
{
|
||||
setSortRole(RIMR_SortOrder);
|
||||
}
|
||||
|
||||
void ReleasesRawItemModel::parseJsonObject(const QJsonObject & obj)
|
||||
{
|
||||
//qDebug() << obj;
|
||||
RepoGitHub *rg = dynamic_cast<RepoGitHub*>(m_parentRepo);
|
||||
if (rg) {
|
||||
parseJsonObjectGitHub(obj);
|
||||
}
|
||||
else {
|
||||
RepoBuild *rb = dynamic_cast<RepoBuild*>(m_parentRepo);
|
||||
if (rb) {
|
||||
parseJsonObjectBuild(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReleasesRawItemModel::parseJsonObjectBuild(const QJsonObject & obj)
|
||||
{
|
||||
Repo *repo = static_cast<Repo*>(m_parentRepo);
|
||||
const QString fwRadio = getCurrentFirmware()->getFlavour();
|
||||
|
||||
if (obj.value("releases").isObject()) {
|
||||
repo->setConfig(obj);
|
||||
const QJsonObject &releases = obj.value("releases").toObject();
|
||||
QStringList list = releases.keys();
|
||||
|
||||
for (auto i = list.cbegin(), end = list.cend(); i != end; ++i) {
|
||||
QStandardItem * item = new QStandardItem();
|
||||
|
||||
int flags = RELFLG_Stable;
|
||||
bool available = true;
|
||||
|
||||
item->setText(*i);
|
||||
item->setData(*i, RIMR_Tag);
|
||||
|
||||
if (item->data(RIMR_Tag).toString().toLower() == repo->nightly().toLower())
|
||||
item->setData((int)QDate::currentDate().toJulianDay(), RIMR_Id);
|
||||
else
|
||||
item->setData(SemanticVersion(*i).toInt(), RIMR_Id);
|
||||
|
||||
item->setData(QDateTime::currentDateTimeUtc(), RIMR_Date);
|
||||
|
||||
SemanticVersion sv;
|
||||
|
||||
if (item->data(RIMR_Tag).toString().toLower() == repo->nightly().toLower()) {
|
||||
flags = RELFLG_Nightly;
|
||||
sv.fromString("255.255.255");
|
||||
}
|
||||
else
|
||||
sv.fromString(item->data(RIMR_Tag).toString());
|
||||
|
||||
if (sv.isPreRelease())
|
||||
flags = RELFLG_PreRelease;
|
||||
|
||||
if (sv.isValid())
|
||||
item->setData(sv.toInt(), RIMR_SortOrder);
|
||||
else
|
||||
item->setData(item->data(RIMR_Id).toInt(), RIMR_SortOrder);
|
||||
|
||||
if (releases.value(*i).isObject()) {
|
||||
const QJsonObject &release = releases.value(*i).toObject();
|
||||
if (!release.value("exclude_targets").isUndefined()) {
|
||||
if (release.value("exclude_targets").isArray()) {
|
||||
const QJsonArray &excluded = release.value("exclude_targets").toArray();
|
||||
foreach (const QJsonValue &v, excluded) {
|
||||
if (v.isString() && v.toString() == fwRadio) {
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item->setData(available, RIMR_Available);
|
||||
item->setData(flags, RIMR_Flags);
|
||||
|
||||
appendRow(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReleasesRawItemModel::parseJsonObjectGitHub(const QJsonObject & obj)
|
||||
{
|
||||
Repo *repo = static_cast<Repo*>(m_parentRepo);
|
||||
QStandardItem * item = new QStandardItem();
|
||||
|
||||
int flags = RELFLG_Stable;
|
||||
@@ -88,7 +169,7 @@ void ReleasesRawItemModel::parseJsonObject(const QJsonObject & obj)
|
||||
|
||||
if (!obj.value("tag_name").isUndefined()) {
|
||||
item->setData(obj.value("tag_name").toString(), RIMR_Tag);
|
||||
if (item->data(RIMR_Tag).toString().toLower() == m_nightlyName)
|
||||
if (item->data(RIMR_Tag).toString().toLower() == repo->nightly().toLower())
|
||||
flags = RELFLG_Nightly;
|
||||
}
|
||||
|
||||
@@ -102,7 +183,7 @@ void ReleasesRawItemModel::parseJsonObject(const QJsonObject & obj)
|
||||
|
||||
SemanticVersion sv;
|
||||
|
||||
if (item->data(RIMR_Tag).toString().toLower() == m_nightlyName)
|
||||
if (item->data(RIMR_Tag).toString().toLower() == repo->nightly().toLower())
|
||||
sv.fromString("255.255.255");
|
||||
else
|
||||
sv.fromString(item->data(RIMR_Tag).toString());
|
||||
@@ -115,20 +196,16 @@ void ReleasesRawItemModel::parseJsonObject(const QJsonObject & obj)
|
||||
appendRow(item);
|
||||
}
|
||||
|
||||
void ReleasesRawItemModel::setNightlyName(const QString name)
|
||||
{
|
||||
m_nightlyName = name.toLower();
|
||||
}
|
||||
|
||||
/*
|
||||
ReleasesFilteredItemModel
|
||||
*/
|
||||
|
||||
ReleasesFilteredItemModel::ReleasesFilteredItemModel(ReleasesRawItemModel * releasesRawItemModel) :
|
||||
RepoFilteredItemModel("Filtered Releases")
|
||||
ReleasesFilteredItemModel::ReleasesFilteredItemModel(QObject * parentRepo, ReleasesRawItemModel * releasesRawItemModel) :
|
||||
RepoFilteredItemModel("Filtered Releases"),
|
||||
m_parentRepo(parentRepo)
|
||||
{
|
||||
setSourceModel(releasesRawItemModel);
|
||||
setSortRole(RIMR_Date);
|
||||
setSortRole(RIMR_SortOrder);
|
||||
}
|
||||
|
||||
const int ReleasesFilteredItemModel::channelLatestId() const
|
||||
@@ -155,11 +232,12 @@ const QString ReleasesFilteredItemModel::version(const int id) const
|
||||
RepoReleases
|
||||
*/
|
||||
|
||||
RepoReleases::RepoReleases(QObject * parent, UpdateStatus * status, UpdateNetwork * network) :
|
||||
QObject(parent),
|
||||
RepoReleases::RepoReleases(QObject * parentRepo, UpdateStatus * status, UpdateNetwork * network) :
|
||||
QObject(parentRepo),
|
||||
RepoMetaData(status, network),
|
||||
m_rawItemModel(new ReleasesRawItemModel()),
|
||||
m_filteredItemModel(new ReleasesFilteredItemModel(m_rawItemModel))
|
||||
m_rawItemModel(new ReleasesRawItemModel(parentRepo)),
|
||||
m_filteredItemModel(new ReleasesFilteredItemModel(parentRepo, m_rawItemModel)),
|
||||
m_parentRepo(parentRepo)
|
||||
{
|
||||
setModels(m_rawItemModel, m_filteredItemModel);
|
||||
}
|
||||
@@ -186,15 +264,16 @@ void RepoReleases::dumpItemModel(const QString modelName, const QAbstractItemMod
|
||||
}
|
||||
}
|
||||
|
||||
void RepoReleases::init(const QString & repoPath,const QString & nightly, const int resultsPerPage)
|
||||
{
|
||||
RepoMetaData::init(repoPath, resultsPerPage);
|
||||
m_rawItemModel->setNightlyName(nightly);
|
||||
}
|
||||
|
||||
bool RepoReleases::retrieveMetaDataAll()
|
||||
{
|
||||
return retrieveMetaData(RepoRawItemModel::MDT_Releases, urlReleases());
|
||||
bool res = false;
|
||||
Repo *repo = dynamic_cast<Repo*>(m_parentRepo);
|
||||
if (repo) {
|
||||
res = retrieveMetaData(repo->releasesMetaDataType(), repo->urlReleases());
|
||||
m_rawItemModel->sort(0, Qt::DescendingOrder);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int RepoReleases::setId(const int id)
|
||||
|
||||
@@ -46,7 +46,7 @@ class ReleasesRawItemModel : public RepoRawItemModel, public ReleasesItemModels
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ReleasesRawItemModel();
|
||||
explicit ReleasesRawItemModel(QObject * parentRepo);
|
||||
virtual ~ReleasesRawItemModel() {}
|
||||
|
||||
protected:
|
||||
@@ -54,10 +54,11 @@ class ReleasesRawItemModel : public RepoRawItemModel, public ReleasesItemModels
|
||||
|
||||
virtual void parseJsonObject(const QJsonObject & obj);
|
||||
|
||||
void setNightlyName(const QString name);
|
||||
|
||||
private:
|
||||
QString m_nightlyName;
|
||||
QObject *m_parentRepo; // cannot use Repo class from repo.h as compile error due to cyclic headers
|
||||
|
||||
void parseJsonObjectGitHub(const QJsonObject & obj);
|
||||
void parseJsonObjectBuild(const QJsonObject & obj);
|
||||
};
|
||||
|
||||
class ReleasesFilteredItemModel: public RepoFilteredItemModel, public ReleasesItemModels
|
||||
@@ -65,7 +66,7 @@ class ReleasesFilteredItemModel: public RepoFilteredItemModel, public ReleasesIt
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ReleasesFilteredItemModel(ReleasesRawItemModel * releasesRawItemModel);
|
||||
explicit ReleasesFilteredItemModel(QObject * parentRepo, ReleasesRawItemModel * releasesRawItemModel);
|
||||
virtual ~ReleasesFilteredItemModel() {}
|
||||
|
||||
protected:
|
||||
@@ -74,6 +75,9 @@ class ReleasesFilteredItemModel: public RepoFilteredItemModel, public ReleasesIt
|
||||
const int channelLatestId() const;
|
||||
void setChannel(const int channel);
|
||||
const QString version(const int id) const;
|
||||
|
||||
private:
|
||||
QObject *m_parentRepo; // cannot use Repo class from repo.h as compile error due to cyclic headers
|
||||
};
|
||||
|
||||
class RepoReleases : public QObject, public RepoMetaData
|
||||
@@ -81,10 +85,9 @@ class RepoReleases : public QObject, public RepoMetaData
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RepoReleases(QObject * parent, UpdateStatus * status, UpdateNetwork * network);
|
||||
explicit RepoReleases(QObject * parentRepo, UpdateStatus * status, UpdateNetwork * network);
|
||||
virtual ~RepoReleases() {}
|
||||
|
||||
void init(const QString & repoPath, const QString & nightly, const int resultsPerPage) override;
|
||||
int setId(const int id) override;
|
||||
bool retrieveMetaDataAll() override;
|
||||
bool retrieveMetaDataOne(const int id) override { return false; } // not implemented
|
||||
@@ -99,6 +102,7 @@ class RepoReleases : public QObject, public RepoMetaData
|
||||
private:
|
||||
ReleasesRawItemModel* const m_rawItemModel;
|
||||
ReleasesFilteredItemModel* const m_filteredItemModel;
|
||||
QObject *m_parentRepo; // cannot use Repo class from repo.h as compile error due to cyclic headers
|
||||
|
||||
void dumpItemModel(const QString modelName, const QAbstractItemModel * itemModel) const override;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "repobuild.h"
|
||||
#include "repogithub.h"
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "updatecloudbuild.h"
|
||||
#include "repo.h"
|
||||
#include "appdata.h"
|
||||
#include "eeprominterface.h"
|
||||
#include "flashfirmwaredialog.h"
|
||||
|
||||
constexpr char STATUS_UNKNOWN[] {"STATUS_UNKNOWN"};
|
||||
constexpr char STATUS_WAITING_FOR_BUILD[] {"WAITING_FOR_BUILD"};
|
||||
constexpr char STATUS_BUILD_IN_PROGRESS[] {"BUILD_IN_PROGRESS"};
|
||||
constexpr char STATUS_BUILD_SUCCESS[] {"BUILD_SUCCESS"};
|
||||
constexpr char STATUS_BUILD_ERROR[] {"BUILD_ERROR"};
|
||||
|
||||
UpdateCloudBuild::UpdateCloudBuild(QWidget * parent) :
|
||||
UpdateInterface(parent, CID_CloudBuild, tr("CloudBuild"), Repo::REPO_TYPE_BUILD,
|
||||
QString("https://cloudbuild.edgetx.org/api"), "nightly"),
|
||||
m_objBody(nullptr),
|
||||
m_docResp(nullptr)
|
||||
{
|
||||
init(); // call after UpdateInterface ctor due to virtual functions
|
||||
}
|
||||
|
||||
UpdateCloudBuild::~UpdateCloudBuild()
|
||||
{
|
||||
if (m_objBody)
|
||||
delete m_objBody;
|
||||
if (m_docResp)
|
||||
delete m_docResp;
|
||||
}
|
||||
|
||||
void UpdateCloudBuild::assetSettingsInit()
|
||||
{
|
||||
if (!isSettingsIndexValid())
|
||||
return;
|
||||
|
||||
g.component[id()].initAllAssets();
|
||||
|
||||
ComponentAssetData &cad = g.component[id()].asset[0];
|
||||
|
||||
cad.desc("binary");
|
||||
cad.processes((UPDFLG_Common_Asset | UPDFLG_AsyncInstall | UPDFLG_Build) &~ UPDFLG_Decompress);
|
||||
cad.flags(cad.processes() | UPDFLG_CopyFiles | UPDFLG_Locked);
|
||||
cad.filterType(UpdateParameters::UFT_Exact);
|
||||
cad.filter("%FWFLAVOUR%");
|
||||
cad.destSubDir("FIRMWARE");
|
||||
cad.copyFilterType(UpdateParameters::UFT_Pattern);
|
||||
cad.copyFilter("^%FWFLAVOUR%-%LANGUAGE%.*-%RELEASE%\\.bin$");
|
||||
cad.maxExpected(1);
|
||||
|
||||
qDebug() << "Asset settings initialised";
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::addBuildLanguage(const QString & flag, const QJsonArray & values, QJsonArray & buildFlags)
|
||||
{
|
||||
QString val;
|
||||
|
||||
if (values.contains(params()->language.toLower()))
|
||||
val = params()->language.toLower();
|
||||
else if (values.contains(params()->language.toUpper()))
|
||||
val = params()->language.toUpper();
|
||||
else {
|
||||
status()->reportProgress(tr("Radio profile language '%1' not supported").arg(params()->language), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
addBuildFlag(buildFlags, flag, val);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::addBuildFai(const QString & flag, const QJsonArray & values, QJsonArray & buildFlags)
|
||||
{
|
||||
if (!m_profileOpts.contains("faichoice") && !m_profileOpts.contains("faimode"))
|
||||
return true;
|
||||
|
||||
if (!values.contains("CHOICE") && !values.contains("YES")) {
|
||||
status()->reportProgress(tr("fai_mode values do not contain CHOICE and YES"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString val;
|
||||
|
||||
if (m_profileOpts.contains("faichoice"))
|
||||
val = "CHOICE";
|
||||
else if (m_profileOpts.contains("faimode"))
|
||||
val = "YES";
|
||||
|
||||
m_buildFlags.append(QString("-%1").arg("fai"));
|
||||
addBuildFlag(buildFlags, flag, val);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateCloudBuild::addBuildFlag(QJsonArray & buildFlags, const QString & flag, const QString & val)
|
||||
{
|
||||
QJsonObject bldFlag;
|
||||
bldFlag.insert("name", flag);
|
||||
bldFlag.insert("value", val);
|
||||
buildFlags.append(QJsonValue(bldFlag));
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::arrayExists(const QJsonObject & parent, const QString child)
|
||||
{
|
||||
return !parent.value(child).isUndefined() && parent.value(child).isArray();
|
||||
}
|
||||
|
||||
int UpdateCloudBuild::asyncInstall()
|
||||
{
|
||||
status()->reportProgress(tr("Write firmware to radio: %1").arg(g.currentProfile().burnFirmware() ? tr("true") : tr("false")), QtDebugMsg);
|
||||
|
||||
if (!g.currentProfile().burnFirmware())
|
||||
return true;
|
||||
|
||||
status()->progressMessage(tr("Install"));
|
||||
|
||||
repo()->assets()->setFilterFlags(UPDFLG_AsyncInstall);
|
||||
status()->reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(repo()->assets()->count()), QtDebugMsg);
|
||||
|
||||
if (repo()->assets()->count() < 1)
|
||||
return true;
|
||||
|
||||
if (repo()->assets()->count() != params()->assets.at(0).maxExpected) {
|
||||
status()->reportProgress(tr("Expected %1 asset for install but %2 found").arg(params()->assets.at(0).maxExpected).arg(repo()->assets()->count()), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString destPath = updateDir();
|
||||
|
||||
if (!repo()->assets()->subDirectory().isEmpty())
|
||||
destPath.append("/" % repo()->assets()->subDirectory());
|
||||
|
||||
const UpdateParameters::AssetParams &ap = params()->assets.at(0);
|
||||
|
||||
QString pattern(params()->buildFilterPattern(ap.copyFilterType, ap.copyFilter));
|
||||
QRegularExpression filter(pattern, QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
QDirIterator it(destPath);
|
||||
|
||||
bool found = false;
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
QFileInfo file(it.next());
|
||||
|
||||
if ((!filter.pattern().isEmpty()) && (!file.fileName().contains(filter)))
|
||||
continue;
|
||||
|
||||
destPath.append("/" % file.fileName());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
status()->reportProgress(tr("Firmware not found in %1 using filter %2").arg(destPath).arg(filter.pattern()), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
g.currentProfile().fwName(destPath);
|
||||
|
||||
int ret = QMessageBox::question(status()->progress(), CPN_STR_APP_NAME, tr("Write the updated firmware to the radio now ?"), QMessageBox::Yes | QMessageBox::No);
|
||||
if (ret == QMessageBox::Yes) {
|
||||
FlashFirmwareDialog *dlg = new FlashFirmwareDialog(this);
|
||||
dlg->exec();
|
||||
dlg->deleteLater();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::buildFlaggedAsset(const int row)
|
||||
{
|
||||
m_buildFlags.clear();
|
||||
m_jobStatus.clear();
|
||||
m_profileOpts.clear();
|
||||
m_radio = repo()->assets()->name(row);
|
||||
|
||||
status()->reportProgress(tr("Building firmware for: %1").arg(m_radio));
|
||||
status()->progressMessage(tr("Building firmware for: %1").arg(m_radio));
|
||||
|
||||
if (params()->logLevel == QtDebugMsg) {
|
||||
m_logDir = QString("%1/logs").arg(downloadDir());
|
||||
|
||||
if (!QDir().mkpath(m_logDir)) {
|
||||
status()->reportProgress(tr("Failed to create directory %1!").arg(m_logDir), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Firmware *fw = getCurrentFirmware();
|
||||
m_profileOpts = fw->getId().split("-");
|
||||
// [0] = edgetx
|
||||
// [1] = radio id
|
||||
// [last] = language code
|
||||
|
||||
QJsonObject target;
|
||||
|
||||
if (params()->logLevel == QtDebugMsg)
|
||||
network()->saveJsonObjToFile(*repo()->config(), QString("%1/targets.txt").arg(m_logDir));
|
||||
|
||||
if (!objectExists(*repo()->config(), "targets")) {
|
||||
status()->reportProgress(tr("Unexpected format for build targets meta data"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
const QJsonObject &targets = repo()->config()->value("targets").toObject();
|
||||
target = targets.value(m_radio).toObject();
|
||||
|
||||
if (target.isEmpty()) {
|
||||
status()->reportProgress(tr("No build support for target %1").arg(m_radio), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params()->logLevel == QtDebugMsg)
|
||||
network()->saveJsonObjToFile(target, QString("%1/%2_target.txt").arg(m_logDir).arg(m_radio));
|
||||
|
||||
QJsonArray targetTags = target.value("tags").toArray();
|
||||
|
||||
if (targetTags.isEmpty()) {
|
||||
status()->reportProgress(tr("Build target %1 has no valid tags").arg(m_radio), QtWarningMsg);
|
||||
}
|
||||
|
||||
const QJsonObject flags = repo()->config()->value("flags").toObject();
|
||||
|
||||
if (flags.isEmpty()) {
|
||||
status()->reportProgress(tr("No flag entries found"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray arrBuildFlags;
|
||||
|
||||
QStringList flagKeys = flags.keys();
|
||||
|
||||
for (auto i = flagKeys.cbegin(), end = flagKeys.cend(); i != end; ++i) {
|
||||
const QJsonObject &flag = flags.value(*i).toObject();
|
||||
const QJsonArray &fvals = flag.value("values").toArray();
|
||||
|
||||
if (*i == "language") {
|
||||
if (!addBuildLanguage(*i, fvals, arrBuildFlags))
|
||||
return false;
|
||||
}
|
||||
else if (*i == "fai_mode") {
|
||||
if (!addBuildFai(*i, fvals, arrBuildFlags))
|
||||
return false;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
// TODO flags more to be added including custom hardware target build eg SPACEMOUSE
|
||||
//==================================================================================
|
||||
|
||||
}
|
||||
|
||||
if (m_objBody)
|
||||
delete m_objBody;
|
||||
|
||||
m_objBody = new QJsonObject();
|
||||
m_objBody->insert("release", QJsonValue(params()->releaseUpdate));
|
||||
m_objBody->insert("target", QJsonValue(m_radio));
|
||||
m_objBody->insert("flags", QJsonValue(arrBuildFlags));
|
||||
|
||||
QJsonDocument *docBody = new QJsonDocument(*m_objBody);
|
||||
|
||||
if (m_docResp)
|
||||
delete m_docResp;
|
||||
|
||||
m_docResp = new QJsonDocument();
|
||||
|
||||
m_buildStartTime = QTime::currentTime();
|
||||
|
||||
if (params()->logLevel == QtDebugMsg)
|
||||
network()->saveJsonDocToFile(docBody, QString("%1/%2_body.txt").arg(m_logDir).arg(m_radio));
|
||||
|
||||
network()->submitRequest(tr("Submit firmware build"), repo()->urlJobs(), docBody, m_docResp);
|
||||
|
||||
if (params()->logLevel == QtDebugMsg)
|
||||
network()->saveBufferToFile(QString("%1/%2_buffer.txt").arg(m_logDir).arg(m_radio));
|
||||
|
||||
if (!network()->isSuccess()) {
|
||||
status()->reportProgress(tr("Failed to initiate build job"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_docResp->isObject()) {
|
||||
status()->reportProgress(tr("Unexpected response format when submitting build job"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params()->logLevel == QtDebugMsg)
|
||||
network()->saveJsonDocToFile(m_docResp, QString("%1/%2_response.txt").arg(m_logDir).arg(m_radio));
|
||||
|
||||
const QJsonObject &obj = m_docResp->object();
|
||||
|
||||
if (stringExists(obj, "error")) {
|
||||
status()->reportProgress(tr("Build error: %1").arg(obj.value("error").toString()), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
else if (obj.value("target").toString() != m_radio || obj.value("release").toString() != params()->releaseUpdate) {
|
||||
status()->reportProgress(tr("Process status not returned when submitting build job"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
m_jobStatus = obj.value("status").toString(QString(STATUS_UNKNOWN));
|
||||
}
|
||||
|
||||
if (isStatusInProgress())
|
||||
waitForBuildFinish();
|
||||
|
||||
status()->reportProgress(tr("Firmware build finished status: %1").arg(m_jobStatus));
|
||||
|
||||
if (m_jobStatus == QString(STATUS_BUILD_SUCCESS) && setAssetDownload())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateCloudBuild::cancel()
|
||||
{
|
||||
status()->reportProgress(tr("Build firmware cancelled"), QtWarningMsg);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void UpdateCloudBuild::checkStatus()
|
||||
{
|
||||
if (m_buildStartTime.secsTo(QTime::currentTime()) > 180) {
|
||||
status()->reportProgress(tr("Build firmware timeout"), QtWarningMsg);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
getStatus();
|
||||
|
||||
if (!isStatusInProgress())
|
||||
cleanup();
|
||||
|
||||
}
|
||||
|
||||
void UpdateCloudBuild::cleanup()
|
||||
{
|
||||
if (m_timer.isActive())
|
||||
m_timer.stop();
|
||||
|
||||
if (m_eventLoop.isRunning())
|
||||
m_eventLoop.quit();
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::getStatus()
|
||||
{
|
||||
m_jobStatus = QString(STATUS_UNKNOWN);
|
||||
QJsonDocument *docBody = new QJsonDocument(*m_objBody);
|
||||
|
||||
if (m_docResp)
|
||||
delete m_docResp;
|
||||
|
||||
m_docResp = new QJsonDocument();
|
||||
network()->submitRequest(tr("Submit get build status"), repo()->urlStatus(), docBody, m_docResp);
|
||||
|
||||
if (params()->logLevel == QtDebugMsg)
|
||||
network()->saveJsonDocToFile(m_docResp, QString("%1/%2_status_%3.txt").arg(m_logDir).arg(m_radio).arg(QTime::currentTime().toString("hhmmss")));
|
||||
|
||||
if (m_docResp->isObject())
|
||||
m_jobStatus = m_docResp->object().value("status").toString(QString(STATUS_UNKNOWN));
|
||||
|
||||
if (m_jobStatus == QString(STATUS_UNKNOWN)) {
|
||||
status()->reportProgress(tr("Build status unknown"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::isStatusInProgress()
|
||||
{
|
||||
if (m_jobStatus == QString(STATUS_WAITING_FOR_BUILD) || m_jobStatus == QString(STATUS_BUILD_IN_PROGRESS))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::objectExists(const QJsonObject & parent, const QString child)
|
||||
{
|
||||
return !parent.value(child).isUndefined() && parent.value(child).isObject();
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::setAssetDownload()
|
||||
{
|
||||
// this format MUST align with the asset copy filter
|
||||
QString name = QString("%1-%2%3-%4.bin").arg(m_radio).arg(params()->language.toLower()).arg(m_buildFlags/* has a leading hyphen*/).arg(repo()->releases()->name());
|
||||
|
||||
repo()->assets()->setDownloadName(name.toLower());
|
||||
|
||||
const QJsonObject &obj = m_docResp->object();
|
||||
|
||||
if (arrayExists(obj, "artifacts")) {
|
||||
const QJsonArray &artifacts = obj.value("artifacts").toArray();
|
||||
if (artifacts.count() > 1) {
|
||||
status()->reportProgress(tr("Build status contains %1 artifacts when only 1 expected").arg(artifacts.count()), QtWarningMsg);
|
||||
}
|
||||
for (int i = 0; i < artifacts.count(); i++) {
|
||||
if (artifacts[i].isObject()) {
|
||||
const QJsonObject &artifact = artifacts[i].toObject();
|
||||
if (stringExists(artifact, "slug") && artifact.value("slug").toString() == "firmware") {
|
||||
if (stringExists(artifact, "download_url")) {
|
||||
repo()->assets()->setDownloadUrl(artifact.value("download_url").toString());
|
||||
}
|
||||
else {
|
||||
status()->reportProgress(tr("Build status does not contain download url"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status()->reportProgress(tr("Build status does not contain firmware artifact"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status()->reportProgress(tr("Build status firmware artifact not in expected format"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
status()->reportProgress(tr("Build status does not contain artifacts"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateCloudBuild::stringExists(const QJsonObject & parent, const QString child)
|
||||
{
|
||||
return !parent.value(child).isUndefined() && parent.value(child).isString();
|
||||
}
|
||||
|
||||
void UpdateCloudBuild::waitForBuildFinish()
|
||||
{
|
||||
status()->progressMessage(tr("Waiting for firmware build to finish..."));
|
||||
m_timer.setInterval(2000); // check status every 2 seconds
|
||||
|
||||
connect(this, &UpdateInterface::stop, this, &UpdateCloudBuild::cancel);
|
||||
connect(&m_timer, &QTimer::timeout, this, &UpdateCloudBuild::checkStatus);
|
||||
|
||||
m_timer.start();
|
||||
m_eventLoop.exec();
|
||||
|
||||
disconnect(this, &UpdateInterface::stop, this, &UpdateCloudBuild::cancel);
|
||||
disconnect(&m_timer, nullptr);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "updateinterface.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
class UpdateCloudBuild: public UpdateInterface
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(UpdateCloudBuild)
|
||||
|
||||
public:
|
||||
|
||||
explicit UpdateCloudBuild(QWidget * parent);
|
||||
virtual ~UpdateCloudBuild();
|
||||
|
||||
protected:
|
||||
int asyncInstall() override;
|
||||
void assetSettingsInit() override;
|
||||
bool buildFlaggedAsset(const int row) override;
|
||||
|
||||
private slots:
|
||||
void cancel();
|
||||
void checkStatus();
|
||||
|
||||
private:
|
||||
QJsonObject *m_objBody;
|
||||
QJsonDocument *m_docResp;
|
||||
QEventLoop m_eventLoop;
|
||||
QTimer m_timer;
|
||||
|
||||
QString m_logDir;
|
||||
QString m_buildFlags;
|
||||
QTime m_buildStartTime;
|
||||
QString m_jobStatus;
|
||||
QStringList m_profileOpts;
|
||||
QString m_radio;
|
||||
|
||||
bool arrayExists(const QJsonObject & parent, const QString child);
|
||||
bool objectExists(const QJsonObject & parent, const QString child);
|
||||
bool stringExists(const QJsonObject & parent, const QString child);
|
||||
|
||||
bool addBuildLanguage(const QString & flag, const QJsonArray & values, QJsonArray & buildFlags);
|
||||
bool addBuildFai(const QString & flag, const QJsonArray & values, QJsonArray & buildFlags);
|
||||
void addBuildFlag(QJsonArray & buildFlags, const QString & flag, const QString & val);
|
||||
void cleanup();
|
||||
bool getStatus();
|
||||
bool isStatusInProgress();
|
||||
bool setAssetDownload();
|
||||
void waitForBuildFinish();
|
||||
};
|
||||
@@ -53,9 +53,10 @@
|
||||
#endif
|
||||
|
||||
UpdateCompanion::UpdateCompanion(QWidget * parent) :
|
||||
UpdateInterface(parent, CID_Companion, QCoreApplication::translate("UpdateCompanion", "Companion"))
|
||||
UpdateInterface(parent, CID_Companion, QCoreApplication::translate("UpdateCompanion", "Companion"), Repo::REPO_TYPE_GITHUB,
|
||||
QString(GH_API_REPOS_EDGETX).append("/edgetx"), "nightly")
|
||||
{
|
||||
init(QString(GH_API_REPOS_EDGETX).append("/edgetx"), "nightly");
|
||||
init(); // call after UpdateInterface ctor due to virtual functions
|
||||
}
|
||||
|
||||
void UpdateCompanion::assetSettingsInit()
|
||||
@@ -77,7 +78,7 @@ void UpdateCompanion::assetSettingsInit()
|
||||
qDebug() << "Asset settings initialised";
|
||||
}
|
||||
|
||||
bool UpdateCompanion::asyncInstall()
|
||||
int UpdateCompanion::asyncInstall()
|
||||
{
|
||||
#ifdef OS_SUPPORTED_INSTALLER
|
||||
//status()->reportProgress(tr("Run application installer: %1").arg(g.runAppInstaller() ? tr("true") : tr("false")), QtDebugMsg);
|
||||
|
||||
@@ -38,6 +38,6 @@ class UpdateCompanion : public UpdateInterface
|
||||
virtual const bool isUpdateAvailable() override;
|
||||
|
||||
protected:
|
||||
virtual bool asyncInstall() override;
|
||||
virtual int asyncInstall() override;
|
||||
virtual void assetSettingsInit() override;
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "updatesounds.h"
|
||||
#include "updatethemes.h"
|
||||
#include "updatemultiprotocol.h"
|
||||
#include "updatecloudbuild.h"
|
||||
|
||||
UpdateFactories::UpdateFactories(QWidget * parent) :
|
||||
QWidget(parent)
|
||||
@@ -64,6 +65,13 @@ const bool UpdateFactories::isUpdateAvailable(QMap<QString, int> & list)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void UpdateFactories::radioProfileChanged()
|
||||
{
|
||||
foreach (UpdateFactoryInterface * factory, registeredUpdateFactories) {
|
||||
factory->instance()->radioProfileChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateFactories::registerUpdateFactory(UpdateFactoryInterface * factory)
|
||||
{
|
||||
foreach (UpdateFactoryInterface * registeredFactory, registeredUpdateFactories) {
|
||||
@@ -85,6 +93,7 @@ void UpdateFactories::registerUpdateFactories()
|
||||
registerUpdateFactory(new UpdateFactory<UpdateSounds>(this));
|
||||
registerUpdateFactory(new UpdateFactory<UpdateThemes>(this));
|
||||
registerUpdateFactory(new UpdateFactory<UpdateMultiProtocol>(this));
|
||||
registerUpdateFactory(new UpdateFactory<UpdateCloudBuild>(this));
|
||||
|
||||
// Note: Companion must be last as its install requires the app to be closed and thus would interrupt the update loop
|
||||
registerUpdateFactory(new UpdateFactory<UpdateCompanion>(this));
|
||||
|
||||
@@ -64,6 +64,7 @@ class UpdateFactories : public QWidget
|
||||
|
||||
UpdateInterface* const instance(const int id);
|
||||
const bool isUpdateAvailable(QMap<QString, int> & names);
|
||||
void radioProfileChanged();
|
||||
void registerUpdateFactories();
|
||||
void registerUpdateFactory(UpdateFactoryInterface * factory);
|
||||
void resetAllEnvironments();
|
||||
|
||||
@@ -23,9 +23,10 @@
|
||||
#include "flashfirmwaredialog.h"
|
||||
|
||||
UpdateFirmware::UpdateFirmware(QWidget * parent) :
|
||||
UpdateInterface(parent, CID_Firmware, tr("Firmware"))
|
||||
UpdateInterface(parent, CID_Firmware, tr("Firmware"), Repo::REPO_TYPE_GITHUB,
|
||||
QString(GH_API_REPOS_EDGETX).append("/edgetx"), "nightly")
|
||||
{
|
||||
init(QString(GH_API_REPOS_EDGETX).append("/edgetx"), "nightly");
|
||||
init(); // call after UpdateInterface ctor due to virtual functions
|
||||
}
|
||||
|
||||
void UpdateFirmware::assetSettingsInit()
|
||||
@@ -50,9 +51,9 @@ void UpdateFirmware::assetSettingsInit()
|
||||
qDebug() << "Asset settings initialised";
|
||||
}
|
||||
|
||||
bool UpdateFirmware::asyncInstall()
|
||||
int UpdateFirmware::asyncInstall()
|
||||
{
|
||||
//status->reportProgress(tr("Write firmware to radio: %1").arg(g.currentProfile().burnFirmware() ? tr("true") : tr("false")), QtDebugMsg);
|
||||
status()->reportProgress(tr("Write firmware to radio: %1").arg(g.currentProfile().burnFirmware() ? tr("true") : tr("false")), QtDebugMsg);
|
||||
|
||||
if (!g.currentProfile().burnFirmware())
|
||||
return true;
|
||||
@@ -60,7 +61,7 @@ bool UpdateFirmware::asyncInstall()
|
||||
status()->progressMessage(tr("Install"));
|
||||
|
||||
repo()->assets()->setFilterFlags(UPDFLG_AsyncInstall);
|
||||
//status->reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(assets->count()), QtDebugMsg);
|
||||
status()->reportProgress(tr("Asset filter applied: %1 Assets found: %2").arg(updateFlagsToString(UPDFLG_AsyncInstall)).arg(repo()->assets()->count()), QtDebugMsg);
|
||||
|
||||
if (repo()->assets()->count() < 1)
|
||||
return true;
|
||||
|
||||
@@ -33,6 +33,6 @@ class UpdateFirmware : public UpdateInterface
|
||||
virtual ~UpdateFirmware() {}
|
||||
|
||||
protected:
|
||||
virtual bool asyncInstall() override;
|
||||
virtual int asyncInstall() override;
|
||||
virtual void assetSettingsInit() override;
|
||||
};
|
||||
|
||||
@@ -20,13 +20,9 @@
|
||||
*/
|
||||
|
||||
#include "updateinterface.h"
|
||||
#include "repobuild.h"
|
||||
#include "repogithub.h"
|
||||
#include "helpers.h"
|
||||
#include "updatefirmware.h"
|
||||
#include "updatecompanion.h"
|
||||
#include "updatesdcard.h"
|
||||
#include "updatesounds.h"
|
||||
#include "updatethemes.h"
|
||||
#include "updatemultiprotocol.h"
|
||||
#include "minizinterface.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
@@ -34,17 +30,20 @@
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QValidator>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
UpdateInterface::UpdateInterface(QWidget * parent, ComponentIdentity id, QString name) :
|
||||
UpdateInterface::UpdateInterface(QWidget * parent, ComponentIdentity id, QString name, Repo::RepoType repoType,
|
||||
const QString & path, const QString & nightly, const int resultsPerPage) :
|
||||
QWidget(parent),
|
||||
m_id(id),
|
||||
m_name(name),
|
||||
m_params(new UpdateParameters(this)),
|
||||
m_status(new UpdateStatus(this)),
|
||||
m_network(new UpdateNetwork(this, m_status)),
|
||||
m_repo(new Repo(this, m_status, m_network))
|
||||
m_repo(repoType == Repo::REPO_TYPE_GITHUB ? static_cast<Repo*>(new RepoGitHub(this, m_status, m_network, path, nightly, resultsPerPage)) :
|
||||
static_cast<Repo*>(new RepoBuild(this, m_status, m_network, path, nightly, resultsPerPage)))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
UpdateInterface::~UpdateInterface()
|
||||
@@ -112,7 +111,37 @@ void UpdateInterface::assetSettingsSave()
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdateInterface::asyncInstall()
|
||||
int UpdateInterface::asyncInstall()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int UpdateInterface::build()
|
||||
{
|
||||
m_status->progressMessage(tr("Building assets"));
|
||||
|
||||
if (!buildFlaggedAssets()) {
|
||||
m_status->reportProgress(tr("Unable to build flagged assets"), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateInterface::buildFlaggedAssets()
|
||||
{
|
||||
m_repo->assets()->setFilterFlags(UPDFLG_Build);
|
||||
m_status->reportProgress(tr("Asset filter applied: %1 - %2 found").arg(updateFlagToString(UPDFLG_Build)).arg(m_repo->assets()->count()), QtDebugMsg);
|
||||
|
||||
for (int i = 0; i < m_repo->assets()->count(); ++i) {
|
||||
if (!buildFlaggedAsset(i))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateInterface::buildFlaggedAsset(const int row)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -358,7 +387,7 @@ bool UpdateInterface::copyStructure()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateInterface::copyToDestination()
|
||||
int UpdateInterface::copyToDestination()
|
||||
{
|
||||
m_status->progressMessage(tr("Copying to destination"));
|
||||
|
||||
@@ -370,6 +399,18 @@ bool UpdateInterface::copyToDestination()
|
||||
return true;
|
||||
}
|
||||
|
||||
int UpdateInterface::decompress()
|
||||
{
|
||||
m_status->progressMessage(tr("Decompressing assets"));
|
||||
|
||||
if (!decompressFlaggedAssets()) {
|
||||
m_status->reportProgress(tr("Unable to decompress flagged assets"), QtDebugMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString UpdateInterface::decompressDir() const
|
||||
{
|
||||
return m_decompressDir;
|
||||
@@ -441,16 +482,21 @@ bool UpdateInterface::downloadFlaggedAssets()
|
||||
m_status->reportProgress(tr("Asset filter applied: %1 - %2 found").arg(updateFlagToString(UPDFLG_Download)).arg(m_repo->assets()->count()), QtDebugMsg);
|
||||
|
||||
for (int i = 0; i < m_repo->assets()->count(); ++i) {
|
||||
if (!m_repo->assets()->downloadToFile(i, m_downloadDir))
|
||||
if (!downloadFlaggedAsset(i))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateInterface::download()
|
||||
bool UpdateInterface::downloadFlaggedAsset(const int row)
|
||||
{
|
||||
m_status->progressMessage(tr("Downloading assets"));
|
||||
return m_repo->assets()->downloadToFile(row, m_downloadDir);
|
||||
}
|
||||
|
||||
int UpdateInterface::download()
|
||||
{
|
||||
m_status->progressMessage(tr("Downloading..."));
|
||||
|
||||
if (!downloadFlaggedAssets()) {
|
||||
m_status->reportProgress(tr("Unable to download flagged assets"), QtDebugMsg);
|
||||
@@ -460,18 +506,6 @@ bool UpdateInterface::download()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateInterface::decompress()
|
||||
{
|
||||
m_status->progressMessage(tr("Decompressing assets"));
|
||||
|
||||
if (!decompressFlaggedAssets()) {
|
||||
m_status->reportProgress(tr("Unable to decompress flagged assets"), QtDebugMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateInterface::filterAssets(const UpdateParameters::AssetParams & ap)
|
||||
{
|
||||
QString pattern(m_params->buildFilterPattern(ap.filterType, ap.filter));
|
||||
@@ -504,7 +538,7 @@ bool UpdateInterface::flagAssets()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateInterface::housekeeping()
|
||||
int UpdateInterface::housekeeping()
|
||||
{
|
||||
m_status->progressMessage(tr("Housekeeping"));
|
||||
int cnt = 0;
|
||||
@@ -554,10 +588,9 @@ const int UpdateInterface::id() const
|
||||
return (int)m_id;
|
||||
}
|
||||
|
||||
void UpdateInterface::init(QString repoPath, QString nightly, int resultsPerPage)
|
||||
void UpdateInterface::init()
|
||||
{
|
||||
appSettingsInit();
|
||||
m_repo->init(repoPath, nightly, resultsPerPage);
|
||||
releaseCurrent();
|
||||
}
|
||||
|
||||
@@ -632,12 +665,20 @@ UpdateNetwork* const UpdateInterface::network() const
|
||||
return m_network;
|
||||
}
|
||||
|
||||
void UpdateInterface::onStatusCancelled()
|
||||
{
|
||||
m_status->setMaximum(0);
|
||||
m_stopping = true;
|
||||
emit stop();
|
||||
m_status->reportProgress(tr("Update cancelled"), QtWarningMsg);
|
||||
}
|
||||
|
||||
UpdateParameters* const UpdateInterface::params() const
|
||||
{
|
||||
return m_params;
|
||||
}
|
||||
|
||||
bool UpdateInterface::preparation()
|
||||
int UpdateInterface::preparation()
|
||||
{
|
||||
m_status->progressMessage(tr("Preparing"));
|
||||
int cnt = 0;
|
||||
@@ -678,6 +719,12 @@ bool UpdateInterface::preparation()
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateInterface::radioProfileChanged()
|
||||
{
|
||||
resetEnvironment();
|
||||
m_repo->releases()->invalidate();
|
||||
}
|
||||
|
||||
void UpdateInterface::releaseClear()
|
||||
{
|
||||
g.component[m_id].releaseClear();
|
||||
@@ -698,7 +745,7 @@ const QStringList UpdateInterface::releaseList()
|
||||
return m_repo->releases()->list();
|
||||
}
|
||||
|
||||
bool UpdateInterface::releaseSettingsSave()
|
||||
int UpdateInterface::releaseSettingsSave()
|
||||
{
|
||||
m_status->reportProgress(tr("Save release settings"), QtDebugMsg);
|
||||
g.component[m_id].release(m_repo->releases()->name());
|
||||
@@ -760,6 +807,62 @@ bool UpdateInterface::retrieveRepoJsonFile(const QString & filename, QJsonDocume
|
||||
return m_repo->getJson(filename, json);
|
||||
}
|
||||
|
||||
void UpdateInterface::runAsyncInstall()
|
||||
{
|
||||
m_result = asyncInstall();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 start async failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void UpdateInterface::runBuild()
|
||||
{
|
||||
m_result = build();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 preparation failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void UpdateInterface::runCopyToDestination()
|
||||
{
|
||||
m_result = copyToDestination();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 copy to destination failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void UpdateInterface::runDecompress()
|
||||
{
|
||||
m_result = decompress();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 decompress failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void UpdateInterface::runDownload()
|
||||
{
|
||||
m_result = download();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 download failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void UpdateInterface::runHousekeeping()
|
||||
{
|
||||
m_result = housekeeping();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 housekeeping failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void UpdateInterface::runPreparation()
|
||||
{
|
||||
m_result = preparation();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 preparation failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
void UpdateInterface::runReleaseSettingsSave()
|
||||
{
|
||||
m_result = releaseSettingsSave();
|
||||
if (m_result == PROC_RESULT_FAIL) m_status->reportProgress(tr("%1 save release settings failed").arg(m_name), QtCriticalMsg);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
bool UpdateInterface::setFilteredAssets(const UpdateParameters::AssetParams & ap)
|
||||
{
|
||||
if (!filterAssets(ap))
|
||||
@@ -917,51 +1020,79 @@ bool UpdateInterface::update(ProgressWidget * progress)
|
||||
if (!(m_params->flags & UPDFLG_Update))
|
||||
return true;
|
||||
|
||||
m_result = PROC_RESULT_SUCCESS;
|
||||
m_stopping = false;
|
||||
m_status->setProgress(progress);
|
||||
m_status->setLogLevel(m_params->logLevel);
|
||||
|
||||
m_status->setInfo(tr("Processing updates for: %1").arg(m_name));
|
||||
m_status->setValue(0);
|
||||
m_status->setMaximum(100);
|
||||
|
||||
m_status->reportProgress(tr("Processing updates for: %1").arg(m_name), QtInfoMsg);
|
||||
// the default behaviour of the dialog is to close on clicking the cancel button
|
||||
// but need to keep it open to display the current process until it can be interrupted
|
||||
// and then display close button
|
||||
status()->keepOpen(true);
|
||||
|
||||
if (!preparation()) {
|
||||
m_status->reportProgress(tr("%1 preparation failed").arg(m_name), QtCriticalMsg);
|
||||
return false;
|
||||
connect(m_status, &UpdateStatus::cancelled, this, &UpdateInterface::onStatusCancelled);
|
||||
|
||||
// need to create a separate thread for the processes so that the cancel request can be handled asap
|
||||
QEventLoop loop;
|
||||
|
||||
// EXTREMELY IMPORTANT: every process within the event loop MUST emit finished() otherwise the loop will not exit
|
||||
// Note: finished does not indicate the exit status
|
||||
connect(this, &UpdateInterface::finished, [&]() { if (loop.isRunning()) loop.quit(); });
|
||||
|
||||
if (okToRun()) {
|
||||
// pause before starting next process to allow time for queued events to be processed ie like the cancel request
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runPreparation);
|
||||
// NOTE: this will not exit until the finished signal is detected and processed
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if (!download()) {
|
||||
m_status->reportProgress(tr("%1 download failed").arg(m_name), QtCriticalMsg);
|
||||
return false;
|
||||
if (okToRun()) {
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runBuild);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if (!decompress()) {
|
||||
m_status->reportProgress(tr("%1 decompress failed").arg(m_name), QtCriticalMsg);
|
||||
return false;
|
||||
if (okToRun()) {
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runDownload);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if (!copyToDestination()) {
|
||||
m_status->reportProgress(tr("%1 copy to destination failed").arg(m_name), QtCriticalMsg);
|
||||
return false;
|
||||
if (okToRun()) {
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runDecompress);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if (okToRun()) {
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runCopyToDestination);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
// perform before async install in case Companion restarted
|
||||
if (!releaseSettingsSave()) {
|
||||
m_status->reportProgress(tr("Failed to save release settings"), QtDebugMsg);
|
||||
return false;
|
||||
if (okToRun()) {
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runReleaseSettingsSave);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if (!asyncInstall()) {
|
||||
m_status->reportProgress(tr("%1 start async failed").arg(m_name), QtCriticalMsg);
|
||||
return false;
|
||||
if (okToRun()) {
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runAsyncInstall);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if (!housekeeping()) {
|
||||
m_status->reportProgress(tr("%1 housekeeping failed").arg(m_name), QtCriticalMsg);
|
||||
return false;
|
||||
if (okToRun()) {
|
||||
QTimer::singleShot(100, this, &UpdateInterface::runHousekeeping);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
disconnect(m_status, &UpdateStatus::cancelled, this, &UpdateInterface::onStatusCancelled);
|
||||
|
||||
// allow the dialog to be closed by clicking the close button
|
||||
status()->keepOpen(false);
|
||||
|
||||
if (!okToRun())
|
||||
return false;
|
||||
|
||||
m_status->reportProgress(tr("%1 update successful").arg(m_name), QtInfoMsg);
|
||||
|
||||
if (!m_status->progress())
|
||||
@@ -1004,6 +1135,8 @@ const QString UpdateInterface::updateFlagToString(const int flag)
|
||||
return "Locked";
|
||||
case UPDFLG_Preparation:
|
||||
return "Preparation";
|
||||
case UPDFLG_Build:
|
||||
return "Build";
|
||||
case UPDFLG_Download:
|
||||
return "Download";
|
||||
case UPDFLG_Decompress:
|
||||
|
||||
@@ -48,6 +48,7 @@ class UpdateInterface : public QWidget
|
||||
CID_Themes = 3,
|
||||
CID_MultiProtocol = 4,
|
||||
CID_Companion = 5,
|
||||
CID_CloudBuild = 6,
|
||||
};
|
||||
Q_ENUM(ComponentIdentity)
|
||||
|
||||
@@ -65,12 +66,23 @@ class UpdateInterface : public QWidget
|
||||
UPDFLG_AsyncInstall = 1 << 10,
|
||||
UPDFLG_DelDownloads = 1 << 11,
|
||||
UPDFLG_DelDecompress = 1 << 12,
|
||||
UPDFLG_Build = 1 << 13,
|
||||
UPDFLG_Common_Asset = UPDFLG_Download | UPDFLG_Decompress | UPDFLG_CopyDest,
|
||||
UPDFLG_Common = UPDFLG_Common_Asset | UPDFLG_Preparation | UPDFLG_Housekeeping,
|
||||
};
|
||||
Q_ENUM(UpdateFlags)
|
||||
|
||||
explicit UpdateInterface(QWidget * parent, ComponentIdentity id, QString name);
|
||||
enum ProcessResult {
|
||||
PROC_RESULT_FAIL,
|
||||
PROC_RESULT_SUCCESS,
|
||||
PROC_RESULT_CANCELLED,
|
||||
};
|
||||
Q_ENUM(ProcessResult)
|
||||
|
||||
typedef bool (*processFunc)();
|
||||
|
||||
explicit UpdateInterface(QWidget * parent, ComponentIdentity id, QString name, Repo::RepoType repoType,
|
||||
const QString & path, const QString & nightly = QString(), const int resultsPerPage= -1);
|
||||
virtual ~UpdateInterface();
|
||||
|
||||
virtual void assetSettingsSave();
|
||||
@@ -80,31 +92,40 @@ class UpdateInterface : public QWidget
|
||||
virtual const QString releaseCurrent();
|
||||
virtual const QString releaseLatest();
|
||||
virtual const QString releaseUpdate();
|
||||
virtual bool update(ProgressWidget * progress = nullptr);
|
||||
virtual const QString versionCurrent();
|
||||
|
||||
const int id() const;
|
||||
const bool isUpdateable() const;
|
||||
const QString name() const;
|
||||
UpdateParameters* const params() const;
|
||||
void radioProfileChanged();
|
||||
void releaseClear();
|
||||
const QStringList releaseList();
|
||||
void resetEnvironment();
|
||||
void setReleaseChannel(const int channel);
|
||||
void setRunUpdate();
|
||||
bool update(ProgressWidget * progress = nullptr);
|
||||
|
||||
signals:
|
||||
void stop();
|
||||
void finished();
|
||||
|
||||
protected:
|
||||
virtual void assetSettingsInit() = 0;
|
||||
virtual void assetSettingsLoad();
|
||||
virtual bool asyncInstall();
|
||||
virtual int asyncInstall();
|
||||
virtual int build();
|
||||
virtual bool buildFlaggedAsset(const int row);
|
||||
virtual bool copyAsset();
|
||||
virtual bool copyToDestination();
|
||||
virtual bool decompress();
|
||||
virtual bool download();
|
||||
virtual int copyToDestination();
|
||||
virtual int decompress();
|
||||
virtual int download();
|
||||
virtual bool downloadFlaggedAsset(const int row);
|
||||
virtual bool flagAssets();
|
||||
virtual bool housekeeping();
|
||||
virtual bool preparation();
|
||||
virtual int housekeeping();
|
||||
virtual int preparation();
|
||||
|
||||
bool buildFlaggedAssets();
|
||||
bool copyFiles();
|
||||
bool copyFlaggedAssets();
|
||||
bool copyStructure();
|
||||
@@ -114,7 +135,7 @@ class UpdateInterface : public QWidget
|
||||
const QString downloadDir() const;
|
||||
bool downloadFlaggedAssets();
|
||||
bool filterAssets(const UpdateParameters::AssetParams & ap);
|
||||
void init(QString repo, QString nightly = "", int resultsPerPage = -1);
|
||||
void init();
|
||||
const bool isSettingsIndexValid() const;
|
||||
UpdateNetwork* const network() const;
|
||||
Repo* const repo() const;
|
||||
@@ -126,12 +147,16 @@ class UpdateInterface : public QWidget
|
||||
void setParamFolders();
|
||||
void setReleaseId(QString name);
|
||||
UpdateStatus* const status() const;
|
||||
const bool isStopping() const { return m_stopping; }
|
||||
const QString updateDir() const;
|
||||
|
||||
static QStringList versionToStringList(QString version);
|
||||
static const QString updateFlagsToString(const int flags);
|
||||
static const QString updateFlagToString(const int flag);
|
||||
|
||||
private slots:
|
||||
void onStatusCancelled();
|
||||
|
||||
private:
|
||||
const ComponentIdentity m_id;
|
||||
const QString m_name;
|
||||
@@ -142,10 +167,22 @@ class UpdateInterface : public QWidget
|
||||
QString m_downloadDir;
|
||||
QString m_decompressDir;
|
||||
QString m_updateDir;
|
||||
bool m_stopping;
|
||||
int m_result;
|
||||
|
||||
void appSettingsInit();
|
||||
bool checkCreateDirectory(const QString & dirSetting, const UpdateFlags flag);
|
||||
bool decompressArchive(const QString & archivePath, const QString & destPath);
|
||||
bool releaseSettingsSave();
|
||||
int releaseSettingsSave();
|
||||
bool setRunFolders();
|
||||
|
||||
bool okToRun() { return m_result == PROC_RESULT_SUCCESS && m_stopping == false; }
|
||||
void runAsyncInstall();
|
||||
void runBuild();
|
||||
void runCopyToDestination();
|
||||
void runDecompress();
|
||||
void runDownload();
|
||||
void runHousekeeping();
|
||||
void runPreparation();
|
||||
void runReleaseSettingsSave();
|
||||
};
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
#include "updatemultiprotocol.h"
|
||||
|
||||
UpdateMultiProtocol::UpdateMultiProtocol(QWidget * parent) :
|
||||
UpdateInterface(parent, CID_MultiProtocol, tr("Multiprotocol"))
|
||||
UpdateInterface(parent, CID_MultiProtocol, tr("Multiprotocol"), Repo::REPO_TYPE_GITHUB,
|
||||
QString(GH_API_REPOS).append("/pascallanger/DIY-Multiprotocol-TX-Module"), "", 100)
|
||||
{
|
||||
// GitHub REST API default ResultsPerPage = 30
|
||||
init(QString(GH_API_REPOS).append("/pascallanger/DIY-Multiprotocol-TX-Module"), "", 100);
|
||||
init(); // call after UpdateInterface ctor due to virtual functions
|
||||
}
|
||||
|
||||
void UpdateMultiProtocol::assetSettingsInit()
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "updateinterface.h"
|
||||
|
||||
class UpdateMultiProtocol : public UpdateInterface
|
||||
|
||||
@@ -49,47 +49,68 @@ UpdateNetwork::~UpdateNetwork()
|
||||
delete m_file;
|
||||
}
|
||||
|
||||
void UpdateNetwork::convertBufferToJson(QJsonDocument * json)
|
||||
{
|
||||
m_success = false;
|
||||
QJsonParseError res;
|
||||
|
||||
*json = QJsonDocument::fromJson(*m_buffer, &res);
|
||||
|
||||
if (res.error || json->isNull()) {
|
||||
m_status->reportProgress(tr("Unable to convert downloaded metadata to json. Error:%1\n%2").arg(res.error).arg(res.errorString()), QtCriticalMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
m_success = true;
|
||||
}
|
||||
|
||||
// static
|
||||
QString UpdateNetwork::downloadDataTypeToString(const DownloadDataType val)
|
||||
{
|
||||
switch ((int)val) {
|
||||
case DDT_Binary:
|
||||
return "Binary";
|
||||
case DDT_Content:
|
||||
return "Content";
|
||||
case DDT_MetaData:
|
||||
return "Meta Data";
|
||||
case DDT_Raw:
|
||||
return "Raw";
|
||||
case DDT_GitHub_Binary:
|
||||
return "GitHub Binary";
|
||||
case DDT_GitHub_Content:
|
||||
return "GitHub Content";
|
||||
case DDT_GitHub_MetaData:
|
||||
return "GitHub Meta Data";
|
||||
case DDT_GitHub_Raw:
|
||||
return "GitHub Raw";
|
||||
case DDT_GitHub_SaveToFile:
|
||||
return "GitHub Save to file";
|
||||
case DDT_Build_MetaData:
|
||||
return "Build Meta Data";
|
||||
case DDT_Build_SaveToFile:
|
||||
return "Build Save to file";
|
||||
default:
|
||||
return CPN_STR_UNKNOWN_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateNetwork::downloadMetaData(const QString & url, QJsonDocument * json)
|
||||
{
|
||||
downloadToBuffer(DDT_MetaData, url);
|
||||
if (m_success)
|
||||
convertBufferToJson(json);
|
||||
}
|
||||
|
||||
void UpdateNetwork::downloadJson(const QString & url, QJsonDocument * json)
|
||||
{
|
||||
downloadToBuffer(DDT_Content, url);
|
||||
downloadToBuffer(DDT_GitHub_Content, url);
|
||||
if (m_success)
|
||||
convertBufferToJson(json);
|
||||
}
|
||||
|
||||
void UpdateNetwork::downloadJsonAsset(const QString & url, QJsonDocument * json)
|
||||
{
|
||||
downloadToBuffer(DDT_Content, url);
|
||||
downloadToBuffer(DDT_GitHub_Content, url);
|
||||
if (m_success)
|
||||
convertBufferToJson(json);
|
||||
}
|
||||
|
||||
void UpdateNetwork::downloadJsonContent(const QString & url, QJsonDocument * json)
|
||||
{
|
||||
downloadToBuffer(DDT_Raw, url);
|
||||
downloadToBuffer(DDT_GitHub_Raw, url);
|
||||
if (m_success)
|
||||
convertBufferToJson(json);
|
||||
}
|
||||
|
||||
void UpdateNetwork::downloadMetaData(const DownloadDataType type, const QString & url, QJsonDocument * json)
|
||||
{
|
||||
downloadToBuffer(type, url);
|
||||
if (m_success)
|
||||
convertBufferToJson(json);
|
||||
}
|
||||
@@ -99,12 +120,12 @@ void UpdateNetwork::downloadToBuffer(const DownloadDataType type, const QString
|
||||
m_success = false;
|
||||
download(type,
|
||||
url,
|
||||
type == DDT_MetaData ? GH_ACCEPT_HEADER_METADATA :
|
||||
type == DDT_Raw ? GH_ACCEPT_HEADER_RAW : GH_ACCEPT_HEADER_CONTENT,
|
||||
type == DDT_GitHub_MetaData ? GH_ACCEPT_HEADER_METADATA :
|
||||
type == DDT_GitHub_Raw ? GH_ACCEPT_HEADER_RAW : GH_ACCEPT_HEADER_CONTENT,
|
||||
QString());
|
||||
}
|
||||
|
||||
void UpdateNetwork::downloadToFile(const QString & url, const QString & filePath)
|
||||
void UpdateNetwork::downloadToFile(const DownloadDataType type, const QString & url, const QString & filePath)
|
||||
{
|
||||
m_success = false;
|
||||
|
||||
@@ -135,56 +156,64 @@ void UpdateNetwork::downloadToFile(const QString & url, const QString & filePath
|
||||
return;
|
||||
}
|
||||
|
||||
download(DDT_SaveToFile, url, GH_ACCEPT_HEADER_CONTENT, fi.absoluteFilePath());
|
||||
download(type,
|
||||
url,
|
||||
type == DDT_GitHub_SaveToFile ? GH_ACCEPT_HEADER_CONTENT : NULL,
|
||||
fi.absoluteFilePath());
|
||||
}
|
||||
|
||||
void UpdateNetwork::download(const DownloadDataType type, const QString & urlStr, const char * header, const QString & filePath)
|
||||
void UpdateNetwork::download(const DownloadDataType type, const QString & url, const char * acceptHeader, const QString & filePath)
|
||||
{
|
||||
m_status->setValue(0);
|
||||
m_status->setMaximum(100);
|
||||
|
||||
m_buffer->clear();
|
||||
m_success = false;
|
||||
if (!init(url))
|
||||
return;
|
||||
|
||||
if (type == DDT_SaveToFile) {
|
||||
if (type == DDT_GitHub_SaveToFile || type == DDT_Build_SaveToFile) {
|
||||
m_file = new QFile(filePath);
|
||||
if (!m_file->open(QIODevice::WriteOnly)) {
|
||||
m_status->criticalMsg(tr("Unable to open the download file %1 for writing. Error: %2").arg(filePath).arg(m_file->errorString()));
|
||||
m_status->reportProgress(tr("Unable to open the download file %1 for writing. Error: %2").arg(filePath).arg(m_file->errorString()), QtCriticalMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_file = nullptr;
|
||||
|
||||
m_url.setUrl(urlStr);
|
||||
|
||||
if (!m_url.isValid()) {
|
||||
m_status->criticalMsg(tr("Invalid URL: %1").arg(urlStr));
|
||||
return;
|
||||
if (type >= DDT_GitHub_First && type <= DDT_GitHub_Last) {
|
||||
m_request.setRawHeader(QByteArray("X-GitHub-Api-Version"), GH_API_VERSION);
|
||||
m_request.setRawHeader(QByteArray("Accept"), QByteArray(acceptHeader));
|
||||
}
|
||||
else
|
||||
m_status->reportProgress(tr("URL: %1").arg(urlStr), QtDebugMsg);
|
||||
|
||||
m_request.setUrl(m_url);
|
||||
m_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
|
||||
m_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
// run GET inside event loop with timer interrupts to allow other events to run
|
||||
QEventLoop loop;
|
||||
|
||||
m_request.setRawHeader(QByteArray("X-GitHub-Api-Version"), GH_API_VERSION);
|
||||
m_request.setRawHeader(QByteArray("Accept"), QByteArray(header));
|
||||
QTimer timer;
|
||||
timer.setInterval(1000);
|
||||
|
||||
connect(&timer, &QTimer::timeout, [&]() {
|
||||
m_status->reportProgress(tr("Downloading..."), QtDebugMsg);
|
||||
});
|
||||
|
||||
m_reply = m_manager.get(m_request);
|
||||
|
||||
connect(m_reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred), [=] (QNetworkReply::NetworkError code) {
|
||||
// leave it to the finished slot to deal with error condition
|
||||
m_status->criticalMsg(tr("Network error has occurred. Error code: %1").arg(code));
|
||||
m_success = false;
|
||||
m_status->reportProgress(tr("Network error has occurred. Error code: %1").arg(code), QtCriticalMsg);
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::sslErrors, [=]() {
|
||||
m_status->reportProgress(tr("Ssl library version: %1").arg(QSslSocket::sslLibraryVersionString()), QtDebugMsg);
|
||||
m_success = false;
|
||||
m_status->reportProgress(tr("Ssl library version: %1").arg(QSslSocket::sslLibraryVersionString()), QtCriticalMsg);
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::downloadProgress, [=](const qint64 bytesRead, const qint64 totalBytes) {
|
||||
m_status->setMaximum(totalBytes);
|
||||
m_status->setValue(bytesRead);
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::readyRead, [=]() {
|
||||
if (type == DDT_SaveToFile) {
|
||||
if (type == DDT_GitHub_SaveToFile || type == DDT_Build_SaveToFile) {
|
||||
m_file->write(m_reply->readAll());
|
||||
}
|
||||
else {
|
||||
@@ -194,19 +223,19 @@ void UpdateNetwork::download(const DownloadDataType type, const QString & urlStr
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::finished, [&]() {
|
||||
if (timer.isActive())
|
||||
timer.stop();
|
||||
|
||||
if (loop.isRunning())
|
||||
loop.quit();
|
||||
|
||||
disconnect(m_reply, nullptr);
|
||||
|
||||
onDownloadFinished(m_reply, type);
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::downloadProgress, [=](const qint64 bytesRead, const qint64 totalBytes) {
|
||||
m_status->setMaximum(totalBytes);
|
||||
m_status->setValue(bytesRead);
|
||||
});
|
||||
|
||||
while (!m_reply->isFinished()) {
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
return;
|
||||
timer.start();
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
QByteArray * UpdateNetwork::getDownloadBuffer()
|
||||
@@ -214,6 +243,27 @@ QByteArray * UpdateNetwork::getDownloadBuffer()
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
bool UpdateNetwork::init(const QString & urlStr)
|
||||
{
|
||||
m_buffer->clear();
|
||||
m_success = false;
|
||||
m_request = QNetworkRequest();
|
||||
|
||||
m_url.setUrl(urlStr);
|
||||
|
||||
if (!m_url.isValid()) {
|
||||
m_status->reportProgress(tr("Invalid URL: %1").arg(urlStr), QtCriticalMsg);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
m_status->reportProgress(tr("URL: %1").arg(urlStr), QtDebugMsg);
|
||||
|
||||
m_request.setUrl(m_url);
|
||||
m_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
|
||||
m_request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool UpdateNetwork::isSuccess()
|
||||
{
|
||||
return m_success;
|
||||
@@ -223,38 +273,126 @@ void UpdateNetwork::onDownloadFinished(QNetworkReply * reply, DownloadDataType t
|
||||
{
|
||||
m_status->setValue(m_status->maximum());
|
||||
|
||||
if (type == DDT_SaveToFile) {
|
||||
if (type == DDT_GitHub_SaveToFile || type == DDT_Build_SaveToFile) {
|
||||
m_file->flush();
|
||||
m_file->close();
|
||||
}
|
||||
|
||||
if (m_reply->error()) {
|
||||
m_success = false;
|
||||
m_status->criticalMsg(tr("Unable to download %1. Error:%2\n%3").arg(downloadDataTypeToString(type)).arg(m_reply->error()).arg(m_reply->errorString()));
|
||||
m_status->reportProgress(tr("Unable to download %1. GET error:%2\n%3").arg(downloadDataTypeToString(type)).arg(m_reply->error()).arg(m_reply->errorString()), QtCriticalMsg);
|
||||
|
||||
if (type == DDT_SaveToFile)
|
||||
if (type == DDT_GitHub_SaveToFile || type == DDT_Build_SaveToFile)
|
||||
m_file->remove();
|
||||
}
|
||||
else
|
||||
m_success = true;
|
||||
|
||||
if (type == DDT_SaveToFile) {
|
||||
if (type == DDT_GitHub_SaveToFile || type == DDT_Build_SaveToFile) {
|
||||
delete m_file;
|
||||
m_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateNetwork::convertBufferToJson(QJsonDocument * json)
|
||||
void UpdateNetwork::post(const QString & action, const QString & url, QJsonDocument * data)
|
||||
{
|
||||
m_success = false;
|
||||
QJsonParseError res;
|
||||
m_status->setValue(0);
|
||||
m_status->setMaximum(0); // put progress bar in wait mode
|
||||
|
||||
*json = QJsonDocument::fromJson(*m_buffer, &res);
|
||||
|
||||
if (res.error || json->isNull()) {
|
||||
m_status->criticalMsg(tr("Unable to convert downloaded metadata to json. Error:%1\n%2").arg(res.error).arg(res.errorString()));
|
||||
if (!init(url))
|
||||
return;
|
||||
|
||||
m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
// run POST inside event loop with timer interrupts to allow other events to run
|
||||
QEventLoop loop;
|
||||
|
||||
QTimer timer;
|
||||
timer.setInterval(1000);
|
||||
|
||||
connect(&timer, &QTimer::timeout, [&]() {
|
||||
m_status->reportProgress(QString("%1...").arg(action), QtDebugMsg);
|
||||
});
|
||||
|
||||
m_reply = m_manager.post(m_request, data->toJson(QJsonDocument::Compact));
|
||||
|
||||
connect(m_reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred), [=] (QNetworkReply::NetworkError code) {
|
||||
m_success = false;
|
||||
m_status->reportProgress(tr("Network error has occurred. Error code: %1").arg(code), QtCriticalMsg);
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::sslErrors, [=]() {
|
||||
m_success = false;
|
||||
m_status->reportProgress(tr("Ssl library version: %1").arg(QSslSocket::sslLibraryVersionString()), QtCriticalMsg);
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::readyRead, [=]() {
|
||||
const QByteArray qba = m_reply->readAll();
|
||||
m_buffer->append(qba);
|
||||
});
|
||||
|
||||
connect(m_reply, &QNetworkReply::finished, [&]() {
|
||||
if (timer.isActive())
|
||||
timer.stop();
|
||||
|
||||
if (loop.isRunning())
|
||||
loop.quit();
|
||||
|
||||
disconnect(m_reply, nullptr);
|
||||
|
||||
if (m_reply->error()) {
|
||||
m_success = false;
|
||||
m_status->reportProgress(tr("POST error: %2\n%3").arg(m_reply->error()).arg(m_reply->errorString()), QtCriticalMsg);
|
||||
}
|
||||
|
||||
m_success = true;
|
||||
});
|
||||
|
||||
timer.start();
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
bool UpdateNetwork::saveBufferToFile(const QString & filePath)
|
||||
{
|
||||
QFile f(QDir::toNativeSeparators(filePath));
|
||||
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
m_status->reportProgress(tr("Failed to open %1 for writing").arg(QDir::toNativeSeparators(filePath)), QtWarningMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_success = true;
|
||||
QTextStream out(&f);
|
||||
out << *m_buffer;
|
||||
f.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateNetwork::saveJsonDocToFile(QJsonDocument * json, const QString & filePath)
|
||||
{
|
||||
QFile f(QDir::toNativeSeparators(filePath));
|
||||
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
m_status->reportProgress(tr("Failed to open %1 for writing").arg(QDir::toNativeSeparators(filePath)), QtWarningMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream out(&f);
|
||||
out << json->toJson();
|
||||
f.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateNetwork::saveJsonObjToFile(QJsonObject & obj, const QString & filePath)
|
||||
{
|
||||
saveJsonDocToFile(new QJsonDocument(obj), filePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateNetwork::submitRequest(const QString & action, const QString & url, QJsonDocument * data, QJsonDocument * response)
|
||||
{
|
||||
post(action, url, data);
|
||||
if (m_success)
|
||||
convertBufferToJson(response);
|
||||
}
|
||||
|
||||
@@ -35,11 +35,18 @@ class UpdateNetwork : public QObject
|
||||
public:
|
||||
|
||||
enum DownloadDataType {
|
||||
DDT_Binary,
|
||||
DDT_Content,
|
||||
DDT_SaveToFile,
|
||||
DDT_MetaData,
|
||||
DDT_Raw,
|
||||
DDT_Unknown,
|
||||
DDT_GitHub_Binary,
|
||||
DDT_GitHub_First = DDT_GitHub_Binary,
|
||||
DDT_GitHub_Content,
|
||||
DDT_GitHub_MetaData,
|
||||
DDT_GitHub_Raw,
|
||||
DDT_GitHub_SaveToFile,
|
||||
DDT_GitHub_Last = DDT_GitHub_SaveToFile,
|
||||
DDT_Build_MetaData,
|
||||
DDT_Build_First = DDT_Build_MetaData,
|
||||
DDT_Build_SaveToFile,
|
||||
DDT_Build_Last = DDT_Build_SaveToFile,
|
||||
};
|
||||
Q_ENUM(DownloadDataType)
|
||||
|
||||
@@ -47,15 +54,20 @@ class UpdateNetwork : public QObject
|
||||
virtual ~UpdateNetwork();
|
||||
|
||||
void convertBufferToJson(QJsonDocument * json);
|
||||
void downloadMetaData(const QString & url, QJsonDocument * json);
|
||||
void downloadMetaData(const DownloadDataType type, const QString & url, QJsonDocument * json);
|
||||
void downloadJson(const QString & url, QJsonDocument * json);
|
||||
void downloadJsonAsset(const QString & url, QJsonDocument * json);
|
||||
void downloadJsonContent(const QString & url, QJsonDocument * json);
|
||||
void downloadToFile(const QString & url, const QString & filePath);
|
||||
void downloadToFile(const DownloadDataType type, const QString & url, const QString & filePath);
|
||||
void downloadToBuffer(const DownloadDataType type, const QString & url);
|
||||
void download(const DownloadDataType type, const QString & urlStr, const char * header, const QString & filePath);
|
||||
void download(const DownloadDataType type, const QString & url, const char * acceptHeader, const QString & filePath);
|
||||
QByteArray * getDownloadBuffer();
|
||||
const bool isSuccess();
|
||||
void post(const QString & action, const QString & url, QJsonDocument * json);
|
||||
bool saveBufferToFile(const QString & filePath);
|
||||
bool saveJsonDocToFile(QJsonDocument * json, const QString & filePath);
|
||||
bool saveJsonObjToFile(QJsonObject & obj, const QString & filePath);
|
||||
void submitRequest(const QString & action, const QString & url, QJsonDocument * data, QJsonDocument * response);
|
||||
|
||||
static QString downloadDataTypeToString(const DownloadDataType val);
|
||||
|
||||
@@ -72,4 +84,5 @@ class UpdateNetwork : public QObject
|
||||
QUrl m_url;
|
||||
bool m_success;
|
||||
|
||||
bool init(const QString & url);
|
||||
};
|
||||
|
||||
@@ -50,6 +50,9 @@ QString UpdateParameters::buildFilterPattern(const UpdateFilterType filterType,
|
||||
|
||||
pattern.replace("%FWFLAVOUR%", fwFlavour);
|
||||
pattern.replace("%LANGUAGE%", language);
|
||||
QString rel = releaseUpdate;
|
||||
rel.replace(".", "\\.");
|
||||
pattern.replace("%RELEASE%", rel);
|
||||
|
||||
switch ((int)filterType) {
|
||||
case UFT_Exact:
|
||||
|
||||
@@ -122,10 +122,10 @@ void Updates::runUpdate()
|
||||
{
|
||||
bool ok = false;
|
||||
ProgressDialog progressDialog(this, tr("Update Components"), CompanionIcon("fuses.png"), true);
|
||||
progressDialog.progress()->lock(true);
|
||||
progressDialog.setProcessStarted();
|
||||
progressDialog.progress()->setInfo(tr("Starting..."));
|
||||
ok = factories->updateAll(progressDialog.progress());
|
||||
progressDialog.progress()->lock(false);
|
||||
progressDialog.setProcessStopped();
|
||||
progressDialog.progress()->setInfo(tr("Finished %1").arg(ok ? tr("successfully") : tr("with errors")));
|
||||
progressDialog.exec();
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
UpdateSDCard::UpdateSDCard(QWidget * parent) :
|
||||
UpdateInterface(parent, CID_SDCard, tr("SD Card"))
|
||||
UpdateInterface(parent, CID_SDCard, tr("SD Card"), Repo::REPO_TYPE_GITHUB,
|
||||
QString(GH_API_REPOS_EDGETX).append("/edgetx-sdcard"), "Latest")
|
||||
{
|
||||
init(QString(GH_API_REPOS_EDGETX).append("/edgetx-sdcard"), "Latest");
|
||||
init(); // call after UpdateInterface ctor due to virtual functions
|
||||
}
|
||||
|
||||
void UpdateSDCard::assetSettingsInit()
|
||||
|
||||
@@ -174,7 +174,6 @@ UpdatesDialog::UpdatesDialog(QWidget * parent, UpdateFactories * factories) :
|
||||
msg->setText(tr("Retrieving latest release information for %1").arg(name));
|
||||
chkUpdate[i] = new QCheckBox();
|
||||
chkUpdate[i]->setProperty("index", i);
|
||||
chkUpdate[i]->setChecked(!iface->isReleaseLatest());
|
||||
grid->addWidget(chkUpdate[i], row, col++);
|
||||
grid->setAlignment(chkUpdate[i], Qt::AlignHCenter);
|
||||
|
||||
@@ -190,32 +189,35 @@ UpdatesDialog::UpdatesDialog(QWidget * parent, UpdateFactories * factories) :
|
||||
grid->addWidget(lblCurrentRel[i], row, col++);
|
||||
|
||||
cboUpdateRel[i] = new QComboBox();
|
||||
cboUpdateRel[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
cboUpdateRel[i]->addItems(iface->releaseList());
|
||||
grid->addWidget(cboUpdateRel[i], row, col++);
|
||||
|
||||
connect(cboRelChannel[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=] (const int index) {
|
||||
iface->setReleaseChannel(index);
|
||||
chkUpdate[i]->setChecked(!iface->isReleaseLatest());
|
||||
cboUpdateRel[i]->clear();
|
||||
cboUpdateRel[i]->addItems(iface->releaseList());
|
||||
chkUpdate[i]->setChecked(cboUpdateRel[i]->currentIndex() != -1 && !iface->isReleaseLatest());
|
||||
});
|
||||
|
||||
connect(cboUpdateRel[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=] (const int index) {
|
||||
chkUpdate[i]->setChecked(cboUpdateRel[i]->currentText() != lblCurrentRel[i]->text());
|
||||
chkUpdate[i]->setChecked(cboUpdateRel[i]->currentIndex() != -1 && cboUpdateRel[i]->currentText() != lblCurrentRel[i]->text());
|
||||
});
|
||||
|
||||
btnOptions[i] = new QPushButton(tr("Options"));
|
||||
connect(btnOptions[i], &QPushButton::clicked, [=]() {
|
||||
UpdateOptionsDialog *dlg = new UpdateOptionsDialog(this, iface, i, true);
|
||||
connect(dlg, &UpdateOptionsDialog::changed, [=](const int i) {
|
||||
chkUpdate[i]->setChecked(!iface->isReleaseLatest());
|
||||
lblCurrentRel[i]->setText(iface->releaseCurrent());
|
||||
cboUpdateRel[i]->setCurrentText(iface->releaseUpdate());
|
||||
chkUpdate[i]->setChecked(cboUpdateRel[i]->currentIndex() != -1 && !iface->isReleaseLatest());
|
||||
});
|
||||
dlg->exec();
|
||||
dlg->deleteLater();
|
||||
});
|
||||
|
||||
chkUpdate[i]->setChecked(cboUpdateRel[i]->currentIndex() != -1 && !iface->isReleaseLatest());
|
||||
|
||||
grid->addWidget(btnOptions[i], row, col++);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
#include <QAbstractItemView>
|
||||
|
||||
UpdateSounds::UpdateSounds(QWidget * parent) :
|
||||
UpdateInterface(parent, CID_Sounds, tr("Sounds"))
|
||||
UpdateInterface(parent, CID_Sounds, tr("Sounds"), Repo::REPO_TYPE_GITHUB,
|
||||
QString(GH_API_REPOS_EDGETX).append("/edgetx-sdcard-sounds"), "latest", 50)
|
||||
{
|
||||
init(QString(GH_API_REPOS_EDGETX).append("/edgetx-sdcard-sounds"), "", 50);
|
||||
init(); // call after UpdateInterface ctor due to virtual functions
|
||||
langPacks = new QStandardItemModel();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ void UpdateStatus::reportProgress(QString text, QtMsgType type)
|
||||
qDebug() << text;
|
||||
}
|
||||
|
||||
if (type == QtCriticalMsg || type == QtFatalMsg)
|
||||
criticalMsg(text);
|
||||
if (type == QtFatalMsg)
|
||||
fatalMsg(text);
|
||||
}
|
||||
|
||||
void UpdateStatus::progressMessage(QString text)
|
||||
@@ -54,8 +54,21 @@ void UpdateStatus::progressMessage(QString text)
|
||||
qDebug() << text;
|
||||
}
|
||||
|
||||
void UpdateStatus::criticalMsg(QString msg)
|
||||
void UpdateStatus::fatalMsg(QString msg)
|
||||
{
|
||||
QMessageBox::critical(m_progress, tr("Update Interface"), msg);
|
||||
}
|
||||
|
||||
void UpdateStatus::setProgress(ProgressWidget * progress)
|
||||
{
|
||||
m_progress = progress;
|
||||
if (m_progress) {
|
||||
disconnect(m_progress, &ProgressWidget::stopped, this, &UpdateStatus::onWidgetStopped); // to avoid duplicate entries in stack
|
||||
connect(m_progress, &ProgressWidget::stopped, this, &UpdateStatus::onWidgetStopped);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateStatus::onWidgetStopped()
|
||||
{
|
||||
emit cancelled();
|
||||
}
|
||||
|
||||
@@ -40,12 +40,19 @@ class UpdateStatus : public QObject
|
||||
void setMaximum(int val) { if (m_progress) m_progress->setMaximum(val); }
|
||||
int maximum() { return m_progress ? m_progress->maximum() : 0; }
|
||||
|
||||
void setProgress(ProgressWidget * progress) { m_progress = progress; }
|
||||
void setProgress(ProgressWidget * progress);
|
||||
ProgressWidget * progress() { return m_progress; }
|
||||
|
||||
void keepOpen(bool val) { if (m_progress) m_progress->forceKeepOpen(val); }
|
||||
void reportProgress(QString text, QtMsgType type = QtInfoMsg);
|
||||
void progressMessage(QString text);
|
||||
void criticalMsg(QString msg);
|
||||
void fatalMsg(QString msg);
|
||||
|
||||
public slots:
|
||||
void onWidgetStopped();
|
||||
|
||||
signals:
|
||||
void cancelled();
|
||||
|
||||
private:
|
||||
ProgressWidget *m_progress;
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
#include "updatethemes.h"
|
||||
|
||||
UpdateThemes::UpdateThemes(QWidget * parent) :
|
||||
UpdateInterface(parent, CID_Themes, tr("Themes"))
|
||||
UpdateInterface(parent, CID_Themes, tr("Themes"), Repo::REPO_TYPE_GITHUB,
|
||||
QString(GH_API_REPOS_EDGETX).append("/themes"))
|
||||
{
|
||||
init(QString(GH_API_REPOS_EDGETX).append("/themes"));
|
||||
init(); // call after UpdateInterface ctor due to virtual functions
|
||||
}
|
||||
|
||||
void UpdateThemes::assetSettingsInit()
|
||||
|
||||
Reference in New Issue
Block a user