diff --git a/fastlane/metadata/android/zh/full_description.txt b/fastlane/metadata/android/zh/full_description.txt index e279e6864..ed95a109c 100644 --- a/fastlane/metadata/android/zh/full_description.txt +++ b/fastlane/metadata/android/zh/full_description.txt @@ -4,7 +4,7 @@ ente 是一个简单的应用程序来备份和分享您的照片和视频。 我们在Android、iOS、web 和桌面上有开源应用, 和您的照片将以端到端加密方式无缝同步 (e2ee)。 -即使您不是亲人,也可以轻松地与您的个人分享您的相册。 即使您不是亲人,也可以轻松地与您的个人分享您的相册。 您可以分享可公开查看的链接,他们可以通过添加照片来查看您的相册并进行协作,即使没有帐户或应用。 +如果你一直在寻找一个隐私友好的可以替代Google Photos,你已经来到了正确的地方。 使用 Ente,它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。 使用 Ente,它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。 即使您不是亲人,也可以轻松地与您的个人分享您的相册。 即使您不是亲人,也可以轻松地与您的个人分享您的相册。 您可以分享可公开查看的链接,他们可以通过添加照片来查看您的相册并进行协作,即使没有帐户或应用。 您的加密数据已复制到三个不同的地点,包括巴黎的一个铺面掩体。 我们认真对待子孙后代,并确保您回忆比您长寿。 我们认真对待子孙后代,并确保您回忆比您长寿。 @@ -30,7 +30,7 @@ ente 是一个简单的应用程序来备份和分享您的照片和视频。 ente requests for certain permissions to serve the purpose of a photo storage provider, which can be reviewed here: https://github.com/ente-io/photos-app/blob/f-droid/android/permissions.md 价格 -我们不会永远提供免费计划,因为我们必须保持可持续性,经受住时间的考验。 相反,我们提供您可以自由分享的负担得起的计划。 您可以在 ente. 相反,我们提供您可以自由分享的负担得起的计划。 您可以在 ente.io找到更多信息。 +我们不会永远提供免费计划,因为我们必须保持可持续性,经受住时间的考验。 相反,我们提供您可以自由分享的负担得起的计划。 您可以在 ente. 相反,我们提供您可以自由分享的负担得起的计划。 您可以在 ente.io找到更多信息。 您可以在 ente.io找到更多信息。 支持 我们对提供人的支持感到自豪。 我们对提供人的支持感到自豪。 如果您是我们的付费客户,您可以联系Team@ente.io并期待我们的团队在24小时内做出回应。 diff --git a/lib/main.dart b/lib/main.dart index d08535a03..2ecb89bbb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:io'; import "package:adaptive_theme/adaptive_theme.dart"; import 'package:background_fetch/background_fetch.dart'; +import "package:computer/computer.dart"; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -153,6 +154,8 @@ Future _init(bool isBackground, {String via = ''}) async { } else { AppLifecycleService.instance.onAppInForeground('init via: $via'); } + // Start workers asynchronously. No need to wait for them to start + Computer.shared().turnOn(workersCount: 4, verbose: kDebugMode); CryptoUtil.init(); await NotificationService.instance.init(); await NetworkClient.instance.init(); diff --git a/lib/services/local/local_sync_util.dart b/lib/services/local/local_sync_util.dart index 6f161517a..9589d2ca4 100644 --- a/lib/services/local/local_sync_util.dart +++ b/lib/services/local/local_sync_util.dart @@ -16,7 +16,6 @@ const assetFetchPageSize = 2000; Future, List>> getLocalPathAssetsAndFiles( int fromTime, int toTime, - Computer computer, ) async { final pathEntities = await _getGalleryList( updateFromTime: fromTime, @@ -31,17 +30,24 @@ Future, List>> getLocalPathAssetsAndFiles( final List uniqueFiles = []; for (AssetPathEntity pathEntity in pathEntities) { final List assetsInPath = await _getAllAssetLists(pathEntity); - final Tuple2, List> result = await computer.compute( - _getLocalIDsAndFilesFromAssets, - param: { - "pathEntity": pathEntity, - "fromTime": fromTime, - "alreadySeenLocalIDs": alreadySeenLocalIDs, - "assetList": assetsInPath, - }, - ); - alreadySeenLocalIDs.addAll(result.item1); - uniqueFiles.addAll(result.item2); + late Tuple2, List> result; + if (assetsInPath.isEmpty) { + result = const Tuple2({}, []); + } else { + result = await Computer.shared().compute( + _getLocalIDsAndFilesFromAssets, + param: { + "pathEntity": pathEntity, + "fromTime": fromTime, + "alreadySeenLocalIDs": alreadySeenLocalIDs, + "assetList": assetsInPath, + }, + taskName: + "getLocalPathAssetsAndFiles-${pathEntity.name}-count-${assetsInPath.length}", + ); + alreadySeenLocalIDs.addAll(result.item1); + uniqueFiles.addAll(result.item2); + } localPathAssets.add( LocalPathAsset( localIDs: result.item1, @@ -118,15 +124,17 @@ Future getDiffWithLocal( Set existingIDs, // localIDs of files already imported in app Map> pathToLocalIDs, Set invalidIDs, - Computer computer, ) async { final Map args = {}; args['assets'] = assets; args['existingIDs'] = existingIDs; args['invalidIDs'] = invalidIDs; args['pathToLocalIDs'] = pathToLocalIDs; - final LocalDiffResult diffResult = - await computer.compute(_getLocalAssetsDiff, param: args); + final LocalDiffResult diffResult = await Computer.shared().compute( + _getLocalAssetsDiff, + param: args, + taskName: "getLocalAssetsDiff", + ); if (diffResult.localPathAssets != null) { diffResult.uniqueLocalFiles = await _convertLocalAssetsToUniqueFiles(diffResult.localPathAssets!); diff --git a/lib/services/local_sync_service.dart b/lib/services/local_sync_service.dart index 43443a3ae..c38975612 100644 --- a/lib/services/local_sync_service.dart +++ b/lib/services/local_sync_service.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:computer/computer.dart'; import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -24,7 +23,6 @@ import 'package:tuple/tuple.dart'; class LocalSyncService { final _logger = Logger("LocalSyncService"); final _db = FilesDB.instance; - final Computer _computer = Computer(); late SharedPreferences _prefs; Completer? _existingSync; @@ -47,7 +45,6 @@ class LocalSyncService { if (!AppLifecycleService.instance.isForeground) { await PhotoManager.setIgnorePermissionCheck(true); } - await _computer.turnOn(workersCount: 1); if (hasGrantedPermissions()) { _registerChangeCallback(); } @@ -164,7 +161,6 @@ class LocalSyncService { existingLocalFileIDs, pathToLocalIDs, invalidIDs, - _computer, ); bool hasAnyMappingChanged = false; if (localDiffResult.newPathToLocalIDs?.isNotEmpty ?? false) { @@ -268,17 +264,18 @@ class LocalSyncService { required int toTime, }) async { final Tuple2, List> result = - await getLocalPathAssetsAndFiles(fromTime, toTime, _computer); - - // Update the mapping for device path_id to local file id. Also, keep track - // of newly discovered device paths - await FilesDB.instance.insertLocalAssets( - result.item1, - shouldAutoBackup: Configuration.instance.hasSelectedAllFoldersForBackup(), - ); + await getLocalPathAssetsAndFiles(fromTime, toTime); final List files = result.item2; if (files.isNotEmpty) { + // Update the mapping for device path_id to local file id. Also, keep track + // of newly discovered device paths + await FilesDB.instance.insertLocalAssets( + result.item1, + shouldAutoBackup: + Configuration.instance.hasSelectedAllFoldersForBackup(), + ); + _logger.info( "Loaded ${files.length} photos from " + DateTime.fromMicrosecondsSinceEpoch(fromTime).toString() + diff --git a/lib/utils/crypto_util.dart b/lib/utils/crypto_util.dart index 1a838c510..27407b950 100644 --- a/lib/utils/crypto_util.dart +++ b/lib/utils/crypto_util.dart @@ -166,10 +166,10 @@ Uint8List chachaDecryptData(Map args) { } class CryptoUtil { - static final Computer _computer = Computer(); + // Note: workers are turned on during app startup. + static final Computer _computer = Computer.shared(); static init() { - _computer.turnOn(workersCount: 4); Sodium.init(); } @@ -231,7 +231,11 @@ class CryptoUtil { args["cipher"] = cipher; args["nonce"] = nonce; args["key"] = key; - return _computer.compute(cryptoSecretboxOpenEasy, param: args); + return _computer.compute( + cryptoSecretboxOpenEasy, + param: args, + taskName: "decrypt", + ); } // Decrypts the given cipher, with the given key and nonce using XSalsa20 @@ -262,7 +266,11 @@ class CryptoUtil { final args = {}; args["source"] = source; args["key"] = key; - return _computer.compute(chachaEncryptData, param: args); + return _computer.compute( + chachaEncryptData, + param: args, + taskName: "encryptChaCha", + ); } // Decrypts the given source, with the given key and header using XChaCha20 @@ -277,7 +285,11 @@ class CryptoUtil { args["source"] = source; args["key"] = key; args["header"] = header; - return _computer.compute(chachaDecryptData, param: args); + return _computer.compute( + chachaDecryptData, + param: args, + taskName: "decryptChaCha", + ); } // Encrypts the file at sourceFilePath, with the key (if provided) and a @@ -293,7 +305,8 @@ class CryptoUtil { args["sourceFilePath"] = sourceFilePath; args["destinationFilePath"] = destinationFilePath; args["key"] = key; - return _computer.compute(chachaEncryptFile, param: args); + return _computer.compute(chachaEncryptFile, + param: args, taskName: "encryptFile"); } // Decrypts the file at sourceFilePath, with the given key and header using @@ -309,7 +322,8 @@ class CryptoUtil { args["destinationFilePath"] = destinationFilePath; args["header"] = header; args["key"] = key; - return _computer.compute(chachaDecryptFile, param: args); + return _computer.compute(chachaDecryptFile, + param: args, taskName: "decryptFile"); } // Generates and returns a 256-bit key. @@ -391,7 +405,7 @@ class CryptoUtil { return DerivedKeyResult(key, memLimit, opsLimit); } - // Derives a key for a given password, salt, memLimit and opsLimit using + // Derives a key for a given password, salt, memLimit and opsLimit using // Argon2id, v1.3. static Future deriveKey( Uint8List password, @@ -407,6 +421,7 @@ class CryptoUtil { "memLimit": memLimit, "opsLimit": opsLimit, }, + taskName: "deriveKey", ); } @@ -417,6 +432,7 @@ class CryptoUtil { param: { "sourceFilePath": source.path, }, + taskName: "fileHash", ); } } diff --git a/pubspec.lock b/pubspec.lock index cba57acc4..efc2b2495 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -275,11 +275,12 @@ packages: computer: dependency: "direct main" description: - name: computer - sha256: "3df9f1ef497aaf69e066b00f4441726eb28037dc33e97b5d56393967f92c5fe8" - url: "https://pub.dev" - source: hosted - version: "2.0.0" + path: "." + ref: HEAD + resolved-ref: "82e365fed8a1a76f6eea0220de98389eed7b0445" + url: "https://github.com/ente-io/computer.git" + source: git + version: "3.2.1" confetti: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b38dbdacf..cb4f7a656 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,7 +28,8 @@ dependencies: chewie: path: thirdparty/chewie collection: # dart - computer: ^2.0.0 + computer: + git: "https://github.com/ente-io/computer.git" confetti: ^0.6.0 connectivity_plus: ^3.0.3 crypto: ^3.0.2