2020-03-24 19:59:36 +00:00
import ' dart:io ' ;
2020-05-27 21:03:30 +00:00
import ' package:logging/logging.dart ' ;
2021-06-28 06:40:19 +00:00
import ' package:photos/models/backup_status.dart ' ;
2021-06-29 05:13:16 +00:00
import ' package:photos/models/file_load_result.dart ' ;
2020-06-19 23:03:26 +00:00
import ' package:photos/models/file_type.dart ' ;
2020-06-03 16:06:49 +00:00
import ' package:photos/models/location.dart ' ;
2020-06-19 23:03:26 +00:00
import ' package:photos/models/file.dart ' ;
2020-03-24 19:59:36 +00:00
import ' package:path/path.dart ' ;
import ' package:sqflite/sqflite.dart ' ;
import ' package:path_provider/path_provider.dart ' ;
2021-01-23 12:43:30 +00:00
import ' package:sqflite_migration/sqflite_migration.dart ' ;
2020-03-24 19:59:36 +00:00
2020-07-20 11:03:09 +00:00
class FilesDB {
2021-05-03 22:48:02 +00:00
/ *
Note: columnUploadedFileID and columnCollectionID have to be compared against
both NULL and - 1 because older clients might have entries where the DEFAULT
was unset , and a migration script to set the DEFAULT would break in case of
duplicate entries for un - uploaded files that were created due to a collision
in background and foreground syncs .
* /
2020-06-19 23:03:26 +00:00
static final _databaseName = " ente.files.db " ;
2020-03-24 19:59:36 +00:00
2020-07-20 11:03:09 +00:00
static final Logger _logger = Logger ( " FilesDB " ) ;
2020-05-27 21:03:30 +00:00
2020-06-19 23:03:26 +00:00
static final table = ' files ' ;
2021-01-25 05:20:57 +00:00
static final tempTable = ' temp_files ' ;
2020-03-26 14:39:31 +00:00
2020-08-09 22:34:59 +00:00
static final columnGeneratedID = ' _id ' ;
static final columnUploadedFileID = ' uploaded_file_id ' ;
static final columnOwnerID = ' owner_id ' ;
2020-10-11 00:00:44 +00:00
static final columnCollectionID = ' collection_id ' ;
2020-08-09 22:34:59 +00:00
static final columnLocalID = ' local_id ' ;
2020-04-24 12:40:24 +00:00
static final columnTitle = ' title ' ;
2020-05-17 12:39:38 +00:00
static final columnDeviceFolder = ' device_folder ' ;
2020-06-03 14:11:34 +00:00
static final columnLatitude = ' latitude ' ;
static final columnLongitude = ' longitude ' ;
2020-06-19 23:03:26 +00:00
static final columnFileType = ' file_type ' ;
2020-04-12 12:38:49 +00:00
static final columnIsDeleted = ' is_deleted ' ;
2020-07-06 19:09:47 +00:00
static final columnCreationTime = ' creation_time ' ;
static final columnModificationTime = ' modification_time ' ;
static final columnUpdationTime = ' updation_time ' ;
2020-10-06 22:55:59 +00:00
static final columnEncryptedKey = ' encrypted_key ' ;
static final columnKeyDecryptionNonce = ' key_decryption_nonce ' ;
static final columnFileDecryptionHeader = ' file_decryption_header ' ;
static final columnThumbnailDecryptionHeader = ' thumbnail_decryption_header ' ;
static final columnMetadataDecryptionHeader = ' metadata_decryption_header ' ;
2020-03-24 19:59:36 +00:00
2021-01-24 11:51:53 +00:00
static final intitialScript = [ . . . createTable ( table ) , . . . addIndex ( ) ] ;
2021-01-23 13:49:37 +00:00
static final migrationScripts = [ . . . alterDeviceFolderToAllowNULL ( ) ] ;
2021-01-23 12:43:30 +00:00
final dbConfig = MigrationConfig (
initializationScript: intitialScript , migrationScripts: migrationScripts ) ;
2020-03-24 19:59:36 +00:00
// make this a singleton class
2020-07-20 11:03:09 +00:00
FilesDB . _privateConstructor ( ) ;
static final FilesDB instance = FilesDB . _privateConstructor ( ) ;
2020-03-24 19:59:36 +00:00
// only have a single app-wide reference to the database
static Database _database ;
Future < Database > get database async {
if ( _database ! = null ) return _database ;
// lazily instantiate the db the first time it is accessed
_database = await _initDatabase ( ) ;
return _database ;
}
2020-03-26 14:39:31 +00:00
2020-03-24 19:59:36 +00:00
// this opens the database (and creates it if it doesn't exist)
_initDatabase ( ) async {
Directory documentsDirectory = await getApplicationDocumentsDirectory ( ) ;
String path = join ( documentsDirectory . path , _databaseName ) ;
2021-01-23 12:43:30 +00:00
return await openDatabaseWithMigration ( path , dbConfig ) ;
2020-03-24 19:59:36 +00:00
}
// SQL code to create the database table
2021-01-25 06:15:37 +00:00
static List < String > createTable ( String tableName ) {
2021-01-23 13:49:37 +00:00
return [
'''
2021-01-25 06:15:37 +00:00
CREATE TABLE $tableName (
2021-01-24 18:59:17 +00:00
$columnGeneratedID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,
$columnLocalID TEXT ,
2021-05-03 22:48:02 +00:00
$columnUploadedFileID INTEGER DEFAULT - 1 ,
2021-01-24 18:59:17 +00:00
$columnOwnerID INTEGER ,
2021-05-03 22:48:02 +00:00
$columnCollectionID INTEGER DEFAULT - 1 ,
2021-01-24 18:59:17 +00:00
$columnTitle TEXT NOT NULL ,
$columnDeviceFolder TEXT ,
$columnLatitude REAL ,
$columnLongitude REAL ,
$columnFileType INTEGER ,
$columnModificationTime TEXT NOT NULL ,
$columnEncryptedKey TEXT ,
$columnKeyDecryptionNonce TEXT ,
$columnFileDecryptionHeader TEXT ,
$columnThumbnailDecryptionHeader TEXT ,
$columnMetadataDecryptionHeader TEXT ,
$columnIsDeleted INTEGER DEFAULT 0 ,
$columnCreationTime TEXT NOT NULL ,
$columnUpdationTime TEXT ,
2021-03-26 06:14:18 +00:00
UNIQUE ( $columnLocalID , $columnUploadedFileID , $columnCollectionID )
2021-01-24 18:59:17 +00:00
) ;
''' ,
2021-01-24 11:51:53 +00:00
] ;
}
static List < String > addIndex ( ) {
return [
2021-01-23 13:49:37 +00:00
'''
2021-01-24 18:59:17 +00:00
CREATE INDEX collection_id_index ON $table ( $columnCollectionID ) ;
CREATE INDEX device_folder_index ON $table ( $columnDeviceFolder ) ;
CREATE INDEX creation_time_index ON $table ( $columnCreationTime ) ;
CREATE INDEX updation_time_index ON $table ( $columnUpdationTime ) ;
2021-01-23 13:49:37 +00:00
'''
] ;
2021-01-23 12:43:30 +00:00
}
2021-01-23 13:49:37 +00:00
static List < String > alterDeviceFolderToAllowNULL ( ) {
return [
2021-01-25 06:15:37 +00:00
. . . createTable ( tempTable ) ,
2021-01-23 13:49:37 +00:00
'''
2021-01-25 05:20:57 +00:00
INSERT INTO $tempTable
2021-01-24 07:23:42 +00:00
SELECT *
FROM $table ;
2021-01-24 18:59:17 +00:00
2021-01-24 07:23:42 +00:00
DROP TABLE $table ;
2021-01-24 18:59:17 +00:00
2021-01-25 05:20:57 +00:00
ALTER TABLE $tempTable
2021-01-24 07:23:42 +00:00
RENAME TO $table ;
2021-01-23 13:49:37 +00:00
'''
] ;
2020-03-24 19:59:36 +00:00
}
2020-03-26 14:39:31 +00:00
2021-03-17 21:07:17 +00:00
Future < void > clearTable ( ) async {
final db = await instance . database ;
await db . delete ( table ) ;
}
2020-11-20 11:05:17 +00:00
Future < void > insertMultiple ( List < File > files ) async {
2021-04-19 12:46:42 +00:00
final startTime = DateTime . now ( ) ;
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2020-03-30 14:28:46 +00:00
var batch = db . batch ( ) ;
int batchCounter = 0 ;
2020-06-19 23:03:26 +00:00
for ( File file in files ) {
2020-03-30 14:28:46 +00:00
if ( batchCounter = = 400 ) {
2021-05-06 20:31:21 +00:00
await batch . commit ( noResult: true ) ;
2020-03-30 14:28:46 +00:00
batch = db . batch ( ) ;
2021-04-19 14:14:34 +00:00
batchCounter = 0 ;
2020-03-30 14:28:46 +00:00
}
2020-10-24 10:32:45 +00:00
batch . insert (
table ,
_getRowForFile ( file ) ,
conflictAlgorithm: ConflictAlgorithm . replace ,
) ;
2020-03-30 14:28:46 +00:00
batchCounter + + ;
}
2020-11-20 11:05:17 +00:00
await batch . commit ( noResult: true ) ;
2021-04-19 12:46:42 +00:00
final endTime = DateTime . now ( ) ;
final duration = Duration (
microseconds:
endTime . microsecondsSinceEpoch - startTime . microsecondsSinceEpoch ) ;
_logger . info ( " Batch insert of " +
files . length . toString ( ) +
" took " +
duration . inMilliseconds . toString ( ) +
" ms. " ) ;
2020-03-26 14:39:31 +00:00
}
2021-06-09 15:32:30 +00:00
Future < int > insert ( File file ) async {
final db = await instance . database ;
return db . insert (
table ,
_getRowForFile ( file ) ,
conflictAlgorithm: ConflictAlgorithm . replace ,
) ;
}
2020-10-24 20:05:50 +00:00
Future < File > getFile ( int generatedID ) async {
final db = await instance . database ;
final results = await db . query ( table ,
where: ' $ columnGeneratedID = ? ' , whereArgs: [ generatedID ] ) ;
2020-11-01 07:37:23 +00:00
if ( results . isEmpty ) {
return null ;
}
2020-10-24 21:25:28 +00:00
return _convertToFiles ( results ) [ 0 ] ;
2020-10-24 20:05:50 +00:00
}
2020-11-30 06:28:37 +00:00
Future < File > getUploadedFile ( int uploadedID , int collectionID ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where: ' $ columnUploadedFileID = ? AND $ columnCollectionID = ? ' ,
whereArgs: [
uploadedID ,
collectionID ,
] ,
) ;
if ( results . isEmpty ) {
return null ;
}
return _convertToFiles ( results ) [ 0 ] ;
}
2021-04-19 11:27:52 +00:00
Future < Set < int > > getUploadedFileIDs ( int collectionID ) async {
final db = await instance . database ;
final results = await db . query (
table ,
columns: [ columnUploadedFileID ] ,
where: ' $ columnCollectionID = ? ' ,
whereArgs: [
collectionID ,
] ,
) ;
final ids = Set < int > ( ) ;
for ( final result in results ) {
ids . add ( result [ columnUploadedFileID ] ) ;
}
return ids ;
}
2021-06-28 06:40:19 +00:00
Future < BackedUpFileIDs > getBackedUpIDs ( ) async {
2021-06-28 05:30:58 +00:00
final db = await instance . database ;
final results = await db . query (
table ,
2021-06-28 06:40:19 +00:00
columns: [ columnLocalID , columnUploadedFileID ] ,
2021-06-28 05:30:58 +00:00
where:
' $ columnLocalID IS NOT NULL AND ( $ columnUploadedFileID IS NOT NULL AND $ columnUploadedFileID IS NOT -1) ' ,
) ;
2021-06-28 06:40:19 +00:00
final localIDs = Set < String > ( ) ;
final uploadedIDs = Set < int > ( ) ;
2021-06-28 05:30:58 +00:00
for ( final result in results ) {
2021-06-28 06:40:19 +00:00
localIDs . add ( result [ columnLocalID ] ) ;
uploadedIDs . add ( result [ columnUploadedFileID ] ) ;
2021-06-28 05:30:58 +00:00
}
2021-06-28 06:40:19 +00:00
return BackedUpFileIDs ( localIDs . toList ( ) , uploadedIDs . toList ( ) ) ;
2021-06-28 05:30:58 +00:00
}
2021-07-01 10:51:13 +00:00
Future < FileLoadResult > getAllUploadedFiles ( int startTime , int endTime ,
2021-05-08 17:46:48 +00:00
{ int limit , bool asc } ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2020-05-06 18:13:24 +00:00
final results = await db . query (
table ,
2021-04-20 20:11:39 +00:00
where:
2021-07-01 10:51:13 +00:00
' $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnIsDeleted = 0 AND ( $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1) ' ,
2021-04-20 20:11:39 +00:00
whereArgs: [ startTime , endTime ] ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-04-20 20:11:39 +00:00
limit: limit ,
2020-05-06 18:13:24 +00:00
) ;
2021-06-29 05:13:16 +00:00
final files = _convertToFiles ( results ) ;
return FileLoadResult ( files , files . length = = limit ) ;
2020-04-12 12:38:49 +00:00
}
2021-07-01 10:51:13 +00:00
Future < FileLoadResult > getImportantFiles (
2021-04-25 12:40:10 +00:00
int startTime , int endTime , List < String > paths ,
2021-05-08 17:46:48 +00:00
{ int limit , bool asc } ) async {
2021-04-25 12:40:10 +00:00
final db = await instance . database ;
2021-05-13 11:13:32 +00:00
String inParam = " " ;
for ( final path in paths ) {
2021-05-25 12:19:20 +00:00
inParam + = " ' " + path . replaceAll ( " ' " , " '' " ) + " ', " ;
2021-05-13 11:13:32 +00:00
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2021-04-25 12:40:10 +00:00
final results = await db . query (
table ,
where:
2021-06-09 15:32:30 +00:00
' $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnIsDeleted = 0 AND (( $ columnLocalID IS NOT NULL AND $ columnDeviceFolder IN ( $ inParam )) OR ( $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1)) ' ,
2021-05-13 11:13:32 +00:00
whereArgs: [ startTime , endTime ] ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-04-25 12:40:10 +00:00
limit: limit ,
) ;
2021-05-13 19:57:27 +00:00
final uploadedFileIDs = Set < int > ( ) ;
final files = _convertToFiles ( results ) ;
final List < File > deduplicatedFiles = [ ] ;
for ( final file in files ) {
final id = file . uploadedFileID ;
if ( id ! = null & & id ! = - 1 & & uploadedFileIDs . contains ( id ) ) {
continue ;
}
uploadedFileIDs . add ( id ) ;
deduplicatedFiles . add ( file ) ;
}
2021-06-29 05:13:16 +00:00
return FileLoadResult ( deduplicatedFiles , files . length = = limit ) ;
2021-04-25 12:40:10 +00:00
}
2021-06-29 05:13:16 +00:00
Future < FileLoadResult > getFilesInCollection (
2021-04-20 20:11:39 +00:00
int collectionID , int startTime , int endTime ,
2021-05-08 17:46:48 +00:00
{ int limit , bool asc } ) async {
2021-04-20 20:11:39 +00:00
final db = await instance . database ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2021-04-20 20:11:39 +00:00
final results = await db . query (
table ,
where:
' $ columnCollectionID = ? AND $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnIsDeleted = 0 ' ,
whereArgs: [ collectionID , startTime , endTime ] ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-04-20 20:11:39 +00:00
limit: limit ,
) ;
final files = _convertToFiles ( results ) ;
_logger . info ( " Fetched " + files . length . toString ( ) + " files " ) ;
2021-06-29 05:13:16 +00:00
return FileLoadResult ( files , files . length = = limit ) ;
2021-04-20 20:11:39 +00:00
}
2021-06-29 05:13:16 +00:00
Future < FileLoadResult > getFilesInPath ( String path , int startTime , int endTime ,
2021-05-08 17:46:48 +00:00
{ int limit , bool asc } ) async {
2021-04-20 20:11:39 +00:00
final db = await instance . database ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2021-04-20 20:11:39 +00:00
final results = await db . query (
table ,
where:
' $ columnDeviceFolder = ? AND $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnLocalID IS NOT NULL AND $ columnIsDeleted = 0 ' ,
whereArgs: [ path , startTime , endTime ] ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-04-20 20:11:39 +00:00
groupBy: ' $ columnLocalID ' ,
limit: limit ,
) ;
2021-06-29 05:13:16 +00:00
final files = _convertToFiles ( results ) ;
return FileLoadResult ( files , files . length = = limit ) ;
2021-04-20 20:11:39 +00:00
}
2020-06-21 00:05:15 +00:00
Future < List < File > > getAllVideos ( ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where:
2020-08-09 22:34:59 +00:00
' $ columnLocalID IS NOT NULL AND $ columnFileType = 1 AND $ columnIsDeleted = 0 ' ,
2020-07-06 19:09:47 +00:00
orderBy: ' $ columnCreationTime DESC ' ,
2020-06-21 00:05:15 +00:00
) ;
return _convertToFiles ( results ) ;
}
2020-11-14 10:57:08 +00:00
Future < List < File > > getAllInPath ( String path ) async {
final db = await instance . database ;
final results = await db . query (
table ,
2020-11-26 11:38:49 +00:00
where:
' $ columnLocalID IS NOT NULL AND $ columnDeviceFolder = ? AND $ columnIsDeleted = 0 ' ,
2020-11-14 10:57:08 +00:00
whereArgs: [ path ] ,
orderBy: ' $ columnCreationTime DESC ' ,
groupBy: ' $ columnLocalID ' ,
) ;
return _convertToFiles ( results ) ;
}
2021-04-27 16:02:33 +00:00
Future < List < File > > getFilesCreatedWithinDurations (
List < List < int > > durations ) async {
2020-07-20 11:53:42 +00:00
final db = await instance . database ;
2021-04-27 16:02:33 +00:00
String whereClause = " " ;
for ( int index = 0 ; index < durations . length ; index + + ) {
whereClause + = " ( $ columnCreationTime > " +
durations [ index ] [ 0 ] . toString ( ) +
" AND $ columnCreationTime < " +
durations [ index ] [ 1 ] . toString ( ) +
" ) " ;
if ( index ! = durations . length - 1 ) {
whereClause + = " OR " ;
}
}
2020-07-20 11:53:42 +00:00
final results = await db . query (
table ,
2021-04-27 16:02:33 +00:00
where: whereClause + " AND $ columnIsDeleted = 0 " ,
2020-07-21 08:10:44 +00:00
orderBy: ' $ columnCreationTime ASC ' ,
2020-07-20 11:53:42 +00:00
) ;
return _convertToFiles ( results ) ;
}
2020-10-30 23:25:28 +00:00
Future < List < int > > getDeletedFileIDs ( ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2020-10-30 23:25:28 +00:00
final rows = await db . query (
2020-05-06 18:13:24 +00:00
table ,
2020-10-30 23:25:28 +00:00
columns: [ columnUploadedFileID ] ,
distinct: true ,
2020-05-06 18:13:24 +00:00
where: ' $ columnIsDeleted = 1 ' ,
2020-07-06 19:09:47 +00:00
orderBy: ' $ columnCreationTime DESC ' ,
2020-05-06 18:13:24 +00:00
) ;
2020-10-30 23:25:28 +00:00
final result = List < int > ( ) ;
for ( final row in rows ) {
result . add ( row [ columnUploadedFileID ] ) ;
}
return result ;
2020-03-27 16:07:55 +00:00
}
2020-09-17 18:48:25 +00:00
Future < List < File > > getFilesToBeUploadedWithinFolders (
Set < String > folders ) async {
2021-03-22 06:29:21 +00:00
if ( folders . isEmpty ) {
return [ ] ;
}
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2020-09-17 18:48:25 +00:00
String inParam = " " ;
for ( final folder in folders ) {
2021-06-04 19:05:13 +00:00
inParam + = " ' " + folder . replaceAll ( " ' " , " '' " ) + " ', " ;
2020-09-17 18:48:25 +00:00
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
2020-05-06 18:13:24 +00:00
final results = await db . query (
table ,
2020-09-17 18:48:25 +00:00
where:
2021-05-03 22:48:02 +00:00
' ( $ columnUploadedFileID IS NULL OR $ columnUploadedFileID IS -1) AND $ columnDeviceFolder IN ( $ inParam ) ' ,
2020-07-06 19:09:47 +00:00
orderBy: ' $ columnCreationTime DESC ' ,
2021-03-25 19:05:23 +00:00
groupBy: ' $ columnLocalID ' ,
2020-05-06 18:13:24 +00:00
) ;
2020-06-19 23:03:26 +00:00
return _convertToFiles ( results ) ;
2020-03-24 19:59:36 +00:00
}
2021-07-01 11:06:20 +00:00
Future < List < File > > getAllLocalFiles ( ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where:
' ( $ columnUploadedFileID IS NULL OR $ columnUploadedFileID IS -1) AND $ columnLocalID IS NOT NULL ' ,
orderBy: ' $ columnCreationTime DESC ' ,
groupBy: ' $ columnLocalID ' ,
) ;
return _convertToFiles ( results ) ;
}
2021-06-04 19:11:39 +00:00
Future < List < File > > getEditedRemoteFiles ( ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where:
' ( $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1) AND ( $ columnUploadedFileID IS NULL OR $ columnUploadedFileID IS -1) ' ,
orderBy: ' $ columnCreationTime DESC ' ,
groupBy: ' $ columnLocalID ' ,
) ;
return _convertToFiles ( results ) ;
}
2020-11-30 18:42:11 +00:00
Future < List < int > > getUploadedFileIDsToBeUpdated ( ) async {
final db = await instance . database ;
final rows = await db . query (
table ,
columns: [ columnUploadedFileID ] ,
where:
2021-05-03 22:48:02 +00:00
' ( $ columnLocalID IS NOT NULL AND ( $ columnUploadedFileID IS NOT NULL AND $ columnUploadedFileID IS NOT -1) AND $ columnUpdationTime IS NULL AND $ columnIsDeleted = 0) ' ,
2020-11-30 18:42:11 +00:00
orderBy: ' $ columnCreationTime DESC ' ,
distinct: true ,
) ;
final uploadedFileIDs = List < int > ( ) ;
for ( final row in rows ) {
uploadedFileIDs . add ( row [ columnUploadedFileID ] ) ;
}
return uploadedFileIDs ;
}
Future < File > getUploadedFileInAnyCollection ( int uploadedFileID ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where: ' $ columnUploadedFileID = ? ' ,
whereArgs: [
uploadedFileID ,
] ,
limit: 1 ,
) ;
if ( results . isEmpty ) {
return null ;
}
return _convertToFiles ( results ) [ 0 ] ;
}
2020-12-01 08:09:02 +00:00
Future < Set < String > > getExistingLocalFileIDs ( ) async {
2020-11-30 18:42:11 +00:00
final db = await instance . database ;
final rows = await db . query (
table ,
columns: [ columnLocalID ] ,
distinct: true ,
2020-12-01 08:09:02 +00:00
where: ' $ columnLocalID IS NOT NULL ' ,
2020-11-30 18:42:11 +00:00
) ;
final result = Set < String > ( ) ;
for ( final row in rows ) {
result . add ( row [ columnLocalID ] ) ;
}
return result ;
}
2021-03-25 19:05:23 +00:00
Future < int > getNumberOfUploadedFiles ( ) async {
final db = await instance . database ;
final rows = await db . query (
table ,
columns: [ columnUploadedFileID ] ,
where:
2021-05-03 22:48:02 +00:00
' ( $ columnLocalID IS NOT NULL AND ( $ columnUploadedFileID IS NOT NULL AND $ columnUploadedFileID IS NOT -1) AND $ columnUpdationTime IS NOT NULL AND $ columnIsDeleted = 0) ' ,
2021-03-25 19:05:23 +00:00
distinct: true ,
) ;
return rows . length ;
}
2020-11-30 18:42:11 +00:00
Future < int > updateUploadedFile (
String localID ,
String title ,
Location location ,
int creationTime ,
int modificationTime ,
int updationTime ,
) async {
final db = await instance . database ;
return await db . update (
table ,
{
columnTitle: title ,
columnLatitude: location . latitude ,
columnLongitude: location . longitude ,
columnCreationTime: creationTime ,
columnModificationTime: modificationTime ,
columnUpdationTime: updationTime ,
} ,
where: ' $ columnLocalID = ? ' ,
whereArgs: [ localID ] ,
) ;
}
2020-10-29 00:14:48 +00:00
Future < List < File > > getMatchingFiles (
2020-11-20 11:05:17 +00:00
String title ,
String deviceFolder ,
) async {
2020-05-17 18:42:03 +00:00
final db = await instance . database ;
2021-06-13 11:15:24 +00:00
final rows = await db . query (
table ,
where: ''' $ columnTitle =? AND $ columnDeviceFolder =? ''' ,
whereArgs: [
title ,
deviceFolder ,
] ,
) ;
2020-05-17 18:42:03 +00:00
if ( rows . isNotEmpty ) {
2020-10-29 00:14:48 +00:00
return _convertToFiles ( rows ) ;
2020-05-17 18:42:03 +00:00
} else {
2020-10-29 00:14:48 +00:00
return null ;
2020-05-17 18:42:03 +00:00
}
}
2020-10-24 23:39:46 +00:00
Future < int > update ( File file ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
return await db . update (
table ,
2020-10-24 23:39:46 +00:00
_getRowForFile ( file ) ,
2020-08-09 22:34:59 +00:00
where: ' $ columnGeneratedID = ? ' ,
2020-10-24 23:39:46 +00:00
whereArgs: [ file . generatedID ] ,
2020-05-06 18:13:24 +00:00
) ;
2020-03-24 19:59:36 +00:00
}
2020-11-30 18:42:11 +00:00
Future < int > updateUploadedFileAcrossCollections ( File file ) async {
final db = await instance . database ;
return await db . update (
table ,
_getRowForFileWithoutCollection ( file ) ,
where: ' $ columnUploadedFileID = ? ' ,
whereArgs: [ file . uploadedFileID ] ,
) ;
}
2021-04-27 20:49:00 +00:00
Future < int > delete ( int uploadedFileID ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2021-04-27 20:49:00 +00:00
return db . delete (
2020-05-06 18:13:24 +00:00
table ,
2020-10-30 23:25:28 +00:00
where: ' $ columnUploadedFileID =? ' ,
whereArgs: [ uploadedFileID ] ,
2020-05-06 18:13:24 +00:00
) ;
2020-04-12 12:38:49 +00:00
}
2021-04-27 20:49:00 +00:00
Future < int > deleteMultipleUploadedFiles ( List < int > uploadedFileIDs ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2021-04-27 22:17:19 +00:00
return await db . delete (
2020-05-06 18:13:24 +00:00
table ,
2021-04-27 22:17:19 +00:00
where: ' $ columnUploadedFileID IN ( ${ uploadedFileIDs . join ( ' , ' ) } ) ' ,
2020-05-06 18:13:24 +00:00
) ;
}
2020-11-01 07:37:23 +00:00
Future < int > deleteLocalFile ( String localID ) async {
final db = await instance . database ;
return db . delete (
table ,
where: ' $ columnLocalID =? ' ,
whereArgs: [ localID ] ,
) ;
}
2021-06-28 10:12:12 +00:00
Future < void > deleteLocalFiles ( List < String > localIDs ) async {
String inParam = " " ;
for ( final localID in localIDs ) {
inParam + = " ' " + localID + " ', " ;
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
final db = await instance . database ;
await db . rawQuery ( '''
UPDATE $table
SET $columnLocalID = NULL
WHERE $columnLocalID IN ( $inParam ) ;
''' );
}
Future < List < File > > getLocalFiles ( List < String > localIDs ) async {
String inParam = " " ;
for ( final localID in localIDs ) {
inParam + = " ' " + localID + " ', " ;
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
final db = await instance . database ;
final results = await db . query (
table ,
where: ' $ columnLocalID IN ( $ inParam ) ' ,
) ;
return _convertToFiles ( results ) ;
}
2020-10-23 14:59:51 +00:00
Future < int > deleteFromCollection ( int uploadedFileID , int collectionID ) async {
final db = await instance . database ;
return db . delete (
table ,
where: ' $ columnUploadedFileID = ? AND $ columnCollectionID = ? ' ,
whereArgs: [ uploadedFileID , collectionID ] ,
) ;
}
2020-10-31 12:48:41 +00:00
Future < int > deleteCollection ( int collectionID ) async {
final db = await instance . database ;
return db . delete (
table ,
where: ' $ columnCollectionID = ? ' ,
whereArgs: [ collectionID ] ,
) ;
}
2020-10-28 14:07:32 +00:00
Future < int > removeFromCollection ( int collectionID , List < int > fileIDs ) async {
2020-05-22 18:22:55 +00:00
final db = await instance . database ;
return db . delete (
table ,
2020-10-28 15:25:32 +00:00
where:
' $ columnCollectionID =? AND $ columnUploadedFileID IN ( ${ fileIDs . join ( ' , ' ) } ) ' ,
2020-10-28 14:07:32 +00:00
whereArgs: [ collectionID ] ,
2020-05-22 18:22:55 +00:00
) ;
}
2021-04-19 17:08:12 +00:00
Future < List < File > > getLatestLocalFiles ( ) async {
final db = await instance . database ;
final rows = await db . rawQuery ( '''
SELECT $table . *
FROM $table
INNER JOIN
(
SELECT $columnDeviceFolder , MAX ( $columnCreationTime ) AS max_creation_time
FROM $table
2021-04-27 22:27:54 +00:00
WHERE $table . $columnLocalID IS NOT NULL
2021-04-19 17:08:12 +00:00
GROUP BY $columnDeviceFolder
) latest_files
ON $table . $columnDeviceFolder = latest_files . $columnDeviceFolder
2021-04-27 22:27:54 +00:00
AND $table . $columnCreationTime = latest_files . max_creation_time ;
2021-04-19 17:08:12 +00:00
''' );
2021-04-27 22:27:54 +00:00
final files = _convertToFiles ( rows ) ;
// TODO: Do this de-duplication within the SQL Query
final folderMap = Map < String , File > ( ) ;
for ( final file in files ) {
if ( folderMap . containsKey ( file . deviceFolder ) ) {
if ( folderMap [ file . deviceFolder ] . updationTime < file . updationTime ) {
continue ;
}
}
folderMap [ file . deviceFolder ] = file ;
}
return folderMap . values . toList ( ) ;
2021-04-19 17:08:12 +00:00
}
2021-04-19 17:46:52 +00:00
Future < List < File > > getLatestCollectionFiles ( ) async {
final db = await instance . database ;
final rows = await db . rawQuery ( '''
SELECT $table . *
FROM $table
INNER JOIN
(
SELECT $columnCollectionID , MAX ( $columnCreationTime ) AS max_creation_time
FROM $table
2021-05-04 15:25:59 +00:00
WHERE ( $columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT - 1 )
2021-04-19 17:46:52 +00:00
GROUP BY $columnCollectionID
) latest_files
ON $table . $columnCollectionID = latest_files . $columnCollectionID
AND $table . $columnCreationTime = latest_files . max_creation_time ;
''' );
2021-04-19 17:49:13 +00:00
final files = _convertToFiles ( rows ) ;
// TODO: Do this de-duplication within the SQL Query
final collectionMap = Map < int , File > ( ) ;
for ( final file in files ) {
if ( collectionMap . containsKey ( file . collectionID ) ) {
if ( collectionMap [ file . collectionID ] . updationTime < file . updationTime ) {
continue ;
}
}
collectionMap [ file . collectionID ] = file ;
}
return collectionMap . values . toList ( ) ;
2021-04-19 17:46:52 +00:00
}
2020-10-29 00:50:39 +00:00
Future < File > getLastModifiedFileInCollection ( int collectionID ) async {
final db = await instance . database ;
final rows = await db . query (
table ,
2020-11-01 06:44:50 +00:00
where: ' $ columnCollectionID = ? AND $ columnIsDeleted = 0 ' ,
2020-10-29 00:50:39 +00:00
whereArgs: [ collectionID ] ,
orderBy: ' $ columnUpdationTime DESC ' ,
limit: 1 ,
) ;
if ( rows . isNotEmpty ) {
return _getFileFromRow ( rows [ 0 ] ) ;
} else {
return null ;
}
}
2020-10-30 20:17:06 +00:00
Future < bool > doesFileExistInCollection (
int uploadedFileID , int collectionID ) async {
final db = await instance . database ;
final rows = await db . query (
table ,
where: ' $ columnUploadedFileID = ? AND $ columnCollectionID = ? ' ,
whereArgs: [ uploadedFileID , collectionID ] ,
limit: 1 ,
) ;
return rows . isNotEmpty ;
}
2020-06-19 23:03:26 +00:00
List < File > _convertToFiles ( List < Map < String , dynamic > > results ) {
2021-04-25 12:26:55 +00:00
final List < File > files = [ ] ;
2020-07-20 11:49:00 +00:00
for ( final result in results ) {
2020-06-19 23:03:26 +00:00
files . add ( _getFileFromRow ( result ) ) ;
2020-03-28 13:56:06 +00:00
}
2020-06-19 23:03:26 +00:00
return files ;
2020-03-28 13:56:06 +00:00
}
2020-03-30 14:28:46 +00:00
2020-06-19 23:03:26 +00:00
Map < String , dynamic > _getRowForFile ( File file ) {
2020-07-20 11:49:00 +00:00
final row = new Map < String , dynamic > ( ) ;
2021-05-16 08:40:11 +00:00
if ( file . generatedID ! = null ) {
row [ columnGeneratedID ] = file . generatedID ;
}
2020-08-09 22:34:59 +00:00
row [ columnLocalID ] = file . localID ;
2021-05-03 22:48:02 +00:00
row [ columnUploadedFileID ] = file . uploadedFileID ? ? - 1 ;
2020-08-09 22:34:59 +00:00
row [ columnOwnerID ] = file . ownerID ;
2021-05-03 22:48:02 +00:00
row [ columnCollectionID ] = file . collectionID ? ? - 1 ;
2020-06-19 23:03:26 +00:00
row [ columnTitle ] = file . title ;
row [ columnDeviceFolder ] = file . deviceFolder ;
if ( file . location ! = null ) {
row [ columnLatitude ] = file . location . latitude ;
row [ columnLongitude ] = file . location . longitude ;
}
switch ( file . fileType ) {
case FileType . image:
row [ columnFileType ] = 0 ;
break ;
case FileType . video:
row [ columnFileType ] = 1 ;
break ;
default :
row [ columnFileType ] = - 1 ;
2020-06-06 20:08:45 +00:00
}
2020-07-06 19:09:47 +00:00
row [ columnCreationTime ] = file . creationTime ;
row [ columnModificationTime ] = file . modificationTime ;
row [ columnUpdationTime ] = file . updationTime ;
2020-10-06 22:55:59 +00:00
row [ columnEncryptedKey ] = file . encryptedKey ;
row [ columnKeyDecryptionNonce ] = file . keyDecryptionNonce ;
row [ columnFileDecryptionHeader ] = file . fileDecryptionHeader ;
row [ columnThumbnailDecryptionHeader ] = file . thumbnailDecryptionHeader ;
row [ columnMetadataDecryptionHeader ] = file . metadataDecryptionHeader ;
2020-03-30 14:28:46 +00:00
return row ;
}
2020-04-12 12:38:49 +00:00
2020-11-30 18:42:11 +00:00
Map < String , dynamic > _getRowForFileWithoutCollection ( File file ) {
final row = new Map < String , dynamic > ( ) ;
row [ columnLocalID ] = file . localID ;
2021-05-03 22:48:02 +00:00
row [ columnUploadedFileID ] = file . uploadedFileID ? ? - 1 ;
2020-11-30 18:42:11 +00:00
row [ columnOwnerID ] = file . ownerID ;
row [ columnTitle ] = file . title ;
row [ columnDeviceFolder ] = file . deviceFolder ;
if ( file . location ! = null ) {
row [ columnLatitude ] = file . location . latitude ;
row [ columnLongitude ] = file . location . longitude ;
}
switch ( file . fileType ) {
case FileType . image:
row [ columnFileType ] = 0 ;
break ;
case FileType . video:
row [ columnFileType ] = 1 ;
break ;
default :
row [ columnFileType ] = - 1 ;
}
row [ columnCreationTime ] = file . creationTime ;
row [ columnModificationTime ] = file . modificationTime ;
row [ columnUpdationTime ] = file . updationTime ;
row [ columnFileDecryptionHeader ] = file . fileDecryptionHeader ;
row [ columnThumbnailDecryptionHeader ] = file . thumbnailDecryptionHeader ;
row [ columnMetadataDecryptionHeader ] = file . metadataDecryptionHeader ;
return row ;
}
2020-06-19 23:03:26 +00:00
File _getFileFromRow ( Map < String , dynamic > row ) {
2020-07-20 11:49:00 +00:00
final file = File ( ) ;
2020-08-09 22:34:59 +00:00
file . generatedID = row [ columnGeneratedID ] ;
file . localID = row [ columnLocalID ] ;
2021-05-03 22:48:02 +00:00
file . uploadedFileID =
row [ columnUploadedFileID ] = = - 1 ? null : row [ columnUploadedFileID ] ;
2020-10-13 21:40:22 +00:00
file . ownerID = row [ columnOwnerID ] ;
2021-05-03 22:48:02 +00:00
file . collectionID =
row [ columnCollectionID ] = = - 1 ? null : row [ columnCollectionID ] ;
2020-06-19 23:03:26 +00:00
file . title = row [ columnTitle ] ;
file . deviceFolder = row [ columnDeviceFolder ] ;
2020-06-06 21:12:49 +00:00
if ( row [ columnLatitude ] ! = null & & row [ columnLongitude ] ! = null ) {
2020-06-19 23:03:26 +00:00
file . location = Location ( row [ columnLatitude ] , row [ columnLongitude ] ) ;
2020-06-06 21:12:49 +00:00
}
2020-06-19 23:03:26 +00:00
file . fileType = getFileType ( row [ columnFileType ] ) ;
2020-07-06 19:09:47 +00:00
file . creationTime = int . parse ( row [ columnCreationTime ] ) ;
file . modificationTime = int . parse ( row [ columnModificationTime ] ) ;
file . updationTime = row [ columnUpdationTime ] = = null
2020-04-12 12:38:49 +00:00
? - 1
2020-07-06 19:09:47 +00:00
: int . parse ( row [ columnUpdationTime ] ) ;
2020-10-06 22:55:59 +00:00
file . encryptedKey = row [ columnEncryptedKey ] ;
file . keyDecryptionNonce = row [ columnKeyDecryptionNonce ] ;
file . fileDecryptionHeader = row [ columnFileDecryptionHeader ] ;
file . thumbnailDecryptionHeader = row [ columnThumbnailDecryptionHeader ] ;
file . metadataDecryptionHeader = row [ columnMetadataDecryptionHeader ] ;
2020-06-19 23:03:26 +00:00
return file ;
2020-04-12 12:38:49 +00:00
}
2020-03-26 14:39:31 +00:00
}