files_db.dart 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. import 'dart:io';
  2. import 'package:logging/logging.dart';
  3. import 'package:photos/models/file_type.dart';
  4. import 'package:photos/models/location.dart';
  5. import 'package:photos/models/file.dart';
  6. import 'package:path/path.dart';
  7. import 'package:sqflite/sqflite.dart';
  8. import 'package:path_provider/path_provider.dart';
  9. import 'package:sqflite_migration/sqflite_migration.dart';
  10. class FilesDB {
  11. static final _databaseName = "ente.files.db";
  12. static final Logger _logger = Logger("FilesDB");
  13. static final table = 'files';
  14. static final tempTable = 'temp_files';
  15. static final columnGeneratedID = '_id';
  16. static final columnUploadedFileID = 'uploaded_file_id';
  17. static final columnOwnerID = 'owner_id';
  18. static final columnCollectionID = 'collection_id';
  19. static final columnLocalID = 'local_id';
  20. static final columnTitle = 'title';
  21. static final columnDeviceFolder = 'device_folder';
  22. static final columnLatitude = 'latitude';
  23. static final columnLongitude = 'longitude';
  24. static final columnFileType = 'file_type';
  25. static final columnIsEncrypted = 'is_encrypted';
  26. static final columnIsDeleted = 'is_deleted';
  27. static final columnCreationTime = 'creation_time';
  28. static final columnModificationTime = 'modification_time';
  29. static final columnUpdationTime = 'updation_time';
  30. static final columnEncryptedKey = 'encrypted_key';
  31. static final columnKeyDecryptionNonce = 'key_decryption_nonce';
  32. static final columnFileDecryptionHeader = 'file_decryption_header';
  33. static final columnThumbnailDecryptionHeader = 'thumbnail_decryption_header';
  34. static final columnMetadataDecryptionHeader = 'metadata_decryption_header';
  35. static final intitialScript = [...createTable(table), ...addIndex()];
  36. static final migrationScripts = [...alterDeviceFolderToAllowNULL()];
  37. final dbConfig = MigrationConfig(
  38. initializationScript: intitialScript, migrationScripts: migrationScripts);
  39. // make this a singleton class
  40. FilesDB._privateConstructor();
  41. static final FilesDB instance = FilesDB._privateConstructor();
  42. // only have a single app-wide reference to the database
  43. static Database _database;
  44. Future<Database> get database async {
  45. if (_database != null) return _database;
  46. // lazily instantiate the db the first time it is accessed
  47. _database = await _initDatabase();
  48. return _database;
  49. }
  50. // this opens the database (and creates it if it doesn't exist)
  51. _initDatabase() async {
  52. Directory documentsDirectory = await getApplicationDocumentsDirectory();
  53. String path = join(documentsDirectory.path, _databaseName);
  54. return await openDatabaseWithMigration(path, dbConfig);
  55. }
  56. // SQL code to create the database table
  57. static List<String> createTable(String tableName) {
  58. return [
  59. '''
  60. CREATE TABLE $tableName (
  61. $columnGeneratedID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  62. $columnLocalID TEXT,
  63. $columnUploadedFileID INTEGER,
  64. $columnOwnerID INTEGER,
  65. $columnCollectionID INTEGER,
  66. $columnTitle TEXT NOT NULL,
  67. $columnDeviceFolder TEXT,
  68. $columnLatitude REAL,
  69. $columnLongitude REAL,
  70. $columnFileType INTEGER,
  71. $columnIsEncrypted INTEGER DEFAULT 1,
  72. $columnModificationTime TEXT NOT NULL,
  73. $columnEncryptedKey TEXT,
  74. $columnKeyDecryptionNonce TEXT,
  75. $columnFileDecryptionHeader TEXT,
  76. $columnThumbnailDecryptionHeader TEXT,
  77. $columnMetadataDecryptionHeader TEXT,
  78. $columnIsDeleted INTEGER DEFAULT 0,
  79. $columnCreationTime TEXT NOT NULL,
  80. $columnUpdationTime TEXT,
  81. UNIQUE($columnUploadedFileID, $columnCollectionID)
  82. );
  83. ''',
  84. ];
  85. }
  86. static List<String> addIndex() {
  87. return [
  88. '''
  89. CREATE INDEX collection_id_index ON $table($columnCollectionID);
  90. CREATE INDEX device_folder_index ON $table($columnDeviceFolder);
  91. CREATE INDEX creation_time_index ON $table($columnCreationTime);
  92. CREATE INDEX updation_time_index ON $table($columnUpdationTime);
  93. '''
  94. ];
  95. }
  96. static List<String> alterDeviceFolderToAllowNULL() {
  97. return [
  98. ...createTable(tempTable),
  99. '''
  100. INSERT INTO $tempTable
  101. SELECT *
  102. FROM $table;
  103. DROP TABLE $table;
  104. ALTER TABLE $tempTable
  105. RENAME TO $table;
  106. '''
  107. ];
  108. }
  109. Future<int> insert(File file) async {
  110. final db = await instance.database;
  111. return await db.insert(table, _getRowForFile(file));
  112. }
  113. Future<void> insertMultiple(List<File> files) async {
  114. final db = await instance.database;
  115. var batch = db.batch();
  116. int batchCounter = 0;
  117. for (File file in files) {
  118. if (batchCounter == 400) {
  119. await batch.commit();
  120. batch = db.batch();
  121. }
  122. batch.insert(
  123. table,
  124. _getRowForFile(file),
  125. conflictAlgorithm: ConflictAlgorithm.replace,
  126. );
  127. batchCounter++;
  128. }
  129. await batch.commit(noResult: true);
  130. }
  131. Future<File> getFile(int generatedID) async {
  132. final db = await instance.database;
  133. final results = await db.query(table,
  134. where: '$columnGeneratedID = ?', whereArgs: [generatedID]);
  135. if (results.isEmpty) {
  136. return null;
  137. }
  138. return _convertToFiles(results)[0];
  139. }
  140. Future<File> getUploadedFile(int uploadedID, int collectionID) async {
  141. final db = await instance.database;
  142. final results = await db.query(
  143. table,
  144. where: '$columnUploadedFileID = ? AND $columnCollectionID = ?',
  145. whereArgs: [
  146. uploadedID,
  147. collectionID,
  148. ],
  149. );
  150. if (results.isEmpty) {
  151. return null;
  152. }
  153. return _convertToFiles(results)[0];
  154. }
  155. Future<List<File>> getDeduplicatedFiles() async {
  156. _logger.info("Getting files for collection");
  157. final db = await instance.database;
  158. final results = await db.query(table,
  159. where: '$columnIsDeleted = 0',
  160. orderBy: '$columnCreationTime DESC',
  161. groupBy:
  162. 'IFNULL($columnUploadedFileID, $columnGeneratedID), IFNULL($columnLocalID, $columnGeneratedID)');
  163. return _convertToFiles(results);
  164. }
  165. Future<List<File>> getFiles() async {
  166. final db = await instance.database;
  167. final results = await db.query(
  168. table,
  169. where: '$columnIsDeleted = 0',
  170. orderBy: '$columnCreationTime DESC',
  171. );
  172. return _convertToFiles(results);
  173. }
  174. Future<List<File>> getAllVideos() async {
  175. final db = await instance.database;
  176. final results = await db.query(
  177. table,
  178. where:
  179. '$columnLocalID IS NOT NULL AND $columnFileType = 1 AND $columnIsDeleted = 0',
  180. orderBy: '$columnCreationTime DESC',
  181. );
  182. return _convertToFiles(results);
  183. }
  184. Future<List<File>> getAllInCollectionBeforeCreationTime(
  185. int collectionID, int beforeCreationTime, int limit) async {
  186. final db = await instance.database;
  187. final results = await db.query(
  188. table,
  189. where:
  190. '$columnCollectionID = ? AND $columnIsDeleted = 0 AND $columnCreationTime < ?',
  191. whereArgs: [collectionID, beforeCreationTime],
  192. orderBy: '$columnCreationTime DESC',
  193. limit: limit,
  194. );
  195. return _convertToFiles(results);
  196. }
  197. Future<List<File>> getAllInPath(String path) async {
  198. final db = await instance.database;
  199. final results = await db.query(
  200. table,
  201. where:
  202. '$columnLocalID IS NOT NULL AND $columnDeviceFolder = ? AND $columnIsDeleted = 0',
  203. whereArgs: [path],
  204. orderBy: '$columnCreationTime DESC',
  205. groupBy: '$columnLocalID',
  206. );
  207. return _convertToFiles(results);
  208. }
  209. Future<List<File>> getAllInPathBeforeCreationTime(
  210. String path, int beforeCreationTime, int limit) async {
  211. final db = await instance.database;
  212. final results = await db.query(
  213. table,
  214. where:
  215. '$columnLocalID IS NOT NULL AND $columnDeviceFolder = ? AND $columnIsDeleted = 0 AND $columnCreationTime < ?',
  216. whereArgs: [path, beforeCreationTime],
  217. orderBy: '$columnCreationTime DESC',
  218. groupBy: '$columnLocalID',
  219. limit: limit,
  220. );
  221. return _convertToFiles(results);
  222. }
  223. Future<List<File>> getAllInCollection(int collectionID) async {
  224. final db = await instance.database;
  225. final results = await db.query(
  226. table,
  227. where: '$columnCollectionID = ?',
  228. whereArgs: [collectionID],
  229. orderBy: '$columnCreationTime DESC',
  230. );
  231. return _convertToFiles(results);
  232. }
  233. Future<List<File>> getFilesCreatedWithinDuration(
  234. int startCreationTime, int endCreationTime) async {
  235. final db = await instance.database;
  236. final results = await db.query(
  237. table,
  238. where:
  239. '$columnCreationTime > ? AND $columnCreationTime < ? AND $columnIsDeleted = 0',
  240. whereArgs: [startCreationTime, endCreationTime],
  241. orderBy: '$columnCreationTime ASC',
  242. );
  243. return _convertToFiles(results);
  244. }
  245. Future<List<int>> getDeletedFileIDs() async {
  246. final db = await instance.database;
  247. final rows = await db.query(
  248. table,
  249. columns: [columnUploadedFileID],
  250. distinct: true,
  251. where: '$columnIsDeleted = 1',
  252. orderBy: '$columnCreationTime DESC',
  253. );
  254. final result = List<int>();
  255. for (final row in rows) {
  256. result.add(row[columnUploadedFileID]);
  257. }
  258. return result;
  259. }
  260. Future<List<File>> getFilesToBeUploadedWithinFolders(
  261. Set<String> folders) async {
  262. final db = await instance.database;
  263. String inParam = "";
  264. for (final folder in folders) {
  265. inParam += "'" + folder + "',";
  266. }
  267. inParam = inParam.substring(0, inParam.length - 1);
  268. final results = await db.query(
  269. table,
  270. where:
  271. '$columnUploadedFileID IS NULL AND $columnDeviceFolder IN ($inParam)',
  272. orderBy: '$columnCreationTime DESC',
  273. );
  274. return _convertToFiles(results);
  275. }
  276. Future<List<int>> getUploadedFileIDsToBeUpdated() async {
  277. final db = await instance.database;
  278. final rows = await db.query(
  279. table,
  280. columns: [columnUploadedFileID],
  281. where:
  282. '($columnLocalID IS NOT NULL AND $columnUploadedFileID IS NOT NULL AND $columnUpdationTime IS NULL AND $columnIsDeleted = 0)',
  283. orderBy: '$columnCreationTime DESC',
  284. distinct: true,
  285. );
  286. final uploadedFileIDs = List<int>();
  287. for (final row in rows) {
  288. uploadedFileIDs.add(row[columnUploadedFileID]);
  289. }
  290. return uploadedFileIDs;
  291. }
  292. Future<File> getUploadedFileInAnyCollection(int uploadedFileID) async {
  293. final db = await instance.database;
  294. final results = await db.query(
  295. table,
  296. where: '$columnUploadedFileID = ?',
  297. whereArgs: [
  298. uploadedFileID,
  299. ],
  300. limit: 1,
  301. );
  302. if (results.isEmpty) {
  303. return null;
  304. }
  305. return _convertToFiles(results)[0];
  306. }
  307. Future<Set<String>> getExistingLocalFileIDs() async {
  308. final db = await instance.database;
  309. final rows = await db.query(
  310. table,
  311. columns: [columnLocalID],
  312. distinct: true,
  313. where: '$columnLocalID IS NOT NULL',
  314. );
  315. final result = Set<String>();
  316. for (final row in rows) {
  317. result.add(row[columnLocalID]);
  318. }
  319. return result;
  320. }
  321. Future<int> updateUploadedFile(
  322. String localID,
  323. String title,
  324. Location location,
  325. int creationTime,
  326. int modificationTime,
  327. int updationTime,
  328. ) async {
  329. final db = await instance.database;
  330. return await db.update(
  331. table,
  332. {
  333. columnTitle: title,
  334. columnLatitude: location.latitude,
  335. columnLongitude: location.longitude,
  336. columnCreationTime: creationTime,
  337. columnModificationTime: modificationTime,
  338. columnUpdationTime: updationTime,
  339. },
  340. where: '$columnLocalID = ?',
  341. whereArgs: [localID],
  342. );
  343. }
  344. Future<Map<int, File>> getLastCreatedFilesInCollections(
  345. List<int> collectionIDs) async {
  346. final db = await instance.database;
  347. final rows = await db.rawQuery('''
  348. SELECT
  349. $columnGeneratedID,
  350. $columnLocalID,
  351. $columnUploadedFileID,
  352. $columnOwnerID,
  353. $columnCollectionID,
  354. $columnTitle,
  355. $columnDeviceFolder,
  356. $columnLatitude,
  357. $columnLongitude,
  358. $columnFileType,
  359. $columnIsEncrypted,
  360. $columnModificationTime,
  361. $columnEncryptedKey,
  362. $columnKeyDecryptionNonce,
  363. $columnFileDecryptionHeader,
  364. $columnThumbnailDecryptionHeader,
  365. $columnMetadataDecryptionHeader,
  366. $columnIsDeleted,
  367. $columnUpdationTime,
  368. MAX($columnCreationTime) as $columnCreationTime
  369. FROM $table
  370. WHERE $columnCollectionID IN (${collectionIDs.join(', ')}) AND $columnIsDeleted = 0
  371. GROUP BY $columnCollectionID
  372. ORDER BY $columnCreationTime DESC;
  373. ''');
  374. final result = Map<int, File>();
  375. final files = _convertToFiles(rows);
  376. for (final file in files) {
  377. result[file.collectionID] = file;
  378. }
  379. return result;
  380. }
  381. Future<Map<int, File>> getLastUpdatedFilesInCollections(
  382. List<int> collectionIDs) async {
  383. final db = await instance.database;
  384. final rows = await db.rawQuery('''
  385. SELECT
  386. $columnGeneratedID,
  387. $columnLocalID,
  388. $columnUploadedFileID,
  389. $columnOwnerID,
  390. $columnCollectionID,
  391. $columnTitle,
  392. $columnDeviceFolder,
  393. $columnLatitude,
  394. $columnLongitude,
  395. $columnFileType,
  396. $columnIsEncrypted,
  397. $columnModificationTime,
  398. $columnEncryptedKey,
  399. $columnKeyDecryptionNonce,
  400. $columnFileDecryptionHeader,
  401. $columnThumbnailDecryptionHeader,
  402. $columnMetadataDecryptionHeader,
  403. $columnIsDeleted,
  404. $columnCreationTime,
  405. MAX($columnUpdationTime) AS $columnUpdationTime
  406. FROM $table
  407. WHERE $columnCollectionID IN (${collectionIDs.join(', ')}) AND $columnIsDeleted = 0
  408. GROUP BY $columnCollectionID
  409. ORDER BY $columnUpdationTime DESC;
  410. ''');
  411. final result = Map<int, File>();
  412. final files = _convertToFiles(rows);
  413. for (final file in files) {
  414. result[file.collectionID] = file;
  415. }
  416. return result;
  417. }
  418. Future<List<File>> getMatchingFiles(
  419. String title,
  420. String deviceFolder,
  421. int creationTime,
  422. ) async {
  423. final db = await instance.database;
  424. var query;
  425. if (deviceFolder != null) {
  426. query = db.query(
  427. table,
  428. where: '''$columnTitle=? AND $columnDeviceFolder=? AND
  429. $columnCreationTime=?''',
  430. whereArgs: [
  431. title,
  432. deviceFolder,
  433. creationTime,
  434. ],
  435. );
  436. } else {
  437. query = db.query(
  438. table,
  439. where: '''$columnTitle=? AND
  440. $columnCreationTime=?''',
  441. whereArgs: [
  442. title,
  443. creationTime,
  444. ],
  445. );
  446. }
  447. final rows = await query;
  448. if (rows.isNotEmpty) {
  449. return _convertToFiles(rows);
  450. } else {
  451. return null;
  452. }
  453. }
  454. Future<File> getMatchingRemoteFile(int uploadedFileID) async {
  455. final db = await instance.database;
  456. final rows = await db.query(
  457. table,
  458. where: '$columnUploadedFileID=?',
  459. whereArgs: [uploadedFileID],
  460. );
  461. if (rows.isNotEmpty) {
  462. return _getFileFromRow(rows[0]);
  463. } else {
  464. throw ("No matching file found");
  465. }
  466. }
  467. Future<int> update(File file) async {
  468. final db = await instance.database;
  469. return await db.update(
  470. table,
  471. _getRowForFile(file),
  472. where: '$columnGeneratedID = ?',
  473. whereArgs: [file.generatedID],
  474. );
  475. }
  476. Future<int> updateUploadedFileAcrossCollections(File file) async {
  477. final db = await instance.database;
  478. return await db.update(
  479. table,
  480. _getRowForFileWithoutCollection(file),
  481. where: '$columnUploadedFileID = ?',
  482. whereArgs: [file.uploadedFileID],
  483. );
  484. }
  485. Future<int> markForDeletion(int uploadedFileID) async {
  486. final db = await instance.database;
  487. final values = new Map<String, dynamic>();
  488. values[columnIsDeleted] = 1;
  489. return db.update(
  490. table,
  491. values,
  492. where: '$columnUploadedFileID =?',
  493. whereArgs: [uploadedFileID],
  494. );
  495. }
  496. Future<int> delete(int uploadedFileID) async {
  497. final db = await instance.database;
  498. return db.delete(
  499. table,
  500. where: '$columnUploadedFileID =?',
  501. whereArgs: [uploadedFileID],
  502. );
  503. }
  504. Future<int> deleteLocalFile(String localID) async {
  505. final db = await instance.database;
  506. return db.delete(
  507. table,
  508. where: '$columnLocalID =?',
  509. whereArgs: [localID],
  510. );
  511. }
  512. Future<int> deleteFromCollection(int uploadedFileID, int collectionID) async {
  513. final db = await instance.database;
  514. return db.delete(
  515. table,
  516. where: '$columnUploadedFileID = ? AND $columnCollectionID = ?',
  517. whereArgs: [uploadedFileID, collectionID],
  518. );
  519. }
  520. Future<int> deleteCollection(int collectionID) async {
  521. final db = await instance.database;
  522. return db.delete(
  523. table,
  524. where: '$columnCollectionID = ?',
  525. whereArgs: [collectionID],
  526. );
  527. }
  528. Future<int> removeFromCollection(int collectionID, List<int> fileIDs) async {
  529. final db = await instance.database;
  530. return db.delete(
  531. table,
  532. where:
  533. '$columnCollectionID =? AND $columnUploadedFileID IN (${fileIDs.join(', ')})',
  534. whereArgs: [collectionID],
  535. );
  536. }
  537. Future<List<String>> getLocalPaths() async {
  538. final db = await instance.database;
  539. final rows = await db.query(
  540. table,
  541. columns: [columnDeviceFolder],
  542. where: '$columnLocalID IS NOT NULL',
  543. distinct: true,
  544. );
  545. List<String> result = List<String>();
  546. for (final row in rows) {
  547. result.add(row[columnDeviceFolder]);
  548. }
  549. return result;
  550. }
  551. Future<File> getLatestFileInCollection(int collectionID) async {
  552. final db = await instance.database;
  553. final rows = await db.query(
  554. table,
  555. where: '$columnCollectionID = ? AND $columnIsDeleted = 0',
  556. whereArgs: [collectionID],
  557. orderBy: '$columnCreationTime DESC',
  558. limit: 1,
  559. );
  560. if (rows.isNotEmpty) {
  561. return _getFileFromRow(rows[0]);
  562. } else {
  563. return null;
  564. }
  565. }
  566. Future<File> getLastCreatedFileInPath(String path) async {
  567. final db = await instance.database;
  568. final rows = await db.query(
  569. table,
  570. where:
  571. '$columnDeviceFolder = ? AND $columnLocalID IS NOT NULL AND $columnIsDeleted = 0',
  572. whereArgs: [path],
  573. orderBy: '$columnCreationTime DESC',
  574. limit: 1,
  575. );
  576. if (rows.isNotEmpty) {
  577. return _getFileFromRow(rows[0]);
  578. } else {
  579. return null;
  580. }
  581. }
  582. Future<File> getLastModifiedFileInCollection(int collectionID) async {
  583. final db = await instance.database;
  584. final rows = await db.query(
  585. table,
  586. where: '$columnCollectionID = ? AND $columnIsDeleted = 0',
  587. whereArgs: [collectionID],
  588. orderBy: '$columnUpdationTime DESC',
  589. limit: 1,
  590. );
  591. if (rows.isNotEmpty) {
  592. return _getFileFromRow(rows[0]);
  593. } else {
  594. return null;
  595. }
  596. }
  597. Future<bool> doesFileExistInCollection(
  598. int uploadedFileID, int collectionID) async {
  599. final db = await instance.database;
  600. final rows = await db.query(
  601. table,
  602. where: '$columnUploadedFileID = ? AND $columnCollectionID = ?',
  603. whereArgs: [uploadedFileID, collectionID],
  604. limit: 1,
  605. );
  606. return rows.isNotEmpty;
  607. }
  608. List<File> _convertToFiles(List<Map<String, dynamic>> results) {
  609. final files = List<File>();
  610. for (final result in results) {
  611. files.add(_getFileFromRow(result));
  612. }
  613. return files;
  614. }
  615. Map<String, dynamic> _getRowForFile(File file) {
  616. final row = new Map<String, dynamic>();
  617. row[columnLocalID] = file.localID;
  618. row[columnUploadedFileID] = file.uploadedFileID;
  619. row[columnOwnerID] = file.ownerID;
  620. row[columnCollectionID] = file.collectionID;
  621. row[columnTitle] = file.title;
  622. row[columnDeviceFolder] = file.deviceFolder;
  623. if (file.location != null) {
  624. row[columnLatitude] = file.location.latitude;
  625. row[columnLongitude] = file.location.longitude;
  626. }
  627. switch (file.fileType) {
  628. case FileType.image:
  629. row[columnFileType] = 0;
  630. break;
  631. case FileType.video:
  632. row[columnFileType] = 1;
  633. break;
  634. default:
  635. row[columnFileType] = -1;
  636. }
  637. row[columnIsEncrypted] = file.isEncrypted ? 1 : 0;
  638. row[columnCreationTime] = file.creationTime;
  639. row[columnModificationTime] = file.modificationTime;
  640. row[columnUpdationTime] = file.updationTime;
  641. row[columnEncryptedKey] = file.encryptedKey;
  642. row[columnKeyDecryptionNonce] = file.keyDecryptionNonce;
  643. row[columnFileDecryptionHeader] = file.fileDecryptionHeader;
  644. row[columnThumbnailDecryptionHeader] = file.thumbnailDecryptionHeader;
  645. row[columnMetadataDecryptionHeader] = file.metadataDecryptionHeader;
  646. return row;
  647. }
  648. Map<String, dynamic> _getRowForFileWithoutCollection(File file) {
  649. final row = new Map<String, dynamic>();
  650. row[columnLocalID] = file.localID;
  651. row[columnUploadedFileID] = file.uploadedFileID;
  652. row[columnOwnerID] = file.ownerID;
  653. row[columnTitle] = file.title;
  654. row[columnDeviceFolder] = file.deviceFolder;
  655. if (file.location != null) {
  656. row[columnLatitude] = file.location.latitude;
  657. row[columnLongitude] = file.location.longitude;
  658. }
  659. switch (file.fileType) {
  660. case FileType.image:
  661. row[columnFileType] = 0;
  662. break;
  663. case FileType.video:
  664. row[columnFileType] = 1;
  665. break;
  666. default:
  667. row[columnFileType] = -1;
  668. }
  669. row[columnIsEncrypted] = file.isEncrypted ? 1 : 0;
  670. row[columnCreationTime] = file.creationTime;
  671. row[columnModificationTime] = file.modificationTime;
  672. row[columnUpdationTime] = file.updationTime;
  673. row[columnFileDecryptionHeader] = file.fileDecryptionHeader;
  674. row[columnThumbnailDecryptionHeader] = file.thumbnailDecryptionHeader;
  675. row[columnMetadataDecryptionHeader] = file.metadataDecryptionHeader;
  676. return row;
  677. }
  678. File _getFileFromRow(Map<String, dynamic> row) {
  679. final file = File();
  680. file.generatedID = row[columnGeneratedID];
  681. file.localID = row[columnLocalID];
  682. file.uploadedFileID = row[columnUploadedFileID];
  683. file.ownerID = row[columnOwnerID];
  684. file.collectionID = row[columnCollectionID];
  685. file.title = row[columnTitle];
  686. file.deviceFolder = row[columnDeviceFolder];
  687. if (row[columnLatitude] != null && row[columnLongitude] != null) {
  688. file.location = Location(row[columnLatitude], row[columnLongitude]);
  689. }
  690. file.fileType = getFileType(row[columnFileType]);
  691. file.isEncrypted = row[columnIsEncrypted] == 1;
  692. file.creationTime = int.parse(row[columnCreationTime]);
  693. file.modificationTime = int.parse(row[columnModificationTime]);
  694. file.updationTime = row[columnUpdationTime] == null
  695. ? -1
  696. : int.parse(row[columnUpdationTime]);
  697. file.encryptedKey = row[columnEncryptedKey];
  698. file.keyDecryptionNonce = row[columnKeyDecryptionNonce];
  699. file.fileDecryptionHeader = row[columnFileDecryptionHeader];
  700. file.thumbnailDecryptionHeader = row[columnThumbnailDecryptionHeader];
  701. file.metadataDecryptionHeader = row[columnMetadataDecryptionHeader];
  702. return file;
  703. }
  704. }