Merge remote-tracking branch 'origin/master' into share_to_ente
This commit is contained in:
commit
77c2489714
28 changed files with 301 additions and 203 deletions
4
lib/core/cache/thumbnail_cache.dart
vendored
4
lib/core/cache/thumbnail_cache.dart
vendored
|
@ -10,7 +10,7 @@ class ThumbnailLruCache {
|
|||
static Uint8List get(File photo, [int size]) {
|
||||
return _map.get(photo.generatedID.toString() +
|
||||
"_" +
|
||||
(size != null ? size.toString() : THUMBNAIL_LARGE_SIZE.toString()));
|
||||
(size != null ? size.toString() : kThumbnailLargeSize.toString()));
|
||||
}
|
||||
|
||||
static void put(
|
||||
|
@ -21,7 +21,7 @@ class ThumbnailLruCache {
|
|||
_map.put(
|
||||
photo.generatedID.toString() +
|
||||
"_" +
|
||||
(size != null ? size.toString() : THUMBNAIL_LARGE_SIZE.toString()),
|
||||
(size != null ? size.toString() : kThumbnailLargeSize.toString()),
|
||||
imageData);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ class Configuration {
|
|||
static const tokenKey = "token";
|
||||
static const encryptedTokenKey = "encrypted_token";
|
||||
static const userIDKey = "user_id";
|
||||
static const hasMigratedSecureStorageToFirstUnlockKey =
|
||||
"has_migrated_secure_storage_to_first_unlock";
|
||||
|
||||
final kTempFolderDeletionTimeBuffer = Duration(days: 1).inMicroseconds;
|
||||
|
||||
|
@ -61,12 +63,15 @@ class Configuration {
|
|||
String _thumbnailCacheDirectory;
|
||||
String _volatilePassword;
|
||||
|
||||
final _secureStorageOptionsIOS =
|
||||
IOSOptions(accessibility: IOSAccessibility.first_unlock_this_device);
|
||||
|
||||
Future<void> init() async {
|
||||
_preferences = await SharedPreferences.getInstance();
|
||||
_secureStorage = FlutterSecureStorage();
|
||||
_documentsDirectory = (await getApplicationDocumentsDirectory()).path;
|
||||
_tempDirectory = _documentsDirectory + "/temp/";
|
||||
final tempDirectory = new io.Directory(_tempDirectory);
|
||||
final tempDirectory = io.Directory(_tempDirectory);
|
||||
try {
|
||||
final currentTime = DateTime.now().microsecondsSinceEpoch;
|
||||
if (tempDirectory.existsSync() &&
|
||||
|
@ -86,10 +91,17 @@ class Configuration {
|
|||
(await getTemporaryDirectory()).path + "/thumbnail-cache";
|
||||
io.Directory(_thumbnailCacheDirectory).createSync(recursive: true);
|
||||
if (!_preferences.containsKey(tokenKey)) {
|
||||
await _secureStorage.deleteAll();
|
||||
await _secureStorage.deleteAll(iOptions: _secureStorageOptionsIOS);
|
||||
} else {
|
||||
_key = await _secureStorage.read(key: keyKey);
|
||||
_secretKey = await _secureStorage.read(key: secretKeyKey);
|
||||
_key = await _secureStorage.read(
|
||||
key: keyKey,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
_secretKey = await _secureStorage.read(
|
||||
key: secretKeyKey,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
await _migrateSecurityStorageToFirstUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +115,7 @@ class Configuration {
|
|||
}
|
||||
}
|
||||
await _preferences.clear();
|
||||
await _secureStorage.deleteAll();
|
||||
await _secureStorage.deleteAll(iOptions: _secureStorageOptionsIOS);
|
||||
_key = null;
|
||||
_cachedToken = null;
|
||||
_secretKey = null;
|
||||
|
@ -197,7 +209,7 @@ class Configuration {
|
|||
attributes.memLimit,
|
||||
attributes.opsLimit,
|
||||
);
|
||||
var key;
|
||||
Uint8List key;
|
||||
try {
|
||||
key = CryptoUtil.decryptSync(Sodium.base642bin(attributes.encryptedKey),
|
||||
kek, Sodium.base642bin(attributes.keyDecryptionNonce));
|
||||
|
@ -241,7 +253,7 @@ class Configuration {
|
|||
|
||||
Future<void> recover(String recoveryKey) async {
|
||||
final keyAttributes = getKeyAttributes();
|
||||
var masterKey;
|
||||
Uint8List masterKey;
|
||||
try {
|
||||
masterKey = await CryptoUtil.decrypt(
|
||||
Sodium.base642bin(keyAttributes.masterKeyEncryptedWithRecoveryKey),
|
||||
|
@ -249,7 +261,7 @@ class Configuration {
|
|||
Sodium.base642bin(keyAttributes.masterKeyDecryptionNonce));
|
||||
} catch (e) {
|
||||
_logger.severe(e);
|
||||
throw e;
|
||||
rethrow;
|
||||
}
|
||||
await setKey(Sodium.bin2base64(masterKey));
|
||||
}
|
||||
|
@ -262,9 +274,7 @@ class Configuration {
|
|||
}
|
||||
|
||||
String getToken() {
|
||||
if (_cachedToken == null) {
|
||||
_cachedToken = _preferences.getString(tokenKey);
|
||||
}
|
||||
_cachedToken ??= _preferences.getString(tokenKey);
|
||||
return _cachedToken;
|
||||
}
|
||||
|
||||
|
@ -309,7 +319,7 @@ class Configuration {
|
|||
if (_preferences.containsKey(foldersToBackUpKey)) {
|
||||
return _preferences.getStringList(foldersToBackUpKey).toSet();
|
||||
} else {
|
||||
return Set<String>();
|
||||
return <String>{};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,8 +352,7 @@ class Configuration {
|
|||
}
|
||||
|
||||
Future<void> setKeyAttributes(KeyAttributes attributes) async {
|
||||
await _preferences.setString(
|
||||
keyAttributesKey, attributes == null ? null : attributes.toJson());
|
||||
await _preferences.setString(keyAttributesKey, attributes?.toJson());
|
||||
}
|
||||
|
||||
KeyAttributes getKeyAttributes() {
|
||||
|
@ -358,18 +367,32 @@ class Configuration {
|
|||
Future<void> setKey(String key) async {
|
||||
_key = key;
|
||||
if (key == null) {
|
||||
await _secureStorage.delete(key: keyKey);
|
||||
await _secureStorage.delete(
|
||||
key: keyKey,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
} else {
|
||||
await _secureStorage.write(key: keyKey, value: key);
|
||||
await _secureStorage.write(
|
||||
key: keyKey,
|
||||
value: key,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setSecretKey(String secretKey) async {
|
||||
_secretKey = secretKey;
|
||||
if (secretKey == null) {
|
||||
await _secureStorage.delete(key: secretKeyKey);
|
||||
await _secureStorage.delete(
|
||||
key: secretKeyKey,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
} else {
|
||||
await _secureStorage.write(key: secretKeyKey, value: secretKey);
|
||||
await _secureStorage.write(
|
||||
key: secretKeyKey,
|
||||
value: secretKey,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,4 +500,25 @@ class Configuration {
|
|||
bool hasSkippedBackupFolderSelection() {
|
||||
return _preferences.getBool(keyHasSkippedBackupFolderSelection) ?? false;
|
||||
}
|
||||
|
||||
Future<void> _migrateSecurityStorageToFirstUnlock() async {
|
||||
final hasMigratedSecureStorageToFirstUnlock =
|
||||
_preferences.getBool(hasMigratedSecureStorageToFirstUnlockKey) ?? false;
|
||||
if (!hasMigratedSecureStorageToFirstUnlock &&
|
||||
_key != null &&
|
||||
_secretKey != null) {
|
||||
await _secureStorage.write(
|
||||
key: keyKey,
|
||||
value: _key,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
await _secureStorage.write(
|
||||
key: secretKeyKey,
|
||||
value: _secretKey,
|
||||
iOptions: _secureStorageOptionsIOS,
|
||||
);
|
||||
await _preferences.setBool(
|
||||
hasMigratedSecureStorageToFirstUnlockKey, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
const int THUMBNAIL_SMALL_SIZE = 256;
|
||||
const int THUMBNAIL_QUALITY = 50;
|
||||
const int THUMBNAIL_LARGE_SIZE = 512;
|
||||
const int COMPRESSED_THUMBNAIL_RESOLUTION = 1080;
|
||||
const int THUMBNAIL_DATA_LIMIT = 100 * 1024;
|
||||
const String SENTRY_DSN =
|
||||
const int kThumbnailSmallSize = 256;
|
||||
const int kThumbnailQuality = 50;
|
||||
const int kThumbnailLargeSize = 512;
|
||||
const int kCompressedThumbnailResolution = 1080;
|
||||
const int kThumbnailDataLimit = 100 * 1024;
|
||||
const String kSentryDSN =
|
||||
"https://93b8ea6f54f442dc8408ebccdff6fe7a@errors.ente.io/2";
|
||||
const String SENTRY_DEBUG_DSN =
|
||||
const String kSentryDebugDSN =
|
||||
"https://b31c8af8384a4ce980509b8f592a67eb@errors.ente.io/3";
|
||||
const String ROADMAP_URL = "https://roadmap.ente.io";
|
||||
const int MICRO_SECONDS_IN_DAY = 86400000000;
|
||||
const int ANDROID_11_SDK_INT = 30;
|
||||
const String kRoadmapURL = "https://roadmap.ente.io";
|
||||
const int kMicroSecondsInDay = 86400000000;
|
||||
const int kAndroid11SDKINT = 30;
|
||||
const int kGalleryLoadStartTime = -8000000000000000; // Wednesday, March 6, 1748
|
||||
|
|
|
@ -39,14 +39,14 @@ class CollectionsDB {
|
|||
CollectionsDB._privateConstructor();
|
||||
static final CollectionsDB instance = CollectionsDB._privateConstructor();
|
||||
|
||||
static Database _database;
|
||||
static Future<Database> _dbFuture;
|
||||
|
||||
Future<Database> get database async {
|
||||
if (_database != null) return _database;
|
||||
_database = await _initDatabase();
|
||||
return _database;
|
||||
_dbFuture ??= _initDatabase();
|
||||
return _dbFuture;
|
||||
}
|
||||
|
||||
_initDatabase() async {
|
||||
Future<Database> _initDatabase() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, _databaseName);
|
||||
return await openDatabaseWithMigration(path, dbConfig);
|
||||
|
|
|
@ -46,28 +46,34 @@ class FilesDB {
|
|||
static final columnThumbnailDecryptionHeader = 'thumbnail_decryption_header';
|
||||
static final columnMetadataDecryptionHeader = 'metadata_decryption_header';
|
||||
|
||||
static final intitialScript = [...createTable(table), ...addIndex()];
|
||||
static final migrationScripts = [...alterDeviceFolderToAllowNULL()];
|
||||
static final initializationScript = [...createTable(table)];
|
||||
static final migrationScripts = [
|
||||
...alterDeviceFolderToAllowNULL(),
|
||||
...alterTimestampColumnTypes(),
|
||||
...addIndices(),
|
||||
];
|
||||
|
||||
final dbConfig = MigrationConfig(
|
||||
initializationScript: intitialScript, migrationScripts: migrationScripts);
|
||||
initializationScript: initializationScript,
|
||||
migrationScripts: migrationScripts);
|
||||
// make this a singleton class
|
||||
FilesDB._privateConstructor();
|
||||
static final FilesDB instance = FilesDB._privateConstructor();
|
||||
|
||||
// only have a single app-wide reference to the database
|
||||
static Database _database;
|
||||
static Future<Database> _dbFuture;
|
||||
|
||||
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;
|
||||
_dbFuture ??= _initDatabase();
|
||||
return _dbFuture;
|
||||
}
|
||||
|
||||
// this opens the database (and creates it if it doesn't exist)
|
||||
_initDatabase() async {
|
||||
Future<Database> _initDatabase() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, _databaseName);
|
||||
_logger.info("DB path " + path);
|
||||
return await openDatabaseWithMigration(path, dbConfig);
|
||||
}
|
||||
|
||||
|
@ -101,13 +107,19 @@ class FilesDB {
|
|||
];
|
||||
}
|
||||
|
||||
static List<String> addIndex() {
|
||||
static List<String> addIndices() {
|
||||
return [
|
||||
'''
|
||||
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);
|
||||
CREATE INDEX IF NOT EXISTS collection_id_index ON $table($columnCollectionID);
|
||||
''',
|
||||
'''
|
||||
CREATE INDEX IF NOT EXISTS device_folder_index ON $table($columnDeviceFolder);
|
||||
''',
|
||||
'''
|
||||
CREATE INDEX IF NOT EXISTS creation_time_index ON $table($columnCreationTime);
|
||||
''',
|
||||
'''
|
||||
CREATE INDEX IF NOT EXISTS updation_time_index ON $table($columnUpdationTime);
|
||||
'''
|
||||
];
|
||||
}
|
||||
|
@ -128,6 +140,67 @@ class FilesDB {
|
|||
];
|
||||
}
|
||||
|
||||
static List<String> alterTimestampColumnTypes() {
|
||||
return [
|
||||
'''
|
||||
DROP TABLE IF EXISTS $tempTable;
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE $tempTable (
|
||||
$columnGeneratedID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
$columnLocalID TEXT,
|
||||
$columnUploadedFileID INTEGER DEFAULT -1,
|
||||
$columnOwnerID INTEGER,
|
||||
$columnCollectionID INTEGER DEFAULT -1,
|
||||
$columnTitle TEXT NOT NULL,
|
||||
$columnDeviceFolder TEXT,
|
||||
$columnLatitude REAL,
|
||||
$columnLongitude REAL,
|
||||
$columnFileType INTEGER,
|
||||
$columnModificationTime INTEGER NOT NULL,
|
||||
$columnEncryptedKey TEXT,
|
||||
$columnKeyDecryptionNonce TEXT,
|
||||
$columnFileDecryptionHeader TEXT,
|
||||
$columnThumbnailDecryptionHeader TEXT,
|
||||
$columnMetadataDecryptionHeader TEXT,
|
||||
$columnCreationTime INTEGER NOT NULL,
|
||||
$columnUpdationTime INTEGER,
|
||||
UNIQUE($columnLocalID, $columnUploadedFileID, $columnCollectionID)
|
||||
);
|
||||
''',
|
||||
'''
|
||||
INSERT INTO $tempTable
|
||||
SELECT
|
||||
$columnGeneratedID,
|
||||
$columnLocalID,
|
||||
$columnUploadedFileID,
|
||||
$columnOwnerID,
|
||||
$columnCollectionID,
|
||||
$columnTitle,
|
||||
$columnDeviceFolder,
|
||||
$columnLatitude,
|
||||
$columnLongitude,
|
||||
$columnFileType,
|
||||
CAST($columnModificationTime AS INTEGER),
|
||||
$columnEncryptedKey,
|
||||
$columnKeyDecryptionNonce,
|
||||
$columnFileDecryptionHeader,
|
||||
$columnThumbnailDecryptionHeader,
|
||||
$columnMetadataDecryptionHeader,
|
||||
CAST($columnCreationTime AS INTEGER),
|
||||
CAST($columnUpdationTime AS INTEGER)
|
||||
FROM $table;
|
||||
''',
|
||||
'''
|
||||
DROP TABLE $table;
|
||||
''',
|
||||
'''
|
||||
ALTER TABLE $tempTable
|
||||
RENAME TO $table;
|
||||
''',
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> clearTable() async {
|
||||
final db = await instance.database;
|
||||
await db.delete(table);
|
||||
|
@ -208,7 +281,7 @@ class FilesDB {
|
|||
collectionID,
|
||||
],
|
||||
);
|
||||
final ids = Set<int>();
|
||||
final ids = <int>{};
|
||||
for (final result in results) {
|
||||
ids.add(result[columnUploadedFileID]);
|
||||
}
|
||||
|
@ -223,8 +296,8 @@ class FilesDB {
|
|||
where:
|
||||
'$columnLocalID IS NOT NULL AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID IS NOT -1)',
|
||||
);
|
||||
final localIDs = Set<String>();
|
||||
final uploadedIDs = Set<int>();
|
||||
final localIDs = <String>{};
|
||||
final uploadedIDs = <int>{};
|
||||
for (final result in results) {
|
||||
localIDs.add(result[columnLocalID]);
|
||||
uploadedIDs.add(result[columnUploadedFileID]);
|
||||
|
@ -239,7 +312,7 @@ class FilesDB {
|
|||
final results = await db.query(
|
||||
table,
|
||||
where:
|
||||
'$columnCreationTime >= ? AND $columnCreationTime <= ? AND $columnIsDeleted = 0 AND ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1)',
|
||||
'$columnCreationTime >= ? AND $columnCreationTime <= ? AND ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1)',
|
||||
whereArgs: [startTime, endTime],
|
||||
orderBy:
|
||||
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
|
||||
|
@ -256,7 +329,7 @@ class FilesDB {
|
|||
final results = await db.query(
|
||||
table,
|
||||
where:
|
||||
'$columnCreationTime >= ? AND $columnCreationTime <= ? AND $columnIsDeleted = 0 AND ($columnLocalID IS NOT NULL OR ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1))',
|
||||
'$columnCreationTime >= ? AND $columnCreationTime <= ? AND ($columnLocalID IS NOT NULL OR ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1))',
|
||||
whereArgs: [startTime, endTime],
|
||||
orderBy:
|
||||
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
|
||||
|
@ -279,13 +352,13 @@ class FilesDB {
|
|||
final results = await db.query(
|
||||
table,
|
||||
where:
|
||||
'$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))',
|
||||
'$columnCreationTime >= ? AND $columnCreationTime <= ? AND (($columnLocalID IS NOT NULL AND $columnDeviceFolder IN ($inParam)) OR ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1))',
|
||||
whereArgs: [startTime, endTime],
|
||||
orderBy:
|
||||
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
|
||||
limit: limit,
|
||||
);
|
||||
final uploadedFileIDs = Set<int>();
|
||||
final uploadedFileIDs = <int>{};
|
||||
final files = _convertToFiles(results);
|
||||
final List<File> deduplicatedFiles = [];
|
||||
for (final file in files) {
|
||||
|
@ -307,7 +380,7 @@ class FilesDB {
|
|||
final results = await db.query(
|
||||
table,
|
||||
where:
|
||||
'$columnCollectionID = ? AND $columnCreationTime >= ? AND $columnCreationTime <= ? AND $columnIsDeleted = 0',
|
||||
'$columnCollectionID = ? AND $columnCreationTime >= ? AND $columnCreationTime <= ?',
|
||||
whereArgs: [collectionID, startTime, endTime],
|
||||
orderBy:
|
||||
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
|
||||
|
@ -325,11 +398,11 @@ class FilesDB {
|
|||
final results = await db.query(
|
||||
table,
|
||||
where:
|
||||
'$columnDeviceFolder = ? AND $columnCreationTime >= ? AND $columnCreationTime <= ? AND $columnLocalID IS NOT NULL AND $columnIsDeleted = 0',
|
||||
'$columnDeviceFolder = ? AND $columnCreationTime >= ? AND $columnCreationTime <= ? AND $columnLocalID IS NOT NULL',
|
||||
whereArgs: [path, startTime, endTime],
|
||||
orderBy:
|
||||
'$columnCreationTime ' + order + ', $columnModificationTime ' + order,
|
||||
groupBy: '$columnLocalID',
|
||||
groupBy: columnLocalID,
|
||||
limit: limit,
|
||||
);
|
||||
final files = _convertToFiles(results);
|
||||
|
@ -340,8 +413,7 @@ class FilesDB {
|
|||
final db = await instance.database;
|
||||
final results = await db.query(
|
||||
table,
|
||||
where:
|
||||
'$columnLocalID IS NOT NULL AND $columnFileType = 1 AND $columnIsDeleted = 0',
|
||||
where: '$columnLocalID IS NOT NULL AND $columnFileType = 1',
|
||||
orderBy: '$columnCreationTime DESC',
|
||||
);
|
||||
return _convertToFiles(results);
|
||||
|
@ -351,11 +423,10 @@ class FilesDB {
|
|||
final db = await instance.database;
|
||||
final results = await db.query(
|
||||
table,
|
||||
where:
|
||||
'$columnLocalID IS NOT NULL AND $columnDeviceFolder = ? AND $columnIsDeleted = 0',
|
||||
where: '$columnLocalID IS NOT NULL AND $columnDeviceFolder = ?',
|
||||
whereArgs: [path],
|
||||
orderBy: '$columnCreationTime DESC',
|
||||
groupBy: '$columnLocalID',
|
||||
groupBy: columnLocalID,
|
||||
);
|
||||
return _convertToFiles(results);
|
||||
}
|
||||
|
@ -376,28 +447,12 @@ class FilesDB {
|
|||
}
|
||||
final results = await db.query(
|
||||
table,
|
||||
where: whereClause + " AND $columnIsDeleted = 0",
|
||||
where: whereClause,
|
||||
orderBy: '$columnCreationTime ASC',
|
||||
);
|
||||
return _convertToFiles(results);
|
||||
}
|
||||
|
||||
Future<List<int>> getDeletedFileIDs() async {
|
||||
final db = await instance.database;
|
||||
final rows = await db.query(
|
||||
table,
|
||||
columns: [columnUploadedFileID],
|
||||
distinct: true,
|
||||
where: '$columnIsDeleted = 1',
|
||||
orderBy: '$columnCreationTime DESC',
|
||||
);
|
||||
final result = List<int>();
|
||||
for (final row in rows) {
|
||||
result.add(row[columnUploadedFileID]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<List<File>> getFilesToBeUploadedWithinFolders(
|
||||
Set<String> folders) async {
|
||||
if (folders.isEmpty) {
|
||||
|
@ -414,7 +469,7 @@ class FilesDB {
|
|||
where:
|
||||
'($columnUploadedFileID IS NULL OR $columnUploadedFileID IS -1) AND $columnDeviceFolder IN ($inParam)',
|
||||
orderBy: '$columnCreationTime DESC',
|
||||
groupBy: '$columnLocalID',
|
||||
groupBy: columnLocalID,
|
||||
);
|
||||
return _convertToFiles(results);
|
||||
}
|
||||
|
@ -426,7 +481,7 @@ class FilesDB {
|
|||
where:
|
||||
'($columnUploadedFileID IS NULL OR $columnUploadedFileID IS -1) AND $columnLocalID IS NOT NULL',
|
||||
orderBy: '$columnCreationTime DESC',
|
||||
groupBy: '$columnLocalID',
|
||||
groupBy: columnLocalID,
|
||||
);
|
||||
return _convertToFiles(results);
|
||||
}
|
||||
|
@ -438,7 +493,7 @@ class FilesDB {
|
|||
where:
|
||||
'($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1) AND ($columnUploadedFileID IS NULL OR $columnUploadedFileID IS -1)',
|
||||
orderBy: '$columnCreationTime DESC',
|
||||
groupBy: '$columnLocalID',
|
||||
groupBy: columnLocalID,
|
||||
);
|
||||
return _convertToFiles(results);
|
||||
}
|
||||
|
@ -449,11 +504,11 @@ class FilesDB {
|
|||
table,
|
||||
columns: [columnUploadedFileID],
|
||||
where:
|
||||
'($columnLocalID IS NOT NULL AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID IS NOT -1) AND $columnUpdationTime IS NULL AND $columnIsDeleted = 0)',
|
||||
'($columnLocalID IS NOT NULL AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID IS NOT -1) AND $columnUpdationTime IS NULL)',
|
||||
orderBy: '$columnCreationTime DESC',
|
||||
distinct: true,
|
||||
);
|
||||
final uploadedFileIDs = List<int>();
|
||||
final uploadedFileIDs = <int>[];
|
||||
for (final row in rows) {
|
||||
uploadedFileIDs.add(row[columnUploadedFileID]);
|
||||
}
|
||||
|
@ -484,7 +539,7 @@ class FilesDB {
|
|||
distinct: true,
|
||||
where: '$columnLocalID IS NOT NULL',
|
||||
);
|
||||
final result = Set<String>();
|
||||
final result = <String>{};
|
||||
for (final row in rows) {
|
||||
result.add(row[columnLocalID]);
|
||||
}
|
||||
|
@ -497,7 +552,7 @@ class FilesDB {
|
|||
table,
|
||||
columns: [columnUploadedFileID],
|
||||
where:
|
||||
'($columnLocalID IS NOT NULL AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID IS NOT -1) AND $columnUpdationTime IS NOT NULL AND $columnIsDeleted = 0)',
|
||||
'($columnLocalID IS NOT NULL AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID IS NOT -1) AND $columnUpdationTime IS NOT NULL)',
|
||||
distinct: true,
|
||||
);
|
||||
return rows.length;
|
||||
|
@ -666,7 +721,7 @@ class FilesDB {
|
|||
''');
|
||||
final files = _convertToFiles(rows);
|
||||
// TODO: Do this de-duplication within the SQL Query
|
||||
final folderMap = Map<String, File>();
|
||||
final folderMap = <String, File>{};
|
||||
for (final file in files) {
|
||||
if (folderMap.containsKey(file.deviceFolder)) {
|
||||
if (folderMap[file.deviceFolder].updationTime < file.updationTime) {
|
||||
|
@ -695,7 +750,7 @@ class FilesDB {
|
|||
''');
|
||||
final files = _convertToFiles(rows);
|
||||
// TODO: Do this de-duplication within the SQL Query
|
||||
final collectionMap = Map<int, File>();
|
||||
final collectionMap = <int, File>{};
|
||||
for (final file in files) {
|
||||
if (collectionMap.containsKey(file.collectionID)) {
|
||||
if (collectionMap[file.collectionID].updationTime < file.updationTime) {
|
||||
|
@ -711,7 +766,7 @@ class FilesDB {
|
|||
final db = await instance.database;
|
||||
final rows = await db.query(
|
||||
table,
|
||||
where: '$columnCollectionID = ? AND $columnIsDeleted = 0',
|
||||
where: '$columnCollectionID = ?',
|
||||
whereArgs: [collectionID],
|
||||
orderBy: '$columnUpdationTime DESC',
|
||||
limit: 1,
|
||||
|
@ -731,7 +786,7 @@ class FilesDB {
|
|||
WHERE $columnLocalID IS NOT NULL
|
||||
GROUP BY $columnDeviceFolder
|
||||
''');
|
||||
final result = Map<String, int>();
|
||||
final result = <String, int>{};
|
||||
for (final row in rows) {
|
||||
result[row[columnDeviceFolder]] = row["count"];
|
||||
}
|
||||
|
@ -759,7 +814,7 @@ class FilesDB {
|
|||
}
|
||||
|
||||
Map<String, dynamic> _getRowForFile(File file) {
|
||||
final row = new Map<String, dynamic>();
|
||||
final row = <String, dynamic>{};
|
||||
if (file.generatedID != null) {
|
||||
row[columnGeneratedID] = file.generatedID;
|
||||
}
|
||||
|
@ -795,7 +850,7 @@ class FilesDB {
|
|||
}
|
||||
|
||||
Map<String, dynamic> _getRowForFileWithoutCollection(File file) {
|
||||
final row = new Map<String, dynamic>();
|
||||
final row = <String, dynamic>{};
|
||||
row[columnLocalID] = file.localID;
|
||||
row[columnUploadedFileID] = file.uploadedFileID ?? -1;
|
||||
row[columnOwnerID] = file.ownerID;
|
||||
|
@ -839,11 +894,9 @@ class FilesDB {
|
|||
file.location = Location(row[columnLatitude], row[columnLongitude]);
|
||||
}
|
||||
file.fileType = getFileType(row[columnFileType]);
|
||||
file.creationTime = int.parse(row[columnCreationTime]);
|
||||
file.modificationTime = int.parse(row[columnModificationTime]);
|
||||
file.updationTime = row[columnUpdationTime] == null
|
||||
? -1
|
||||
: int.parse(row[columnUpdationTime]);
|
||||
file.creationTime = row[columnCreationTime];
|
||||
file.modificationTime = row[columnModificationTime];
|
||||
file.updationTime = row[columnUpdationTime] ?? -1;
|
||||
file.encryptedKey = row[columnEncryptedKey];
|
||||
file.keyDecryptionNonce = row[columnKeyDecryptionNonce];
|
||||
file.fileDecryptionHeader = row[columnFileDecryptionHeader];
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'dart:async';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:photos/models/memory.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class MemoriesDB {
|
||||
static final _databaseName = "ente.memories.db";
|
||||
|
@ -18,14 +18,13 @@ class MemoriesDB {
|
|||
MemoriesDB._privateConstructor();
|
||||
static final MemoriesDB instance = MemoriesDB._privateConstructor();
|
||||
|
||||
static Database _database;
|
||||
static Future<Database> _dbFuture;
|
||||
Future<Database> get database async {
|
||||
if (_database != null) return _database;
|
||||
_database = await _initDatabase();
|
||||
return _database;
|
||||
_dbFuture ??= _initDatabase();
|
||||
return _dbFuture;
|
||||
}
|
||||
|
||||
_initDatabase() async {
|
||||
Future<Database> _initDatabase() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, _databaseName);
|
||||
return await openDatabase(
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'dart:async';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:photos/models/public_key.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class PublicKeysDB {
|
||||
static final _databaseName = "ente.public_keys.db";
|
||||
|
@ -18,14 +18,14 @@ class PublicKeysDB {
|
|||
PublicKeysDB._privateConstructor();
|
||||
static final PublicKeysDB instance = PublicKeysDB._privateConstructor();
|
||||
|
||||
static Database _database;
|
||||
static Future<Database> _dbFuture;
|
||||
|
||||
Future<Database> get database async {
|
||||
if (_database != null) return _database;
|
||||
_database = await _initDatabase();
|
||||
return _database;
|
||||
_dbFuture ??= _initDatabase();
|
||||
return _dbFuture;
|
||||
}
|
||||
|
||||
_initDatabase() async {
|
||||
Future<Database> _initDatabase() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, _databaseName);
|
||||
return await openDatabase(
|
||||
|
|
|
@ -2,8 +2,8 @@ import 'dart:async';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
class UploadLocksDB {
|
||||
static const _databaseName = "ente.upload_locks.db";
|
||||
|
@ -17,14 +17,13 @@ class UploadLocksDB {
|
|||
UploadLocksDB._privateConstructor();
|
||||
static final UploadLocksDB instance = UploadLocksDB._privateConstructor();
|
||||
|
||||
static Database _database;
|
||||
static Future<Database> _dbFuture;
|
||||
Future<Database> get database async {
|
||||
if (_database != null) return _database;
|
||||
_database = await _initDatabase();
|
||||
return _database;
|
||||
_dbFuture ??= _initDatabase();
|
||||
return _dbFuture;
|
||||
}
|
||||
|
||||
_initDatabase() async {
|
||||
Future<Database> _initDatabase() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, _databaseName);
|
||||
return await openDatabase(
|
||||
|
|
|
@ -5,9 +5,10 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/network.dart';
|
||||
import 'package:photos/db/upload_locks_db.dart';
|
||||
import 'package:photos/services/billing_service.dart';
|
||||
|
@ -25,7 +26,6 @@ import 'package:photos/utils/crypto_util.dart';
|
|||
import 'package:photos/utils/file_uploader.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:super_logging/super_logging.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final _logger = Logger("main");
|
||||
|
||||
|
@ -166,7 +166,7 @@ Future _runWithLogs(Function() function, {String prefix = ""}) async {
|
|||
body: function,
|
||||
logDirPath: (await getTemporaryDirectory()).path + "/logs",
|
||||
maxLogFiles: 5,
|
||||
sentryDsn: kDebugMode ? SENTRY_DEBUG_DSN : SENTRY_DSN,
|
||||
sentryDsn: kDebugMode ? kSentryDebugDSN : kSentryDSN,
|
||||
enableInDebugMode: true,
|
||||
prefix: prefix,
|
||||
));
|
||||
|
|
|
@ -134,10 +134,16 @@ class File {
|
|||
return localID == null && uploadedFileID != null;
|
||||
}
|
||||
|
||||
|
||||
bool isCachedInAppSandbox() {
|
||||
return localID != null && localID.startsWith("ente-upload-cache");
|
||||
}
|
||||
|
||||
bool hasLocation() {
|
||||
return location != null &&
|
||||
(location.longitude != 0 || location.latitude != 0);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''File(generatedId: $generatedID, uploadedFileId: $uploadedFileID,
|
||||
|
|
|
@ -35,9 +35,9 @@ class CollectionsService {
|
|||
SharedPreferences _prefs;
|
||||
Future<List<File>> _cachedLatestFiles;
|
||||
final _dio = Network.instance.getDio();
|
||||
final _localCollections = Map<String, Collection>();
|
||||
final _collectionIDToCollections = Map<int, Collection>();
|
||||
final _cachedKeys = Map<int, Uint8List>();
|
||||
final _localCollections = <String, Collection>{};
|
||||
final _collectionIDToCollections = <int, Collection>{};
|
||||
final _cachedKeys = <int, Uint8List>{};
|
||||
|
||||
CollectionsService._privateConstructor() {
|
||||
_db = CollectionsDB.instance;
|
||||
|
@ -72,7 +72,7 @@ class CollectionsService {
|
|||
// Might not have synced the collection fully
|
||||
final fetchedCollections =
|
||||
await _fetchCollections(lastCollectionUpdationTime ?? 0);
|
||||
final updatedCollections = List<Collection>();
|
||||
final updatedCollections = <Collection>[];
|
||||
int maxUpdationTime = lastCollectionUpdationTime;
|
||||
for (final collection in fetchedCollections) {
|
||||
if (collection.isDeleted) {
|
||||
|
@ -100,7 +100,7 @@ class CollectionsService {
|
|||
return collections;
|
||||
}
|
||||
|
||||
Future<void> clearCache() {
|
||||
void clearCache() {
|
||||
_localCollections.clear();
|
||||
_collectionIDToCollections.clear();
|
||||
_cachedKeys.clear();
|
||||
|
@ -108,7 +108,7 @@ class CollectionsService {
|
|||
|
||||
Future<List<Collection>> getCollectionsToBeSynced() async {
|
||||
final collections = await _db.getAllCollections();
|
||||
final updatedCollections = List<Collection>();
|
||||
final updatedCollections = <Collection>[];
|
||||
for (final c in collections) {
|
||||
if (c.updationTime > getCollectionSyncTime(c.id)) {
|
||||
updatedCollections.add(c);
|
||||
|
@ -118,18 +118,13 @@ class CollectionsService {
|
|||
}
|
||||
|
||||
int getCollectionSyncTime(int collectionID) {
|
||||
var syncTime =
|
||||
_prefs.getInt(_collectionSyncTimeKeyPrefix + collectionID.toString());
|
||||
if (syncTime == null) {
|
||||
syncTime = 0;
|
||||
}
|
||||
return syncTime;
|
||||
return _prefs
|
||||
.getInt(_collectionSyncTimeKeyPrefix + collectionID.toString()) ??
|
||||
0;
|
||||
}
|
||||
|
||||
Future<List<File>> getLatestCollectionFiles() {
|
||||
if (_cachedLatestFiles == null) {
|
||||
_cachedLatestFiles = _filesDB.getLatestCollectionFiles();
|
||||
}
|
||||
_cachedLatestFiles ??= _filesDB.getLatestCollectionFiles();
|
||||
return _cachedLatestFiles;
|
||||
}
|
||||
|
||||
|
@ -161,7 +156,7 @@ class CollectionsService {
|
|||
)
|
||||
.then((response) {
|
||||
_logger.info(response.toString());
|
||||
final sharees = List<User>();
|
||||
final sharees = <User>[];
|
||||
for (final user in response.data["sharees"]) {
|
||||
sharees.add(User.fromMap(user));
|
||||
}
|
||||
|
@ -187,7 +182,7 @@ class CollectionsService {
|
|||
if (e.response.statusCode == 402) {
|
||||
throw SharingNotPermittedForFreeAccountsError();
|
||||
}
|
||||
throw e;
|
||||
rethrow;
|
||||
}
|
||||
RemoteSyncService.instance.sync(silently: true);
|
||||
}
|
||||
|
@ -209,7 +204,7 @@ class CollectionsService {
|
|||
_db.insert([_collectionIDToCollections[collectionID]]);
|
||||
} catch (e) {
|
||||
_logger.severe(e);
|
||||
throw e;
|
||||
rethrow;
|
||||
}
|
||||
RemoteSyncService.instance.sync(silently: true);
|
||||
}
|
||||
|
@ -257,7 +252,7 @@ class CollectionsService {
|
|||
if (e is DioError && e.response?.statusCode == 401) {
|
||||
throw UnauthorizedError();
|
||||
}
|
||||
throw e;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +308,7 @@ class CollectionsService {
|
|||
}
|
||||
|
||||
Future<void> addToCollection(int collectionID, List<File> files) {
|
||||
final params = Map<String, dynamic>();
|
||||
final params = <String, dynamic>{};
|
||||
params["collectionID"] = collectionID;
|
||||
for (final file in files) {
|
||||
final key = decryptFileKey(file);
|
||||
|
@ -344,11 +339,11 @@ class CollectionsService {
|
|||
}
|
||||
|
||||
Future<void> removeFromCollection(int collectionID, List<File> files) async {
|
||||
final params = Map<String, dynamic>();
|
||||
final params = <String, dynamic>{};
|
||||
params["collectionID"] = collectionID;
|
||||
for (final file in files) {
|
||||
if (params["fileIDs"] == null) {
|
||||
params["fileIDs"] = List<int>();
|
||||
params["fileIDs"] = <int>[];
|
||||
}
|
||||
params["fileIDs"].add(file.uploadedFileID);
|
||||
}
|
||||
|
@ -401,7 +396,7 @@ class CollectionsService {
|
|||
Collection _getCollectionWithDecryptedName(Collection collection) {
|
||||
if (collection.encryptedName != null &&
|
||||
collection.encryptedName.isNotEmpty) {
|
||||
var name;
|
||||
String name;
|
||||
try {
|
||||
final result = CryptoUtil.decryptSync(
|
||||
Sodium.base642bin(collection.encryptedName),
|
||||
|
@ -429,7 +424,7 @@ class CollectionsService {
|
|||
if (attempt < kMaximumWriteAttempts) {
|
||||
return _updateDB(collections, attempt: attempt++);
|
||||
} else {
|
||||
throw e;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class MemoriesService extends ChangeNotifier {
|
|||
_cachedMemories = null;
|
||||
});
|
||||
await _memoriesDB.clearMemoriesSeenBeforeTime(
|
||||
DateTime.now().microsecondsSinceEpoch - (7 * MICRO_SECONDS_IN_DAY));
|
||||
DateTime.now().microsecondsSinceEpoch - (7 * kMicroSecondsInDay));
|
||||
}
|
||||
|
||||
void clearCache() {
|
||||
|
|
|
@ -79,7 +79,7 @@ class RemoteSyncService {
|
|||
|
||||
Future<bool> _uploadDiff() async {
|
||||
final foldersToBackUp = Configuration.instance.getPathsToBackUp();
|
||||
var filesToBeUploaded;
|
||||
List<File> filesToBeUploaded;
|
||||
if (LocalSyncService.instance.hasGrantedLimitedPermissions() &&
|
||||
foldersToBackUp.isEmpty) {
|
||||
filesToBeUploaded = await _db.getAllLocalFiles();
|
||||
|
@ -149,7 +149,7 @@ class RemoteSyncService {
|
|||
} on UserCancelledUploadError {
|
||||
// Do nothing
|
||||
} catch (e) {
|
||||
throw e;
|
||||
rethrow;
|
||||
}
|
||||
return _completedUploads > 0;
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ class RemoteSyncService {
|
|||
}
|
||||
}
|
||||
await _db.insertMultiple(toBeInserted);
|
||||
if (toBeInserted.length > 0) {
|
||||
if (toBeInserted.isNotEmpty) {
|
||||
await _collectionsService.setCollectionSyncTime(
|
||||
collectionID, toBeInserted[toBeInserted.length - 1].updationTime);
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@ class SyncService {
|
|||
final lastNotificationShownTime =
|
||||
_prefs.getInt(kLastStorageLimitExceededNotificationPushTime) ?? 0;
|
||||
final now = DateTime.now().microsecondsSinceEpoch;
|
||||
if ((now - lastNotificationShownTime) > MICRO_SECONDS_IN_DAY) {
|
||||
if ((now - lastNotificationShownTime) > kMicroSecondsInDay) {
|
||||
await _prefs.setInt(kLastStorageLimitExceededNotificationPushTime, now);
|
||||
NotificationService.instance.showNotification(
|
||||
"storage limit exceeded", "sorry, we had to pause your backups");
|
||||
|
|
|
@ -51,7 +51,7 @@ class UpdateService {
|
|||
_prefs.getInt(kUpdateAvailableShownTimeKey) ?? 0;
|
||||
final now = DateTime.now().microsecondsSinceEpoch;
|
||||
final hasBeen3DaysSinceLastNotification =
|
||||
(now - lastNotificationShownTime) > (3 * MICRO_SECONDS_IN_DAY);
|
||||
(now - lastNotificationShownTime) > (3 * kMicroSecondsInDay);
|
||||
if (shouldUpdate &&
|
||||
hasBeen3DaysSinceLastNotification &&
|
||||
_latestVersion.shouldNotify) {
|
||||
|
|
|
@ -22,9 +22,9 @@ class BlurredFileBackdrop extends StatelessWidget {
|
|||
key: Key("memory_backdrop" + file.tag()),
|
||||
),
|
||||
BackdropFilter(
|
||||
filter: new ImageFilter.blur(sigmaX: 64.0, sigmaY: 64.0),
|
||||
child: new Container(
|
||||
decoration: new BoxDecoration(color: Colors.white.withOpacity(0.0)),
|
||||
filter: ImageFilter.blur(sigmaX: 64.0, sigmaY: 64.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.white.withOpacity(0.0)),
|
||||
),
|
||||
),
|
||||
]),
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/models/file_type.dart';
|
||||
import 'package:photos/ui/fading_app_bar.dart';
|
||||
|
@ -208,7 +209,7 @@ class _DetailPageState extends State<DetailPage> {
|
|||
}
|
||||
if (_selectedIndex == _files.length - 1 && !_hasLoadedTillEnd) {
|
||||
final result = await widget.config.asyncLoader(
|
||||
0, _files[_selectedIndex].creationTime - 1,
|
||||
kGalleryLoadStartTime, _files[_selectedIndex].creationTime - 1,
|
||||
limit: kLoadLimit);
|
||||
setState(() {
|
||||
if (!result.hasMore) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/events/event.dart';
|
||||
import 'package:photos/events/files_updated_event.dart';
|
||||
|
@ -100,8 +101,9 @@ class _GalleryState extends State<Gallery> {
|
|||
Future<FileLoadResult> _loadFiles({int limit}) async {
|
||||
_logger.info("Loading files");
|
||||
final startTime = DateTime.now().microsecondsSinceEpoch;
|
||||
final result = await widget
|
||||
.asyncLoader(0, DateTime.now().microsecondsSinceEpoch, limit: limit);
|
||||
final result = await widget.asyncLoader(
|
||||
kGalleryLoadStartTime, DateTime.now().microsecondsSinceEpoch,
|
||||
limit: limit);
|
||||
final endTime = DateTime.now().microsecondsSinceEpoch;
|
||||
final duration = Duration(microseconds: endTime - startTime);
|
||||
_logger.info("Time taken to load " +
|
||||
|
|
|
@ -99,7 +99,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
DateTime(galleryDate.year, galleryDate.month, galleryDate.day);
|
||||
final result = await widget.asyncLoader(
|
||||
dayStartTime.microsecondsSinceEpoch,
|
||||
dayStartTime.microsecondsSinceEpoch + MICRO_SECONDS_IN_DAY - 1);
|
||||
dayStartTime.microsecondsSinceEpoch + kMicroSecondsInDay - 1);
|
||||
if (result.files.isEmpty) {
|
||||
// All files on this day were deleted, let gallery trigger the reload
|
||||
} else {
|
||||
|
|
|
@ -319,8 +319,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
newFile.creationTime = widget.originalFile.creationTime;
|
||||
newFile.collectionID = widget.originalFile.collectionID;
|
||||
newFile.location = widget.originalFile.location;
|
||||
if (newFile.location == null ||
|
||||
(newFile.location.latitude == 0 && newFile.location.longitude == 0)) {
|
||||
if (!newFile.hasLocation() && widget.originalFile.localID != null) {
|
||||
final latLong =
|
||||
await (await widget.originalFile.getAsset()).latlngAsync();
|
||||
newFile.location = Location(latLong.latitude, latLong.longitude);
|
||||
|
|
|
@ -72,7 +72,7 @@ class SupportSectionWidget extends StatelessWidget {
|
|||
final isLoggedIn = Configuration.instance.getToken() != null;
|
||||
final url = isLoggedIn
|
||||
? endpoint + "?token=" + Configuration.instance.getToken()
|
||||
: ROADMAP_URL;
|
||||
: kRoadmapURL;
|
||||
return WebPage("roadmap", url);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/cache/thumbnail_cache.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/errors.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import 'package:photos/events/local_photos_updated_event.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/models/file_type.dart';
|
||||
import 'package:photos/ui/common_elements.dart';
|
||||
import 'package:photos/utils/thumbnail_util.dart';
|
||||
|
@ -152,7 +152,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
!_isLoadingThumbnail) {
|
||||
_isLoadingThumbnail = true;
|
||||
final cachedSmallThumbnail =
|
||||
ThumbnailLruCache.get(widget.file, THUMBNAIL_SMALL_SIZE);
|
||||
ThumbnailLruCache.get(widget.file, kThumbnailSmallSize);
|
||||
if (cachedSmallThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedSmallThumbnail).image;
|
||||
_hasLoadedThumbnail = true;
|
||||
|
@ -185,11 +185,12 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (thumbData != null && mounted) {
|
||||
final imageProvider = Image.memory(thumbData).image;
|
||||
_cacheAndRender(imageProvider);
|
||||
}
|
||||
ThumbnailLruCache.put(widget.file, thumbData, THUMBNAIL_SMALL_SIZE);
|
||||
ThumbnailLruCache.put(widget.file, thumbData, kThumbnailSmallSize);
|
||||
}).catchError((e) {
|
||||
_logger.warning("Could not load image: ", e);
|
||||
_encounteredErrorLoadingThumbnail = true;
|
||||
|
|
|
@ -2,15 +2,15 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photos/core/cache/image_cache.dart';
|
||||
import 'package:photos/core/cache/thumbnail_cache.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import 'package:photos/events/local_photos_updated_event.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/ui/loading_widget.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/utils/file_util.dart';
|
||||
import 'package:photos/utils/thumbnail_util.dart';
|
||||
|
||||
|
@ -119,7 +119,7 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
!_loadedLargeThumbnail &&
|
||||
!_loadedFinalImage) {
|
||||
final cachedThumbnail =
|
||||
ThumbnailLruCache.get(_photo, THUMBNAIL_SMALL_SIZE);
|
||||
ThumbnailLruCache.get(_photo, kThumbnailSmallSize);
|
||||
if (cachedThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedThumbnail).image;
|
||||
_loadedSmallThumbnail = true;
|
||||
|
@ -131,7 +131,7 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
!_loadedFinalImage) {
|
||||
_loadingLargeThumbnail = true;
|
||||
final cachedThumbnail =
|
||||
ThumbnailLruCache.get(_photo, THUMBNAIL_LARGE_SIZE);
|
||||
ThumbnailLruCache.get(_photo, kThumbnailLargeSize);
|
||||
if (cachedThumbnail != null) {
|
||||
_onLargeThumbnailLoaded(Image.memory(cachedThumbnail).image, context);
|
||||
} else {
|
||||
|
@ -141,14 +141,14 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
return;
|
||||
}
|
||||
asset
|
||||
.thumbDataWithSize(THUMBNAIL_LARGE_SIZE, THUMBNAIL_LARGE_SIZE)
|
||||
.thumbDataWithSize(kThumbnailLargeSize, kThumbnailLargeSize)
|
||||
.then((data) {
|
||||
if (data == null) {
|
||||
// Deleted file
|
||||
return;
|
||||
}
|
||||
_onLargeThumbnailLoaded(Image.memory(data).image, context);
|
||||
ThumbnailLruCache.put(_photo, data, THUMBNAIL_LARGE_SIZE);
|
||||
ThumbnailLruCache.put(_photo, data, kThumbnailLargeSize);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:io' as io;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'dart:io' as io;
|
||||
import 'package:computer/computer.dart';
|
||||
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -109,15 +109,15 @@ void chachaDecryptFile(Map<String, dynamic> args) {
|
|||
}
|
||||
|
||||
Uint8List chachaDecryptData(Map<String, dynamic> args) {
|
||||
final pullState =
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305InitPull(args["header"], args["key"]);
|
||||
final pullResult =
|
||||
Sodium.cryptoSecretstreamXchacha20poly1305Pull(pullState, args["source"], null);
|
||||
final pullState = Sodium.cryptoSecretstreamXchacha20poly1305InitPull(
|
||||
args["header"], args["key"]);
|
||||
final pullResult = Sodium.cryptoSecretstreamXchacha20poly1305Pull(
|
||||
pullState, args["source"], null);
|
||||
return pullResult.m;
|
||||
}
|
||||
|
||||
class CryptoUtil {
|
||||
static Computer _computer = Computer();
|
||||
static final Computer _computer = Computer();
|
||||
|
||||
static init() {
|
||||
_computer.turnOn(workersCount: 4);
|
||||
|
@ -127,7 +127,7 @@ class CryptoUtil {
|
|||
static EncryptionResult encryptSync(Uint8List source, Uint8List key) {
|
||||
final nonce = Sodium.randombytesBuf(Sodium.cryptoSecretboxNoncebytes);
|
||||
|
||||
final args = Map<String, dynamic>();
|
||||
final args = <String, dynamic>{};
|
||||
args["source"] = source;
|
||||
args["nonce"] = nonce;
|
||||
args["key"] = key;
|
||||
|
@ -141,7 +141,7 @@ class CryptoUtil {
|
|||
Uint8List key,
|
||||
Uint8List nonce,
|
||||
) async {
|
||||
final args = Map<String, dynamic>();
|
||||
final args = <String, dynamic>{};
|
||||
args["cipher"] = cipher;
|
||||
args["nonce"] = nonce;
|
||||
args["key"] = key;
|
||||
|
@ -153,7 +153,7 @@ class CryptoUtil {
|
|||
Uint8List key,
|
||||
Uint8List nonce,
|
||||
) {
|
||||
final args = Map<String, dynamic>();
|
||||
final args = <String, dynamic>{};
|
||||
args["cipher"] = cipher;
|
||||
args["nonce"] = nonce;
|
||||
args["key"] = key;
|
||||
|
@ -162,7 +162,7 @@ class CryptoUtil {
|
|||
|
||||
static Future<EncryptionResult> encryptChaCha(
|
||||
Uint8List source, Uint8List key) async {
|
||||
final args = Map<String, dynamic>();
|
||||
final args = <String, dynamic>{};
|
||||
args["source"] = source;
|
||||
args["key"] = key;
|
||||
return _computer.compute(chachaEncryptData, param: args);
|
||||
|
@ -170,7 +170,7 @@ class CryptoUtil {
|
|||
|
||||
static Future<Uint8List> decryptChaCha(
|
||||
Uint8List source, Uint8List key, Uint8List header) async {
|
||||
final args = Map<String, dynamic>();
|
||||
final args = <String, dynamic>{};
|
||||
args["source"] = source;
|
||||
args["key"] = key;
|
||||
args["header"] = header;
|
||||
|
@ -182,7 +182,7 @@ class CryptoUtil {
|
|||
String destinationFilePath, {
|
||||
Uint8List key,
|
||||
}) {
|
||||
final args = Map<String, dynamic>();
|
||||
final args = <String, dynamic>{};
|
||||
args["sourceFilePath"] = sourceFilePath;
|
||||
args["destinationFilePath"] = destinationFilePath;
|
||||
args["key"] = key;
|
||||
|
@ -195,7 +195,7 @@ class CryptoUtil {
|
|||
Uint8List header,
|
||||
Uint8List key,
|
||||
) {
|
||||
final args = Map<String, dynamic>();
|
||||
final args = <String, dynamic>{};
|
||||
args["sourceFilePath"] = sourceFilePath;
|
||||
args["destinationFilePath"] = destinationFilePath;
|
||||
args["header"] = header;
|
||||
|
|
|
@ -147,7 +147,7 @@ Future<bool> deleteLocalFiles(
|
|||
final List<String> deletedIDs = [];
|
||||
if (Platform.isAndroid) {
|
||||
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||
if (androidInfo.version.sdkInt < ANDROID_11_SDK_INT) {
|
||||
if (androidInfo.version.sdkInt < kAndroid11SDKINT) {
|
||||
deletedIDs.addAll(await _deleteLocalFilesInBatches(context, localIDs));
|
||||
} else {
|
||||
deletedIDs.addAll(await _deleteLocalFilesInOneShot(context, localIDs));
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/errors.dart';
|
||||
import 'package:photos/models/location.dart';
|
||||
import 'package:photos/models/file.dart' as ente;
|
||||
import 'package:photos/models/location.dart';
|
||||
|
||||
import 'file_util.dart';
|
||||
|
||||
|
@ -63,12 +62,12 @@ Future<MediaUploadData> _getMediaUploadDataFromAssetFile(ente.File file) async {
|
|||
throw InvalidFileError();
|
||||
}
|
||||
thumbnailData = await asset.thumbDataWithSize(
|
||||
THUMBNAIL_SMALL_SIZE,
|
||||
THUMBNAIL_SMALL_SIZE,
|
||||
quality: THUMBNAIL_QUALITY,
|
||||
kThumbnailSmallSize,
|
||||
kThumbnailSmallSize,
|
||||
quality: kThumbnailQuality,
|
||||
);
|
||||
int compressionAttempts = 0;
|
||||
while (thumbnailData.length > THUMBNAIL_DATA_LIMIT &&
|
||||
while (thumbnailData.length > kThumbnailDataLimit &&
|
||||
compressionAttempts < kMaximumThumbnailCompressionAttempts) {
|
||||
_logger.info("Thumbnail size " + thumbnailData.length.toString());
|
||||
thumbnailData = await compressThumbnail(thumbnailData);
|
||||
|
@ -115,7 +114,7 @@ Future<Uint8List> getThumbnailFromInAppCacheFile(ente.File file) async {
|
|||
var localPath = file.localID.replaceAll(RegExp(r'ente-upload-cache:'), '');
|
||||
var thumbnailData = io.File(localPath).readAsBytesSync();
|
||||
int compressionAttempts = 0;
|
||||
while (thumbnailData.length > THUMBNAIL_DATA_LIMIT &&
|
||||
while (thumbnailData.length > kThumbnailDataLimit &&
|
||||
compressionAttempts < kMaximumThumbnailCompressionAttempts) {
|
||||
_logger.info("Thumbnail size " + thumbnailData.length.toString());
|
||||
thumbnailData = await compressThumbnail(thumbnailData);
|
||||
|
|
|
@ -2,12 +2,12 @@ import 'dart:async';
|
|||
import 'dart:io' as io;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:photos/core/cache/image_cache.dart';
|
||||
import 'package:photos/core/cache/thumbnail_cache.dart';
|
||||
import 'package:photos/core/cache/video_cache_manager.dart';
|
||||
|
@ -175,8 +175,8 @@ Uint8List decryptFileKey(ente.File file) {
|
|||
Future<Uint8List> compressThumbnail(Uint8List thumbnail) {
|
||||
return FlutterImageCompress.compressWithList(
|
||||
thumbnail,
|
||||
minHeight: COMPRESSED_THUMBNAIL_RESOLUTION,
|
||||
minWidth: COMPRESSED_THUMBNAIL_RESOLUTION,
|
||||
minHeight: kCompressedThumbnailResolution,
|
||||
minWidth: kCompressedThumbnailResolution,
|
||||
quality: 25,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'dart:io' as io;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
|
@ -16,7 +16,6 @@ import 'package:photos/utils/crypto_util.dart';
|
|||
import 'package:photos/utils/file_util.dart';
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'file_uploader_util.dart';
|
||||
|
||||
final _logger = Logger("ThumbnailUtil");
|
||||
|
@ -60,7 +59,7 @@ Future<Uint8List> getThumbnailFromServer(File file) async {
|
|||
}
|
||||
|
||||
Future<Uint8List> getThumbnailFromLocal(File file) async {
|
||||
if (ThumbnailLruCache.get(file, THUMBNAIL_SMALL_SIZE) != null) {
|
||||
if (ThumbnailLruCache.get(file, kThumbnailSmallSize) != null) {
|
||||
return ThumbnailLruCache.get(file);
|
||||
}
|
||||
final cachedThumbnail = getCachedThumbnail(file);
|
||||
|
@ -73,7 +72,7 @@ Future<Uint8List> getThumbnailFromLocal(File file) async {
|
|||
return getThumbnailFromInAppCacheFile(file)
|
||||
.then((data) {
|
||||
if (data != null) {
|
||||
ThumbnailLruCache.put(file, data, THUMBNAIL_SMALL_SIZE);
|
||||
ThumbnailLruCache.put(file, data, kThumbnailSmallSize);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
|
@ -83,10 +82,10 @@ Future<Uint8List> getThumbnailFromLocal(File file) async {
|
|||
return null;
|
||||
}
|
||||
return asset
|
||||
.thumbDataWithSize(THUMBNAIL_SMALL_SIZE, THUMBNAIL_SMALL_SIZE,
|
||||
quality: THUMBNAIL_QUALITY)
|
||||
.thumbDataWithSize(kThumbnailSmallSize, kThumbnailSmallSize,
|
||||
quality: kThumbnailQuality)
|
||||
.then((data) {
|
||||
ThumbnailLruCache.put(file, data, THUMBNAIL_SMALL_SIZE);
|
||||
ThumbnailLruCache.put(file, data, kThumbnailSmallSize);
|
||||
return data;
|
||||
});
|
||||
});
|
||||
|
@ -146,7 +145,7 @@ Future<void> _downloadAndDecryptThumbnail(FileDownloadItem item) async {
|
|||
Sodium.base642bin(file.thumbnailDecryptionHeader),
|
||||
);
|
||||
final thumbnailSize = data.length;
|
||||
if (thumbnailSize > THUMBNAIL_DATA_LIMIT) {
|
||||
if (thumbnailSize > kThumbnailDataLimit) {
|
||||
data = await compressThumbnail(data);
|
||||
}
|
||||
ThumbnailLruCache.put(item.file, data);
|
||||
|
|
Loading…
Reference in a new issue