Просмотр исходного кода

Merge branch 'reupload_hash_check' into rewrite_device_sync

Neeraj Gupta 2 лет назад
Родитель
Сommit
1533ac4098
90 измененных файлов с 519 добавлено и 394 удалено
  1. 1 0
      analysis_options.yaml
  2. 3 3
      lib/core/cache/lru_map.dart
  3. 15 1
      lib/core/configuration.dart
  4. 10 10
      lib/core/error-reporting/super_logging.dart
  5. 5 5
      lib/db/collections_db.dart
  6. 18 18
      lib/db/file_updation_db.dart
  7. 34 25
      lib/db/files_db.dart
  8. 2 2
      lib/db/ignored_files_db.dart
  9. 3 3
      lib/db/memories_db.dart
  10. 3 3
      lib/db/public_keys_db.dart
  11. 4 4
      lib/db/trash_db.dart
  12. 2 2
      lib/db/upload_locks_db.dart
  13. 6 6
      lib/main.dart
  14. 1 1
      lib/models/collection.dart
  15. 15 10
      lib/models/file.dart
  16. 3 3
      lib/models/selected_files.dart
  17. 1 1
      lib/models/subscription.dart
  18. 24 19
      lib/services/collections_service.dart
  19. 1 1
      lib/services/deduplication_service.dart
  20. 1 1
      lib/services/favorites_service.dart
  21. 1 1
      lib/services/feature_flag_service.dart
  22. 4 4
      lib/services/file_magic_service.dart
  23. 2 2
      lib/services/local/local_sync_util.dart
  24. 40 33
      lib/services/local_file_update_service.dart
  25. 6 8
      lib/services/local_sync_service.dart
  26. 4 4
      lib/services/remote_sync_service.dart
  27. 2 2
      lib/services/user_service.dart
  28. 2 2
      lib/ui/account/delete_account_page.dart
  29. 29 2
      lib/ui/account/password_reentry_page.dart
  30. 1 1
      lib/ui/account/recovery_key_page.dart
  31. 2 2
      lib/ui/account/sessions_page.dart
  32. 2 2
      lib/ui/account/two_factor_setup_page.dart
  33. 4 4
      lib/ui/collections/collection_item_widget.dart
  34. 2 2
      lib/ui/collections/remote_collections_grid_view_widget.dart
  35. 1 1
      lib/ui/common/dialogs.dart
  36. 2 2
      lib/ui/create_collection_page.dart
  37. 1 1
      lib/ui/grant_permissions_widget.dart
  38. 3 3
      lib/ui/home_widget.dart
  39. 1 1
      lib/ui/huge_listview/draggable_scrollbar.dart
  40. 1 1
      lib/ui/huge_listview/huge_listview.dart
  41. 2 2
      lib/ui/huge_listview/lazy_loading_gallery.dart
  42. 2 2
      lib/ui/memories_widget.dart
  43. 1 1
      lib/ui/nav_bar.dart
  44. 8 8
      lib/ui/payment/payment_web_page.dart
  45. 6 6
      lib/ui/payment/stripe_subscription_page.dart
  46. 1 1
      lib/ui/payment/subscription_common_widgets.dart
  47. 2 2
      lib/ui/payment/subscription_plan_widget.dart
  48. 3 3
      lib/ui/settings/account_section_widget.dart
  49. 1 1
      lib/ui/settings/app_update_dialog.dart
  50. 1 1
      lib/ui/settings/app_version_widget.dart
  51. 6 6
      lib/ui/settings/backup_section_widget.dart
  52. 1 1
      lib/ui/settings/danger_section_widget.dart
  53. 1 1
      lib/ui/settings/debug_section_widget.dart
  54. 3 3
      lib/ui/settings/security_section_widget.dart
  55. 1 1
      lib/ui/settings/social_section_widget.dart
  56. 7 7
      lib/ui/shared_collections_gallery.dart
  57. 9 9
      lib/ui/sharing/manage_links_widget.dart
  58. 4 4
      lib/ui/sharing/share_collection_widget.dart
  59. 1 1
      lib/ui/status_bar_widget.dart
  60. 5 5
      lib/ui/tools/editor/filtered_image.dart
  61. 9 9
      lib/ui/tools/editor/image_editor_page.dart
  62. 1 0
      lib/ui/viewer/file/custom_app_bar.dart
  63. 1 1
      lib/ui/viewer/file/detail_page.dart
  64. 3 3
      lib/ui/viewer/file/fading_app_bar.dart
  65. 3 3
      lib/ui/viewer/file/fading_bottom_bar.dart
  66. 0 1
      lib/ui/viewer/file/file_icons_widget.dart
  67. 2 2
      lib/ui/viewer/file/file_info_dialog.dart
  68. 2 2
      lib/ui/viewer/file/thumbnail_widget.dart
  69. 1 1
      lib/ui/viewer/file/video_controls.dart
  70. 1 1
      lib/ui/viewer/file/zoomable_image.dart
  71. 3 3
      lib/ui/viewer/file/zoomable_live_image.dart
  72. 3 3
      lib/ui/viewer/gallery/gallery.dart
  73. 2 2
      lib/ui/viewer/gallery/gallery_app_bar_widget.dart
  74. 3 3
      lib/ui/viewer/gallery/gallery_overlay_widget.dart
  75. 1 1
      lib/ui/viewer/gallery/trash_page.dart
  76. 2 2
      lib/utils/data_util.dart
  77. 6 6
      lib/utils/date_time_util.dart
  78. 2 2
      lib/utils/delete_file_util.dart
  79. 2 2
      lib/utils/dialog_util.dart
  80. 1 1
      lib/utils/diff_fetcher.dart
  81. 6 6
      lib/utils/email_util.dart
  82. 33 37
      lib/utils/file_uploader.dart
  83. 33 20
      lib/utils/file_uploader_util.dart
  84. 10 10
      lib/utils/file_util.dart
  85. 4 4
      lib/utils/hex.dart
  86. 2 2
      lib/utils/magic_util.dart
  87. 4 4
      lib/utils/share_util.dart
  88. 1 1
      lib/utils/trash_diff_fetcher.dart
  89. 50 0
      lib/utils/validator_util.dart
  90. 1 1
      pubspec.yaml

+ 1 - 0
analysis_options.yaml

@@ -15,6 +15,7 @@ linter:
     - prefer_const_constructors_in_immutables
     - prefer_const_constructors_in_immutables
     - prefer_const_declarations
     - prefer_const_declarations
     - prefer_const_literals_to_create_immutables
     - prefer_const_literals_to_create_immutables
+    - prefer_final_locals
     - require_trailing_commas
     - require_trailing_commas
     - sized_box_for_whitespace
     - sized_box_for_whitespace
     - use_full_hex_values_for_flutter_colors
     - use_full_hex_values_for_flutter_colors

+ 3 - 3
lib/core/cache/lru_map.dart

