Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QF-1414-Download and writing data to a temporary file in QFieldCloudProjects. #5905

Merged
merged 7 commits into from
Dec 30, 2024
8 changes: 3 additions & 5 deletions src/core/networkreply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,9 @@ void NetworkReply::abort()
}


QNetworkReply *NetworkReply::reply() const
QNetworkReply *NetworkReply::currentRawReply() const
{
if ( mIsFinished )
return mReply;

return nullptr;
return mReply;
mohsenD98 marked this conversation as resolved.
Show resolved Hide resolved
}


Expand Down Expand Up @@ -90,6 +87,7 @@ void NetworkReply::initiateRequest()
case QNetworkAccessManager::UnknownOperation:
throw QStringLiteral( "Not implemented!" );
}
emit currentRawReplyChanged();

mReply->ignoreSslErrors( mExpectedSslErrors );

Expand Down
11 changes: 8 additions & 3 deletions src/core/networkreply.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ class NetworkReply : public QObject


/**
* Get the QNetworkReply object once the CloudReply is finilized. Do not delete it manually.
* @return network reply
* Get the current `QNetworkReply` object. Note that it might get deleted even if the parent `NetworkReply` is not in case of redirect or internal retry. Do not delete it manually.
* @return network currentRawReply
*/
QNetworkReply *reply() const;
QNetworkReply *currentRawReply() const;


/**
Expand Down Expand Up @@ -145,6 +145,11 @@ class NetworkReply : public QObject
*/
void temporaryErrorOccurred( QNetworkReply::NetworkError code );

/**
* Emitted when reply has changed.
*/
void currentRawReplyChanged();

