Skip to content

Commit 4af30a0

Browse files
authored
User data backup (#27)
* 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
1 parent 8c388bc commit 4af30a0

22 files changed

Lines changed: 716 additions & 14 deletions

application/components/FlipperListDelegate.qml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Item {
1414
signal versionListRequested(var device)
1515
signal screenStreamRequested(var device)
1616

17+
signal backupRequested(var device)
18+
signal restoreRequested(var device)
19+
1720
id: item
1821
width: parent.width
1922
height: 85
@@ -183,6 +186,20 @@ Item {
183186

184187
MenuSeparator {}
185188

189+
Menu {
190+
title: qsTr("Backup && Restore")
191+
192+
MenuItem {
193+
text: qsTr("Backup User Data...")
194+
onTriggered: backupRequested(device)
195+
}
196+
197+
MenuItem {
198+
text: qsTr("Restore User Data...")
199+
onTriggered: restoreRequested(device)
200+
}
201+
}
202+
186203
Menu {
187204
title: qsTr("Expert options")
188205

application/screens/homescreen.qml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,29 @@ Item {
8080
}
8181
}
8282

83+
FileDialog {
84+
id: dirDialog
85+
title: qsTr("Please choose a directory")
86+
folder: shortcuts.home
87+
selectFolder: true
88+
89+
function openWithConfirmation(onAcceptedFunc, messageObj = {}) {
90+
const onDialogRejected = function() {
91+
dirDialog.rejected.disconnect(onDialogRejected);
92+
dirDialog.accepted.disconnect(onDialogAccepted);
93+
}
94+
95+
const onDialogAccepted = function() {
96+
onDialogRejected();
97+
confirmationDialog.openWithMessage(onAcceptedFunc, messageObj)
98+
}
99+
100+
dirDialog.accepted.connect(onDialogAccepted);
101+
dirDialog.rejected.connect(onDialogRejected);
102+
dirDialog.open();
103+
}
104+
}
105+
83106
ListView {
84107
id: deviceList
85108
model: deviceRegistry
@@ -127,6 +150,28 @@ Item {
127150
}, messageObj);
128151
}
129152

153+
onBackupRequested: {
154+
const messageObj = {
155+
title : qsTr("Backup user data?"),
156+
subtitle : qsTr("This will backup the contents of internal storage.")
157+
};
158+
159+
dirDialog.openWithConfirmation(function() {
160+
downloader.backupUserData(device, dirDialog.fileUrl);
161+
}, messageObj);
162+
}
163+
164+
onRestoreRequested: {
165+
const messageObj = {
166+
title : qsTr("Restore user data?"),
167+
subtitle : qsTr("This will restore the contents of internal storage.")
168+
};
169+
170+
dirDialog.openWithConfirmation(function() {
171+
downloader.restoreUserData(device, dirDialog.fileUrl);
172+
}, messageObj);
173+
}
174+
130175
onLocalAssetsUpdateRequested: {
131176
const messageObj = {
132177
title : qsTr("Update the databases?"),

backend/backend.pro

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ SOURCES += \
2525
flipperzero/operations/fixbootissuesoperation.cpp \
2626
flipperzero/operations/fixoptionbytesoperation.cpp \
2727
flipperzero/operations/flipperzerooperation.cpp \
28+
flipperzero/operations/getfiletreeoperation.cpp \
29+
flipperzero/operations/userbackupoperation.cpp \
30+
flipperzero/operations/userrestoreoperation.cpp \
2831
flipperzero/operations/wirelessstackdownloadoperation.cpp \
2932
flipperzero/recoverycontroller.cpp \
3033
flipperzero/remotecontroller.cpp \
34+
flipperzero/storage/listoperation.cpp \
3135
flipperzero/storage/mkdiroperation.cpp \
3236
flipperzero/storage/readoperation.cpp \
3337
flipperzero/storage/removeoperation.cpp \
@@ -47,6 +51,7 @@ HEADERS += \
4751
abstractserialoperation.h \
4852
deviceregistry.h \
4953
failable.h \
54+
fileinfo.h \
5055
filenode.h \
5156
firmwaredownloader.h \
5257
flipperupdates.h \
@@ -61,9 +66,13 @@ HEADERS += \
6166
flipperzero/operations/fixbootissuesoperation.h \
6267
flipperzero/operations/fixoptionbytesoperation.h \
6368
flipperzero/operations/flipperzerooperation.h \
69+
flipperzero/operations/getfiletreeoperation.h \
70+
flipperzero/operations/userbackupoperation.h \
71+
flipperzero/operations/userrestoreoperation.h \
6472
flipperzero/operations/wirelessstackdownloadoperation.h \
6573
flipperzero/recoverycontroller.h \
6674
flipperzero/remotecontroller.h \
75+
flipperzero/storage/listoperation.h \
6776
flipperzero/storage/mkdiroperation.h \
6877
flipperzero/storage/readoperation.h \
6978
flipperzero/storage/removeoperation.h \

backend/fileinfo.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <QList>
4+
#include <QByteArray>
5+
6+
enum class FileType {
7+
Directory,
8+
RegularFile,
9+
Storage,
10+
Unknown
11+
};
12+
13+
struct FileInfo {
14+
QByteArray name;
15+
QByteArray absolutePath;
16+
FileType type;
17+
qint64 size;
18+
};
19+
20+
using FileInfoList = QList<FileInfo>;

backend/firmwaredownloader.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
#include <QBuffer>
77

88
#include "flipperzero/flipperzero.h"
9-
#include "flipperzero/operations/wirelessstackdownloadoperation.h"
10-
#include "flipperzero/operations/firmwaredownloadoperation.h"
11-
#include "flipperzero/operations/fixoptionbytesoperation.h"
12-
#include "flipperzero/operations/assetsdownloadoperation.h"
9+
#include "flipperzero/operations/userbackupoperation.h"
10+
#include "flipperzero/operations/userrestoreoperation.h"
1311
#include "flipperzero/operations/fixbootissuesoperation.h"
12+
#include "flipperzero/operations/assetsdownloadoperation.h"
13+
#include "flipperzero/operations/fixoptionbytesoperation.h"
14+
#include "flipperzero/operations/firmwaredownloadoperation.h"
15+
#include "flipperzero/operations/wirelessstackdownloadoperation.h"
1416

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

95+
void FirmwareDownloader::backupUserData(FlipperZero *device, const QString &backupPath)
96+
{
97+
enqueueOperation(new Flipper::Zero::UserBackupOperation(device, backupPath, this));
98+
}
99+
100+
void FirmwareDownloader::restoreUserData(FlipperZero *device, const QString &backupPath)
101+
{
102+
enqueueOperation(new Flipper::Zero::UserRestoreOperation(device, backupPath, this));
103+
}
104+
93105
void FirmwareDownloader::processQueue()
94106
{
95107
if(m_operationQueue.isEmpty()) {
@@ -102,7 +114,8 @@ void FirmwareDownloader::processQueue()
102114
connect(currentOperation, &AbstractOperation::finished, this, [=]() {
103115
info_msg(QStringLiteral("Operation '%1' finished with status: %2.").arg(currentOperation->description(), currentOperation->errorString()));
104116
currentOperation->deleteLater();
105-
processQueue();
117+
118+
QTimer::singleShot(0, this, &FirmwareDownloader::processQueue);
106119
});
107120

108121
currentOperation->start();

backend/firmwaredownloader.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
#include <QQueue>
66

77
#include "flipperupdates.h"
8-
#include "abstractoperation.h"
98

109
class QIODevice;
10+
class AbstractOperation;
1111

1212
namespace Flipper {
1313

@@ -36,6 +36,9 @@ public slots:
3636

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

39+
void backupUserData(Flipper::FlipperZero *device, const QString &backupPath);
40+
void restoreUserData(Flipper::FlipperZero *device, const QString &backupPath);
41+
3942
private slots:
4043
void processQueue();
4144

backend/flipperzero/operations/assetsdownloadoperation.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,16 +305,18 @@ bool AssetsDownloadOperation::deleteFiles()
305305

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

308-
int i = m_delete.size();
308+
int numFiles = m_delete.size();
309309

310310
for(const auto &fileInfo : qAsConst(m_delete)) {
311-
--i;
311+
const auto isLastFile = (--numFiles == 0);
312312
const auto fileName = QByteArrayLiteral("/ext/") + fileInfo.absolutePath.toLocal8Bit();
313+
313314
auto *op = device()->storage()->remove(fileName);
315+
314316
connect(op, &AbstractOperation::finished, this, [=]() {
315317
if(op->isError()) {
316318
finishWithError(op->errorString());
317-
} else if(i == 0) {
319+
} else if(isLastFile) {
318320
QTimer::singleShot(0, this, &AssetsDownloadOperation::transitionToNextState);
319321
}
320322
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "getfiletreeoperation.h"
2+
3+
#include <QTimer>
4+
5+
#include "flipperzero/flipperzero.h"
6+
#include "flipperzero/storagecontroller.h"
7+
#include "flipperzero/storage/listoperation.h"
8+
9+
#include "macros.h"
10+
11+
using namespace Flipper;
12+
using namespace Zero;
13+
14+
GetFileTreeOperation::GetFileTreeOperation(FlipperZero *device, const QByteArray &rootPath, QObject *parent):
15+
Operation(device, parent),
16+
m_rootPath(rootPath),
17+
m_pendingCount(0)
18+
{}
19+
20+
const QString GetFileTreeOperation::description() const
21+
{
22+
return QStringLiteral("Get File Tree @%1").arg(QString(m_rootPath));
23+
}
24+
25+
const FileInfoList &GetFileTreeOperation::result() const
26+
{
27+
return m_result;
28+
}
29+
30+
void GetFileTreeOperation::transitionToNextState()
31+
{
32+
if(state() == BasicState::Ready) {
33+
setState(State::Running);
34+
listDirectory(m_rootPath);
35+
36+
} else if(state() == State::Running) {
37+
--m_pendingCount;
38+
auto *op = qobject_cast<ListOperation*>(sender());
39+
40+
if(op->isError()) {
41+
finishWithError(op->errorString());
42+
return;
43+
}
44+
45+
for(const auto &fileInfo : qAsConst(op->result())) {
46+
if(fileInfo.type == FileType::Directory) {
47+
listDirectory(fileInfo.absolutePath);
48+
}
49+
50+
m_result.push_back(fileInfo);
51+
}
52+
53+
op->deleteLater();
54+
55+
if(!m_pendingCount) {
56+
finish();
57+
}
58+
}
59+
}
60+
61+
void GetFileTreeOperation::listDirectory(const QByteArray &path)
62+
{
63+
++m_pendingCount;
64+
auto *op = device()->storage()->list(path);
65+
connect(op, &AbstractOperation::finished, this, &GetFileTreeOperation::transitionToNextState);
66+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include "flipperzerooperation.h"
4+
#include "fileinfo.h"
5+
6+
class QSerialPort;
7+
8+
namespace Flipper {
9+
namespace Zero {
10+
11+
class GetFileTreeOperation : public Operation
12+
{
13+
Q_OBJECT
14+
15+
enum State {
16+
Running = BasicState::User
17+
};
18+
19+
public:
20+
GetFileTreeOperation(FlipperZero *device, const QByteArray &rootPath, QObject *parent = nullptr);
21+
const QString description() const override;
22+
const FileInfoList &result() const;
23+
24+
private slots:
25+
void transitionToNextState() override;
26+
27+
private:
28+
void listDirectory(const QByteArray &path);
29+
30+
QByteArray m_rootPath;
31+
QByteArray m_currentPath;
32+
FileInfoList m_result;
33+
int m_pendingCount;
34+
};
35+
36+
}
37+
}

0 commit comments

Comments
 (0)