@@ -10,7 +10,7 @@ class LRUMap<K, V> {
   LRUMap(this._maxSize, [this._handler]);
   LRUMap(this._maxSize, [this._handler]);
 
 
   V get(K key) {
   V get(K key) {
-    V value = _map.remove(key);
+    final V value = _map.remove(key);
     if (value != null) {
     if (value != null) {
       _map[key] = value;
       _map[key] = value;
     }
     }
@@ -21,8 +21,8 @@ class LRUMap<K, V> {
     _map.remove(key);
     _map.remove(key);
     _map[key] = value;
     _map[key] = value;
     if (_map.length > _maxSize) {
     if (_map.length > _maxSize) {
-      K evictedKey = _map.keys.first;
-      V evictedValue = _map.remove(evictedKey);
+      final K evictedKey = _map.keys.first;
+      final V evictedValue = _map.remove(evictedKey);
       if (_handler != null) {
       if (_handler != null) {
         _handler(evictedKey, evictedValue);
         _handler(evictedKey, evictedValue);
       }
       }

+ 15 - 1
lib/core/configuration.dart

@@ -29,6 +29,7 @@ import 'package:photos/services/memories_service.dart';
 import 'package:photos/services/search_service.dart';
 import 'package:photos/services/search_service.dart';
 import 'package:photos/services/sync_service.dart';
 import 'package:photos/services/sync_service.dart';
 import 'package:photos/utils/crypto_util.dart';
 import 'package:photos/utils/crypto_util.dart';
+import 'package:photos/utils/validator_util.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 import 'package:uuid/uuid.dart';
 import 'package:uuid/uuid.dart';
 import 'package:wakelock/wakelock.dart';
 import 'package:wakelock/wakelock.dart';
@@ -111,7 +112,7 @@ class Configuration {
       _logger.warning(e);
       _logger.warning(e);
     }
     }
     tempDirectory.createSync(recursive: true);
     tempDirectory.createSync(recursive: true);
-    var tempDirectoryPath = (await getTemporaryDirectory()).path;
+    final tempDirectoryPath = (await getTemporaryDirectory()).path;
     _thumbnailCacheDirectory = tempDirectoryPath + "/thumbnail-cache";
     _thumbnailCacheDirectory = tempDirectoryPath + "/thumbnail-cache";
     io.Directory(_thumbnailCacheDirectory).createSync(recursive: true);
     io.Directory(_thumbnailCacheDirectory).createSync(recursive: true);
     _sharedTempMediaDirectory = tempDirectoryPath + "/ente-shared-media";
     _sharedTempMediaDirectory = tempDirectoryPath + "/ente-shared-media";
@@ -242,12 +243,21 @@ class Configuration {
     String password,
     String password,
     KeyAttributes attributes,
     KeyAttributes attributes,
   ) async {
   ) async {
+    _logger.info('Start decryptAndSaveSecrets');
+    validatePreVerificationStateCheck(
+      attributes,
+      password,
+      getEncryptedToken(),
+    );
+    _logger.info('state validation done');
     final kek = await CryptoUtil.deriveKey(
     final kek = await CryptoUtil.deriveKey(
       utf8.encode(password),
       utf8.encode(password),
       Sodium.base642bin(attributes.kekSalt),
       Sodium.base642bin(attributes.kekSalt),
       attributes.memLimit,
       attributes.memLimit,
       attributes.opsLimit,
       attributes.opsLimit,
     );
     );
+
+    _logger.info('user-key done');
     Uint8List key;
     Uint8List key;
     try {
     try {
       key = CryptoUtil.decryptSync(
       key = CryptoUtil.decryptSync(
@@ -256,20 +266,24 @@ class Configuration {
         Sodium.base642bin(attributes.keyDecryptionNonce),
         Sodium.base642bin(attributes.keyDecryptionNonce),
       );
       );
     } catch (e) {
     } catch (e) {
+      _logger.severe('master-key failed, incorrect password?', e);
       throw Exception("Incorrect password");
       throw Exception("Incorrect password");
     }
     }
+    _logger.info("master-key done");
     await setKey(Sodium.bin2base64(key));
     await setKey(Sodium.bin2base64(key));
     final secretKey = CryptoUtil.decryptSync(
     final secretKey = CryptoUtil.decryptSync(
       Sodium.base642bin(attributes.encryptedSecretKey),
       Sodium.base642bin(attributes.encryptedSecretKey),
       key,
       key,
       Sodium.base642bin(attributes.secretKeyDecryptionNonce),
       Sodium.base642bin(attributes.secretKeyDecryptionNonce),
     );
     );
+    _logger.info("secret-key done");
     await setSecretKey(Sodium.bin2base64(secretKey));
     await setSecretKey(Sodium.bin2base64(secretKey));
     final token = CryptoUtil.openSealSync(
     final token = CryptoUtil.openSealSync(
       Sodium.base642bin(getEncryptedToken()),
       Sodium.base642bin(getEncryptedToken()),
       Sodium.base642bin(attributes.publicKey),
       Sodium.base642bin(attributes.publicKey),
       secretKey,
       secretKey,
     );
     );
+    _logger.info('appToken done');
     await setToken(
     await setToken(
       Sodium.bin2base64(token, variant: Sodium.base64VariantUrlsafe),
       Sodium.bin2base64(token, variant: Sodium.base64VariantUrlsafe),
     );
     );

+ 10 - 10
lib/core/error-reporting/super_logging.dart

@@ -23,7 +23,7 @@ extension SuperString on String {
     var start = 0;
     var start = 0;
 
 
     while (true) {
     while (true) {
-      var stop = start + chunkSize;
+      final stop = start + chunkSize;
       if (stop > length) break;
       if (stop > length) break;
       yield substring(start, stop);
       yield substring(start, stop);
       start = stop;
       start = stop;
@@ -37,7 +37,7 @@ extension SuperString on String {
 
 
 extension SuperLogRecord on LogRecord {
 extension SuperLogRecord on LogRecord {
   String toPrettyString([String extraLines]) {
   String toPrettyString([String extraLines]) {
-    var header = "[$loggerName] [$level] [$time]";
+    final header = "[$loggerName] [$level] [$time]";
 
 
     var msg = "$header $message";
     var msg = "$header $message";
 
 
@@ -236,7 +236,7 @@ class SuperLogging {
       extraLines = null;
       extraLines = null;
     }
     }
 
 
-    var str = config.prefix + " " + rec.toPrettyString(extraLines);
+    final str = config.prefix + " " + rec.toPrettyString(extraLines);
 
 
     // write to stdout
     // write to stdout
     printLog(str);
     printLog(str);
@@ -316,21 +316,21 @@ class SuperLogging {
 
 
     // choose [logDir]
     // choose [logDir]
     if (dirPath.isEmpty) {
     if (dirPath.isEmpty) {
-      var root = await getExternalStorageDirectory();
+      final root = await getExternalStorageDirectory();
       dirPath = '${root.path}/logs';
       dirPath = '${root.path}/logs';
     }
     }
 
 
     // create [logDir]
     // create [logDir]
-    var dir = Directory(dirPath);
+    final dir = Directory(dirPath);
     await dir.create(recursive: true);
     await dir.create(recursive: true);
 
 
-    var files = <File>[];
-    var dates = <File, DateTime>{};
+    final files = <File>[];
+    final dates = <File, DateTime>{};
 
 
     // collect all log files with valid names
     // collect all log files with valid names
     await for (final file in dir.list()) {
     await for (final file in dir.list()) {
       try {
       try {
-        var date = config.dateFmt.parse(basename(file.path));
+        final date = config.dateFmt.parse(basename(file.path));
         dates[file as File] = date;
         dates[file as File] = date;
         files.add(file);
         files.add(file);
       } on FormatException {}
       } on FormatException {}
@@ -363,7 +363,7 @@ class SuperLogging {
   static String appVersion;
   static String appVersion;
 
 
   static Future<String> getAppVersion() async {
   static Future<String> getAppVersion() async {
-    var pkgInfo = await PackageInfo.fromPlatform();
+    final pkgInfo = await PackageInfo.fromPlatform();
     return "${pkgInfo.version}+${pkgInfo.buildNumber}";
     return "${pkgInfo.version}+${pkgInfo.buildNumber}";
   }
   }
 
 
@@ -372,7 +372,7 @@ class SuperLogging {
     if (!Platform.isAndroid) {
     if (!Platform.isAndroid) {
       return false;
       return false;
     }
     }
-    var pkgName = (await PackageInfo.fromPlatform()).packageName;
+    final pkgName = (await PackageInfo.fromPlatform()).packageName;
     return pkgName.startsWith("io.ente.photos.fdroid");
     return pkgName.startsWith("io.ente.photos.fdroid");
   }
   }
 }
 }

+ 5 - 5
lib/db/collections_db.dart

@@ -60,8 +60,8 @@ class CollectionsDB {
   }
   }
 
 
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory = await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     return await openDatabaseWithMigration(path, dbConfig);
     return await openDatabaseWithMigration(path, dbConfig);
   }
   }
 
 
@@ -157,7 +157,7 @@ class CollectionsDB {
 
 
   Future<List<dynamic>> insert(List<Collection> collections) async {
   Future<List<dynamic>> insert(List<Collection> collections) async {
     final db = await instance.database;
     final db = await instance.database;
-    var batch = db.batch();
+    final batch = db.batch();
     for (final collection in collections) {
     for (final collection in collections) {
       batch.insert(
       batch.insert(
         table,
         table,
@@ -202,7 +202,7 @@ class CollectionsDB {
   }
   }
 
 
   Map<String, dynamic> _getRowForCollection(Collection collection) {
   Map<String, dynamic> _getRowForCollection(Collection collection) {
-    var row = <String, dynamic>{};
+    final row = <String, dynamic>{};
     row[columnID] = collection.id;
     row[columnID] = collection.id;
     row[columnOwner] = collection.owner.toJson();
     row[columnOwner] = collection.owner.toJson();
     row[columnEncryptedKey] = collection.encryptedKey;
     row[columnEncryptedKey] = collection.encryptedKey;
@@ -230,7 +230,7 @@ class CollectionsDB {
   }
   }
 
 
   Collection _convertToCollection(Map<String, dynamic> row) {
   Collection _convertToCollection(Map<String, dynamic> row) {
-    Collection result = Collection(
+    final Collection result = Collection(
       row[columnID],
       row[columnID],
       User.fromJson(row[columnOwner]),
       User.fromJson(row[columnOwner]),
       row[columnEncryptedKey],
       row[columnEncryptedKey],

+ 18 - 18
lib/db/file_migration_db.dart → lib/db/file_updation_db.dart

@@ -7,13 +7,13 @@ import 'package:path_provider/path_provider.dart';
 import 'package:sqflite/sqflite.dart';
 import 'package:sqflite/sqflite.dart';
 import 'package:sqflite_migration/sqflite_migration.dart';
 import 'package:sqflite_migration/sqflite_migration.dart';
 
 
-class FilesMigrationDB {
+class FileUpdationDB {
   static const _databaseName = "ente.files_migration.db";
   static const _databaseName = "ente.files_migration.db";
-  static final Logger _logger = Logger((FilesMigrationDB).toString());
+  static final Logger _logger = Logger((FileUpdationDB).toString());
 
 
   static const tableName = 're_upload_tracker';
   static const tableName = 're_upload_tracker';
-  static const _columnLocalID = 'local_id';
-  static const _columnReason = 'reason';
+  static const columnLocalID = 'local_id';
+  static const columnReason = 'reason';
   static const missingLocation = 'missing_location';
   static const missingLocation = 'missing_location';
   static const modificationTimeUpdated = 'modificationTimeUpdated';
   static const modificationTimeUpdated = 'modificationTimeUpdated';
 
 
@@ -22,8 +22,8 @@ class FilesMigrationDB {
     return [
     return [
       ''' 
       ''' 
       CREATE TABLE $tableName (
       CREATE TABLE $tableName (
-      $_columnLocalID TEXT NOT NULL,
-      UNIQUE($_columnLocalID)
+      $columnLocalID TEXT NOT NULL,
+      UNIQUE($columnLocalID)
       ); 
       ); 
       ''',
       ''',
     ];
     ];
@@ -32,10 +32,10 @@ class FilesMigrationDB {
   static List<String> addReasonColumn() {
   static List<String> addReasonColumn() {
     return [
     return [
       '''
       '''
-        ALTER TABLE $tableName ADD COLUMN $_columnReason TEXT;
+        ALTER TABLE $tableName ADD COLUMN $columnReason TEXT;
       ''',
       ''',
       '''
       '''
-        UPDATE $tableName SET $_columnReason = '$missingLocation';
+        UPDATE $tableName SET $columnReason = '$missingLocation';
       ''',
       ''',
     ];
     ];
   }
   }
@@ -49,10 +49,9 @@ class FilesMigrationDB {
     migrationScripts: migrationScripts,
     migrationScripts: migrationScripts,
   );
   );
 
 
-  FilesMigrationDB._privateConstructor();
+  FileUpdationDB._privateConstructor();
 
 
-  static final FilesMigrationDB instance =
-      FilesMigrationDB._privateConstructor();
+  static final FileUpdationDB instance = FileUpdationDB._privateConstructor();
 
 
   // only have a single app-wide reference to the database
   // only have a single app-wide reference to the database
   static Future<Database> _dbFuture;
   static Future<Database> _dbFuture;
@@ -65,8 +64,9 @@ class FilesMigrationDB {
 
 
   // this opens the database (and creates it if it doesn't exist)
   // this opens the database (and creates it if it doesn't exist)
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory =
+        await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     debugPrint("DB path " + path);
     debugPrint("DB path " + path);
     return await openDatabaseWithMigration(path, dbConfig);
     return await openDatabaseWithMigration(path, dbConfig);
   }
   }
@@ -122,7 +122,7 @@ class FilesMigrationDB {
     await db.rawQuery(
     await db.rawQuery(
       '''
       '''
       DELETE FROM $tableName
       DELETE FROM $tableName
-      WHERE $_columnLocalID IN ($inParam) AND $_columnReason = '$reason';
+      WHERE $columnLocalID IN ($inParam) AND $columnReason = '$reason';
     ''',
     ''',
     );
     );
   }
   }
@@ -132,7 +132,7 @@ class FilesMigrationDB {
     String reason,
     String reason,
   ) async {
   ) async {
     final db = await instance.database;
     final db = await instance.database;
-    String whereClause = '$_columnReason = "$reason"';
+    String whereClause = '$columnReason = "$reason"';
     final rows = await db.query(
     final rows = await db.query(
       tableName,
       tableName,
       limit: limit,
       limit: limit,
@@ -140,7 +140,7 @@ class FilesMigrationDB {
     );
     );
     final result = <String>[];
     final result = <String>[];
     for (final row in rows) {
     for (final row in rows) {
-      result.add(row[_columnLocalID]);
+      result.add(row[columnLocalID]);
     }
     }
     return result;
     return result;
   }
   }
@@ -148,8 +148,8 @@ class FilesMigrationDB {
   Map<String, dynamic> _getRowForReUploadTable(String localID, String reason) {
   Map<String, dynamic> _getRowForReUploadTable(String localID, String reason) {
     assert(localID != null);
     assert(localID != null);
     final row = <String, dynamic>{};
     final row = <String, dynamic>{};
-    row[_columnLocalID] = localID;
-    row[_columnReason] = reason;
+    row[columnLocalID] = localID;
+    row[columnReason] = reason;
     return row;
     return row;
   }
   }
 }
 }

+ 34 - 25
lib/db/files_db.dart

@@ -1,4 +1,5 @@
 import 'dart:io' as io;
 import 'dart:io' as io;
+import 'dart:io';
 
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
 import 'package:logging/logging.dart';
 import 'package:logging/logging.dart';
@@ -10,6 +11,7 @@ import 'package:photos/models/file_load_result.dart';
 import 'package:photos/models/file_type.dart';
 import 'package:photos/models/file_type.dart';
 import 'package:photos/models/location.dart';
 import 'package:photos/models/location.dart';
 import 'package:photos/models/magic_metadata.dart';
 import 'package:photos/models/magic_metadata.dart';
+import 'package:photos/utils/file_uploader_util.dart';
 import 'package:sqflite/sqflite.dart';
 import 'package:sqflite/sqflite.dart';
 import 'package:sqflite_migration/sqflite_migration.dart';
 import 'package:sqflite_migration/sqflite_migration.dart';
 
 
@@ -97,8 +99,9 @@ class FilesDB {
 
 
   // this opens the database (and creates it if it doesn't exist)
   // this opens the database (and creates it if it doesn't exist)
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory =
+        await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     _logger.info("DB path " + path);
     _logger.info("DB path " + path);
     return await openDatabaseWithMigration(path, dbConfig);
     return await openDatabaseWithMigration(path, dbConfig);
   }
   }
@@ -298,7 +301,7 @@ class FilesDB {
   static List<String> createOnDeviceFilesAndPathCollection() {
   static List<String> createOnDeviceFilesAndPathCollection() {
     return [
     return [
       '''
       '''
-        CREATE TABLE device_files (
+        CREATE TABLE IF NOT EXISTS device_files (
           id TEXT NOT NULL,
           id TEXT NOT NULL,
           path_id TEXT NOT NULL,
           path_id TEXT NOT NULL,
           synced INTEGER NOT NULL DEFAULT 0,
           synced INTEGER NOT NULL DEFAULT 0,
@@ -306,7 +309,7 @@ class FilesDB {
        );
        );
        ''',
        ''',
       '''
       '''
-       CREATE TABLE device_path_collections (
+       CREATE TABLE IF NOT EXISTS device_path_collections (
           id TEXT PRIMARY KEY NOT NULL,
           id TEXT PRIMARY KEY NOT NULL,
           name TEXT,
           name TEXT,
           modified_at INTEGER NOT NULL DEFAULT 0,
           modified_at INTEGER NOT NULL DEFAULT 0,
@@ -471,7 +474,7 @@ class FilesDB {
       limit: limit,
       limit: limit,
     );
     );
     final files = convertToFiles(results);
     final files = convertToFiles(results);
-    List<File> deduplicatedFiles =
+    final List<File> deduplicatedFiles =
         _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs);
         _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs);
     return FileLoadResult(deduplicatedFiles, files.length == limit);
     return FileLoadResult(deduplicatedFiles, files.length == limit);
   }
   }
@@ -497,7 +500,7 @@ class FilesDB {
       limit: limit,
       limit: limit,
     );
     );
     final files = convertToFiles(results);
     final files = convertToFiles(results);
-    List<File> deduplicatedFiles =
+    final List<File> deduplicatedFiles =
         _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs);
         _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs);
     return FileLoadResult(deduplicatedFiles, files.length == limit);
     return FileLoadResult(deduplicatedFiles, files.length == limit);
   }
   }
@@ -529,7 +532,7 @@ class FilesDB {
       limit: limit,
       limit: limit,
     );
     );
     final files = convertToFiles(results);
     final files = convertToFiles(results);
-    List<File> deduplicatedFiles =
+    final List<File> deduplicatedFiles =
         _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs);
         _deduplicatedAndFilterIgnoredFiles(files, ignoredCollectionIDs);
     return FileLoadResult(deduplicatedFiles, files.length == limit);
     return FileLoadResult(deduplicatedFiles, files.length == limit);
   }
   }
@@ -720,7 +723,7 @@ class FilesDB {
       orderBy: '$columnCreationTime DESC',
       orderBy: '$columnCreationTime DESC',
       groupBy: columnLocalID,
       groupBy: columnLocalID,
     );
     );
-    var files = convertToFiles(results);
+    final files = convertToFiles(results);
     // future-safe filter just to ensure that the query doesn't end up  returning files
     // future-safe filter just to ensure that the query doesn't end up  returning files
     // which should not be backed up
     // which should not be backed up
     files.removeWhere(
     files.removeWhere(
@@ -861,22 +864,28 @@ class FilesDB {
   }
   }
 
 
   Future<List<File>> getUploadedFilesWithHashes(
   Future<List<File>> getUploadedFilesWithHashes(
-    List<String> hash,
+    FileHashData hashData,
     FileType fileType,
     FileType fileType,
     int ownerID,
     int ownerID,
   ) async {
   ) async {
-    // look up two hash at max, for handling live photos
-    assert(hash.length < 3, "number of hash can not be more than 2");
-    final db = await instance.database;
-    String rawQuery = 'SELECT * from files where ($columnUploadedFileID != '
-        'NULL OR $columnUploadedFileID != -1) AND $columnOwnerID = $ownerID '
-        'AND ($columnHash = "${hash.first}" OR $columnHash = "${hash.last}")';
-    final rows = await db.rawQuery(rawQuery, []);
-    if (rows.isNotEmpty) {
-      return convertToFiles(rows);
-    } else {
-      return [];
+    String inParam = "'${hashData.fileHash}'";
+    if (fileType == FileType.livePhoto && hashData.zipHash != null) {
+      inParam += ",'${hashData.zipHash}'";
     }
     }
+
+    final db = await instance.database;
+    final rows = await db.query(
+      filesTable,
+      where: '($columnUploadedFileID != NULL OR $columnUploadedFileID != -1) '
+          'AND $columnOwnerID = ? AND $columnFileType ='
+          ' ? '
+          'AND $columnHash IN ($inParam)',
+      whereArgs: [
+        ownerID,
+        getInt(fileType),
+      ],
+    );
+    return convertToFiles(rows);
   }
   }
 
 
   Future<int> update(File file) async {
   Future<int> update(File file) async {
@@ -1011,7 +1020,7 @@ class FilesDB {
 
 
   Future<int> collectionFileCount(int collectionID) async {
   Future<int> collectionFileCount(int collectionID) async {
     final db = await instance.database;
     final db = await instance.database;
-    var count = Sqflite.firstIntValue(
+    final count = Sqflite.firstIntValue(
       await db.rawQuery(
       await db.rawQuery(
         'SELECT COUNT(*) FROM $filesTable where $columnCollectionID = $collectionID',
         'SELECT COUNT(*) FROM $filesTable where $columnCollectionID = $collectionID',
       ),
       ),
@@ -1021,7 +1030,7 @@ class FilesDB {
 
 
   Future<int> fileCountWithVisibility(int visibility, int ownerID) async {
   Future<int> fileCountWithVisibility(int visibility, int ownerID) async {
     final db = await instance.database;
     final db = await instance.database;
-    var count = Sqflite.firstIntValue(
+    final count = Sqflite.firstIntValue(
       await db.rawQuery(
       await db.rawQuery(
         'SELECT COUNT(*) FROM $filesTable where $columnMMdVisibility = $visibility AND $columnOwnerID = $ownerID',
         'SELECT COUNT(*) FROM $filesTable where $columnMMdVisibility = $visibility AND $columnOwnerID = $ownerID',
       ),
       ),
@@ -1227,9 +1236,9 @@ class FilesDB {
 
 
   Future<List<File>> getAllFilesFromDB() async {
   Future<List<File>> getAllFilesFromDB() async {
     final db = await instance.database;
     final db = await instance.database;
-    List<Map<String, dynamic>> result = await db.query(filesTable);
-    List<File> files = convertToFiles(result);
-    List<File> deduplicatedFiles =
+    final List<Map<String, dynamic>> result = await db.query(filesTable);
+    final List<File> files = convertToFiles(result);
+    final List<File> deduplicatedFiles =
         _deduplicatedAndFilterIgnoredFiles(files, null);
         _deduplicatedAndFilterIgnoredFiles(files, null);
     return deduplicatedFiles;
     return deduplicatedFiles;
   }
   }

+ 2 - 2
lib/db/ignored_files_db.dart

@@ -52,8 +52,8 @@ class IgnoredFilesDB {
 
 
   // this opens the database (and creates it if it doesn't exist)
   // this opens the database (and creates it if it doesn't exist)
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory = await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     return await openDatabase(
     return await openDatabase(
       path,
       path,
       version: _databaseVersion,
       version: _databaseVersion,

+ 3 - 3
lib/db/memories_db.dart

@@ -25,8 +25,8 @@ class MemoriesDB {
   }
   }
 
 
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory = await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     return await openDatabase(
     return await openDatabase(
       path,
       path,
       version: _databaseVersion,
       version: _databaseVersion,
@@ -74,7 +74,7 @@ class MemoriesDB {
   }
   }
 
 
   Map<String, dynamic> _getRowForSeenMemory(Memory memory, int timestamp) {
   Map<String, dynamic> _getRowForSeenMemory(Memory memory, int timestamp) {
-    var row = <String, dynamic>{};
+    final row = <String, dynamic>{};
     row[columnFileID] = memory.file.generatedID;
     row[columnFileID] = memory.file.generatedID;
     row[columnSeenTime] = timestamp;
     row[columnSeenTime] = timestamp;
     return row;
     return row;

+ 3 - 3
lib/db/public_keys_db.dart

@@ -26,8 +26,8 @@ class PublicKeysDB {
   }
   }
 
 
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory = await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     return await openDatabase(
     return await openDatabase(
       path,
       path,
       version: _databaseVersion,
       version: _databaseVersion,
@@ -72,7 +72,7 @@ class PublicKeysDB {
   }
   }
 
 
   Map<String, dynamic> _getRow(PublicKey key) {
   Map<String, dynamic> _getRow(PublicKey key) {
-    var row = <String, dynamic>{};
+    final row = <String, dynamic>{};
     row[columnEmail] = key.email;
     row[columnEmail] = key.email;
     row[columnPublicKey] = key.publicKey;
     row[columnPublicKey] = key.publicKey;
     return row;
     return row;

+ 4 - 4
lib/db/trash_db.dart

@@ -85,8 +85,8 @@ class TrashDB {
 
 
   // this opens the database (and creates it if it doesn't exist)
   // this opens the database (and creates it if it doesn't exist)
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory = await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     _logger.info("DB path " + path);
     _logger.info("DB path " + path);
     return await openDatabase(
     return await openDatabase(
       path,
       path,
@@ -103,7 +103,7 @@ class TrashDB {
   // getRecentlyTrashedFile returns the file which was trashed recently
   // getRecentlyTrashedFile returns the file which was trashed recently
   Future<TrashFile> getRecentlyTrashedFile() async {
   Future<TrashFile> getRecentlyTrashedFile() async {
     final db = await instance.database;
     final db = await instance.database;
-    var rows = await db.query(
+    final rows = await db.query(
       tableName,
       tableName,
       orderBy: '$columnTrashDeleteBy DESC',
       orderBy: '$columnTrashDeleteBy DESC',
       limit: 1,
       limit: 1,
@@ -116,7 +116,7 @@ class TrashDB {
 
 
   Future<int> count() async {
   Future<int> count() async {
     final db = await instance.database;
     final db = await instance.database;
-    var count = Sqflite.firstIntValue(
+    final count = Sqflite.firstIntValue(
       await db.rawQuery('SELECT COUNT(*) FROM $tableName'),
       await db.rawQuery('SELECT COUNT(*) FROM $tableName'),
     );
     );
     return count;
     return count;

+ 2 - 2
lib/db/upload_locks_db.dart

@@ -24,8 +24,8 @@ class UploadLocksDB {
   }
   }
 
 
   Future<Database> _initDatabase() async {
   Future<Database> _initDatabase() async {
-    Directory documentsDirectory = await getApplicationDocumentsDirectory();
-    String path = join(documentsDirectory.path, _databaseName);
+    final Directory documentsDirectory = await getApplicationDocumentsDirectory();
+    final String path = join(documentsDirectory.path, _databaseName);
     return await openDatabase(
     return await openDatabase(
       path,
       path,
       version: _databaseVersion,
       version: _databaseVersion,

+ 6 - 6
lib/main.dart

@@ -231,8 +231,8 @@ Future<void> _killBGTask([String taskId]) async {
 }
 }
 
 
 Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
 Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
-  bool isRunningInFG = await _isRunningInForeground(); // hb
-  bool isInForeground = AppLifecycleService.instance.isForeground;
+  final bool isRunningInFG = await _isRunningInForeground(); // hb
+  final bool isInForeground = AppLifecycleService.instance.isForeground;
   if (_isProcessRunning) {
   if (_isProcessRunning) {
     _logger.info(
     _logger.info(
       "Background push received when app is alive and runningInFS: $isRunningInFG inForeground: $isInForeground",
       "Background push received when app is alive and runningInFS: $isRunningInFG inForeground: $isInForeground",
@@ -259,18 +259,18 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
 }
 }
 
 
 Future<void> _logFGHeartBeatInfo() async {
 Future<void> _logFGHeartBeatInfo() async {
-  bool isRunningInFG = await _isRunningInForeground();
+  final bool isRunningInFG = await _isRunningInForeground();
   final prefs = await SharedPreferences.getInstance();
   final prefs = await SharedPreferences.getInstance();
   await prefs.reload();
   await prefs.reload();
-  var lastFGTaskHeartBeatTime = prefs.getInt(kLastFGTaskHeartBeatTime) ?? 0;
-  String lastRun = lastFGTaskHeartBeatTime == 0
+  final lastFGTaskHeartBeatTime = prefs.getInt(kLastFGTaskHeartBeatTime) ?? 0;
+  final String lastRun = lastFGTaskHeartBeatTime == 0
       ? 'never'
       ? 'never'
       : DateTime.fromMicrosecondsSinceEpoch(lastFGTaskHeartBeatTime).toString();
       : DateTime.fromMicrosecondsSinceEpoch(lastFGTaskHeartBeatTime).toString();
   _logger.info('isAlreaduunningFG: $isRunningInFG, last Beat: $lastRun');
   _logger.info('isAlreaduunningFG: $isRunningInFG, last Beat: $lastRun');
 }
 }
 
 
 void _scheduleSuicide(Duration duration, [String taskID]) {
 void _scheduleSuicide(Duration duration, [String taskID]) {
-  var taskIDVal = taskID ?? 'no taskID';
+  final taskIDVal = taskID ?? 'no taskID';
   _logger.warning("Schedule seppuku taskID: $taskIDVal");
   _logger.warning("Schedule seppuku taskID: $taskIDVal");
   Future.delayed(duration, () {
   Future.delayed(duration, () {
     _logger.warning("TLE, committing seppuku for taskID: $taskIDVal");
     _logger.warning("TLE, committing seppuku for taskID: $taskIDVal");

+ 1 - 1
lib/models/collection.dart

@@ -85,7 +85,7 @@ class Collection {
     String mMdEncodedJson,
     String mMdEncodedJson,
     int mMdVersion,
     int mMdVersion,
   }) {
   }) {
-    Collection result = Collection(
+    final Collection result = Collection(
       id ?? this.id,
       id ?? this.id,
       owner ?? this.owner,
       owner ?? this.owner,
       encryptedKey ?? this.encryptedKey,
       encryptedKey ?? this.encryptedKey,

+ 15 - 10
lib/models/file.dart

@@ -54,12 +54,14 @@ class File extends EnteFile {
 
 
   set pubMagicMetadata(val) => _pubMmd = val;
   set pubMagicMetadata(val) => _pubMmd = val;
 
 
-  static const kCurrentMetadataVersion = 1;
+  // in Version 1, live photo hash is stored as zip's hash.
+  // in V2: LivePhoto hash is stored as imgHash:vidHash
+  static const kCurrentMetadataVersion = 2;
 
 
   File();
   File();
 
 
-  static File fromAsset(String pathName, AssetEntity asset) {
-    File file = File();
+  static Future<File> fromAsset(String pathName, AssetEntity asset) async {
+    final File file = File();
     file.localID = asset.id;
     file.localID = asset.id;
     file.title = asset.title;
     file.title = asset.title;
     file.deviceFolder = pathName;
     file.deviceFolder = pathName;
@@ -134,6 +136,15 @@ class File extends EnteFile {
     duration = metadata["duration"] ?? 0;
     duration = metadata["duration"] ?? 0;
     exif = metadata["exif"];
     exif = metadata["exif"];
     hash = metadata["hash"];
     hash = metadata["hash"];
+    // handle past live photos upload from web client
+    if (hash == null &&
+        fileType == FileType.livePhoto &&
+        metadata.containsKey('imgHash') &&
+        metadata.containsKey('vidHash')) {
+      // convert to imgHash:vidHash
+      hash =
+          '${metadata['imgHash']}$kLivePhotoHashSeparator${metadata['vidHash']}';
+    }
     metadataVersion = metadata["version"] ?? 0;
     metadataVersion = metadata["version"] ?? 0;
   }
   }
 
 
@@ -155,13 +166,7 @@ class File extends EnteFile {
         creationTime = exifTime.microsecondsSinceEpoch;
         creationTime = exifTime.microsecondsSinceEpoch;
       }
       }
     }
     }
-    // in metadataVersion V1, the hash for livePhoto is the hash of the
-    // zipped file.
-    // web uploads files without MetadataVersion and upload image hash as 'ha
-    // sh' key and video as 'vidHash'
-    hash = (fileType == FileType.livePhoto)
-        ? mediaUploadData.zipHash
-        : mediaUploadData.fileHash;
+    hash = mediaUploadData.hashData?.fileHash;
     return getMetadata();
     return getMetadata();
   }
   }
 
 

+ 3 - 3
lib/models/selected_files.dart

@@ -9,7 +9,7 @@ class SelectedFiles extends ChangeNotifier {
     // To handle the cases, where the file might have changed due to upload
     // To handle the cases, where the file might have changed due to upload
     // or any other update, using file.generatedID to track if this file was already
     // or any other update, using file.generatedID to track if this file was already
     // selected or not
     // selected or not
-    File alreadySelected = files.firstWhere(
+    final File alreadySelected = files.firstWhere(
       (element) => element.generatedID == file.generatedID,
       (element) => element.generatedID == file.generatedID,
       orElse: () => null,
       orElse: () => null,
     );
     );
@@ -24,7 +24,7 @@ class SelectedFiles extends ChangeNotifier {
   }
   }
 
 
   bool isFileSelected(File file) {
   bool isFileSelected(File file) {
-    File alreadySelected = files.firstWhere(
+    final File alreadySelected = files.firstWhere(
       (element) => element.generatedID == file.generatedID,
       (element) => element.generatedID == file.generatedID,
       orElse: () => null,
       orElse: () => null,
     );
     );
@@ -32,7 +32,7 @@ class SelectedFiles extends ChangeNotifier {
   }
   }
 
 
   bool isPartOfLastSection(File file) {
   bool isPartOfLastSection(File file) {
-    File alreadySelected = lastSelections.firstWhere(
+    final File alreadySelected = lastSelections.firstWhere(
       (element) => element.generatedID == file.generatedID,
       (element) => element.generatedID == file.generatedID,
       orElse: () => null,
       orElse: () => null,
     );
     );

+ 1 - 1
lib/models/subscription.dart

@@ -144,7 +144,7 @@ class Attributes {
   }
   }
 
 
   Map<String, dynamic> toJson() {
   Map<String, dynamic> toJson() {
-    var map = <String, dynamic>{};
+    final map = <String, dynamic>{};
     map["isCancelled"] = isCancelled;
     map["isCancelled"] = isCancelled;
     map["customerID"] = customerID;
     map["customerID"] = customerID;
     return map;
     return map;

+ 24 - 19
lib/services/collections_service.dart

@@ -308,7 +308,7 @@ class CollectionsService {
       // read the existing magic metadata and apply new updates to existing data
       // read the existing magic metadata and apply new updates to existing data
       // current update is simple replace. This will be enhanced in the future,
       // current update is simple replace. This will be enhanced in the future,
       // as required.
       // as required.
-      Map<String, dynamic> jsonToUpdate =
+      final Map<String, dynamic> jsonToUpdate =
           jsonDecode(collection.mMdEncodedJson ?? '{}');
           jsonDecode(collection.mMdEncodedJson ?? '{}');
       newMetadataUpdate.forEach((key, value) {
       newMetadataUpdate.forEach((key, value) {
         jsonToUpdate[key] = value;
         jsonToUpdate[key] = value;
@@ -325,7 +325,7 @@ class CollectionsService {
       );
       );
       // for required field, the json validator on golang doesn't treat 0 as valid
       // for required field, the json validator on golang doesn't treat 0 as valid
       // value. Instead of changing version to ptr, decided to start version with 1.
       // value. Instead of changing version to ptr, decided to start version with 1.
-      int currentVersion = max(collection.mMdVersion, 1);
+      final int currentVersion = max(collection.mMdVersion, 1);
       final params = UpdateMagicMetadataRequest(
       final params = UpdateMagicMetadataRequest(
         id: collection.id,
         id: collection.id,
         magicMetadata: MetadataRequest(
         magicMetadata: MetadataRequest(
@@ -631,29 +631,30 @@ class CollectionsService {
   }
   }
 
 
   Future<void> linkLocalFileToExistingUploadedFileInAnotherCollection(
   Future<void> linkLocalFileToExistingUploadedFileInAnotherCollection(
-    int destCollectionID,
-    File localFileToUpload,
-    File file,
-  ) async {
+    int destCollectionID, {
+    @required File localFileToUpload,
+    @required File existingUploadedFile,
+  }) async {
     final params = <String, dynamic>{};
     final params = <String, dynamic>{};
     params["collectionID"] = destCollectionID;
     params["collectionID"] = destCollectionID;
     params["files"] = [];
     params["files"] = [];
+    final int uploadedFileID = existingUploadedFile.uploadedFileID;
 
 
-    final key = decryptFileKey(file);
-    file.generatedID = localFileToUpload.generatedID; // So that a new entry is
-    // created in the FilesDB
-    file.localID = localFileToUpload.localID;
-    file.collectionID = destCollectionID;
+    // encrypt the fileKey with destination collection's key
+    final fileKey = decryptFileKey(existingUploadedFile);
     final encryptedKeyData =
     final encryptedKeyData =
-        CryptoUtil.encryptSync(key, getCollectionKey(destCollectionID));
-    file.encryptedKey = Sodium.bin2base64(encryptedKeyData.encryptedData);
-    file.keyDecryptionNonce = Sodium.bin2base64(encryptedKeyData.nonce);
+        CryptoUtil.encryptSync(fileKey, getCollectionKey(destCollectionID));
+
+    localFileToUpload.encryptedKey =
+        Sodium.bin2base64(encryptedKeyData.encryptedData);
+    localFileToUpload.keyDecryptionNonce =
+        Sodium.bin2base64(encryptedKeyData.nonce);
 
 
     params["files"].add(
     params["files"].add(
       CollectionFileItem(
       CollectionFileItem(
-        file.uploadedFileID,
-        file.encryptedKey,
-        file.keyDecryptionNonce,
+        uploadedFileID,
+        localFileToUpload.encryptedKey,
+        localFileToUpload.keyDecryptionNonce,
       ).toMap(),
       ).toMap(),
     );
     );
 
 
@@ -665,8 +666,12 @@ class CollectionsService {
           headers: {"X-Auth-Token": Configuration.instance.getToken()},
           headers: {"X-Auth-Token": Configuration.instance.getToken()},
         ),
         ),
       );
       );
-      await _filesDB.insertMultiple([file]);
-      Bus.instance.fire(CollectionUpdatedEvent(destCollectionID, [file]));
+      localFileToUpload.collectionID = destCollectionID;
+      localFileToUpload.uploadedFileID = uploadedFileID;
+      await _filesDB.insertMultiple([localFileToUpload]);
+      Bus.instance.fire(
+        CollectionUpdatedEvent(destCollectionID, [localFileToUpload]),
+      );
     } catch (e) {
     } catch (e) {
       rethrow;
       rethrow;
     }
     }

+ 1 - 1
lib/services/deduplication_service.dart

@@ -18,7 +18,7 @@ class DeduplicationService {
 
 
   Future<List<DuplicateFiles>> getDuplicateFiles() async {
   Future<List<DuplicateFiles>> getDuplicateFiles() async {
     try {
     try {
-      DuplicateFilesResponse dupes = await _fetchDuplicateFileIDs();
+      final DuplicateFilesResponse dupes = await _fetchDuplicateFileIDs();
       final ids = <int>[];
       final ids = <int>[];
       for (final dupe in dupes.duplicates) {
       for (final dupe in dupes.duplicates) {
         ids.addAll(dupe.fileIDs);
         ids.addAll(dupe.fileIDs);

+ 1 - 1
lib/services/favorites_service.dart

@@ -53,7 +53,7 @@ class FavoritesService {
 
 
   Future<void> removeFromFavorites(File file) async {
   Future<void> removeFromFavorites(File file) async {
     final collectionID = await _getOrCreateFavoriteCollectionID();
     final collectionID = await _getOrCreateFavoriteCollectionID();
-    var fileID = file.uploadedFileID;
+    final fileID = file.uploadedFileID;
     if (fileID == null) {
     if (fileID == null) {
       // Do nothing, ignore
       // Do nothing, ignore
     } else {
     } else {

+ 1 - 1
lib/services/feature_flag_service.dart

@@ -94,7 +94,7 @@ class FeatureFlagService {
   }
   }
 
 
   bool _isInternalUserOrDebugBuild() {
   bool _isInternalUserOrDebugBuild() {
-    String email = Configuration.instance.getEmail();
+    final String email = Configuration.instance.getEmail();
     return (email != null && email.endsWith("@ente.io")) || kDebugMode;
     return (email != null && email.endsWith("@ente.io")) || kDebugMode;
   }
   }
 
 

+ 4 - 4
lib/services/file_magic_service.dart

@@ -30,7 +30,7 @@ class FileMagicService {
       FileMagicService._privateConstructor();
       FileMagicService._privateConstructor();
 
 
   Future<void> changeVisibility(List<File> files, int visibility) async {
   Future<void> changeVisibility(List<File> files, int visibility) async {
-    Map<String, dynamic> update = {kMagicKeyVisibility: visibility};
+    final Map<String, dynamic> update = {kMagicKeyVisibility: visibility};
     await _updateMagicData(files, update);
     await _updateMagicData(files, update);
     if (visibility == kVisibilityVisible) {
     if (visibility == kVisibilityVisible) {
       // Force reload home gallery to pull in the now unarchived files
       // Force reload home gallery to pull in the now unarchived files
@@ -62,7 +62,7 @@ class FileMagicService {
         // read the existing magic metadata and apply new updates to existing data
         // read the existing magic metadata and apply new updates to existing data
         // current update is simple replace. This will be enhanced in the future,
         // current update is simple replace. This will be enhanced in the future,
         // as required.
         // as required.
-        Map<String, dynamic> jsonToUpdate = jsonDecode(file.pubMmdEncodedJson);
+        final Map<String, dynamic> jsonToUpdate = jsonDecode(file.pubMmdEncodedJson);
         newMetadataUpdate.forEach((key, value) {
         newMetadataUpdate.forEach((key, value) {
           jsonToUpdate[key] = value;
           jsonToUpdate[key] = value;
         });
         });
@@ -132,7 +132,7 @@ class FileMagicService {
         // read the existing magic metadata and apply new updates to existing data
         // read the existing magic metadata and apply new updates to existing data
         // current update is simple replace. This will be enhanced in the future,
         // current update is simple replace. This will be enhanced in the future,
         // as required.
         // as required.
-        Map<String, dynamic> jsonToUpdate = jsonDecode(file.mMdEncodedJson);
+        final Map<String, dynamic> jsonToUpdate = jsonDecode(file.mMdEncodedJson);
         newMetadataUpdate.forEach((key, value) {
         newMetadataUpdate.forEach((key, value) {
           jsonToUpdate[key] = value;
           jsonToUpdate[key] = value;
         });
         });
@@ -224,7 +224,7 @@ class MetadataRequest {
   }
   }
 
 
   Map<String, dynamic> toJson() {
   Map<String, dynamic> toJson() {
-    var map = <String, dynamic>{};
+    final map = <String, dynamic>{};
     map['version'] = version;
     map['version'] = version;
     map['count'] = count;
     map['count'] = count;
     map['data'] = data;
     map['data'] = data;

+ 2 - 2
lib/services/local/local_sync_util.dart

@@ -194,7 +194,7 @@ Future<List<File>> _convertLocalAssetsToUniqueFiles(
       if (!alreadySeenLocalIDs.contains(localID)) {
       if (!alreadySeenLocalIDs.contains(localID)) {
         var assetEntity = await AssetEntity.fromId(localID);
         var assetEntity = await AssetEntity.fromId(localID);
         files.add(
         files.add(
-          File.fromAsset(localPathName, assetEntity),
+          await File.fromAsset(localPathName, assetEntity),
         );
         );
         alreadySeenLocalIDs.add(localID);
         alreadySeenLocalIDs.add(localID);
       }
       }
@@ -285,7 +285,7 @@ Future<Tuple2<Set<String>, List<File>>> _getLocalIDsAndFilesFromAssets(
     if (!alreadySeenLocalIDs.contains(entity.id) &&
     if (!alreadySeenLocalIDs.contains(entity.id) &&
         assetCreatedOrUpdatedAfterGivenTime) {
         assetCreatedOrUpdatedAfterGivenTime) {
       try {
       try {
-        final file = File.fromAsset(pathEntity.name, entity);
+        final file = await File.fromAsset(pathEntity.name, entity);
         files.add(file);
         files.add(file);
       } catch (e) {
       } catch (e) {
         _logger.severe(e);
         _logger.severe(e);

+ 40 - 33
lib/services/local_file_update_service.dart

@@ -5,8 +5,9 @@ import 'dart:io';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
 import 'package:logging/logging.dart';
 import 'package:logging/logging.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photo_manager/photo_manager.dart';
-import 'package:photos/db/file_migration_db.dart';
+import 'package:photos/db/file_updation_db.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/db/files_db.dart';
+import 'package:photos/models/file.dart' as ente;
 import 'package:photos/utils/file_uploader_util.dart';
 import 'package:photos/utils/file_uploader_util.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
 
@@ -14,7 +15,7 @@ import 'package:shared_preferences/shared_preferences.dart';
 // changed/modified on the device and needed to be uploaded again.
 // changed/modified on the device and needed to be uploaded again.
 class LocalFileUpdateService {
 class LocalFileUpdateService {
   FilesDB _filesDB;
   FilesDB _filesDB;
-  FilesMigrationDB _filesMigrationDB;
+  FileUpdationDB _fileUpdationDB;
   SharedPreferences _prefs;
   SharedPreferences _prefs;
   Logger _logger;
   Logger _logger;
   static const isLocationMigrationComplete = "fm_isLocationMigrationComplete";
   static const isLocationMigrationComplete = "fm_isLocationMigrationComplete";
@@ -24,7 +25,7 @@ class LocalFileUpdateService {
   LocalFileUpdateService._privateConstructor() {
   LocalFileUpdateService._privateConstructor() {
     _logger = Logger((LocalFileUpdateService).toString());
     _logger = Logger((LocalFileUpdateService).toString());
     _filesDB = FilesDB.instance;
     _filesDB = FilesDB.instance;
-    _filesMigrationDB = FilesMigrationDB.instance;
+    _fileUpdationDB = FileUpdationDB.instance;
   }
   }
 
 
   Future<void> init() async {
   Future<void> init() async {
@@ -55,11 +56,10 @@ class LocalFileUpdateService {
         await _runMigrationForFilesWithMissingLocation();
         await _runMigrationForFilesWithMissingLocation();
       }
       }
       await _markFilesWhichAreActuallyUpdated();
       await _markFilesWhichAreActuallyUpdated();
-      _existingMigration.complete();
-      _existingMigration = null;
     } catch (e, s) {
     } catch (e, s) {
       _logger.severe('failed to perform migration', e, s);
       _logger.severe('failed to perform migration', e, s);
-      _existingMigration.complete();
+    } finally {
+      _existingMigration?.complete();
       _existingMigration = null;
       _existingMigration = null;
     }
     }
   }
   }
@@ -73,10 +73,10 @@ class LocalFileUpdateService {
     bool hasData = true;
     bool hasData = true;
     const int limitInBatch = 100;
     const int limitInBatch = 100;
     while (hasData) {
     while (hasData) {
-      var localIDsToProcess =
-          await _filesMigrationDB.getLocalIDsForPotentialReUpload(
+      final localIDsToProcess =
+          await _fileUpdationDB.getLocalIDsForPotentialReUpload(
         limitInBatch,
         limitInBatch,
-        FilesMigrationDB.modificationTimeUpdated,
+        FileUpdationDB.modificationTimeUpdated,
       );
       );
       if (localIDsToProcess.isEmpty) {
       if (localIDsToProcess.isEmpty) {
         hasData = false;
         hasData = false;
@@ -97,18 +97,21 @@ class LocalFileUpdateService {
     List<String> localIDsToProcess,
     List<String> localIDsToProcess,
   ) async {
   ) async {
     _logger.info("files to process ${localIDsToProcess.length} for reupload");
     _logger.info("files to process ${localIDsToProcess.length} for reupload");
-    var localFiles = await FilesDB.instance.getLocalFiles(localIDsToProcess);
+    List<ente.File> localFiles =
+        (await FilesDB.instance.getLocalFiles(localIDsToProcess));
     Set<String> processedIDs = {};
     Set<String> processedIDs = {};
-    for (var file in localFiles) {
+    for (ente.File file in localFiles) {
       if (processedIDs.contains(file.localID)) {
       if (processedIDs.contains(file.localID)) {
         continue;
         continue;
       }
       }
       MediaUploadData uploadData;
       MediaUploadData uploadData;
       try {
       try {
-        uploadData = await getUploadDataFromEnteFile(file);
-        if (file.hash != null ||
-            (file.hash == uploadData.fileHash ||
-                file.hash == uploadData.zipHash)) {
+        uploadData = await getUploadData(file);
+        if (uploadData != null &&
+            uploadData.hashData != null &&
+            file.hash != null &&
+            (file.hash == uploadData.hashData.fileHash ||
+                file.hash == uploadData.hashData.zipHash)) {
           _logger.info("Skip file update as hash matched ${file.tag()}");
           _logger.info("Skip file update as hash matched ${file.tag()}");
         } else {
         } else {
           _logger.info(
           _logger.info(
@@ -126,24 +129,28 @@ class LocalFileUpdateService {
         processedIDs.add(file.localID);
         processedIDs.add(file.localID);
       } catch (e) {
       } catch (e) {
         _logger.severe("Failed to get file uploadData", e);
         _logger.severe("Failed to get file uploadData", e);
-      } finally {
-        // delete the file from app's internal cache if it was copied to app
-        // for upload. Shared Media should only be cleared when the upload
-        // succeeds.
-        if (Platform.isIOS &&
-            uploadData != null &&
-            uploadData.sourceFile != null) {
-          await uploadData.sourceFile.delete();
-        }
-      }
+      } finally {}
     }
     }
     debugPrint("Deleting files ${processedIDs.length}");
     debugPrint("Deleting files ${processedIDs.length}");
-    await _filesMigrationDB.deleteByLocalIDs(
+    await _fileUpdationDB.deleteByLocalIDs(
       processedIDs.toList(),
       processedIDs.toList(),
-      FilesMigrationDB.modificationTimeUpdated,
+      FileUpdationDB.modificationTimeUpdated,
     );
     );
   }
   }
 
 
+  Future<MediaUploadData> getUploadData(ente.File file) async {
+    final mediaUploadData = await getUploadDataFromEnteFile(file);
+    // delete the file from app's internal cache if it was copied to app
+    // for upload. Shared Media should only be cleared when the upload
+    // succeeds.
+    if (Platform.isIOS &&
+        mediaUploadData != null &&
+        mediaUploadData.sourceFile != null) {
+      await mediaUploadData.sourceFile.delete();
+    }
+    return mediaUploadData;
+  }
+
   Future<void> _runMigrationForFilesWithMissingLocation() async {
   Future<void> _runMigrationForFilesWithMissingLocation() async {
     if (!Platform.isAndroid) {
     if (!Platform.isAndroid) {
       return;
       return;
@@ -158,9 +165,9 @@ class LocalFileUpdateService {
       const int limitInBatch = 100;
       const int limitInBatch = 100;
       while (hasData) {
       while (hasData) {
         var localIDsToProcess =
         var localIDsToProcess =
-            await _filesMigrationDB.getLocalIDsForPotentialReUpload(
+            await _fileUpdationDB.getLocalIDsForPotentialReUpload(
           limitInBatch,
           limitInBatch,
-          FilesMigrationDB.missingLocation,
+          FileUpdationDB.missingLocation,
         );
         );
         if (localIDsToProcess.isEmpty) {
         if (localIDsToProcess.isEmpty) {
           hasData = false;
           hasData = false;
@@ -206,9 +213,9 @@ class LocalFileUpdateService {
     }
     }
     _logger.info('marking ${localIDsWithLocation.length} files for re-upload');
     _logger.info('marking ${localIDsWithLocation.length} files for re-upload');
     await _filesDB.markForReUploadIfLocationMissing(localIDsWithLocation);
     await _filesDB.markForReUploadIfLocationMissing(localIDsWithLocation);
-    await _filesMigrationDB.deleteByLocalIDs(
+    await _fileUpdationDB.deleteByLocalIDs(
       localIDsToProcess,
       localIDsToProcess,
-      FilesMigrationDB.missingLocation,
+      FileUpdationDB.missingLocation,
     );
     );
   }
   }
 
 
@@ -219,9 +226,9 @@ class LocalFileUpdateService {
     final sTime = DateTime.now().microsecondsSinceEpoch;
     final sTime = DateTime.now().microsecondsSinceEpoch;
     _logger.info('importing files without location info');
     _logger.info('importing files without location info');
     var fileLocalIDs = await _filesDB.getLocalFilesBackedUpWithoutLocation();
     var fileLocalIDs = await _filesDB.getLocalFilesBackedUpWithoutLocation();
-    await _filesMigrationDB.insertMultiple(
+    await _fileUpdationDB.insertMultiple(
       fileLocalIDs,
       fileLocalIDs,
-      FilesMigrationDB.missingLocation,
+      FileUpdationDB.missingLocation,
     );
     );
     final eTime = DateTime.now().microsecondsSinceEpoch;
     final eTime = DateTime.now().microsecondsSinceEpoch;
     final d = Duration(microseconds: eTime - sTime);
     final d = Duration(microseconds: eTime - sTime);

+ 6 - 8
lib/services/local_sync_service.dart

@@ -8,7 +8,7 @@ import 'package:photo_manager/photo_manager.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/db/device_files_db.dart';
 import 'package:photos/db/device_files_db.dart';
-import 'package:photos/db/file_migration_db.dart';
+import 'package:photos/db/file_updation_db.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
 import 'package:photos/events/sync_status_update_event.dart';
 import 'package:photos/events/sync_status_update_event.dart';
@@ -207,7 +207,7 @@ class LocalSyncService {
     if (_prefs.containsKey(kEditedFileIDsKey)) {
     if (_prefs.containsKey(kEditedFileIDsKey)) {
       return _prefs.getStringList(kEditedFileIDsKey);
       return _prefs.getStringList(kEditedFileIDsKey);
     } else {
     } else {
-      List<String> editedIDs = [];
+      final List<String> editedIDs = [];
       return editedIDs;
       return editedIDs;
     }
     }
   }
   }
@@ -332,15 +332,13 @@ class LocalSyncService {
       _logger.info(
       _logger.info(
         updatedFiles.length.toString() + " local files were updated.",
         updatedFiles.length.toString() + " local files were updated.",
       );
       );
-      List<String> updatedLocalIDs = [];
+      final List<String> updatedLocalIDs = [];
       for (final file in updatedFiles) {
       for (final file in updatedFiles) {
-        if (file.localID != null) {
-          updatedLocalIDs.add(file.localID);
-        }
+        updatedLocalIDs.add(file.localID);
       }
       }
-      await FilesMigrationDB.instance.insertMultiple(
+      await FileUpdationDB.instance.insertMultiple(
         updatedLocalIDs,
         updatedLocalIDs,
-        FilesMigrationDB.modificationTimeUpdated,
+        FileUpdationDB.modificationTimeUpdated,
       );
       );
     }
     }
   }
   }

+ 4 - 4
lib/services/remote_sync_service.dart

@@ -306,7 +306,7 @@ class RemoteSyncService {
     _logger.info(editedFiles.length.toString() + " files edited.");
     _logger.info(editedFiles.length.toString() + " files edited.");
 
 
     _completedUploads = 0;
     _completedUploads = 0;
-    int toBeUploaded =
+    final int toBeUploaded =
         filesToBeUploaded.length + updatedFileIDs.length + editedFiles.length;
         filesToBeUploaded.length + updatedFileIDs.length + editedFiles.length;
 
 
     if (toBeUploaded > 0) {
     if (toBeUploaded > 0) {
@@ -415,8 +415,8 @@ class RemoteSyncService {
         localButUpdatedOnRemote = 0,
         localButUpdatedOnRemote = 0,
         localButAddedToNewCollectionOnRemote = 0;
         localButAddedToNewCollectionOnRemote = 0;
     bool hasAnyCreationTimeChanged = false;
     bool hasAnyCreationTimeChanged = false;
-    List<File> toBeInserted = [];
-    int userID = Configuration.instance.getUserID();
+    final List<File> toBeInserted = [];
+    final int userID = Configuration.instance.getUserID();
     for (File file in diff) {
     for (File file in diff) {
       final existingFiles = file.deviceFolder == null
       final existingFiles = file.deviceFolder == null
           ? null
           ? null
@@ -461,7 +461,7 @@ class RemoteSyncService {
         } else {
         } else {
           file.localID = null;
           file.localID = null;
         }
         }
-        bool wasUploadedOnAPreviousInstallation =
+        final bool wasUploadedOnAPreviousInstallation =
             existingFiles.length == 1 && existingFiles[0].collectionID == null;
             existingFiles.length == 1 && existingFiles[0].collectionID == null;
         if (wasUploadedOnAPreviousInstallation) {
         if (wasUploadedOnAPreviousInstallation) {
           file.generatedID = existingFiles[0].generatedID;
           file.generatedID = existingFiles[0].generatedID;

+ 2 - 2
lib/services/user_service.dart

@@ -806,7 +806,7 @@ class UserService {
 
 
   Future<String> getPaymentToken() async {
   Future<String> getPaymentToken() async {
     try {
     try {
-      var response = await _dio.get(
+      final response = await _dio.get(
         "${_config.getHttpEndpoint()}/users/payment-token",
         "${_config.getHttpEndpoint()}/users/payment-token",
         options: Options(
         options: Options(
           headers: {
           headers: {
@@ -827,7 +827,7 @@ class UserService {
 
 
   Future<String> getFamiliesToken() async {
   Future<String> getFamiliesToken() async {
     try {
     try {
-      var response = await _dio.get(
+      final response = await _dio.get(
         "${_config.getHttpEndpoint()}/users/families-token",
         "${_config.getHttpEndpoint()}/users/families-token",
         options: Options(
         options: Options(
           headers: {
           headers: {

+ 2 - 2
lib/ui/account/delete_account_page.dart

@@ -145,7 +145,7 @@ class DeleteAccountPage extends StatelessWidget {
     DeleteChallengeResponse response,
     DeleteChallengeResponse response,
   ) async {
   ) async {
     AppLock.of(context).setEnabled(false);
     AppLock.of(context).setEnabled(false);
-    String reason = "Please authenticate to initiate account deletion";
+    const String reason = "Please authenticate to initiate account deletion";
     final result = await requestAuthentication(reason);
     final result = await requestAuthentication(reason);
     AppLock.of(context).setEnabled(
     AppLock.of(context).setEnabled(
       Configuration.instance.shouldShowLockScreen(),
       Configuration.instance.shouldShowLockScreen(),
@@ -177,7 +177,7 @@ class DeleteAccountPage extends StatelessWidget {
   }
   }
 
 
   Future<void> _requestEmailForDeletion(BuildContext context) async {
   Future<void> _requestEmailForDeletion(BuildContext context) async {
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text(
       title: const Text(
         "Delete account",
         "Delete account",
         style: TextStyle(
         style: TextStyle(

+ 29 - 2
lib/ui/account/password_reentry_page.dart

@@ -3,10 +3,13 @@ import 'package:logging/logging.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/events/subscription_purchased_event.dart';
 import 'package:photos/events/subscription_purchased_event.dart';
+import 'package:photos/models/key_attributes.dart';
 import 'package:photos/ui/account/recovery_page.dart';
 import 'package:photos/ui/account/recovery_page.dart';
+import 'package:photos/ui/common/dialogs.dart';
 import 'package:photos/ui/common/dynamic_fab.dart';
 import 'package:photos/ui/common/dynamic_fab.dart';
 import 'package:photos/ui/home_widget.dart';
 import 'package:photos/ui/home_widget.dart';
 import 'package:photos/utils/dialog_util.dart';
 import 'package:photos/utils/dialog_util.dart';
+import 'package:photos/utils/email_util.dart';
 
 
 class PasswordReentryPage extends StatefulWidget {
 class PasswordReentryPage extends StatefulWidget {
   const PasswordReentryPage({Key key}) : super(key: key);
   const PasswordReentryPage({Key key}) : super(key: key);
@@ -16,6 +19,7 @@ class PasswordReentryPage extends StatefulWidget {
 }
 }
 
 
 class _PasswordReentryPageState extends State<PasswordReentryPage> {
 class _PasswordReentryPageState extends State<PasswordReentryPage> {
+  final _logger = Logger((_PasswordReentryPageState).toString());
   final _passwordController = TextEditingController();
   final _passwordController = TextEditingController();
   final FocusNode _passwordFocusNode = FocusNode();
   final FocusNode _passwordFocusNode = FocusNode();
   String email;
   String email;
@@ -72,9 +76,26 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
               Configuration.instance.getKeyAttributes(),
               Configuration.instance.getKeyAttributes(),
             );
             );
           } catch (e, s) {
           } catch (e, s) {
-            Logger("PRP").severe("Password verification failed", e, s);
+            _logger.severe("Password verification failed", e, s);
             await dialog.hide();
             await dialog.hide();
-            showErrorDialog(context, "Incorrect password", "Please try again");
+
+            final dialogUserChoice = await showChoiceDialog(
+              context,
+              "Incorrect password",
+              "Please try again",
+              firstAction: "Contact Support",
+              firstActionColor: Theme.of(context).colorScheme.primary,
+              secondAction: "Ok",
+              secondActionColor: Theme.of(context).colorScheme.primary,
+            );
+            if (dialogUserChoice == DialogUserChoice.firstChoice) {
+              await sendLogs(
+                context,
+                "Contact support",
+                "support@ente.io",
+                postShare: () {},
+              );
+            }
             return;
             return;
           }
           }
           await dialog.hide();
           await dialog.hide();
@@ -231,4 +252,10 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
       ],
       ],
     );
     );
   }
   }
+
+  void validatePreVerificationState(KeyAttributes keyAttributes) {
+    if (keyAttributes == null) {
+      throw Exception("Key Attributes can not be null");
+    }
+  }
 }
 }

+ 1 - 1
lib/ui/account/recovery_key_page.dart

@@ -186,7 +186,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
   }
   }
 
 
   List<Widget> _saveOptions(BuildContext context, String recoveryKey) {
   List<Widget> _saveOptions(BuildContext context, String recoveryKey) {
-    List<Widget> childrens = [];
+    final List<Widget> childrens = [];
     if (!_hasTriedToSave) {
     if (!_hasTriedToSave) {
       childrens.add(
       childrens.add(
         ElevatedButton(
         ElevatedButton(

+ 2 - 2
lib/ui/account/sessions_page.dart

@@ -41,7 +41,7 @@ class _SessionsPageState extends State<SessionsPage> {
     if (_sessions == null) {
     if (_sessions == null) {
       return const Center(child: EnteLoadingWidget());
       return const Center(child: EnteLoadingWidget());
     }
     }
-    List<Widget> rows = [];
+    final List<Widget> rows = [];
     rows.add(const Padding(padding: EdgeInsets.all(4)));
     rows.add(const Padding(padding: EdgeInsets.all(4)));
     for (final session in _sessions.sessions) {
     for (final session in _sessions.sessions) {
       rows.add(_getSessionWidget(session));
       rows.add(_getSessionWidget(session));
@@ -163,7 +163,7 @@ class _SessionsPageState extends State<SessionsPage> {
         ),
         ),
       );
       );
     }
     }
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("Terminate session?"),
       title: const Text("Terminate session?"),
       content: text,
       content: text,
       actions: [
       actions: [

+ 2 - 2
lib/ui/account/two_factor_setup_page.dart

@@ -116,7 +116,7 @@ class _TwoFactorSetupPageState extends State<TwoFactorSetupPage>
             Divider(
             Divider(
               height: 1,
               height: 1,
               thickness: 1,
               thickness: 1,
-              color: Theme.of(context).accentColor,
+              color: Theme.of(context).colorScheme.secondary,
             ),
             ),
             _getVerificationWidget(),
             _getVerificationWidget(),
           ],
           ],
@@ -126,7 +126,7 @@ class _TwoFactorSetupPageState extends State<TwoFactorSetupPage>
   }
   }
 
 
   Widget _getSecretCode() {
   Widget _getSecretCode() {
-    Color textColor = Theme.of(context).colorScheme.onSurface;
+    final Color textColor = Theme.of(context).colorScheme.onSurface;
     return GestureDetector(
     return GestureDetector(
       onTap: () async {
       onTap: () async {
         await Clipboard.setData(ClipboardData(text: widget.secretCode));
         await Clipboard.setData(ClipboardData(text: widget.secretCode));

+ 4 - 4
lib/ui/collections/collection_item_widget.dart

@@ -19,11 +19,11 @@ class CollectionItem extends StatelessWidget {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     const double horizontalPaddingOfGridRow = 16;
     const double horizontalPaddingOfGridRow = 16;
     const double crossAxisSpacingOfGrid = 9;
     const double crossAxisSpacingOfGrid = 9;
-    Size size = MediaQuery.of(context).size;
-    int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
-    double totalWhiteSpaceOfRow = (horizontalPaddingOfGridRow * 2) +
+    final Size size = MediaQuery.of(context).size;
+    final int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
+    final double totalWhiteSpaceOfRow = (horizontalPaddingOfGridRow * 2) +
         (albumsCountInOneRow - 1) * crossAxisSpacingOfGrid;
         (albumsCountInOneRow - 1) * crossAxisSpacingOfGrid;
-    TextStyle albumTitleTextStyle =
+    final TextStyle albumTitleTextStyle =
         Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 14);
         Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 14);
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
         (totalWhiteSpaceOfRow / albumsCountInOneRow);
         (totalWhiteSpaceOfRow / albumsCountInOneRow);

+ 2 - 2
lib/ui/collections/remote_collections_grid_view_widget.dart

@@ -17,8 +17,8 @@ class RemoteCollectionsGridViewWidget extends StatelessWidget {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     const double horizontalPaddingOfGridRow = 16;
     const double horizontalPaddingOfGridRow = 16;
     const double crossAxisSpacingOfGrid = 9;
     const double crossAxisSpacingOfGrid = 9;
-    Size size = MediaQuery.of(context).size;
-    int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
+    final Size size = MediaQuery.of(context).size;
+    final int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
         horizontalPaddingOfGridRow -
         horizontalPaddingOfGridRow -
         ((crossAxisSpacingOfGrid / 2) * (albumsCountInOneRow - 1));
         ((crossAxisSpacingOfGrid / 2) * (albumsCountInOneRow - 1));

+ 1 - 1
lib/ui/common/dialogs.dart

@@ -19,7 +19,7 @@ Future<DialogUserChoice> showChoiceDialog<T>(
   Color secondActionColor,
   Color secondActionColor,
   ActionType actionType = ActionType.confirm,
   ActionType actionType = ActionType.confirm,
 }) {
 }) {
-  AlertDialog alert = AlertDialog(
+  final AlertDialog alert = AlertDialog(
     title: Text(
     title: Text(
       title,
       title,
       style: TextStyle(
       style: TextStyle(

+ 2 - 2
lib/ui/create_collection_page.dart

@@ -203,7 +203,7 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
   }
   }
 
 
   void _showNameAlbumDialog() async {
   void _showNameAlbumDialog() async {
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("Album title"),
       title: const Text("Album title"),
       content: TextFormField(
       content: TextFormField(
         decoration: const InputDecoration(
         decoration: const InputDecoration(
@@ -285,7 +285,7 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
     final dialog = createProgressDialog(context, "Moving files to album...");
     final dialog = createProgressDialog(context, "Moving files to album...");
     await dialog.show();
     await dialog.show();
     try {
     try {
-      int fromCollectionID = widget.selectedFiles.files?.first?.collectionID;
+      final int fromCollectionID = widget.selectedFiles.files?.first?.collectionID;
       await CollectionsService.instance.move(
       await CollectionsService.instance.move(
         toCollectionID,
         toCollectionID,
         fromCollectionID,
         fromCollectionID,

+ 1 - 1
lib/ui/grant_permissions_widget.dart

@@ -94,7 +94,7 @@ class GrantPermissionsWidget extends StatelessWidget {
                 state == PermissionState.limited) {
                 state == PermissionState.limited) {
               await SyncService.instance.onPermissionGranted(state);
               await SyncService.instance.onPermissionGranted(state);
             } else if (state == PermissionState.denied) {
             } else if (state == PermissionState.denied) {
-              AlertDialog alert = AlertDialog(
+              final AlertDialog alert = AlertDialog(
                 title: const Text("Please grant permissions"),
                 title: const Text("Please grant permissions"),
                 content: const Text(
                 content: const Text(
                   "ente can encrypt and preserve files only if you grant access to them",
                   "ente can encrypt and preserve files only if you grant access to them",

+ 3 - 3
lib/ui/home_widget.dart

@@ -123,7 +123,7 @@ class _HomeWidgetState extends State<HomeWidget> {
     });
     });
     _triggerLogoutEvent =
     _triggerLogoutEvent =
         Bus.instance.on<TriggerLogoutEvent>().listen((event) async {
         Bus.instance.on<TriggerLogoutEvent>().listen((event) async {
-      AlertDialog alert = AlertDialog(
+      final AlertDialog alert = AlertDialog(
         title: const Text("Session expired"),
         title: const Text("Session expired"),
         content: const Text("Please login again"),
         content: const Text("Please login again"),
         actions: [
         actions: [
@@ -344,7 +344,7 @@ class _HomeWidgetState extends State<HomeWidget> {
   Future<bool> _initDeepLinks() async {
   Future<bool> _initDeepLinks() async {
     // Platform messages may fail, so we use a try/catch PlatformException.
     // Platform messages may fail, so we use a try/catch PlatformException.
     try {
     try {
-      String initialLink = await getInitialLink();
+      final String initialLink = await getInitialLink();
       // Parse the link and warn the user, if it is not correct,
       // Parse the link and warn the user, if it is not correct,
       // but keep in mind it could be `null`.
       // but keep in mind it could be `null`.
       if (initialLink != null) {
       if (initialLink != null) {
@@ -609,7 +609,7 @@ class _HomeBottomNavigationBarState extends State<HomeBottomNavigationBar> {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    bool filesAreSelected = widget.selectedFiles.files.isNotEmpty;
+    final bool filesAreSelected = widget.selectedFiles.files.isNotEmpty;
     return AnimatedContainer(
     return AnimatedContainer(
       duration: const Duration(milliseconds: 300),
       duration: const Duration(milliseconds: 300),
       curve: Curves.easeInOut,
       curve: Curves.easeInOut,

+ 1 - 1
lib/ui/huge_listview/draggable_scrollbar.dart

@@ -175,7 +175,7 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
       if (isDragging && details.delta.dy != 0) {
       if (isDragging && details.delta.dy != 0) {
         thumbOffset += details.delta.dy;
         thumbOffset += details.delta.dy;
         thumbOffset = thumbOffset.clamp(thumbMin, thumbMax);
         thumbOffset = thumbOffset.clamp(thumbMin, thumbMax);
-        double position = thumbOffset / (thumbMax - thumbMin);
+        final double position = thumbOffset / (thumbMax - thumbMin);
         widget.onChange?.call(position);
         widget.onChange?.call(position);
       }
       }
     });
     });

+ 1 - 1
lib/ui/huge_listview/huge_listview.dart

@@ -97,7 +97,7 @@ class HugeListViewState<T> extends State<HugeListView<T>> {
   }
   }
 
 
   void _sendScroll() {
   void _sendScroll() {
-    int current = _currentFirst();
+    final int current = _currentFirst();
     widget.firstShown?.call(current);
     widget.firstShown?.call(current);
     scrollKey.currentState?.setPosition(current / widget.totalCount, current);
     scrollKey.currentState?.setPosition(current / widget.totalCount, current);
   }
   }

+ 2 - 2
lib/ui/huge_listview/lazy_loading_gallery.dart

@@ -71,7 +71,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
 
 
     _currentIndexSubscription =
     _currentIndexSubscription =
         widget.currentIndexStream.listen((currentIndex) {
         widget.currentIndexStream.listen((currentIndex) {
-      bool shouldRender = (currentIndex - widget.index).abs() <
+      final bool shouldRender = (currentIndex - widget.index).abs() <
           kNumberOfDaysToRenderBeforeAndAfter;
           kNumberOfDaysToRenderBeforeAndAfter;
       if (mounted && shouldRender != _shouldRender) {
       if (mounted && shouldRender != _shouldRender) {
         setState(() {
         setState(() {
@@ -163,7 +163,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
   }
   }
 
 
   Widget _getGallery() {
   Widget _getGallery() {
-    List<Widget> childGalleries = [];
+    final List<Widget> childGalleries = [];
     for (int index = 0; index < _files.length; index += kSubGalleryItemLimit) {
     for (int index = 0; index < _files.length; index += kSubGalleryItemLimit) {
       childGalleries.add(
       childGalleries.add(
         LazyLoadingGridView(
         LazyLoadingGridView(

+ 2 - 2
lib/ui/memories_widget.dart

@@ -66,9 +66,9 @@ class MemoriesWidget extends StatelessWidget {
   }
   }
 
 
   bool _areMemoriesFromSameYear(Memory first, Memory second) {
   bool _areMemoriesFromSameYear(Memory first, Memory second) {
-    var firstDate =
+    final firstDate =
         DateTime.fromMicrosecondsSinceEpoch(first.file.creationTime);
         DateTime.fromMicrosecondsSinceEpoch(first.file.creationTime);
-    var secondDate =
+    final secondDate =
         DateTime.fromMicrosecondsSinceEpoch(second.file.creationTime);
         DateTime.fromMicrosecondsSinceEpoch(second.file.creationTime);
     return firstDate.year == secondDate.year;
     return firstDate.year == secondDate.year;
   }
   }

+ 1 - 1
lib/ui/nav_bar.dart

@@ -321,7 +321,7 @@ class _ButtonState extends State<Button> with TickerProviderStateMixin {
       expandController.forward();
       expandController.forward();
     }
     }
 
 
-    Widget icon = widget.leading ??
+    final Widget icon = widget.leading ??
         Icon(
         Icon(
           widget.icon,
           widget.icon,
           color: _expanded ? widget.iconColor : widget.iconActiveColor,
           color: _expanded ? widget.iconColor : widget.iconActiveColor,

+ 8 - 8
lib/ui/payment/payment_web_page.dart

@@ -77,7 +77,7 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
                   ),
                   ),
                 ),
                 ),
                 shouldOverrideUrlLoading: (controller, navigationAction) async {
                 shouldOverrideUrlLoading: (controller, navigationAction) async {
-                  var loadingUri = navigationAction.request.url;
+                  final loadingUri = navigationAction.request.url;
                   _logger.info("Loading url $loadingUri");
                   _logger.info("Loading url $loadingUri");
                   // handle the payment response
                   // handle the payment response
                   if (_isPaymentActionComplete(loadingUri)) {
                   if (_isPaymentActionComplete(loadingUri)) {
@@ -130,7 +130,7 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
       'action': widget.actionType,
       'action': widget.actionType,
       'redirectURL': kWebPaymentRedirectUrl,
       'redirectURL': kWebPaymentRedirectUrl,
     };
     };
-    var tryParse = Uri.tryParse(kWebPaymentBaseEndpoint);
+    final tryParse = Uri.tryParse(kWebPaymentBaseEndpoint);
     if (kDebugMode && kWebPaymentBaseEndpoint.startsWith("http://")) {
     if (kDebugMode && kWebPaymentBaseEndpoint.startsWith("http://")) {
       return Uri.http(tryParse.authority, tryParse.path, queryParameters);
       return Uri.http(tryParse.authority, tryParse.path, queryParameters);
     } else {
     } else {
@@ -173,13 +173,13 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
   }
   }
 
 
   Future<void> _handlePaymentResponse(Uri uri) async {
   Future<void> _handlePaymentResponse(Uri uri) async {
-    var queryParams = uri.queryParameters;
-    var paymentStatus = uri.queryParameters['status'] ?? '';
+    final queryParams = uri.queryParameters;
+    final paymentStatus = uri.queryParameters['status'] ?? '';
     _logger.fine('handle payment response with status $paymentStatus');
     _logger.fine('handle payment response with status $paymentStatus');
     if (paymentStatus == 'success') {
     if (paymentStatus == 'success') {
       await _handlePaymentSuccess(queryParams);
       await _handlePaymentSuccess(queryParams);
     } else if (paymentStatus == 'fail') {
     } else if (paymentStatus == 'fail') {
-      var reason = queryParams['reason'] ?? '';
+      final reason = queryParams['reason'] ?? '';
       await _handlePaymentFailure(reason);
       await _handlePaymentFailure(reason);
     } else {
     } else {
       // should never reach here
       // should never reach here
@@ -210,17 +210,17 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
 
 
   // return true if verifySubscription didn't throw any exceptions
   // return true if verifySubscription didn't throw any exceptions
   Future<void> _handlePaymentSuccess(Map<String, String> queryParams) async {
   Future<void> _handlePaymentSuccess(Map<String, String> queryParams) async {
-    var checkoutSessionID = queryParams['session_id'] ?? '';
+    final checkoutSessionID = queryParams['session_id'] ?? '';
     await _dialog.show();
     await _dialog.show();
     try {
     try {
-      var response = await billingService.verifySubscription(
+      final response = await billingService.verifySubscription(
         widget.planId,
         widget.planId,
         checkoutSessionID,
         checkoutSessionID,
         paymentProvider: kStripe,
         paymentProvider: kStripe,
       );
       );
       await _dialog.hide();
       await _dialog.hide();
       if (response != null) {
       if (response != null) {
-        var content = widget.actionType == 'buy'
+        final content = widget.actionType == 'buy'
             ? 'Your purchase was successful'
             ? 'Your purchase was successful'
             : 'Your subscription was updated successfully';
             : 'Your subscription was updated successfully';
         await _showExitPageDialog(title: 'Thank you', content: content);
         await _showExitPageDialog(title: 'Thank you', content: content);

+ 6 - 6
lib/ui/payment/stripe_subscription_page.dart

@@ -307,7 +307,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
   Future<void> _launchStripePortal() async {
   Future<void> _launchStripePortal() async {
     await _dialog.show();
     await _dialog.show();
     try {
     try {
-      String url = await _billingService.getStripeCustomerPortalUrl();
+      final String url = await _billingService.getStripeCustomerPortalUrl();
       Navigator.of(context).push(
       Navigator.of(context).push(
         MaterialPageRoute(
         MaterialPageRoute(
           builder: (BuildContext context) {
           builder: (BuildContext context) {
@@ -353,9 +353,9 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
   }
   }
 
 
   Widget _stripeRenewOrCancelButton() {
   Widget _stripeRenewOrCancelButton() {
-    bool isRenewCancelled =
+    final bool isRenewCancelled =
         _currentSubscription.attributes?.isCancelled ?? false;
         _currentSubscription.attributes?.isCancelled ?? false;
-    String title =
+    final String title =
         isRenewCancelled ? "Renew subscription" : "Cancel subscription";
         isRenewCancelled ? "Renew subscription" : "Cancel subscription";
     return TextButton(
     return TextButton(
       child: Text(
       child: Text(
@@ -370,7 +370,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
       onPressed: () async {
       onPressed: () async {
         bool confirmAction = false;
         bool confirmAction = false;
         if (isRenewCancelled) {
         if (isRenewCancelled) {
-          var choice = await showChoiceDialog(
+          final choice = await showChoiceDialog(
             context,
             context,
             title,
             title,
             "Are you sure you want to renew?",
             "Are you sure you want to renew?",
@@ -379,7 +379,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
           );
           );
           confirmAction = choice == DialogUserChoice.secondChoice;
           confirmAction = choice == DialogUserChoice.secondChoice;
         } else {
         } else {
-          var choice = await showChoiceDialog(
+          final choice = await showChoiceDialog(
             context,
             context,
             title,
             title,
             'Are you sure you want to cancel?',
             'Are you sure you want to cancel?',
@@ -455,7 +455,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
               String stripPurChaseAction = 'buy';
               String stripPurChaseAction = 'buy';
               if (_isStripeSubscriber && _hasActiveSubscription) {
               if (_isStripeSubscriber && _hasActiveSubscription) {
                 // confirm if user wants to change plan or not
                 // confirm if user wants to change plan or not
-                var result = await showChoiceDialog(
+                final result = await showChoiceDialog(
                   context,
                   context,
                   "Confirm plan change",
                   "Confirm plan change",
                   "Are you sure you want to change your plan?",
                   "Are you sure you want to change your plan?",

+ 1 - 1
lib/ui/payment/subscription_common_widgets.dart

@@ -87,7 +87,7 @@ class ValidityWidget extends StatelessWidget {
     if (currentSubscription == null) {
     if (currentSubscription == null) {
       return Container();
       return Container();
     }
     }
-    var endDate = getDateAndMonthAndYear(
+    final endDate = getDateAndMonthAndYear(
       DateTime.fromMicrosecondsSinceEpoch(currentSubscription.expiryTime),
       DateTime.fromMicrosecondsSinceEpoch(currentSubscription.expiryTime),
     );
     );
     var message = "Renews on $endDate";
     var message = "Renews on $endDate";

+ 2 - 2
lib/ui/payment/subscription_plan_widget.dart

@@ -16,13 +16,13 @@ class SubscriptionPlanWidget extends StatelessWidget {
   final bool isActive;
   final bool isActive;
 
 
   String _displayPrice() {
   String _displayPrice() {
-    var result = price + (period.isNotEmpty ? " / " + period : "");
+    final result = price + (period.isNotEmpty ? " / " + period : "");
     return result.isNotEmpty ? result : "Trial plan";
     return result.isNotEmpty ? result : "Trial plan";
   }
   }
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    Color textColor = isActive ? Colors.white : Colors.black;
+    final Color textColor = isActive ? Colors.white : Colors.black;
     return Container(
     return Container(
       width: double.infinity,
       width: double.infinity,
       color: Theme.of(context).colorScheme.onPrimary,
       color: Theme.of(context).colorScheme.onPrimary,

+ 3 - 3
lib/ui/settings/account_section_widget.dart

@@ -40,7 +40,7 @@ class AccountSectionWidgetState extends State<AccountSectionWidget> {
           behavior: HitTestBehavior.translucent,
           behavior: HitTestBehavior.translucent,
           onTap: () async {
           onTap: () async {
             AppLock.of(context).setEnabled(false);
             AppLock.of(context).setEnabled(false);
-            String reason = "Please authenticate to view your recovery key";
+            const String reason = "Please authenticate to view your recovery key";
             final result = await requestAuthentication(reason);
             final result = await requestAuthentication(reason);
             AppLock.of(context)
             AppLock.of(context)
                 .setEnabled(Configuration.instance.shouldShowLockScreen());
                 .setEnabled(Configuration.instance.shouldShowLockScreen());
@@ -76,7 +76,7 @@ class AccountSectionWidgetState extends State<AccountSectionWidget> {
           behavior: HitTestBehavior.translucent,
           behavior: HitTestBehavior.translucent,
           onTap: () async {
           onTap: () async {
             AppLock.of(context).setEnabled(false);
             AppLock.of(context).setEnabled(false);
-            String reason = "Please authenticate to change your email";
+            const String reason = "Please authenticate to change your email";
             final result = await requestAuthentication(reason);
             final result = await requestAuthentication(reason);
             AppLock.of(context)
             AppLock.of(context)
                 .setEnabled(Configuration.instance.shouldShowLockScreen());
                 .setEnabled(Configuration.instance.shouldShowLockScreen());
@@ -103,7 +103,7 @@ class AccountSectionWidgetState extends State<AccountSectionWidget> {
           behavior: HitTestBehavior.translucent,
           behavior: HitTestBehavior.translucent,
           onTap: () async {
           onTap: () async {
             AppLock.of(context).setEnabled(false);
             AppLock.of(context).setEnabled(false);
-            String reason = "Please authenticate to change your password";
+            const String reason = "Please authenticate to change your password";
             final result = await requestAuthentication(reason);
             final result = await requestAuthentication(reason);
             AppLock.of(context)
             AppLock.of(context)
                 .setEnabled(Configuration.instance.shouldShowLockScreen());
                 .setEnabled(Configuration.instance.shouldShowLockScreen());

+ 1 - 1
lib/ui/settings/app_update_dialog.dart

@@ -153,7 +153,7 @@ class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
       OpenFile.open(_saveUrl);
       OpenFile.open(_saveUrl);
     } catch (e) {
     } catch (e) {
       Logger("ApkDownloader").severe(e);
       Logger("ApkDownloader").severe(e);
-      AlertDialog alert = AlertDialog(
+      final AlertDialog alert = AlertDialog(
         title: const Text("Sorry"),
         title: const Text("Sorry"),
         content: const Text("The download could not be completed"),
         content: const Text("The download could not be completed"),
         actions: [
         actions: [

+ 1 - 1
lib/ui/settings/app_version_widget.dart

@@ -25,7 +25,7 @@ class _AppVersionWidgetState extends State<AppVersionWidget> {
     return GestureDetector(
     return GestureDetector(
       behavior: HitTestBehavior.translucent,
       behavior: HitTestBehavior.translucent,
       onTap: () async {
       onTap: () async {
-        int now = DateTime.now().millisecondsSinceEpoch;
+        final int now = DateTime.now().millisecondsSinceEpoch;
         if (now - (_lastTap ?? now) < kConsecutiveTapTimeWindowInMilliseconds) {
         if (now - (_lastTap ?? now) < kConsecutiveTapTimeWindowInMilliseconds) {
           _consecutiveTaps++;
           _consecutiveTaps++;
           if (_consecutiveTaps == kTapThresholdForInspector) {
           if (_consecutiveTaps == kTapThresholdForInspector) {

+ 6 - 6
lib/ui/settings/backup_section_widget.dart

@@ -113,7 +113,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
                 value: Configuration.instance.shouldKeepDeviceAwake(),
                 value: Configuration.instance.shouldKeepDeviceAwake(),
                 onChanged: (value) async {
                 onChanged: (value) async {
                   if (value) {
                   if (value) {
-                    var choice = await showChoiceDialog(
+                    final choice = await showChoiceDialog(
                       context,
                       context,
                       "Disable automatic screen lock when ente is running?",
                       "Disable automatic screen lock when ente is running?",
                       "This will ensure faster uploads by ensuring your device does not sleep when uploads are in progress.",
                       "This will ensure faster uploads by ensuring your device does not sleep when uploads are in progress.",
@@ -158,7 +158,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
                 "You've no files on this device that can be deleted",
                 "You've no files on this device that can be deleted",
               );
               );
             } else {
             } else {
-              bool result = await routeToPage(context, FreeSpacePage(status));
+              final bool result = await routeToPage(context, FreeSpacePage(status));
               if (result == true) {
               if (result == true) {
                 _showSpaceFreedDialog(status);
                 _showSpaceFreedDialog(status);
               }
               }
@@ -193,7 +193,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
                 "You've no duplicate files that can be cleared",
                 "You've no duplicate files that can be cleared",
               );
               );
             } else {
             } else {
-              DeduplicationResult result =
+              final DeduplicationResult result =
                   await routeToPage(context, DeduplicatePage(duplicates));
                   await routeToPage(context, DeduplicatePage(duplicates));
               if (result != null) {
               if (result != null) {
                 _showDuplicateFilesDeletedDialog(result);
                 _showDuplicateFilesDeletedDialog(result);
@@ -213,7 +213,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
   }
   }
 
 
   void _showSpaceFreedDialog(BackupStatus status) {
   void _showSpaceFreedDialog(BackupStatus status) {
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("Success"),
       title: const Text("Success"),
       content: Text(
       content: Text(
         "You have successfully freed up " + formatBytes(status.size) + "!",
         "You have successfully freed up " + formatBytes(status.size) + "!",
@@ -268,10 +268,10 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
   }
   }
 
 
   void _showDuplicateFilesDeletedDialog(DeduplicationResult result) {
   void _showDuplicateFilesDeletedDialog(DeduplicationResult result) {
-    String countText = result.count.toString() +
+    final String countText = result.count.toString() +
         " duplicate file" +
         " duplicate file" +
         (result.count == 1 ? "" : "s");
         (result.count == 1 ? "" : "s");
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("✨ Success"),
       title: const Text("✨ Success"),
       content: Text(
       content: Text(
         "You have cleaned up " +
         "You have cleaned up " +

+ 1 - 1
lib/ui/settings/danger_section_widget.dart

@@ -53,7 +53,7 @@ class _DangerSectionWidgetState extends State<DangerSectionWidget> {
   }
   }
 
 
   Future<void> _onLogoutTapped() async {
   Future<void> _onLogoutTapped() async {
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text(
       title: const Text(
         "Logout",
         "Logout",
         style: TextStyle(
         style: TextStyle(

+ 1 - 1
lib/ui/settings/debug_section_widget.dart

@@ -61,7 +61,7 @@ class DebugSectionWidget extends StatelessWidget {
 
 
   void _showKeyAttributesDialog(BuildContext context) {
   void _showKeyAttributesDialog(BuildContext context) {
     final keyAttributes = Configuration.instance.getKeyAttributes();
     final keyAttributes = Configuration.instance.getKeyAttributes();
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("key attributes"),
       title: const Text("key attributes"),
       content: SingleChildScrollView(
       content: SingleChildScrollView(
         child: Column(
         child: Column(

+ 3 - 3
lib/ui/settings/security_section_widget.dart

@@ -83,7 +83,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
                         value: snapshot.data,
                         value: snapshot.data,
                         onChanged: (value) async {
                         onChanged: (value) async {
                           AppLock.of(context).setEnabled(false);
                           AppLock.of(context).setEnabled(false);
-                          String reason =
+                          const String reason =
                               "Please authenticate to configure two-factor authentication";
                               "Please authenticate to configure two-factor authentication";
                           final result = await requestAuthentication(reason);
                           final result = await requestAuthentication(reason);
                           AppLock.of(context).setEnabled(
                           AppLock.of(context).setEnabled(
@@ -164,7 +164,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
                   value: _config.shouldHideFromRecents(),
                   value: _config.shouldHideFromRecents(),
                   onChanged: (value) async {
                   onChanged: (value) async {
                     if (value) {
                     if (value) {
-                      AlertDialog alert = AlertDialog(
+                      final AlertDialog alert = AlertDialog(
                         title: const Text("Hide from recents?"),
                         title: const Text("Hide from recents?"),
                         content: SingleChildScrollView(
                         content: SingleChildScrollView(
                           child: Column(
                           child: Column(
@@ -278,7 +278,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
   }
   }
 
 
   void _disableTwoFactor() {
   void _disableTwoFactor() {
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("Disable two-factor"),
       title: const Text("Disable two-factor"),
       content: const Text(
       content: const Text(
         "Are you sure you want to disable two-factor authentication?",
         "Are you sure you want to disable two-factor authentication?",

+ 1 - 1
lib/ui/settings/social_section_widget.dart

@@ -22,7 +22,7 @@ class SocialSectionWidget extends StatelessWidget {
   }
   }
 
 
   Widget _getSectionOptions(BuildContext context) {
   Widget _getSectionOptions(BuildContext context) {
-    List<Widget> options = [
+    final List<Widget> options = [
       GestureDetector(
       GestureDetector(
         behavior: HitTestBehavior.translucent,
         behavior: HitTestBehavior.translucent,
         onTap: () {
         onTap: () {

+ 7 - 7
lib/ui/shared_collections_gallery.dart

@@ -112,9 +112,9 @@ class _SharedCollectionGalleryState extends State<SharedCollectionGallery>
   Widget _getSharedCollectionsGallery(SharedCollections collections) {
   Widget _getSharedCollectionsGallery(SharedCollections collections) {
     const double horizontalPaddingOfGridRow = 16;
     const double horizontalPaddingOfGridRow = 16;
     const double crossAxisSpacingOfGrid = 9;
     const double crossAxisSpacingOfGrid = 9;
-    Size size = MediaQuery.of(context).size;
-    int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
-    double totalWhiteSpaceOfRow = (horizontalPaddingOfGridRow * 2) +
+    final Size size = MediaQuery.of(context).size;
+    final int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
+    final double totalWhiteSpaceOfRow = (horizontalPaddingOfGridRow * 2) +
         (albumsCountInOneRow - 1) * crossAxisSpacingOfGrid;
         (albumsCountInOneRow - 1) * crossAxisSpacingOfGrid;
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
         (totalWhiteSpaceOfRow / albumsCountInOneRow);
         (totalWhiteSpaceOfRow / albumsCountInOneRow);
@@ -363,11 +363,11 @@ class IncomingCollectionItem extends StatelessWidget {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     const double horizontalPaddingOfGridRow = 16;
     const double horizontalPaddingOfGridRow = 16;
     const double crossAxisSpacingOfGrid = 9;
     const double crossAxisSpacingOfGrid = 9;
-    TextStyle albumTitleTextStyle =
+    final TextStyle albumTitleTextStyle =
         Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 14);
         Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 14);
-    Size size = MediaQuery.of(context).size;
-    int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
-    double totalWhiteSpaceOfRow = (horizontalPaddingOfGridRow * 2) +
+    final Size size = MediaQuery.of(context).size;
+    final int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
+    final double totalWhiteSpaceOfRow = (horizontalPaddingOfGridRow * 2) +
         (albumsCountInOneRow - 1) * crossAxisSpacingOfGrid;
         (albumsCountInOneRow - 1) * crossAxisSpacingOfGrid;
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
     final double sideOfThumbnail = (size.width / albumsCountInOneRow) -
         (totalWhiteSpaceOfRow / albumsCountInOneRow);
         (totalWhiteSpaceOfRow / albumsCountInOneRow);

+ 9 - 9
lib/ui/sharing/manage_links_widget.dart

@@ -131,12 +131,12 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
                               false,
                               false,
                           onChanged: (enablePassword) async {
                           onChanged: (enablePassword) async {
                             if (enablePassword) {
                             if (enablePassword) {
-                              var inputResult =
+                              final inputResult =
                                   await _displayLinkPasswordInput(context);
                                   await _displayLinkPasswordInput(context);
                               if (inputResult != null &&
                               if (inputResult != null &&
                                   inputResult == 'ok' &&
                                   inputResult == 'ok' &&
                                   _textFieldController.text.trim().isNotEmpty) {
                                   _textFieldController.text.trim().isNotEmpty) {
-                                var propToUpdate = await _getEncryptedPassword(
+                                final propToUpdate = await _getEncryptedPassword(
                                   _textFieldController.text,
                                   _textFieldController.text,
                                 );
                                 );
                                 await _updateUrlSettings(context, propToUpdate);
                                 await _updateUrlSettings(context, propToUpdate);
@@ -250,10 +250,10 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
                   CupertinoButton(
                   CupertinoButton(
                     onPressed: () async {
                     onPressed: () async {
                       int newValidTill = -1;
                       int newValidTill = -1;
-                      int expireAfterInMicroseconds = _selectedExpiry.item3;
+                      final int expireAfterInMicroseconds = _selectedExpiry.item3;
                       // need to manually select time
                       // need to manually select time
                       if (expireAfterInMicroseconds < 0) {
                       if (expireAfterInMicroseconds < 0) {
-                        var timeInMicrosecondsFromEpoch =
+                        final timeInMicrosecondsFromEpoch =
                             await _showDateTimePicker();
                             await _showDateTimePicker();
                         if (timeInMicrosecondsFromEpoch != null) {
                         if (timeInMicrosecondsFromEpoch != null) {
                           newValidTill = timeInMicrosecondsFromEpoch;
                           newValidTill = timeInMicrosecondsFromEpoch;
@@ -293,7 +293,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
                 backgroundColor:
                 backgroundColor:
                     Theme.of(context).backgroundColor.withOpacity(0.95),
                     Theme.of(context).backgroundColor.withOpacity(0.95),
                 onSelectedItemChanged: (value) {
                 onSelectedItemChanged: (value) {
-                  var firstWhere = _expiryOptions
+                  final firstWhere = _expiryOptions
                       .firstWhere((element) => element.item1 == value);
                       .firstWhere((element) => element.item1 == value);
                   setState(() {
                   setState(() {
                     _selectedExpiry = firstWhere;
                     _selectedExpiry = firstWhere;
@@ -410,8 +410,8 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
       Sodium.cryptoPwhashAlgArgon2id13 == Sodium.cryptoPwhashAlgDefault,
       Sodium.cryptoPwhashAlgArgon2id13 == Sodium.cryptoPwhashAlgDefault,
       "mismatch in expected default pw hashing algo",
       "mismatch in expected default pw hashing algo",
     );
     );
-    int memLimit = Sodium.cryptoPwhashMemlimitInteractive;
-    int opsLimit = Sodium.cryptoPwhashOpslimitInteractive;
+    final int memLimit = Sodium.cryptoPwhashMemlimitInteractive;
+    final int opsLimit = Sodium.cryptoPwhashOpslimitInteractive;
     final kekSalt = CryptoUtil.getSaltToDeriveKey();
     final kekSalt = CryptoUtil.getSaltToDeriveKey();
     final result = await CryptoUtil.deriveKey(
     final result = await CryptoUtil.deriveKey(
       utf8.encode(pass),
       utf8.encode(pass),
@@ -444,7 +444,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
   }
   }
 
 
   Text _getLinkExpiryTimeWidget() {
   Text _getLinkExpiryTimeWidget() {
-    int validTill = widget.collection.publicURLs?.first?.validTill ?? 0;
+    final int validTill = widget.collection.publicURLs?.first?.validTill ?? 0;
     if (validTill == 0) {
     if (validTill == 0) {
       return const Text(
       return const Text(
         'Never',
         'Never',
@@ -470,7 +470,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
   }
   }
 
 
   Future<void> _showDeviceLimitPicker() async {
   Future<void> _showDeviceLimitPicker() async {
-    List<Text> options = [];
+    final List<Text> options = [];
     for (int i = 50; i > 0; i--) {
     for (int i = 50; i > 0; i--) {
       options.add(
       options.add(
         Text(i.toString(), style: Theme.of(context).textTheme.subtitle1),
         Text(i.toString(), style: Theme.of(context).textTheme.subtitle1),

+ 4 - 4
lib/ui/sharing/share_collection_widget.dart

@@ -96,7 +96,7 @@ class _SharingDialogState extends State<SharingDialog> {
     }
     }
 
 
     if (!FeatureFlagService.instance.disableUrlSharing()) {
     if (!FeatureFlagService.instance.disableUrlSharing()) {
-      bool hasUrl = widget.collection.publicURLs?.isNotEmpty ?? false;
+      final bool hasUrl = widget.collection.publicURLs?.isNotEmpty ?? false;
       children.addAll([
       children.addAll([
         const Padding(padding: EdgeInsets.all(16)),
         const Padding(padding: EdgeInsets.all(16)),
         const Divider(height: 1),
         const Divider(height: 1),
@@ -249,10 +249,10 @@ class _SharingDialogState extends State<SharingDialog> {
   }
   }
 
 
   Widget _getShareableUrlWidget(BuildContext parentContext) {
   Widget _getShareableUrlWidget(BuildContext parentContext) {
-    String collectionKey = Base58Encode(
+    final String collectionKey = Base58Encode(
       CollectionsService.instance.getCollectionKey(widget.collection.id),
       CollectionsService.instance.getCollectionKey(widget.collection.id),
     );
     );
-    String url = "${widget.collection.publicURLs.first.url}#$collectionKey";
+    final String url = "${widget.collection.publicURLs.first.url}#$collectionKey";
     return SingleChildScrollView(
     return SingleChildScrollView(
       child: Column(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.start,
         mainAxisAlignment: MainAxisAlignment.start,
@@ -441,7 +441,7 @@ class _SharingDialogState extends State<SharingDialog> {
   }
   }
 
 
   void _showUnSupportedAlert() {
   void _showUnSupportedAlert() {
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("Sorry"),
       title: const Text("Sorry"),
       content: const Text(
       content: const Text(
         "Sharing is not permitted for free accounts, please subscribe",
         "Sharing is not permitted for free accounts, please subscribe",

+ 1 - 1
lib/ui/status_bar_widget.dart

@@ -135,7 +135,7 @@ class _SyncStatusWidgetState extends State<SyncStatusWidget> {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    bool isNotOutdatedEvent = _event != null &&
+    final bool isNotOutdatedEvent = _event != null &&
         (_event.status == SyncStatus.completedBackup ||
         (_event.status == SyncStatus.completedBackup ||
             _event.status == SyncStatus.completedFirstGalleryImport) &&
             _event.status == SyncStatus.completedFirstGalleryImport) &&
         (DateTime.now().microsecondsSinceEpoch - _event.timestamp >
         (DateTime.now().microsecondsSinceEpoch - _event.timestamp >

+ 5 - 5
lib/ui/tools/editor/filtered_image.dart

@@ -70,11 +70,11 @@ class ColorFilterGenerator {
         0,
         0,
       ];
       ];
     }
     }
-    double cosVal = cos(value);
-    double sinVal = sin(value);
-    double lumR = 0.213;
-    double lumG = 0.715;
-    double lumB = 0.072;
+    final double cosVal = cos(value);
+    final double sinVal = sin(value);
+    const double lumR = 0.213;
+    const double lumG = 0.715;
+    const double lumB = 0.072;
 
 
     return List<double>.from(<double>[
     return List<double>.from(<double>[
       (lumR + (cosVal * (1 - lumR))) + (sinVal * (-lumR)),
       (lumR + (cosVal * (1 - lumR))) + (sinVal * (-lumR)),

+ 9 - 9
lib/ui/tools/editor/image_editor_page.dart

@@ -157,7 +157,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
   }
   }
 
 
   Widget _buildFlipButton() {
   Widget _buildFlipButton() {
-    TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
+    final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
 
 
     return GestureDetector(
     return GestureDetector(
       behavior: HitTestBehavior.translucent,
       behavior: HitTestBehavior.translucent,
@@ -191,7 +191,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
   }
   }
 
 
   Widget _buildRotateLeftButton() {
   Widget _buildRotateLeftButton() {
-    TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
+    final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
 
 
     return GestureDetector(
     return GestureDetector(
       behavior: HitTestBehavior.translucent,
       behavior: HitTestBehavior.translucent,
@@ -221,7 +221,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
   }
   }
 
 
   Widget _buildRotateRightButton() {
   Widget _buildRotateRightButton() {
-    TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
+    final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
 
 
     return GestureDetector(
     return GestureDetector(
       behavior: HitTestBehavior.translucent,
       behavior: HitTestBehavior.translucent,
@@ -251,7 +251,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
   }
   }
 
 
   Widget _buildSaveButton() {
   Widget _buildSaveButton() {
-    TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
+    final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
 
 
     return GestureDetector(
     return GestureDetector(
       behavior: HitTestBehavior.translucent,
       behavior: HitTestBehavior.translucent,
@@ -344,12 +344,12 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
         title: fileName,
         title: fileName,
       );
       );
       final newFile =
       final newFile =
-          ente.File.fromAsset(widget.originalFile.deviceFolder, newAsset);
+          await ente.File.fromAsset(widget.originalFile.deviceFolder, newAsset);
       newFile.creationTime = widget.originalFile.creationTime;
       newFile.creationTime = widget.originalFile.creationTime;
       newFile.collectionID = widget.originalFile.collectionID;
       newFile.collectionID = widget.originalFile.collectionID;
       newFile.location = widget.originalFile.location;
       newFile.location = widget.originalFile.location;
       if (!newFile.hasLocation() && widget.originalFile.localID != null) {
       if (!newFile.hasLocation() && widget.originalFile.localID != null) {
-        var assetEntity = await widget.originalFile.getAsset();
+        final assetEntity = await widget.originalFile.getAsset();
         if (assetEntity != null) {
         if (assetEntity != null) {
           final latLong = await assetEntity.latlngAsync();
           final latLong = await assetEntity.latlngAsync();
           newFile.location = Location(latLong.latitude, latLong.longitude);
           newFile.location = Location(latLong.latitude, latLong.longitude);
@@ -394,7 +394,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
   }
   }
 
 
   Widget _buildSat() {
   Widget _buildSat() {
-    TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
+    final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
 
 
     return Container(
     return Container(
       padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
       padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
@@ -440,7 +440,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
   }
   }
 
 
   Widget _buildBrightness() {
   Widget _buildBrightness() {
-    TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
+    final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
 
 
     return Container(
     return Container(
       padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
       padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
@@ -486,7 +486,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
   }
   }
 
 
   Future<void> _showExitConfirmationDialog() async {
   Future<void> _showExitConfirmationDialog() async {
-    AlertDialog alert = AlertDialog(
+    final AlertDialog alert = AlertDialog(
       title: const Text("Discard edits?"),
       title: const Text("Discard edits?"),
       actions: [
       actions: [
         TextButton(
         TextButton(

+ 1 - 0
lib/ui/viewer/file/custom_app_bar.dart

@@ -1,6 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 
 
 class CustomAppBar extends PreferredSize {
 class CustomAppBar extends PreferredSize {
+  @override
   final Widget child;
   final Widget child;
   final double height;
   final double height;
 
 

+ 1 - 1
lib/ui/viewer/file/detail_page.dart

@@ -139,7 +139,7 @@ class _DetailPageState extends State<DetailPage> {
     return PageView.builder(
     return PageView.builder(
       itemBuilder: (context, index) {
       itemBuilder: (context, index) {
         final file = _files[index];
         final file = _files[index];
-        Widget content = FileWidget(
+        final Widget content = FileWidget(
           file,
           file,
           autoPlay: !_hasPageChanged,
           autoPlay: !_hasPageChanged,
           tagPrefix: widget.config.tagPrefix,
           tagPrefix: widget.config.tagPrefix,

+ 3 - 3
lib/ui/viewer/file/fading_app_bar.dart

@@ -300,9 +300,9 @@ class FadingAppBarState extends State<FadingAppBar> {
   Future<void> _download(File file) async {
   Future<void> _download(File file) async {
     final dialog = createProgressDialog(context, "Downloading...");
     final dialog = createProgressDialog(context, "Downloading...");
     await dialog.show();
     await dialog.show();
-    FileType type = file.fileType;
+    final FileType type = file.fileType;
     // save and track image for livePhoto/image and video for FileType.video
     // save and track image for livePhoto/image and video for FileType.video
-    io.File fileToSave = await getFile(file);
+    final io.File fileToSave = await getFile(file);
     final savedAsset = type == FileType.video
     final savedAsset = type == FileType.video
         ? (await PhotoManager.editor.saveVideo(fileToSave, title: file.title))
         ? (await PhotoManager.editor.saveVideo(fileToSave, title: file.title))
         : (await PhotoManager.editor
         : (await PhotoManager.editor
@@ -313,7 +313,7 @@ class FadingAppBarState extends State<FadingAppBar> {
     await FilesDB.instance.insert(file);
     await FilesDB.instance.insert(file);
 
 
     if (type == FileType.livePhoto) {
     if (type == FileType.livePhoto) {
-      io.File liveVideo = await getFileFromServer(file, liveVideo: true);
+      final io.File liveVideo = await getFileFromServer(file, liveVideo: true);
       if (liveVideo == null) {
       if (liveVideo == null) {
         _logger.warning("Failed to find live video" + file.tag());
         _logger.warning("Failed to find live video" + file.tag());
       } else {
       } else {

+ 3 - 3
lib/ui/viewer/file/fading_bottom_bar.dart

@@ -59,7 +59,7 @@ class FadingBottomBarState extends State<FadingBottomBar> {
   }
   }
 
 
   Widget _getBottomBar() {
   Widget _getBottomBar() {
-    List<Widget> children = [];
+    final List<Widget> children = [];
     children.add(
     children.add(
       Tooltip(
       Tooltip(
         message: "Info",
         message: "Info",
@@ -103,7 +103,7 @@ class FadingBottomBarState extends State<FadingBottomBar> {
       }
       }
       if (widget.file.uploadedFileID != null &&
       if (widget.file.uploadedFileID != null &&
           widget.file.ownerID == Configuration.instance.getUserID()) {
           widget.file.ownerID == Configuration.instance.getUserID()) {
-        bool isArchived =
+        final bool isArchived =
             widget.file.magicMetadata.visibility == kVisibilityArchive;
             widget.file.magicMetadata.visibility == kVisibilityArchive;
         children.add(
         children.add(
           Tooltip(
           Tooltip(
@@ -151,7 +151,7 @@ class FadingBottomBarState extends State<FadingBottomBar> {
         ),
         ),
       );
       );
     }
     }
-    var safeAreaBottomPadding = MediaQuery.of(context).padding.bottom * .5;
+    final safeAreaBottomPadding = MediaQuery.of(context).padding.bottom * .5;
     return IgnorePointer(
     return IgnorePointer(
       ignoring: _shouldHide,
       ignoring: _shouldHide,
       child: AnimatedOpacity(
       child: AnimatedOpacity(

+ 0 - 1
lib/ui/viewer/file/file_icons_widget.dart

@@ -8,7 +8,6 @@ class ThumbnailPlaceHolder extends StatelessWidget {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    // debugPrint("building placeHolder for thumbnail");
     return Container(
     return Container(
       alignment: Alignment.center,
       alignment: Alignment.center,
       color: Theme.of(context).colorScheme.galleryThumbBackgroundColor,
       color: Theme.of(context).colorScheme.galleryThumbBackgroundColor,

+ 2 - 2
lib/ui/viewer/file/file_info_dialog.dart

@@ -76,7 +76,7 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
         _exifData["ISO"] != null;
         _exifData["ISO"] != null;
     final bool showDimension =
     final bool showDimension =
         _exifData["resolution"] != null && _exifData["megaPixels"] != null;
         _exifData["resolution"] != null && _exifData["megaPixels"] != null;
-    var listTiles = <Widget>[
+    final listTiles = <Widget>[
       ListTile(
       ListTile(
         leading: const Padding(
         leading: const Padding(
           padding: EdgeInsets.only(top: 8, left: 6),
           padding: EdgeInsets.only(top: 8, left: 6),
@@ -207,7 +207,7 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
           onTap: () {
           onTap: () {
             if (file.collectionID != null) {
             if (file.collectionID != null) {
               Navigator.pop(context); // info dialog
               Navigator.pop(context); // info dialog
-              Collection c = CollectionsService.instance
+              final Collection c = CollectionsService.instance
                   .getCollectionByID(file.collectionID);
                   .getCollectionByID(file.collectionID);
               routeToPage(
               routeToPage(
                 context,
                 context,

+ 2 - 2
lib/ui/viewer/file/thumbnail_widget.dart

@@ -91,7 +91,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
     // If yes, parent thumbnail widget can be stateless
     // If yes, parent thumbnail widget can be stateless
     Widget content;
     Widget content;
     if (image != null) {
     if (image != null) {
-      List<Widget> contentChildren = [image];
+      final List<Widget> contentChildren = [image];
       if (widget.file.fileType == FileType.video) {
       if (widget.file.fileType == FileType.video) {
         contentChildren.add(const VideoOverlayIcon());
         contentChildren.add(const VideoOverlayIcon());
       } else if (widget.file.fileType == FileType.livePhoto &&
       } else if (widget.file.fileType == FileType.livePhoto &&
@@ -105,7 +105,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
               children: contentChildren,
               children: contentChildren,
             );
             );
     }
     }
-    List<Widget> viewChildren = [
+    final List<Widget> viewChildren = [
       const ThumbnailPlaceHolder(),
       const ThumbnailPlaceHolder(),
       AnimatedOpacity(
       AnimatedOpacity(
         opacity: content == null ? 0 : 1.0,
         opacity: content == null ? 0 : 1.0,

+ 1 - 1
lib/ui/viewer/file/video_controls.dart

@@ -242,7 +242,7 @@ class _VideoControlsState extends State<VideoControls> {
   }
   }
 
 
   void _playPause() {
   void _playPause() {
-    bool isFinished = _latestValue.position >= _latestValue.duration;
+    final bool isFinished = _latestValue.position >= _latestValue.duration;
 
 
     setState(() {
     setState(() {
       if (controller.value.isPlaying) {
       if (controller.value.isPlaying) {

+ 1 - 1
lib/ui/viewer/file/zoomable_image.dart

@@ -88,7 +88,7 @@ class _ZoomableImageState extends State<ZoomableImage>
       content = const EnteLoadingWidget();
       content = const EnteLoadingWidget();
     }
     }
 
 
-    GestureDragUpdateCallback verticalDragCallback = _isZooming
+    final GestureDragUpdateCallback verticalDragCallback = _isZooming
         ? null
         ? null
         : (d) => {
         : (d) => {
               if (!_isZooming && d.delta.dy > kDragSensitivity)
               if (!_isZooming && d.delta.dy > kDragSensitivity)

+ 3 - 3
lib/ui/viewer/file/zoomable_live_image.dart

@@ -152,7 +152,7 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
   }
   }
 
 
   VideoPlayerController _setVideoPlayerController({io.File file}) {
   VideoPlayerController _setVideoPlayerController({io.File file}) {
-    var videoPlayerController = VideoPlayerController.file(file);
+    final videoPlayerController = VideoPlayerController.file(file);
     return _videoPlayerController = videoPlayerController
     return _videoPlayerController = videoPlayerController
       ..initialize().whenComplete(() {
       ..initialize().whenComplete(() {
         if (mounted) {
         if (mounted) {
@@ -164,8 +164,8 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
   }
   }
 
 
   void _showLivePhotoToast() async {
   void _showLivePhotoToast() async {
-    var preferences = await SharedPreferences.getInstance();
-    int promptTillNow = preferences.getInt(kLivePhotoToastCounterKey) ?? 0;
+    final preferences = await SharedPreferences.getInstance();
+    final int promptTillNow = preferences.getInt(kLivePhotoToastCounterKey) ?? 0;
     if (promptTillNow < kMaxLivePhotoToastCount && mounted) {
     if (promptTillNow < kMaxLivePhotoToastCount && mounted) {
       showToast(context, "Press and hold to play video");
       showToast(context, "Press and hold to play video");
       preferences.setInt(kLivePhotoToastCounterKey, promptTillNow + 1);
       preferences.setInt(kLivePhotoToastCounterKey, promptTillNow + 1);

+ 3 - 3
lib/ui/viewer/gallery/gallery.dart

@@ -183,7 +183,7 @@ class _GalleryState extends State<Gallery> {
         return const EnteLoadingWidget();
         return const EnteLoadingWidget();
       },
       },
       emptyResultBuilder: (_) {
       emptyResultBuilder: (_) {
-        List<Widget> children = [];
+        final List<Widget> children = [];
         if (widget.header != null) {
         if (widget.header != null) {
           children.add(widget.header);
           children.add(widget.header);
         }
         }
@@ -269,8 +269,8 @@ class _GalleryState extends State<Gallery> {
   }
   }
 
 
   bool _areFromSameDay(int firstCreationTime, int secondCreationTime) {
   bool _areFromSameDay(int firstCreationTime, int secondCreationTime) {
-    var firstDate = DateTime.fromMicrosecondsSinceEpoch(firstCreationTime);
-    var secondDate = DateTime.fromMicrosecondsSinceEpoch(secondCreationTime);
+    final firstDate = DateTime.fromMicrosecondsSinceEpoch(firstCreationTime);
+    final secondDate = DateTime.fromMicrosecondsSinceEpoch(secondCreationTime);
     return firstDate.year == secondDate.year &&
     return firstDate.year == secondDate.year &&
         firstDate.month == secondDate.month &&
         firstDate.month == secondDate.month &&
         firstDate.day == secondDate.day;
         firstDate.day == secondDate.day;

+ 2 - 2
lib/ui/viewer/gallery/gallery_app_bar_widget.dart

@@ -119,7 +119,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
   }
   }
 
 
   List<Widget> _getDefaultActions(BuildContext context) {
   List<Widget> _getDefaultActions(BuildContext context) {
-    List<Widget> actions = <Widget>[];
+    final List<Widget> actions = <Widget>[];
     if (Configuration.instance.hasConfiguredAccount() &&
     if (Configuration.instance.hasConfiguredAccount() &&
         widget.selectedFiles.files.isEmpty &&
         widget.selectedFiles.files.isEmpty &&
         (widget.type == GalleryType.localFolder ||
         (widget.type == GalleryType.localFolder ||
@@ -157,7 +157,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
                 ),
                 ),
               );
               );
             }
             }
-            bool isArchived = widget.collection.isArchived();
+            final bool isArchived = widget.collection.isArchived();
             items.add(
             items.add(
               PopupMenuItem(
               PopupMenuItem(
                 value: 2,
                 value: 2,

+ 3 - 3
lib/ui/viewer/gallery/gallery_overlay_widget.dart

@@ -66,7 +66,7 @@ class _GalleryOverlayWidgetState extends State<GalleryOverlayWidget> {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    bool filesAreSelected = widget.selectedFiles.files.isNotEmpty;
+    final bool filesAreSelected = widget.selectedFiles.files.isNotEmpty;
     final bottomPadding = Platform.isAndroid ? 0.0 : 12.0;
     final bottomPadding = Platform.isAndroid ? 0.0 : 12.0;
     return Padding(
     return Padding(
       padding: EdgeInsets.only(bottom: bottomPadding),
       padding: EdgeInsets.only(bottom: bottomPadding),
@@ -254,7 +254,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
   }
   }
 
 
   List<Widget> _getActions(BuildContext context) {
   List<Widget> _getActions(BuildContext context) {
-    List<Widget> actions = <Widget>[];
+    final List<Widget> actions = <Widget>[];
     if (widget.type == GalleryType.trash) {
     if (widget.type == GalleryType.trash) {
       _addTrashAction(actions);
       _addTrashAction(actions);
       return actions;
       return actions;
@@ -372,7 +372,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
 
 
     if (widget.type == GalleryType.homepage ||
     if (widget.type == GalleryType.homepage ||
         widget.type == GalleryType.archive) {
         widget.type == GalleryType.archive) {
-      bool showArchive = widget.type == GalleryType.homepage;
+      final bool showArchive = widget.type == GalleryType.homepage;
       actions.add(
       actions.add(
         Tooltip(
         Tooltip(
           message: showArchive ? "Hide" : "Unhide",
           message: showArchive ? "Hide" : "Unhide",

+ 1 - 1
lib/ui/viewer/gallery/trash_page.dart

@@ -48,7 +48,7 @@ class _TrashPageState extends State<TrashPage> {
 
 
   @override
   @override
   Widget build(Object context) {
   Widget build(Object context) {
-    bool filesAreSelected = widget._selectedFiles.files.isNotEmpty;
+    final bool filesAreSelected = widget._selectedFiles.files.isNotEmpty;
 
 
     final gallery = Gallery(
     final gallery = Gallery(
       asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) {
       asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) {

+ 2 - 2
lib/utils/data_util.dart

@@ -20,7 +20,7 @@ String convertBytesToReadableFormat(int bytes) {
 String formatBytes(int bytes, [int decimals = 2]) {
 String formatBytes(int bytes, [int decimals = 2]) {
   if (bytes == 0) return '0 bytes';
   if (bytes == 0) return '0 bytes';
   const k = 1024;
   const k = 1024;
-  int dm = decimals < 0 ? 0 : decimals;
-  int i = (log(bytes) / log(k)).floor();
+  final int dm = decimals < 0 ? 0 : decimals;
+  final int i = (log(bytes) / log(k)).floor();
   return ((bytes / pow(k, i)).toStringAsFixed(dm)) + ' ' + kStorageUnits[i];
   return ((bytes / pow(k, i)).toStringAsFixed(dm)) + ' ' + kStorageUnits[i];
 }
 }

+ 6 - 6
lib/utils/date_time_util.dart

@@ -131,7 +131,7 @@ String getFullDate(DateTime dateTime) {
 }
 }
 
 
 String daysLeft(int futureTime) {
 String daysLeft(int futureTime) {
-  int daysLeft = ((futureTime - DateTime.now().microsecondsSinceEpoch) /
+  final int daysLeft = ((futureTime - DateTime.now().microsecondsSinceEpoch) /
           Duration.microsecondsPerDay)
           Duration.microsecondsPerDay)
       .ceil();
       .ceil();
   return '$daysLeft day' + (daysLeft <= 1 ? "" : "s");
   return '$daysLeft day' + (daysLeft <= 1 ? "" : "s");
@@ -143,7 +143,7 @@ String formatDuration(Duration position) {
   int seconds = ms ~/ 1000;
   int seconds = ms ~/ 1000;
   final int hours = seconds ~/ 3600;
   final int hours = seconds ~/ 3600;
   seconds = seconds % 3600;
   seconds = seconds % 3600;
-  var minutes = seconds ~/ 60;
+  final minutes = seconds ~/ 60;
   seconds = seconds % 60;
   seconds = seconds % 60;
 
 
   final hoursString = hours >= 10
   final hoursString = hours >= 10
@@ -230,15 +230,15 @@ String secondsToHHMMSS(int value) {
   h = value ~/ 3600;
   h = value ~/ 3600;
   m = ((value - h * 3600)) ~/ 60;
   m = ((value - h * 3600)) ~/ 60;
   s = value - (h * 3600) - (m * 60);
   s = value - (h * 3600) - (m * 60);
-  String hourLeft = h.toString().length < 2 ? "0" + h.toString() : h.toString();
+  final String hourLeft = h.toString().length < 2 ? "0" + h.toString() : h.toString();
 
 
-  String minuteLeft =
+  final String minuteLeft =
       m.toString().length < 2 ? "0" + m.toString() : m.toString();
       m.toString().length < 2 ? "0" + m.toString() : m.toString();
 
 
-  String secondsLeft =
+  final String secondsLeft =
       s.toString().length < 2 ? "0" + s.toString() : s.toString();
       s.toString().length < 2 ? "0" + s.toString() : s.toString();
 
 
-  String result = "$hourLeft:$minuteLeft:$secondsLeft";
+  final String result = "$hourLeft:$minuteLeft:$secondsLeft";
 
 
   return result;
   return result;
 }
 }

+ 2 - 2
lib/utils/delete_file_util.dart

@@ -418,7 +418,7 @@ Future<List<String>> _deleteLocalFilesInBatches(
 
 
 Future<bool> _localFileExist(File file) {
 Future<bool> _localFileExist(File file) {
   if (file.isSharedMediaToAppSandbox()) {
   if (file.isSharedMediaToAppSandbox()) {
-    var localFile = io.File(getSharedMediaFilePath(file));
+    final localFile = io.File(getSharedMediaFilePath(file));
     return localFile.exists();
     return localFile.exists();
   } else {
   } else {
     return file.getAsset().then((asset) {
     return file.getAsset().then((asset) {
@@ -434,7 +434,7 @@ Future<List<String>> _tryDeleteSharedMediaFiles(List<String> localIDs) {
   final List<String> actuallyDeletedIDs = [];
   final List<String> actuallyDeletedIDs = [];
   try {
   try {
     return Future.forEach(localIDs, (id) async {
     return Future.forEach(localIDs, (id) async {
-      String localPath = getSharedMediaPathFromLocalID(id);
+      final String localPath = getSharedMediaPathFromLocalID(id);
       try {
       try {
         // verify the file exists as the OS may have already deleted it from cache
         // verify the file exists as the OS may have already deleted it from cache
         if (io.File(localPath).existsSync()) {
         if (io.File(localPath).existsSync()) {

+ 2 - 2
lib/utils/dialog_util.dart

@@ -29,7 +29,7 @@ Future<dynamic> showErrorDialog(
   String title,
   String title,
   String content,
   String content,
 ) {
 ) {
-  AlertDialog alert = AlertDialog(
+  final AlertDialog alert = AlertDialog(
     shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
     shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
     title: Text(
     title: Text(
       title,
       title,
@@ -77,7 +77,7 @@ Future<T> showConfettiDialog<T>({
   final pageBuilder = Builder(
   final pageBuilder = Builder(
     builder: builder,
     builder: builder,
   );
   );
-  ConfettiController confettiController =
+  final ConfettiController confettiController =
       ConfettiController(duration: const Duration(seconds: 1));
       ConfettiController(duration: const Duration(seconds: 1));
   confettiController.play();
   confettiController.play();
   return showDialog(
   return showDialog(

+ 1 - 1
lib/utils/diff_fetcher.dart

@@ -76,7 +76,7 @@ class DiffFetcher {
             fileDecryptionKey,
             fileDecryptionKey,
             Sodium.base642bin(file.metadataDecryptionHeader),
             Sodium.base642bin(file.metadataDecryptionHeader),
           );
           );
-          Map<String, dynamic> metadata =
+          final Map<String, dynamic> metadata =
               jsonDecode(utf8.decode(encodedMetadata));
               jsonDecode(utf8.decode(encodedMetadata));
           file.applyMetadata(metadata);
           file.applyMetadata(metadata);
           if (item['magicMetadata'] != null) {
           if (item['magicMetadata'] != null) {

+ 6 - 6
lib/utils/email_util.dart

@@ -126,7 +126,7 @@ Future<void> _sendLogs(
   String subject,
   String subject,
   String body,
   String body,
 ) async {
 ) async {
-  String zipFilePath = await getZippedLogsFile(context);
+  final String zipFilePath = await getZippedLogsFile(context);
   final Email email = Email(
   final Email email = Email(
     recipients: [toEmail],
     recipients: [toEmail],
     subject: subject,
     subject: subject,
@@ -150,7 +150,7 @@ Future<String> getZippedLogsFile(BuildContext context) async {
   final tempPath = (await getTemporaryDirectory()).path;
   final tempPath = (await getTemporaryDirectory()).path;
   final zipFilePath =
   final zipFilePath =
       tempPath + "/logs-${Configuration.instance.getUserID() ?? 0}.zip";
       tempPath + "/logs-${Configuration.instance.getUserID() ?? 0}.zip";
-  var encoder = ZipFileEncoder();
+  final encoder = ZipFileEncoder();
   encoder.create(zipFilePath);
   encoder.create(zipFilePath);
   encoder.addDirectory(logsDirectory);
   encoder.addDirectory(logsDirectory);
   encoder.close();
   encoder.close();
@@ -187,8 +187,8 @@ Future<void> sendEmail(
   String body,
   String body,
 }) async {
 }) async {
   try {
   try {
-    String clientDebugInfo = await _clientInfo();
-    EmailContent email = EmailContent(
+    final String clientDebugInfo = await _clientInfo();
+    final EmailContent email = EmailContent(
       to: [
       to: [
         to,
         to,
       ],
       ],
@@ -210,7 +210,7 @@ Future<void> sendEmail(
         throw Exception('Could not launch ${params.toString()}');
         throw Exception('Could not launch ${params.toString()}');
       }
       }
     } else {
     } else {
-      OpenMailAppResult result = await OpenMailApp.composeNewEmailInMailApp(
+      final OpenMailAppResult result = await OpenMailApp.composeNewEmailInMailApp(
         nativePickerTitle: 'Select email app',
         nativePickerTitle: 'Select email app',
         emailContent: email,
         emailContent: email,
       );
       );
@@ -257,7 +257,7 @@ Future<void> sendEmail(
 
 
 Future<String> _clientInfo() async {
 Future<String> _clientInfo() async {
   final packageInfo = await PackageInfo.fromPlatform();
   final packageInfo = await PackageInfo.fromPlatform();
-  String debugInfo = '\n\n\n\n ------------------- \nFollowing information can '
+  final String debugInfo = '\n\n\n\n ------------------- \nFollowing information can '
       'help us in debugging if you are facing any issue '
       'help us in debugging if you are facing any issue '
       '\nRegistered email: ${Configuration.instance.getEmail()}'
       '\nRegistered email: ${Configuration.instance.getEmail()}'
       '\nClient: ${packageInfo.packageName}'
       '\nClient: ${packageInfo.packageName}'

+ 33 - 37
lib/utils/file_uploader.dart

@@ -155,7 +155,7 @@ class FileUploader {
   }
   }
 
 
   void removeFromQueueWhere(final bool Function(File) fn, final Error reason) {
   void removeFromQueueWhere(final bool Function(File) fn, final Error reason) {
-    List<String> uploadsToBeRemoved = [];
+    final List<String> uploadsToBeRemoved = [];
     _queue.entries
     _queue.entries
         .where((entry) => entry.value.status == UploadStatus.notStarted)
         .where((entry) => entry.value.status == UploadStatus.notStarted)
         .forEach((pendingUpload) {
         .forEach((pendingUpload) {
@@ -254,7 +254,7 @@ class FileUploader {
     bool forcedUpload,
     bool forcedUpload,
   ) async {
   ) async {
     final connectivityResult = await (Connectivity().checkConnectivity());
     final connectivityResult = await (Connectivity().checkConnectivity());
-    var canUploadUnderCurrentNetworkConditions =
+    final canUploadUnderCurrentNetworkConditions =
         (connectivityResult == ConnectivityResult.wifi ||
         (connectivityResult == ConnectivityResult.wifi ||
             Configuration.instance.shouldBackupOverMobileData());
             Configuration.instance.shouldBackupOverMobileData());
     if (!canUploadUnderCurrentNetworkConditions && !forcedUpload) {
     if (!canUploadUnderCurrentNetworkConditions && !forcedUpload) {
@@ -311,7 +311,7 @@ class FileUploader {
       }
       }
 
 
       Uint8List key;
       Uint8List key;
-      bool isUpdatedFile =
+      final bool isUpdatedFile =
           file.uploadedFileID != null && file.updationTime == -1;
           file.uploadedFileID != null && file.updationTime == -1;
       if (isUpdatedFile) {
       if (isUpdatedFile) {
         _logger.info("File was updated " + file.toString());
         _logger.info("File was updated " + file.toString());
@@ -342,7 +342,7 @@ class FileUploader {
         encryptedFilePath,
         encryptedFilePath,
         key: key,
         key: key,
       );
       );
-      var thumbnailData = mediaUploadData.thumbnail;
+      final thumbnailData = mediaUploadData.thumbnail;
 
 
       final encryptedThumbnailData =
       final encryptedThumbnailData =
           await CryptoUtil.encryptChaCha(thumbnailData, fileAttributes.key);
           await CryptoUtil.encryptChaCha(thumbnailData, fileAttributes.key);
@@ -354,11 +354,11 @@ class FileUploader {
           .writeAsBytes(encryptedThumbnailData.encryptedData);
           .writeAsBytes(encryptedThumbnailData.encryptedData);
 
 
       final thumbnailUploadURL = await _getUploadURL();
       final thumbnailUploadURL = await _getUploadURL();
-      String thumbnailObjectKey =
+      final String thumbnailObjectKey =
           await _putFile(thumbnailUploadURL, encryptedThumbnailFile);
           await _putFile(thumbnailUploadURL, encryptedThumbnailFile);
 
 
       final fileUploadURL = await _getUploadURL();
       final fileUploadURL = await _getUploadURL();
-      String fileObjectKey = await _putFile(fileUploadURL, encryptedFile);
+      final String fileObjectKey = await _putFile(fileUploadURL, encryptedFile);
 
 
       final metadata = await file.getMetadataForUpload(mediaUploadData);
       final metadata = await file.getMetadataForUpload(mediaUploadData);
       final encryptedMetadataData = await CryptoUtil.encryptChaCha(
       final encryptedMetadataData = await CryptoUtil.encryptChaCha(
@@ -447,10 +447,10 @@ class FileUploader {
   }
   }
 
 
   /*
   /*
-  // _mapToExistingUpload links the current file to be uploaded with the
-  // existing files. If the link is successful, it returns true other false.
-   When false, we should go ahead and re-upload or update the file
-    It performs following checks:
+  _mapToExistingUpload links the fileToUpload with the existing uploaded
+  files. if the link is successful, it returns true otherwise false.
+  When false, we should go ahead and re-upload or update the file.
+  It performs following checks:
     a) Uploaded file with same localID and destination collection. Delete the
     a) Uploaded file with same localID and destination collection. Delete the
      fileToUpload entry
      fileToUpload entry
     b) Uploaded file in destination collection but with missing localID.
     b) Uploaded file in destination collection but with missing localID.
@@ -469,32 +469,28 @@ class FileUploader {
     File fileToUpload,
     File fileToUpload,
     int toCollectionID,
     int toCollectionID,
   ) async {
   ) async {
-    if (fileToUpload.uploadedFileID != -1 &&
-        fileToUpload.uploadedFileID != null) {
-      _logger.warning('file is already uploaded, skipping mapping logic');
+    if (fileToUpload.uploadedFileID != null) {
+      _logger.severe(
+        'Critical: file is already uploaded, skipped mapping',
+      );
       return false;
       return false;
     }
     }
-    List<String> hash = [mediaUploadData.fileHash];
-    if (fileToUpload.fileType == FileType.livePhoto) {
-      hash.add(mediaUploadData.zipHash);
-    }
-    List<File> existingFiles =
+
+    final List<File> existingUploadedFiles =
         await FilesDB.instance.getUploadedFilesWithHashes(
         await FilesDB.instance.getUploadedFilesWithHashes(
-      hash,
+      mediaUploadData.hashData,
       fileToUpload.fileType,
       fileToUpload.fileType,
       Configuration.instance.getUserID(),
       Configuration.instance.getUserID(),
     );
     );
-    if (existingFiles?.isEmpty ?? true) {
+    if (existingUploadedFiles?.isEmpty ?? true) {
       return false;
       return false;
     } else {
     } else {
       debugPrint("Found some matches");
       debugPrint("Found some matches");
     }
     }
     // case a
     // case a
-    File sameLocalSameCollection = existingFiles.firstWhere(
-      (element) =>
-          element.uploadedFileID != -1 &&
-          element.collectionID == toCollectionID &&
-          element.localID == fileToUpload.localID,
+    final File sameLocalSameCollection = existingUploadedFiles.firstWhere(
+      (e) =>
+          e.collectionID == toCollectionID && e.localID == fileToUpload.localID,
       orElse: () => null,
       orElse: () => null,
     );
     );
     if (sameLocalSameCollection != null) {
     if (sameLocalSameCollection != null) {
@@ -503,16 +499,14 @@ class FileUploader {
         "\n existing: ${sameLocalSameCollection.tag()}",
         "\n existing: ${sameLocalSameCollection.tag()}",
       );
       );
       // should delete the fileToUploadEntry
       // should delete the fileToUploadEntry
-      FilesDB.instance.deleteByGeneratedID(fileToUpload.generatedID);
+      await FilesDB.instance.deleteByGeneratedID(fileToUpload.generatedID);
       return true;
       return true;
     }
     }
 
 
     // case b
     // case b
-    File fileMissingLocalButSameCollection = existingFiles.firstWhere(
-      (element) =>
-          element.uploadedFileID != -1 &&
-          element.collectionID == toCollectionID &&
-          element.localID == null,
+    final File fileMissingLocalButSameCollection =
+        existingUploadedFiles.firstWhere(
+      (e) => e.collectionID == toCollectionID && e.localID == null,
       orElse: () => null,
       orElse: () => null,
     );
     );
     if (fileMissingLocalButSameCollection != null) {
     if (fileMissingLocalButSameCollection != null) {
@@ -529,10 +523,9 @@ class FileUploader {
     }
     }
 
 
     // case c and d
     // case c and d
-    File fileExistsButDifferentCollection = existingFiles.firstWhere(
-      (element) =>
-          element.uploadedFileID != -1 &&
-          element.collectionID != toCollectionID,
+    final File fileExistsButDifferentCollection =
+        existingUploadedFiles.firstWhere(
+      (e) => e.collectionID != toCollectionID,
       orElse: () => null,
       orElse: () => null,
     );
     );
     if (fileExistsButDifferentCollection != null) {
     if (fileExistsButDifferentCollection != null) {
@@ -542,7 +535,10 @@ class FileUploader {
       );
       );
       await CollectionsService.instance
       await CollectionsService.instance
           .linkLocalFileToExistingUploadedFileInAnotherCollection(
           .linkLocalFileToExistingUploadedFileInAnotherCollection(
-              toCollectionID, fileToUpload, fileExistsButDifferentCollection);
+        toCollectionID,
+        localFileToUpload: fileToUpload,
+        existingUploadedFile: fileExistsButDifferentCollection,
+      );
       return true;
       return true;
     }
     }
     // case e
     // case e
@@ -575,7 +571,7 @@ class FileUploader {
   }
   }
 
 
   Future _onInvalidFileError(File file, InvalidFileError e) async {
   Future _onInvalidFileError(File file, InvalidFileError e) async {
-    String ext = file.title == null ? "no title" : extension(file.title);
+    final String ext = file.title == null ? "no title" : extension(file.title);
     _logger.severe(
     _logger.severe(
       "Invalid file: (ext: $ext) encountered: " + file.toString(),
       "Invalid file: (ext: $ext) encountered: " + file.toString(),
       e,
       e,

+ 33 - 20
lib/utils/file_uploader_util.dart

@@ -21,25 +21,31 @@ import 'package:video_thumbnail/video_thumbnail.dart';
 
 
 final _logger = Logger("FileUtil");
 final _logger = Logger("FileUtil");
 const kMaximumThumbnailCompressionAttempts = 2;
 const kMaximumThumbnailCompressionAttempts = 2;
+const kLivePhotoHashSeparator = ':';
 
 
 class MediaUploadData {
 class MediaUploadData {
   final io.File sourceFile;
   final io.File sourceFile;
   final Uint8List thumbnail;
   final Uint8List thumbnail;
   final bool isDeleted;
   final bool isDeleted;
-  // presents the hash for the original video or image file.
-  // for livePhotos, fileHash represents the image hash value
-  final String fileHash;
-  final String liveVideoHash;
-  final String zipHash;
+  final FileHashData hashData;
 
 
   MediaUploadData(
   MediaUploadData(
     this.sourceFile,
     this.sourceFile,
     this.thumbnail,
     this.thumbnail,
-    this.isDeleted, {
-    this.fileHash,
-    this.liveVideoHash,
-    this.zipHash,
-  });
+    this.isDeleted,
+    this.hashData,
+  );
+}
+
+class FileHashData {
+  // For livePhotos, the fileHash value will be imageHash:videoHash
+  final String fileHash;
+
+  // zipHash is used to take care of existing live photo uploads from older
+  // mobile clients
+  String zipHash;
+
+  FileHashData(this.fileHash, {this.zipHash});
 }
 }
 
 
 Future<MediaUploadData> getUploadDataFromEnteFile(ente.File file) async {
 Future<MediaUploadData> getUploadDataFromEnteFile(ente.File file) async {
@@ -54,7 +60,7 @@ Future<MediaUploadData> _getMediaUploadDataFromAssetFile(ente.File file) async {
   io.File sourceFile;
   io.File sourceFile;
   Uint8List thumbnailData;
   Uint8List thumbnailData;
   bool isDeleted;
   bool isDeleted;
-  String fileHash, livePhotoVideoHash, zipHash;
+  String fileHash, zipHash;
 
 
   // The timeouts are to safeguard against https://github.com/CaiJingLong/flutter_photo_manager/issues/467
   // The timeouts are to safeguard against https://github.com/CaiJingLong/flutter_photo_manager/issues/467
   final asset = await file
   final asset = await file
@@ -92,17 +98,20 @@ Future<MediaUploadData> _getMediaUploadDataFromAssetFile(ente.File file) async {
   if (file.fileType == FileType.livePhoto && io.Platform.isIOS) {
   if (file.fileType == FileType.livePhoto && io.Platform.isIOS) {
     final io.File videoUrl = await Motionphoto.getLivePhotoFile(file.localID);
     final io.File videoUrl = await Motionphoto.getLivePhotoFile(file.localID);
     if (videoUrl == null || !videoUrl.existsSync()) {
     if (videoUrl == null || !videoUrl.existsSync()) {
-      String errMsg =
+      final String errMsg =
           "missing livePhoto url for  ${file.toString()} with subType ${file.fileSubType}";
           "missing livePhoto url for  ${file.toString()} with subType ${file.fileSubType}";
       _logger.severe(errMsg);
       _logger.severe(errMsg);
       throw InvalidFileUploadState(errMsg);
       throw InvalidFileUploadState(errMsg);
     }
     }
-    livePhotoVideoHash = Sodium.bin2base64(await CryptoUtil.getHash(videoUrl));
+    String livePhotoVideoHash =
+        Sodium.bin2base64(await CryptoUtil.getHash(videoUrl));
+    // imgHash:vidHash
+    fileHash = '$fileHash$kLivePhotoHashSeparator$livePhotoVideoHash';
     final tempPath = Configuration.instance.getTempDirectory();
     final tempPath = Configuration.instance.getTempDirectory();
     // .elp -> ente live photo
     // .elp -> ente live photo
     final livePhotoPath = tempPath + file.generatedID.toString() + ".elp";
     final livePhotoPath = tempPath + file.generatedID.toString() + ".elp";
     _logger.fine("Uploading zipped live photo from " + livePhotoPath);
     _logger.fine("Uploading zipped live photo from " + livePhotoPath);
-    var encoder = ZipFileEncoder();
+    final encoder = ZipFileEncoder();
     encoder.create(livePhotoPath);
     encoder.create(livePhotoPath);
     encoder.addFile(videoUrl, "video" + extension(videoUrl.path));
     encoder.addFile(videoUrl, "video" + extension(videoUrl.path));
     encoder.addFile(sourceFile, "image" + extension(sourceFile.path));
     encoder.addFile(sourceFile, "image" + extension(sourceFile.path));
@@ -138,9 +147,7 @@ Future<MediaUploadData> _getMediaUploadDataFromAssetFile(ente.File file) async {
     sourceFile,
     sourceFile,
     thumbnailData,
     thumbnailData,
     isDeleted,
     isDeleted,
-    fileHash: fileHash,
-    liveVideoHash: livePhotoVideoHash,
-    zipHash: zipHash,
+    FileHashData(fileHash, zipHash: zipHash),
   );
   );
 }
 }
 
 
@@ -161,8 +168,8 @@ Future<void> _decorateEnteFileData(ente.File file, AssetEntity asset) async {
 Future<MediaUploadData> _getMediaUploadDataFromAppCache(ente.File file) async {
 Future<MediaUploadData> _getMediaUploadDataFromAppCache(ente.File file) async {
   io.File sourceFile;
   io.File sourceFile;
   Uint8List thumbnailData;
   Uint8List thumbnailData;
-  bool isDeleted = false;
-  var localPath = getSharedMediaFilePath(file);
+  const bool isDeleted = false;
+  final localPath = getSharedMediaFilePath(file);
   sourceFile = io.File(localPath);
   sourceFile = io.File(localPath);
   if (!sourceFile.existsSync()) {
   if (!sourceFile.existsSync()) {
     _logger.warning("File doesn't exist in app sandbox");
     _logger.warning("File doesn't exist in app sandbox");
@@ -170,7 +177,13 @@ Future<MediaUploadData> _getMediaUploadDataFromAppCache(ente.File file) async {
   }
   }
   try {
   try {
     thumbnailData = await getThumbnailFromInAppCacheFile(file);
     thumbnailData = await getThumbnailFromInAppCacheFile(file);
-    return MediaUploadData(sourceFile, thumbnailData, isDeleted);
+    final fileHash = Sodium.bin2base64(await CryptoUtil.getHash(sourceFile));
+    return MediaUploadData(
+      sourceFile,
+      thumbnailData,
+      isDeleted,
+      FileHashData(fileHash),
+    );
   } catch (e, s) {
   } catch (e, s) {
     _logger.severe("failed to generate thumbnail", e, s);
     _logger.severe("failed to generate thumbnail", e, s);
     throw InvalidFileError(
     throw InvalidFileError(

+ 10 - 10
lib/utils/file_util.dart

@@ -40,7 +40,7 @@ Future<io.File> getFile(
   if (file.isRemoteFile()) {
   if (file.isRemoteFile()) {
     return getFileFromServer(file, liveVideo: liveVideo);
     return getFileFromServer(file, liveVideo: liveVideo);
   } else {
   } else {
-    String key = file.tag() + liveVideo.toString() + isOrigin.toString();
+    final String key = file.tag() + liveVideo.toString() + isOrigin.toString();
     final cachedFile = FileLruCache.get(key);
     final cachedFile = FileLruCache.get(key);
     if (cachedFile == null) {
     if (cachedFile == null) {
       final diskFile = await _getLocalDiskFile(
       final diskFile = await _getLocalDiskFile(
@@ -69,7 +69,7 @@ Future<io.File> _getLocalDiskFile(
   bool isOrigin = false,
   bool isOrigin = false,
 }) async {
 }) async {
   if (file.isSharedMediaToAppSandbox()) {
   if (file.isSharedMediaToAppSandbox()) {
-    var localFile = io.File(getSharedMediaFilePath(file));
+    final localFile = io.File(getSharedMediaFilePath(file));
     return localFile.exists().then((exist) {
     return localFile.exists().then((exist) {
       return exist ? localFile : null;
       return exist ? localFile : null;
     });
     });
@@ -194,17 +194,17 @@ Future<_LivePhoto> _downloadLivePhoto(
     }
     }
     _logger.fine("Decoded zipped live photo from " + decryptedFile.path);
     _logger.fine("Decoded zipped live photo from " + decryptedFile.path);
     io.File imageFileCache, videoFileCache;
     io.File imageFileCache, videoFileCache;
-    List<int> bytes = await decryptedFile.readAsBytes();
-    Archive archive = ZipDecoder().decodeBytes(bytes);
+    final List<int> bytes = await decryptedFile.readAsBytes();
+    final Archive archive = ZipDecoder().decodeBytes(bytes);
     final tempPath = Configuration.instance.getTempDirectory();
     final tempPath = Configuration.instance.getTempDirectory();
     // Extract the contents of Zip compressed archive to disk
     // Extract the contents of Zip compressed archive to disk
     for (ArchiveFile archiveFile in archive) {
     for (ArchiveFile archiveFile in archive) {
       if (archiveFile.isFile) {
       if (archiveFile.isFile) {
-        String filename = archiveFile.name;
-        String fileExtension = getExtension(archiveFile.name);
-        String decodePath =
+        final String filename = archiveFile.name;
+        final String fileExtension = getExtension(archiveFile.name);
+        final String decodePath =
             tempPath + file.uploadedFileID.toString() + filename;
             tempPath + file.uploadedFileID.toString() + filename;
-        List<int> data = archiveFile.content;
+        final List<int> data = archiveFile.content;
         if (filename.startsWith("image")) {
         if (filename.startsWith("image")) {
           final imageFile = io.File(decodePath);
           final imageFile = io.File(decodePath);
           await imageFile.create(recursive: true);
           await imageFile.create(recursive: true);
@@ -259,8 +259,8 @@ Future<io.File> _downloadAndCache(
     if (decryptedFile == null) {
     if (decryptedFile == null) {
       return null;
       return null;
     }
     }
-    var decryptedFilePath = decryptedFile.path;
-    String fileExtension = getExtension(file.title);
+    final decryptedFilePath = decryptedFile.path;
+    final String fileExtension = getExtension(file.title);
     var outputFile = decryptedFile;
     var outputFile = decryptedFile;
     if ((fileExtension == "unknown" && file.fileType == FileType.image) ||
     if ((fileExtension == "unknown" && file.fileType == FileType.image) ||
         (io.Platform.isAndroid && fileExtension == "heic")) {
         (io.Platform.isAndroid && fileExtension == "heic")) {

+ 4 - 4
lib/utils/hex.dart

@@ -27,7 +27,7 @@ class HexEncoder extends Converter<List<int>, String> {
 
 
   @override
   @override
   String convert(List<int> bytes) {
   String convert(List<int> bytes) {
-    StringBuffer buffer = StringBuffer();
+    final StringBuffer buffer = StringBuffer();
     for (int part in bytes) {
     for (int part in bytes) {
       if (part & 0xff != part) {
       if (part & 0xff != part) {
         throw const FormatException("Non-byte integer detected");
         throw const FormatException("Non-byte integer detected");
@@ -53,10 +53,10 @@ class HexDecoder extends Converter<String, List<int>> {
     if (str.length % 2 != 0) {
     if (str.length % 2 != 0) {
       str = "0" + str;
       str = "0" + str;
     }
     }
-    Uint8List result = Uint8List(str.length ~/ 2);
+    final Uint8List result = Uint8List(str.length ~/ 2);
     for (int i = 0; i < result.length; i++) {
     for (int i = 0; i < result.length; i++) {
-      int firstDigit = _alphabet.indexOf(str[i * 2]);
-      int secondDigit = _alphabet.indexOf(str[i * 2 + 1]);
+      final int firstDigit = _alphabet.indexOf(str[i * 2]);
+      final int secondDigit = _alphabet.indexOf(str[i * 2 + 1]);
       if (firstDigit == -1 || secondDigit == -1) {
       if (firstDigit == -1 || secondDigit == -1) {
         throw FormatException("Non-hex character detected in $hex");
         throw FormatException("Non-hex character detected in $hex");
       }
       }

+ 2 - 2
lib/utils/magic_util.dart

@@ -52,7 +52,7 @@ Future<void> changeCollectionVisibility(
   );
   );
   await dialog.show();
   await dialog.show();
   try {
   try {
-    Map<String, dynamic> update = {kMagicKeyVisibility: newVisibility};
+    final Map<String, dynamic> update = {kMagicKeyVisibility: newVisibility};
     await CollectionsService.instance.updateMagicMetadata(collection, update);
     await CollectionsService.instance.updateMagicMetadata(collection, update);
     // Force reload home gallery to pull in the now unarchived files
     // Force reload home gallery to pull in the now unarchived files
     Bus.instance.fire(ForceReloadHomeGalleryEvent());
     Bus.instance.fire(ForceReloadHomeGalleryEvent());
@@ -135,7 +135,7 @@ Future<void> _updatePublicMetadata(
   final dialog = createProgressDialog(context, 'please wait...');
   final dialog = createProgressDialog(context, 'please wait...');
   await dialog.show();
   await dialog.show();
   try {
   try {
-    Map<String, dynamic> update = {key: value};
+    final Map<String, dynamic> update = {key: value};
     await FileMagicService.instance.updatePublicMagicMetadata(files, update);
     await FileMagicService.instance.updatePublicMagicMetadata(files, update);
     showShortToast(context, 'done');
     showShortToast(context, 'done');
     await dialog.hide();
     await dialog.hide();

+ 4 - 4
lib/utils/share_util.dart

@@ -44,12 +44,12 @@ Future<void> share(
 
 
 Rect shareButtonRect(BuildContext context, GlobalKey shareButtonKey) {
 Rect shareButtonRect(BuildContext context, GlobalKey shareButtonKey) {
   Size size = MediaQuery.of(context).size;
   Size size = MediaQuery.of(context).size;
-  RenderBox renderBox = shareButtonKey?.currentContext?.findRenderObject();
+  final RenderBox renderBox = shareButtonKey?.currentContext?.findRenderObject();
   if (renderBox == null) {
   if (renderBox == null) {
     return Rect.fromLTWH(0, 0, size.width, size.height / 2);
     return Rect.fromLTWH(0, 0, size.width, size.height / 2);
   }
   }
   size = renderBox.size;
   size = renderBox.size;
-  Offset position = renderBox.localToGlobal(Offset.zero);
+  final Offset position = renderBox.localToGlobal(Offset.zero);
   return Rect.fromCenter(
   return Rect.fromCenter(
     center: position + Offset(size.width / 2, size.height / 2),
     center: position + Offset(size.width / 2, size.height / 2),
     width: size.width,
     width: size.width,
@@ -65,7 +65,7 @@ Future<List<File>> convertIncomingSharedMediaToFile(
   List<SharedMediaFile> sharedMedia,
   List<SharedMediaFile> sharedMedia,
   int collectionID,
   int collectionID,
 ) async {
 ) async {
-  List<File> localFiles = [];
+  final List<File> localFiles = [];
   for (var media in sharedMedia) {
   for (var media in sharedMedia) {
     if (!(media.type == SharedMediaType.IMAGE ||
     if (!(media.type == SharedMediaType.IMAGE ||
         media.type == SharedMediaType.VIDEO)) {
         media.type == SharedMediaType.VIDEO)) {
@@ -74,7 +74,7 @@ Future<List<File>> convertIncomingSharedMediaToFile(
       );
       );
       continue;
       continue;
     }
     }
-    var enteFile = File();
+    final enteFile = File();
     // fileName: img_x.jpg
     // fileName: img_x.jpg
     enteFile.title = basename(media.path);
     enteFile.title = basename(media.path);
     var ioFile = dartio.File(media.path);
     var ioFile = dartio.File(media.path);

+ 1 - 1
lib/utils/trash_diff_fetcher.dart

@@ -62,7 +62,7 @@ class TrashDiffFetcher {
             fileDecryptionKey,
             fileDecryptionKey,
             Sodium.base642bin(trash.metadataDecryptionHeader),
             Sodium.base642bin(trash.metadataDecryptionHeader),
           );
           );
-          Map<String, dynamic> metadata =
+          final Map<String, dynamic> metadata =
               jsonDecode(utf8.decode(encodedMetadata));
               jsonDecode(utf8.decode(encodedMetadata));
           trash.applyMetadata(metadata);
           trash.applyMetadata(metadata);
           if (item["file"]['magicMetadata'] != null) {
           if (item["file"]['magicMetadata'] != null) {

+ 50 - 0
lib/utils/validator_util.dart

@@ -0,0 +1,50 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:logging/logging.dart';
+import 'package:photos/models/key_attributes.dart';
+
+Logger _logger = Logger("Validator");
+
+void validatePreVerificationStateCheck(
+  KeyAttributes keyAttr,
+  String password,
+  String encryptedToken,
+) {
+  nullOrEmptyArgCheck(encryptedToken, "encryptedToken");
+  nullOrEmptyArgCheck(password, "userPassword");
+  if (keyAttr == null) {
+    throw ArgumentError("key Attributes can not be null");
+  }
+  nullOrEmptyArgCheck(keyAttr.kekSalt, "keySalt");
+  nullOrEmptyArgCheck(keyAttr.encryptedKey, "encryptedKey");
+  nullOrEmptyArgCheck(keyAttr.keyDecryptionNonce, "keyDecryptionNonce");
+  nullOrEmptyArgCheck(keyAttr.encryptedSecretKey, "encryptedSecretKey");
+  nullOrEmptyArgCheck(
+    keyAttr.secretKeyDecryptionNonce,
+    "secretKeyDecryptionNonce",
+  );
+  nullOrEmptyArgCheck(keyAttr.publicKey, "publicKey");
+  if ((keyAttr.memLimit ?? 0) <= 0 || (keyAttr.opsLimit ?? 0) <= 0) {
+    throw ArgumentError("Key mem/OpsLimit can not be null or <0");
+  }
+  // check password encoding issues
+  try {
+    final Uint8List passwordL = utf8.encode(password);
+    try {
+      utf8.decode(passwordL);
+    } catch (e) {
+      _logger.severe("CRITICAL: password decode failed", e);
+      rethrow;
+    }
+  } catch (e) {
+    _logger.severe('CRITICAL: password encode failed');
+    rethrow;
+  }
+}
+
+void nullOrEmptyArgCheck(String value, String name) {
+  if (value == null || value.isEmpty) {
+    throw ArgumentError("Critical: $name is nullOrEmpty");
+  }
+}

+ 1 - 1
pubspec.yaml

@@ -11,7 +11,7 @@ description: ente photos application
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # Read more about iOS versioning at
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 0.6.25+355
+version: 0.6.29+359
 
 
 environment:
 environment:
   sdk: ">=2.10.0 <3.0.0"
   sdk: ">=2.10.0 <3.0.0"