private:
/**
* Upper bound of the delay between retries in milliseconds.
Expand Down
12 changes: 6 additions & 6 deletions src/core/platforms/android/androidplatformutilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,19 +573,19 @@ bool AndroidPlatformUtilities::checkPositioningPermissions() const
auto r = QtAndroidPrivate::checkPermission( "android.permission.ACCESS_COARSE_LOCATION" ).result();
if ( r == QtAndroidPrivate::Denied )
{
return checkAndAcquirePermissions( QStringList() << "android.permission.ACCESS_FINE_LOCATION" );
return checkAndAcquirePermissions( { QStringLiteral( "android.permission.ACCESS_FINE_LOCATION" ) } );
}
return true;
}

bool AndroidPlatformUtilities::checkCameraPermissions() const
{
return checkAndAcquirePermissions( QStringList() << "android.permission.CAMERA" );
return checkAndAcquirePermissions( { QStringLiteral( "android.permission.CAMERA" ) } );
}

bool AndroidPlatformUtilities::checkMicrophonePermissions() const
{
return checkAndAcquirePermissions( QStringList() << "android.permission.RECORD_AUDIO" );
return checkAndAcquirePermissions( { QStringLiteral( "android.permission.RECORD_AUDIO" ) } );
}

bool AndroidPlatformUtilities::checkAndAcquirePermissions( QStringList permissions, bool forceAsk ) const
Expand Down Expand Up @@ -714,7 +714,7 @@ QVariantMap AndroidPlatformUtilities::sceneMargins( QQuickWindow *window ) const
void AndroidPlatformUtilities::uploadPendingAttachments( QFieldCloudConnection *connection ) const
{
// Request notification permission
checkAndAcquirePermissions( QStringList() << QStringLiteral( "android.permission.POST_NOTIFICATIONS" ) );
checkAndAcquirePermissions( { QStringLiteral( "android.permission.POST_NOTIFICATIONS" ) } );

QTimer::singleShot( 500, [connection]() {
if ( connection )
Expand Down Expand Up @@ -754,13 +754,13 @@ void AndroidPlatformUtilities::vibrate( int milliseconds ) const

void AndroidPlatformUtilities::requestBackgroundPositioningPermissions()
{
checkAndAcquirePermissions( QStringLiteral( "android.permission.ACCESS_BACKGROUND_LOCATION" ) );
checkAndAcquirePermissions( { QStringLiteral( "android.permission.ACCESS_BACKGROUND_LOCATION" ) } );
}

void AndroidPlatformUtilities::startPositioningService() const
{
// Request notification permission
checkAndAcquirePermissions( QStringLiteral( "android.permission.POST_NOTIFICATIONS" ) );
checkAndAcquirePermissions( { QStringLiteral( "android.permission.POST_NOTIFICATIONS" ) } );

qInfo() << "Launching QField positioning service...";
QJniObject::callStaticMethod<void>( "ch/opengis/" APP_PACKAGE_NAME "/QFieldPositioningService",
Expand Down
10 changes: 5 additions & 5 deletions src/core/qfieldcloudconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ void QFieldCloudConnection::login()

// Handle login redirect as an error state
connect( reply, &NetworkReply::redirected, this, [=]() {
QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();
reply->deleteLater();
rawReply->deleteLater();

Expand All @@ -175,7 +175,7 @@ void QFieldCloudConnection::login()
} );

connect( reply, &NetworkReply::finished, this, [=]() {
QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();

Q_ASSERT( reply->isFinished() );
Q_ASSERT( rawReply );
Expand Down Expand Up @@ -334,7 +334,7 @@ NetworkReply *QFieldCloudConnection::post( const QString &endpoint, const QVaria
mPendingRequests++;
setState( ConnectionState::Busy );
connect( reply, &NetworkReply::finished, this, [=]() {
QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();
if ( --mPendingRequests == 0 )
{
if ( rawReply->error() != QNetworkReply::NoError )
Expand Down Expand Up @@ -395,7 +395,7 @@ NetworkReply *QFieldCloudConnection::get( QNetworkRequest &request, const QUrl &
mPendingRequests++;
setState( ConnectionState::Busy );
connect( reply, &NetworkReply::finished, this, [=]() {
QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();
if ( --mPendingRequests == 0 )
{
if ( rawReply->error() != QNetworkReply::NoError )
Expand Down Expand Up @@ -602,7 +602,7 @@ void QFieldCloudConnection::processPendingAttachments()
QString projectId = it.key();
QString fileName = it.value();
connect( attachmentCloudReply, &NetworkReply::finished, this, [=]() {
QNetworkReply *attachmentReply = attachmentCloudReply->reply();
QNetworkReply *attachmentReply = attachmentCloudReply->currentRawReply();
attachmentCloudReply->deleteLater();

Q_ASSERT( attachmentCloudReply->isFinished() );
Expand Down
114 changes: 68 additions & 46 deletions src/core/qfieldcloudprojectsmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ void QFieldCloudProjectsModel::refreshProjectFileOutdatedStatus( const QString &
return;
}

QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();
reply->deleteLater();

if ( rawReply->error() != QNetworkReply::NoError )
Expand Down Expand Up @@ -519,7 +519,7 @@ void QFieldCloudProjectsModel::projectRefreshData( const QString &projectId, con
if ( !findProject( projectId ) )
return;

QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();

reply->deleteLater();

Expand Down Expand Up @@ -620,7 +620,7 @@ void QFieldCloudProjectsModel::projectStartJob( const QString &projectId, const
return;
}

QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();

if ( rawReply->error() != QNetworkReply::NoError )
{
Expand Down Expand Up @@ -699,7 +699,7 @@ void QFieldCloudProjectsModel::projectGetJobStatus( const QString &projectId, co
return;
}

QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();

if ( rawReply->error() != QNetworkReply::NoError )
{
Expand Down Expand Up @@ -1005,7 +1005,7 @@ void QFieldCloudProjectsModel::projectDownload( const QString &projectId )
return;
}

QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();

reply->deleteLater();

Expand Down Expand Up @@ -1379,7 +1379,7 @@ void QFieldCloudProjectsModel::projectUpload( const QString &projectId, const bo
emit dataChanged( projectIndex, projectIndex, QVector<int>() << UploadDeltaProgressRole );
} );
connect( deltasCloudReply, &NetworkReply::finished, this, [=]() {
QNetworkReply *deltasReply = deltasCloudReply->reply();
QNetworkReply *deltasReply = deltasCloudReply->currentRawReply();
deltasCloudReply->deleteLater();

Q_ASSERT( deltasCloudReply->isFinished() );
Expand Down Expand Up @@ -1561,7 +1561,7 @@ void QFieldCloudProjectsModel::refreshProjectDeltaList( const QString &projectId
}

connect( deltaStatusReply, &NetworkReply::finished, this, [=]() {
QNetworkReply *rawReply = deltaStatusReply->reply();
QNetworkReply *rawReply = deltaStatusReply->currentRawReply();
deltaStatusReply->deleteLater();

Q_ASSERT( deltaStatusReply->isFinished() );
Expand Down Expand Up @@ -1591,7 +1591,7 @@ void QFieldCloudProjectsModel::projectGetDeltaStatus( const QString &projectId )

project->deltaFileUploadStatusString = QString();
connect( deltaStatusReply, &NetworkReply::finished, this, [=]() {
QNetworkReply *rawReply = deltaStatusReply->reply();
QNetworkReply *rawReply = deltaStatusReply->currentRawReply();
deltaStatusReply->deleteLater();

Q_ASSERT( deltaStatusReply->isFinished() );
Expand Down Expand Up @@ -1688,7 +1688,7 @@ void QFieldCloudProjectsModel::layerObserverLayerEdited( const QString &layerId
void QFieldCloudProjectsModel::projectListReceived()
{
NetworkReply *reply = qobject_cast<NetworkReply *>( sender() );
QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();

Q_ASSERT( rawReply );

Expand Down Expand Up @@ -1736,7 +1736,6 @@ NetworkReply *QFieldCloudProjectsModel::downloadFile( const QString &projectId,
return mCloudConnection->get( request, QStringLiteral( "/api/v1/packages/%1/latest/files/%2/" ).arg( projectId, fileName ) );
}


void QFieldCloudProjectsModel::downloadFileConnections( const QString &projectId, const QString &fileName )
{
const QModelIndex projectIndex = findProjectIndex( projectId );
Expand Down Expand Up @@ -1809,6 +1808,44 @@ void QFieldCloudProjectsModel::downloadFileConnections( const QString &projectId
} );

connect( reply, &NetworkReply::downloadProgress, reply, [=]( int bytesReceived, int bytesTotal ) {
QNetworkReply *rawReply = reply->currentRawReply();
if ( !rawReply )
{
return;
}

const QString temporaryFileName = project->downloadFileTransfers[fileName].tmpFile;
QFile file( temporaryFileName );
QString errorMessageDetail;
QString errorMessage;
bool hasError = false;

if ( file.open( QIODevice::WriteOnly | QIODevice::Append ) )
{
file.write( rawReply->readAll() );

if ( file.error() != QFile::NoError )
{
hasError = true;
errorMessageDetail = file.errorString();
errorMessage = tr( "File system error. Failed to write file to temporary location `%1`." ).arg( temporaryFileName );
}
}
else
{
hasError = true;
errorMessageDetail = file.errorString();
errorMessage = tr( "File system error. Failed to open file for writing on temporary `%1`." ).arg( temporaryFileName );
}

// check if the code above failed with error
if ( hasError )
{
logFailedDownload( project, fileName, errorMessage, errorMessageDetail );
rawReply->abort();
return;
}

if ( !findProject( projectId ) )
{
QgsLogger::debug( QStringLiteral( "Project %1, file `%2`: updating download progress, but the project is deleted." ).arg( projectId, fileName ) );
Expand All @@ -1835,7 +1872,7 @@ void QFieldCloudProjectsModel::downloadFileConnections( const QString &projectId
}

QVector<int> rolesChanged;
QNetworkReply *rawReply = reply->reply();
QNetworkReply *rawReply = reply->currentRawReply();

Q_ASSERT( reply->isFinished() );
Q_ASSERT( reply );
Expand Down Expand Up @@ -1863,46 +1900,13 @@ void QFieldCloudProjectsModel::downloadFileConnections( const QString &projectId
project->downloadBytesReceived += project->downloadFileTransfers[fileName].bytesTotal;
project->downloadProgress = std::clamp( ( static_cast<double>( project->downloadBytesReceived ) / std::max( project->downloadBytesTotal, 1 ) ), 0., 1. );
emit dataChanged( projectIndex, projectIndex, QVector<int>() << DownloadProgressRole );

QFile file( project->downloadFileTransfers[fileName].tmpFile );

if ( file.open( QIODevice::ReadWrite ) )
{
file.write( rawReply->readAll() );

if ( file.error() != QFile::NoError )
{
hasError = true;
errorMessageDetail = file.errorString();
errorMessage = tr( "File system error. Failed to write file to temporary location `%1`." ).arg( project->downloadFileTransfers[fileName].tmpFile );
}
}
else
{
hasError = true;
errorMessageDetail = file.errorString();
errorMessage = tr( "File system error. Failed to open file for writing on temporary `%1`." ).arg( project->downloadFileTransfers[fileName].tmpFile );
}
}

// check if the code above failed with error
if ( hasError )
{
project->downloadFilesFailed++;

QgsLogger::debug( QStringLiteral( "Project %1, file `%2`: %3 %4" ).arg( errorMessage, fileName, errorMessage, errorMessageDetail ) );

// translate the user messages
const QString baseMessage = tr( "Project `%1`, file `%2`: %3" ).arg( project->name, fileName, errorMessage );
const QString trimmedMessage = baseMessage + QStringLiteral( " " ) + tr( "System message: " )
+ ( ( errorMessageDetail.size() > 100 )
? ( errorMessageDetail.left( 100 ) + tr( " (see more in the QField error log)…" ) )
: errorMessageDetail );

QgsMessageLog::logMessage( QStringLiteral( "%1\n%2" ).arg( baseMessage, errorMessageDetail ) );

emit projectDownloadFinished( projectId, trimmedMessage );

logFailedDownload( project, fileName, errorMessage, errorMessageDetail );
rawReply->abort();
return;
}

Expand Down Expand Up @@ -2080,6 +2084,24 @@ void QFieldCloudProjectsModel::insertProjects( const QList<CloudProject *> &proj
endInsertRows();
}

void QFieldCloudProjectsModel::logFailedDownload( CloudProject *project, const QString &fileName, const QString &errorMessage, const QString &errorMessageDetail )
{
project->downloadFilesFailed++;

QgsLogger::debug( QStringLiteral( "Project %1, file `%2`: %3 %4" ).arg( errorMessage, fileName, errorMessage, errorMessageDetail ) );

// translate the user messages
const QString baseMessage = tr( "Project `%1`, file `%2`: %3" ).arg( project->name, fileName, errorMessage );
const QString trimmedMessage = baseMessage + QStringLiteral( " " ) + tr( "System message: " )
+ ( ( errorMessageDetail.size() > 100 )
? ( errorMessageDetail.left( 100 ) + tr( " (see more in the QField error log)…" ) )
: errorMessageDetail );

QgsMessageLog::logMessage( QStringLiteral( "%1\n%2" ).arg( baseMessage, errorMessageDetail ) );

emit projectDownloadFinished( project->id, trimmedMessage );
}

void QFieldCloudProjectsModel::loadProjects( const QJsonArray &remoteProjects, bool skipLocalProjects )
{
QgsProject *qgisProject = QgsProject::instance();
Expand Down
1 change: 1 addition & 0 deletions src/core/qfieldcloudprojectsmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ class QFieldCloudProjectsModel : public QAbstractListModel
void downloadFileConnections( const QString &projectId, const QString &fileName );
void loadProjects( const QJsonArray &remoteProjects = QJsonArray(), bool skipLocalProjects = false );
void insertProjects( const QList<CloudProject *> &projects );
void logFailedDownload( CloudProject *project, const QString &fileName, const QString &errorMessage, const QString &errorMessageDetail );
};

Q_DECLARE_METATYPE( QFieldCloudProjectsModel::ProjectStatus )
Expand Down
4 changes: 2 additions & 2 deletions src/qml/NavigationBar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Rectangle {
property int distance: 0
property bool isTracing: false

onPressed: {
onPressed: mouse => {
startX = mouse.x;
startY = mouse.y;
lastX = mouse.x;
Expand All @@ -142,7 +142,7 @@ Rectangle {
distance = 0;
isTracing = true;
}
onPositionChanged: {
onPositionChanged: mouse => {
if (!isTracing)
return;
var currentVelocity = Math.abs(mouse.y - lastY);
Expand Down
Loading
Loading