Skip to content

Commit

Permalink
User data backup (#27)
Browse files Browse the repository at this point in the history
* Skeleton implementation for user data backup

* Get flipper file tree

* Support file names with spaces

* Working implementation of backup save

* Skeleton implementation of backup restore

* Working implementation of backup restore
  • Loading branch information
gsurkov authored Sep 28, 2021
1 parent 8c388bc commit 4af30a0
Show file tree
Hide file tree
Showing 22 changed files with 716 additions and 14 deletions.
17 changes: 17 additions & 0 deletions application/components/FlipperListDelegate.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Item {
signal versionListRequested(var device)
signal screenStreamRequested(var device)

signal backupRequested(var device)
signal restoreRequested(var device)

id: item
width: parent.width
height: 85
Expand Down Expand Up @@ -183,6 +186,20 @@ Item {

MenuSeparator {}

Menu {
title: qsTr("Backup && Restore")

MenuItem {
text: qsTr("Backup User Data...")
onTriggered: backupRequested(device)
}

MenuItem {
text: qsTr("Restore User Data...")
onTriggered: restoreRequested(device)
}
}

Menu {
title: qsTr("Expert options")

Expand Down
45 changes: 45 additions & 0 deletions application/screens/homescreen.qml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,29 @@ Item {
}
}

FileDialog {
id: dirDialog
title: qsTr("Please choose a directory")
folder: shortcuts.home
selectFolder: true

function openWithConfirmation(onAcceptedFunc, messageObj = {}) {
const onDialogRejected = function() {
dirDialog.rejected.disconnect(onDialogRejected);
dirDialog.accepted.disconnect(onDialogAccepted);
}

const onDialogAccepted = function() {
onDialogRejected();
confirmationDialog.openWithMessage(onAcceptedFunc, messageObj)
}

dirDialog.accepted.connect(onDialogAccepted);
dirDialog.rejected.connect(onDialogRejected);
dirDialog.open();
}
}

ListView {
id: deviceList
model: deviceRegistry
Expand Down Expand Up @@ -127,6 +150,28 @@ Item {
}, messageObj);
}

onBackupRequested: {
const messageObj = {
title : qsTr("Backup user data?"),
subtitle : qsTr("This will backup the contents of internal storage.")
};

dirDialog.openWithConfirmation(function() {
downloader.backupUserData(device, dirDialog.fileUrl);
}, messageObj);
}

onRestoreRequested: {
const messageObj = {
title : qsTr("Restore user data?"),
subtitle : qsTr("This will restore the contents of internal storage.")
};

dirDialog.openWithConfirmation(function() {
downloader.restoreUserData(device, dirDialog.fileUrl);
}, messageObj);
}

onLocalAssetsUpdateRequested: {
const messageObj = {
title : qsTr("Update the databases?"),
Expand Down
9 changes: 9 additions & 0 deletions backend/backend.pro
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ SOURCES += \
flipperzero/operations/fixbootissuesoperation.cpp \
flipperzero/operations/fixoptionbytesoperation.cpp \
flipperzero/operations/flipperzerooperation.cpp \
flipperzero/operations/getfiletreeoperation.cpp \
flipperzero/operations/userbackupoperation.cpp \
flipperzero/operations/userrestoreoperation.cpp \
flipperzero/operations/wirelessstackdownloadoperation.cpp \
flipperzero/recoverycontroller.cpp \
flipperzero/remotecontroller.cpp \
flipperzero/storage/listoperation.cpp \
flipperzero/storage/mkdiroperation.cpp \
flipperzero/storage/readoperation.cpp \
flipperzero/storage/removeoperation.cpp \
Expand All @@ -47,6 +51,7 @@ HEADERS += \
abstractserialoperation.h \
deviceregistry.h \
failable.h \
fileinfo.h \
filenode.h \
firmwaredownloader.h \
flipperupdates.h \
Expand All @@ -61,9 +66,13 @@ HEADERS += \
flipperzero/operations/fixbootissuesoperation.h \
flipperzero/operations/fixoptionbytesoperation.h \
flipperzero/operations/flipperzerooperation.h \
flipperzero/operations/getfiletreeoperation.h \
flipperzero/operations/userbackupoperation.h \
flipperzero/operations/userrestoreoperation.h \
flipperzero/operations/wirelessstackdownloadoperation.h \
flipperzero/recoverycontroller.h \
flipperzero/remotecontroller.h \
flipperzero/storage/listoperation.h \
flipperzero/storage/mkdiroperation.h \
flipperzero/storage/readoperation.h \
flipperzero/storage/removeoperation.h \
Expand Down
20 changes: 20 additions & 0 deletions backend/fileinfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <QList>
#include <QByteArray>

enum class FileType {
Directory,
RegularFile,
Storage,
Unknown
};

struct FileInfo {
QByteArray name;
QByteArray absolutePath;
FileType type;
qint64 size;
};

using FileInfoList = QList<FileInfo>;
23 changes: 18 additions & 5 deletions backend/firmwaredownloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
#include <QBuffer>

#include "flipperzero/flipperzero.h"
#include "flipperzero/operations/wirelessstackdownloadoperation.h"
#include "flipperzero/operations/firmwaredownloadoperation.h"
#include "flipperzero/operations/fixoptionbytesoperation.h"
#include "flipperzero/operations/assetsdownloadoperation.h"
#include "flipperzero/operations/userbackupoperation.h"
#include "flipperzero/operations/userrestoreoperation.h"
#include "flipperzero/operations/fixbootissuesoperation.h"
#include "flipperzero/operations/assetsdownloadoperation.h"
#include "flipperzero/operations/fixoptionbytesoperation.h"
#include "flipperzero/operations/firmwaredownloadoperation.h"
#include "flipperzero/operations/wirelessstackdownloadoperation.h"

#include "remotefilefetcher.h"
#include "macros.h"
Expand Down Expand Up @@ -90,6 +92,16 @@ void FirmwareDownloader::downloadAssets(FlipperZero *device, const QString &file
enqueueOperation(new Flipper::Zero::AssetsDownloadOperation(device, file, this));
}

void FirmwareDownloader::backupUserData(FlipperZero *device, const QString &backupPath)
{
enqueueOperation(new Flipper::Zero::UserBackupOperation(device, backupPath, this));
}

void FirmwareDownloader::restoreUserData(FlipperZero *device, const QString &backupPath)
{
enqueueOperation(new Flipper::Zero::UserRestoreOperation(device, backupPath, this));
}

void FirmwareDownloader::processQueue()
{
if(m_operationQueue.isEmpty()) {
Expand All @@ -102,7 +114,8 @@ void FirmwareDownloader::processQueue()
connect(currentOperation, &AbstractOperation::finished, this, [=]() {
info_msg(QStringLiteral("Operation '%1' finished with status: %2.").arg(currentOperation->description(), currentOperation->errorString()));
currentOperation->deleteLater();
processQueue();

QTimer::singleShot(0, this, &FirmwareDownloader::processQueue);
});

currentOperation->start();
Expand Down
5 changes: 4 additions & 1 deletion backend/firmwaredownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#include <QQueue>

#include "flipperupdates.h"
#include "abstractoperation.h"

class QIODevice;
class AbstractOperation;

namespace Flipper {

Expand Down Expand Up @@ -36,6 +36,9 @@ public slots:

void downloadAssets(Flipper::FlipperZero *device, const QString &filePath);

void backupUserData(Flipper::FlipperZero *device, const QString &backupPath);
void restoreUserData(Flipper::FlipperZero *device, const QString &backupPath);

private slots:
void processQueue();

Expand Down
8 changes: 5 additions & 3 deletions backend/flipperzero/operations/assetsdownloadoperation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,16 +305,18 @@ bool AssetsDownloadOperation::deleteFiles()

device()->setMessage(tr("Deleting unneeded files..."));

int i = m_delete.size();
int numFiles = m_delete.size();

for(const auto &fileInfo : qAsConst(m_delete)) {
--i;
const auto isLastFile = (--numFiles == 0);
const auto fileName = QByteArrayLiteral("/ext/") + fileInfo.absolutePath.toLocal8Bit();

auto *op = device()->storage()->remove(fileName);

connect(op, &AbstractOperation::finished, this, [=]() {
if(op->isError()) {
finishWithError(op->errorString());
} else if(i == 0) {
} else if(isLastFile) {
QTimer::singleShot(0, this, &AssetsDownloadOperation::transitionToNextState);
}
});
Expand Down
66 changes: 66 additions & 0 deletions backend/flipperzero/operations/getfiletreeoperation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "getfiletreeoperation.h"

#include <QTimer>

#include "flipperzero/flipperzero.h"
#include "flipperzero/storagecontroller.h"
#include "flipperzero/storage/listoperation.h"

#include "macros.h"

using namespace Flipper;
using namespace Zero;

GetFileTreeOperation::GetFileTreeOperation(FlipperZero *device, const QByteArray &rootPath, QObject *parent):
Operation(device, parent),
m_rootPath(rootPath),
m_pendingCount(0)
{}

const QString GetFileTreeOperation::description() const
{
return QStringLiteral("Get File Tree @%1").arg(QString(m_rootPath));
}

const FileInfoList &GetFileTreeOperation::result() const
{
return m_result;
}

void GetFileTreeOperation::transitionToNextState()
{
if(state() == BasicState::Ready) {
setState(State::Running);
listDirectory(m_rootPath);

} else if(state() == State::Running) {
--m_pendingCount;
auto *op = qobject_cast<ListOperation*>(sender());

if(op->isError()) {
finishWithError(op->errorString());
return;
}

for(const auto &fileInfo : qAsConst(op->result())) {
if(fileInfo.type == FileType::Directory) {
listDirectory(fileInfo.absolutePath);
}

m_result.push_back(fileInfo);
}

op->deleteLater();

if(!m_pendingCount) {
finish();
}
}
}

void GetFileTreeOperation::listDirectory(const QByteArray &path)
{
++m_pendingCount;
auto *op = device()->storage()->list(path);
connect(op, &AbstractOperation::finished, this, &GetFileTreeOperation::transitionToNextState);
}
37 changes: 37 additions & 0 deletions backend/flipperzero/operations/getfiletreeoperation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include "flipperzerooperation.h"
#include "fileinfo.h"

class QSerialPort;

namespace Flipper {
namespace Zero {

class GetFileTreeOperation : public Operation
{
Q_OBJECT

enum State {
Running = BasicState::User
};

public:
GetFileTreeOperation(FlipperZero *device, const QByteArray &rootPath, QObject *parent = nullptr);
const QString description() const override;
const FileInfoList &result() const;

private slots:
void transitionToNextState() override;

private:
void listDirectory(const QByteArray &path);

QByteArray m_rootPath;
QByteArray m_currentPath;
FileInfoList m_result;
int m_pendingCount;
};

}
}
Loading

0 comments on commit 4af30a0

Please sign in to comment.