From ffe22e8a48fe3c29a6ee4d00e3a5b1d97e91adb1 Mon Sep 17 00:00:00 2001 From: Damir Mihaljinec Date: Tue, 21 Apr 2026 17:13:39 +0200 Subject: [PATCH] 2.36.0 --- .../domain/usecase/ToggleLayoutType.kt | 6 +- .../domain/usecase/UpdateLayoutType.kt | 4 +- .../domain/usecase/UpdateThemeStyle.kt | 4 +- app/build.gradle.kts | 4 +- .../android/drive/di/ApplicationModule.kt | 6 + .../drive/initializer/DownloadInitializer.kt | 4 +- .../initializer/ProtonDriveSdkInitializer.kt | 9 +- .../drive/initializer/ShortcutInitializer.kt | 6 + .../initializer/TagsMigrationInitializer.kt | 2 +- .../drive/initializer/WebViewInitializer.kt | 5 +- .../AppNotificationBuilderProvider.kt | 11 +- .../provider/AppProtonSdkClientProvider.kt | 80 + .../receiver/NotificationBroadcastReceiver.kt | 1 + .../android/drive/settings/DebugSettings.kt | 1 + .../proton/android/drive/ui/options/Option.kt | 2 +- .../drive/ui/screen/AccountSettingsScreen.kt | 2 + .../drive/ui/screen/AppAccessScreen.kt | 1 + .../drive/ui/screen/ComputersScreen.kt | 1 + .../drive/ui/screen/CreateNewAlbumScreen.kt | 1 + .../android/drive/ui/screen/FileInfoScreen.kt | 3 + .../ui/screen/GetMoreFreeStorageScreen.kt | 1 + .../android/drive/ui/screen/LogScreen.kt | 1 + .../android/drive/ui/screen/MoveToFolder.kt | 1 + .../drive/ui/screen/PhotosAndAlbumsScreen.kt | 4 +- .../drive/ui/screen/PhotosBackupScreen.kt | 1 + .../android/drive/ui/screen/PhotosScreen.kt | 3 +- .../drive/ui/screen/PickerPhotosScreen.kt | 1 + .../drive/ui/screen/ScanDocumentNameScreen.kt | 2 + .../drive/ui/screen/SharedTabsScreen.kt | 3 + .../drive/ui/screen/SpringSalePromoScreen.kt | 1 + .../drive/ui/screen/SyncedFoldersScreen.kt | 1 + .../android/drive/ui/screen/UploadToScreen.kt | 1 + .../viewmodel/AddToAlbumsOptionsViewModel.kt | 8 +- .../ui/viewmodel/AlbumOptionsViewModel.kt | 6 +- .../drive/ui/viewmodel/AlbumViewModel.kt | 13 +- .../drive/ui/viewmodel/AlbumsViewModel.kt | 8 +- .../viewmodel/AutoLockDurationsViewModel.kt | 6 +- .../ui/viewmodel/BackupIssuesViewModel.kt | 8 +- .../ui/viewmodel/CommonSharedViewModel.kt | 8 +- .../drive/ui/viewmodel/ComputersViewModel.kt | 8 +- .../ConfirmDeletionDialogViewModel.kt | 6 +- .../ConfirmEmptyTrashDialogViewModel.kt | 6 +- .../ConfirmLeaveAlbumDialogViewModel.kt | 17 +- .../ConfirmSkipIssuesDialogViewModel.kt | 6 +- .../ConfirmStopAllSharingDialogViewModel.kt | 4 +- .../ConfirmStopLinkSharingDialogViewModel.kt | 4 +- .../ConfirmStopSyncFolderDialogViewModel.kt | 6 +- .../viewmodel/FileOrFolderOptionsViewModel.kt | 47 +- .../drive/ui/viewmodel/FilesViewModel.kt | 20 +- .../viewmodel/GetMoreFreeStorageViewModel.kt | 2 - .../drive/ui/viewmodel/HostFilesViewModel.kt | 1 + .../ui/viewmodel/MoveToFolderViewModel.kt | 26 +- .../MultipleFileOrFolderOptionsViewModel.kt | 28 +- .../MultiplePhotosOptionsViewModel.kt | 4 +- .../drive/ui/viewmodel/OfflineViewModel.kt | 17 +- .../drive/ui/viewmodel/OnboardingViewModel.kt | 13 +- .../viewmodel/ParentFolderOptionsViewModel.kt | 36 +- .../ui/viewmodel/PhotosBackupViewModel.kt | 8 +- .../ui/viewmodel/PhotosExportDataViewModel.kt | 4 +- .../PhotosPickerAndSelectionViewModel.kt | 33 +- .../ui/viewmodel/PhotosUpsellViewModel.kt | 6 +- .../drive/ui/viewmodel/PhotosViewModel.kt | 170 +- .../drive/ui/viewmodel/PreviewViewModel.kt | 12 +- .../ui/viewmodel/ScanDocumentNameViewModel.kt | 15 +- .../drive/ui/viewmodel/SelectionViewModel.kt | 37 +- .../drive/ui/viewmodel/SendFileViewModel.kt | 2 +- .../drive/ui/viewmodel/SettingsViewModel.kt | 8 +- .../ShareInvitationOptionsViewModel.kt | 6 +- .../ShareLinkPermissionsViewModel.kt | 6 +- .../viewmodel/ShareMemberOptionsViewModel.kt | 9 +- .../ShareMultiplePhotosOptionsViewModel.kt | 6 +- .../drive/ui/viewmodel/SharedTabsViewModel.kt | 5 +- .../ui/viewmodel/SortingDialogViewModel.kt | 6 +- .../viewmodel/SubscriptionPromoViewModel.kt | 6 +- .../ui/viewmodel/SyncedFoldersViewModel.kt | 22 +- .../drive/ui/viewmodel/TrashViewModel.kt | 12 +- .../drive/ui/viewmodel/UploadToViewModel.kt | 10 +- .../ui/viewmodel/UserInvitationViewModel.kt | 11 +- .../drive/ui/viewmodel/WhatsNewViewModel.kt | 7 +- .../drive/ui/viewstate/ComputersViewState.kt | 1 + .../drive/ui/viewstate/MoveFileViewState.kt | 1 + .../drive/ui/viewstate/SharedTabsViewState.kt | 1 + .../drive/ui/viewstate/UploadToViewState.kt | 1 + .../drive/usecase/HandleUncaughtException.kt | 25 +- .../drive/usecase/ShowRatingBooster.kt | 7 +- .../notification/AcceptNotificationEvent.kt | 1 + .../notification/CreateUserNotificationId.kt | 5 +- ...DownloadFileProgressNotificationBuilder.kt | 93 + .../DownloadNotificationBuilder.kt | 10 +- .../notification/ShouldCancelNotification.kt | 1 + .../drive/worker/NotificationActionWorker.kt | 4 + build.gradle.kts | 6 +- buildSrc/build.gradle.kts | 1 + buildSrc/src/main/kotlin/Config.kt | 2 +- buildSrc/src/main/kotlin/DriveModule.kt | 70 +- buildSrc/src/main/kotlin/Jacoco.kt | 10 +- .../announce/event/domain/entity/Event.kt | 9 + .../event/domain/usecase/AnnounceEvent.kt | 1 - .../backup/data/manager/BackupManagerImpl.kt | 2 +- .../me/proton/core/drive/base/data/api/Dto.kt | 1 + .../proton/core/drive/base/data/db/Column.kt | 2 + .../base/data/db/LoggingOpenHelperFactory.kt | 11 +- .../core/drive/base/data/db/paging/Flow.kt | 27 +- .../drive/base/data/extension/ApiException.kt | 19 +- .../extension/OperationAbortedException.kt | 9 +- .../base/data/extension/RuntimeException.kt | 8 +- .../domain/provider/ConfigurationProvider.kt | 5 +- .../drive/base/domain/util/RequestBatcher.kt | 4 +- .../presentation/component/FastScroller.kt | 2 +- .../base/presentation/component/TopAppBar.kt | 12 +- .../domain/usecase/GetContactEmails.kt | 9 + .../crypto/domain/usecase/base/UseHashKey.kt | 15 +- .../usecase/file/AvoidDuplicateFileNameSdk.kt | 3 +- .../103.json | 11100 ++++++++++++++++ .../104.json | 10910 +++++++++++++++ .../proton/android/drive/db/DriveDatabase.kt | 10 +- .../drive/db/DriveDatabaseMigrations.kt | 12 + .../data/DriveDocumentsProvider.kt | 2 +- .../data/worker/ExportToDownloadWorker.kt | 6 +- .../usecase/IntegrityMetricsNotifierImpl.kt | 3 + .../usecase/IntegrityMetricsNotifier.kt | 1 + .../drivelink-download/data/build.gradle.kts | 1 + .../data/manager/DownloadManagerImpl.kt | 22 +- .../data/worker/DownloadEventWorker.kt | 206 + .../domain/manager/DownloadManager.kt | 3 +- .../domain/manager/DownloadSdkManager.kt | 42 +- .../download/domain/usecase/Download.kt | 14 +- .../domain/usecase/DownloadFileLegacy.kt | 36 +- .../domain/usecase/DownloadFileSdk.kt | 43 +- .../domain/usecase/GetDownloadProgress.kt | 2 +- .../download/domain/usecase/GetFile.kt | 2 +- .../domain/usecase/VerifyDownloadedFile.kt | 1 - .../offline/domain/usecase/ToggleOffline.kt | 5 +- .../domain/usecase/UpdateOfflineContent.kt | 21 +- .../photo/data/db/DriveLinkPhotoDatabase.kt | 26 +- .../data/db/dao/PhotoListingAnchorDao.kt | 45 + .../db/entity/PhotoListingAnchorEntity.kt | 55 + .../photo/data/di/DriveLinkPhotoBindModule.kt | 16 +- .../photo/data/di/DriveLinkPhotoModule.kt | 42 +- .../extension/PhotoListingAnchorEntity.kt | 31 + .../PhotoListingAnchorRepositoryImpl.kt | 58 + .../photo/domain/entity/PhotoListingAnchor.kt | 36 + .../domain/entity/PhotoListingsSyncState.kt | 26 + .../PhotoListingAnchorRepository.kt | 32 + .../usecase/GetPhotoListingsPagingData.kt | 118 + .../domain/usecase/PhotoListingsLoader.kt | 122 + .../domain/usecase/RefreshPhotoListings.kt | 40 + .../rename/domain/usecase/RenameLink.kt | 1 - .../rename/domain/usecase/RenameLinkSdk.kt | 3 +- .../domain/usecase/GetSelectedDriveLinks.kt | 11 +- .../selection/domain/usecase/SelectAll.kt | 2 +- .../presentation/component/LinkSettings.kt | 1 + .../presentation/component/ManageAccess.kt | 1 + .../presentation/component/ShareViaLink.kt | 1 + .../component/SharedDriveInvitations.kt | 1 + .../presentation/component/UserInvitation.kt | 5 + .../viewstate/UserInvitationViewState.kt | 1 + .../drivelink/domain/extension/DriveLink.kt | 3 +- .../drivelink/domain/usecase/GetDriveLinks.kt | 9 +- .../domain/usecase/UpdateSharePermissions.kt | 77 +- .../usecase/UpdateShareUserDisplayName.kt | 26 + .../base/domain/usecase/UpdateEventAction.kt | 6 +- .../usecase/HandleCreateOrUpdateLinksEvent.kt | 3 +- .../usecase/OnUpdateContentEvent.kt | 2 +- .../flag/domain/entity/FeatureFlagId.kt | 4 + drive/file-info/build.gradle.kts | 1 - .../file/base/data/api/entity/RevisionDto.kt | 3 + .../file/base/data/extension/RevisionDto.kt | 3 +- .../drive/file/base/domain/entity/Revision.kt | 1 + .../operation/move/worker/MoveFileWorker.kt | 14 +- .../operation/FileOperationActionProvider.kt | 11 +- .../drive/files/domain/usecase/MoveFile.kt | 8 +- .../files/presentation/component/Files.kt | 1 + .../component/files/FilesGridItem.kt | 4 +- .../component/files/FilesListItem.kt | 6 +- .../presentation/state/FilesViewState.kt | 1 + .../create/domain/usecase/CreateFolder.kt | 4 +- .../create/domain/usecase/CreateFolderSdk.kt | 3 +- .../i18n/src/main/res/values-b+es+419/app.xml | 10 + drive/i18n/src/main/res/values-be/app.xml | 12 + drive/i18n/src/main/res/values-ca/app.xml | 10 + drive/i18n/src/main/res/values-ca/common.xml | 2 +- drive/i18n/src/main/res/values-cs/app.xml | 12 + drive/i18n/src/main/res/values-da/app.xml | 10 + drive/i18n/src/main/res/values-de/app.xml | 10 + drive/i18n/src/main/res/values-el/app.xml | 10 + drive/i18n/src/main/res/values-es-rES/app.xml | 10 + drive/i18n/src/main/res/values-fi/app.xml | 10 + drive/i18n/src/main/res/values-fr/app.xml | 10 + drive/i18n/src/main/res/values-hu/app.xml | 10 + drive/i18n/src/main/res/values-id/app.xml | 9 + drive/i18n/src/main/res/values-it/app.xml | 10 + drive/i18n/src/main/res/values-ja/app.xml | 13 +- drive/i18n/src/main/res/values-ja/common.xml | 6 +- .../main/res/values-ja/document_scanner.xml | 6 +- drive/i18n/src/main/res/values-ja/photos.xml | 6 +- .../i18n/src/main/res/values-ja/whats_new.xml | 4 +- drive/i18n/src/main/res/values-ka/app.xml | 10 + drive/i18n/src/main/res/values-ko/app.xml | 9 + drive/i18n/src/main/res/values-nb-rNO/app.xml | 10 + drive/i18n/src/main/res/values-nl/app.xml | 10 + drive/i18n/src/main/res/values-pl/app.xml | 12 + drive/i18n/src/main/res/values-pt-rBR/app.xml | 10 + .../src/main/res/values-pt-rBR/photos.xml | 2 +- drive/i18n/src/main/res/values-pt-rPT/app.xml | 10 + drive/i18n/src/main/res/values-ro/app.xml | 11 + drive/i18n/src/main/res/values-ru/app.xml | 12 + drive/i18n/src/main/res/values-sk/app.xml | 12 + drive/i18n/src/main/res/values-sl/app.xml | 12 + drive/i18n/src/main/res/values-sv-rSE/app.xml | 10 + drive/i18n/src/main/res/values-tr/app.xml | 10 + drive/i18n/src/main/res/values-uk/app.xml | 12 + drive/i18n/src/main/res/values-zh-rCN/app.xml | 9 + drive/i18n/src/main/res/values-zh-rTW/app.xml | 9 + drive/i18n/src/main/res/values/app.xml | 10 + drive/i18n/src/main/res/values/common.xml | 4 + .../selection/domain/usecase/DeselectLinks.kt | 7 +- .../selection/domain/usecase/SelectLinks.kt | 9 +- drive/link-upload/data/build.gradle.kts | 1 + .../linkupload/data/db/dao/LinkUploadDao.kt | 93 +- .../repository/LinkUploadRepositoryImpl.kt | 46 +- .../domain/repository/LinkUploadRepository.kt | 5 +- .../domain/usecase/GetUploadFileLinksCount.kt | 5 +- .../GetUploadFileLinksWithUriByPriority.kt | 10 +- drive/link/data/build.gradle.kts | 3 +- .../data/api/entity/LinkActiveRevisionDto.kt | 3 + .../core/drive/link/data/db/LinkDatabase.kt | 11 + .../db/entity/LinkFilePropertiesEntity.kt | 3 + .../core/drive/link/data/extension/Link.kt | 1 + .../data/extension/LinkFilePropertiesDto.kt | 1 + .../link/data/extension/LinkWithProperties.kt | 1 + .../core/drive/link/domain/entity/Link.kt | 1 + .../core/drive/link/domain/extension/Link.kt | 1 - .../drive/link/domain/extension/LinkId.kt | 16 +- .../core/drive/link/domain/extension/Node.kt | 12 +- .../drive/link/domain/extension/NodeUid.kt | 24 + .../link/domain/extension/RevisionUid.kt | 25 + .../provider/ProtonSdkClientProvider.kt | 32 + .../extension/Event.DownloadFileProgress.kt | 32 + .../log/domain/handler/LogEventHandler.kt | 1 + .../metrics/DownloadVerifierAttemptsTotal.kt | 4 +- .../photo/data/db/dao/AlbumPhotoListingDao.kt | 8 +- .../photo/data/db/dao/PhotoListingDao.kt | 30 - .../domain/usecase/FetchAllPhotoListings.kt | 11 + drive/preview/build.gradle.kts | 1 - .../preview/presentation/component/Preview.kt | 2 + .../presentation/component/PreviewEmpty.kt | 1 + .../component/state/PreviewViewState.kt | 1 + drive/settings/build.gradle.kts | 1 - .../drive/settings/presentation/Settings.kt | 2 + .../presentation/component/DefaultHomeTab.kt | 1 + .../presentation/state/SettingsViewState.kt | 1 + .../sorting/domain/usecase/UpdateSorting.kt | 4 +- drive/test/build.gradle.kts | 19 +- .../core/drive/test/di/ApplicationModule.kt | 6 + .../provider/TestProtonSdkClientProvider.kt | 52 + .../usecase/TestIntegrityMetricsNotifier.kt | 1 + .../domain/usecase/GetThumbnailSdk.kt | 59 +- .../presentation/extension/ThumbnailVO.kt | 4 +- .../data/test/manager/StubbedTrashManager.kt | 5 +- .../manager/worker/TrashFileNodesWorker.kt | 21 +- .../repository/DriveTrashRepositoryImpl.kt | 115 +- .../repository/DriveTrashRepositoryLegacy.kt | 147 + .../repository/DriveTrashRepositorySdk.kt | 93 + drive/trash/domain/build.gradle.kts | 1 + .../notification/TrashExtraActionProvider.kt | 10 +- .../domain/notification/TrashFilesExtra.kt | 1 + .../trash/domain/usecase/DeleteFromTrash.kt | 18 +- .../drive/trash/domain/usecase/SendToTrash.kt | 20 +- .../trash/domain/usecase/UseSdkForTrash.kt | 40 + .../data/resolver/ContentUriResolver.kt | 9 +- .../upload/data/worker/FileUploadFlow.kt | 30 + .../upload/data/worker/UploadEventWorker.kt | 71 +- .../upload/data/worker/UploadFileSdkWorker.kt | 88 +- .../upload/domain/manager/UploadSdkManager.kt | 27 +- .../upload/domain/usecase/CreateNewFileSdk.kt | 9 +- .../domain/usecase/GetNextUploadFileLinks.kt | 25 +- .../upload/domain/usecase/UploadFileSdk.kt | 3 - .../drive/volume/domain/extension/NodeUid.kt | 25 + .../volume/domain/extension/RevisionUid.kt | 24 + gradle/libs.versions.toml | 23 +- .../usecase/FetchAllPhotoListingsImpl.kt | 70 +- .../photos/presentation/component/Album.kt | 7 + .../presentation/component/BackupIssues.kt | 1 + .../presentation/component/LibraryFolders.kt | 13 +- .../presentation/component/MediaItem.kt | 18 +- .../photos/presentation/component/Photos.kt | 6 +- .../presentation/component/PhotosContent.kt | 52 +- .../presentation/extension/PhotosItem.kt | 23 +- .../presentation/viewstate/AlbumViewState.kt | 1 + .../presentation/viewstate/AlbumsViewState.kt | 1 + .../presentation/viewstate/PhotosViewState.kt | 1 + settings.gradle.kts | 2 +- 293 files changed, 25547 insertions(+), 924 deletions(-) create mode 100644 app/src/main/kotlin/me/proton/android/drive/provider/AppProtonSdkClientProvider.kt create mode 100644 app/src/main/kotlin/me/proton/android/drive/usecase/notification/DownloadFileProgressNotificationBuilder.kt create mode 100644 drive/db/schemas/me.proton.android.drive.db.DriveDatabase/103.json create mode 100644 drive/db/schemas/me.proton.android.drive.db.DriveDatabase/104.json create mode 100644 drive/drivelink-download/data/src/main/kotlin/me/proton/core/drive/drivelink/download/data/worker/DownloadEventWorker.kt create mode 100644 drive/drivelink-photo/data/src/main/kotlin/me/proton/core/drive/drivelink/photo/data/db/dao/PhotoListingAnchorDao.kt create mode 100644 drive/drivelink-photo/data/src/main/kotlin/me/proton/core/drive/drivelink/photo/data/db/entity/PhotoListingAnchorEntity.kt create mode 100644 drive/drivelink-photo/data/src/main/kotlin/me/proton/core/drive/drivelink/photo/data/extension/PhotoListingAnchorEntity.kt create mode 100644 drive/drivelink-photo/data/src/main/kotlin/me/proton/core/drive/drivelink/photo/data/repository/PhotoListingAnchorRepositoryImpl.kt create mode 100644 drive/drivelink-photo/domain/src/main/kotlin/me/proton/core/drive/drivelink/photo/domain/entity/PhotoListingAnchor.kt create mode 100644 drive/drivelink-photo/domain/src/main/kotlin/me/proton/core/drive/drivelink/photo/domain/entity/PhotoListingsSyncState.kt create mode 100644 drive/drivelink-photo/domain/src/main/kotlin/me/proton/core/drive/drivelink/photo/domain/repository/PhotoListingAnchorRepository.kt create mode 100644 drive/drivelink-photo/domain/src/main/kotlin/me/proton/core/drive/drivelink/photo/domain/usecase/GetPhotoListingsPagingData.kt create mode 100644 drive/drivelink-photo/domain/src/main/kotlin/me/proton/core/drive/drivelink/photo/domain/usecase/PhotoListingsLoader.kt create mode 100644 drive/drivelink-photo/domain/src/main/kotlin/me/proton/core/drive/drivelink/photo/domain/usecase/RefreshPhotoListings.kt create mode 100644 drive/link/domain/src/main/kotlin/me/proton/core/drive/link/domain/extension/NodeUid.kt create mode 100644 drive/link/domain/src/main/kotlin/me/proton/core/drive/link/domain/extension/RevisionUid.kt create mode 100644 drive/link/domain/src/main/kotlin/me/proton/core/drive/link/domain/provider/ProtonSdkClientProvider.kt create mode 100644 drive/log/domain/src/main/kotlin/me/proton/core/drive/log/domain/extension/Event.DownloadFileProgress.kt create mode 100644 drive/test/src/main/kotlin/me/proton/core/drive/test/provider/TestProtonSdkClientProvider.kt create mode 100644 drive/trash/data/src/main/kotlin/me/proton/core/drive/trash/data/repository/DriveTrashRepositoryLegacy.kt create mode 100644 drive/trash/data/src/main/kotlin/me/proton/core/drive/trash/data/repository/DriveTrashRepositorySdk.kt create mode 100644 drive/trash/domain/src/main/kotlin/me/proton/core/drive/trash/domain/usecase/UseSdkForTrash.kt create mode 100644 drive/volume/domain/src/main/kotlin/me/proton/core/drive/volume/domain/extension/NodeUid.kt create mode 100644 drive/volume/domain/src/main/kotlin/me/proton/core/drive/volume/domain/extension/RevisionUid.kt diff --git a/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/ToggleLayoutType.kt b/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/ToggleLayoutType.kt index ce6ed66f..55f167d1 100644 --- a/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/ToggleLayoutType.kt +++ b/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/ToggleLayoutType.kt @@ -19,13 +19,14 @@ package me.proton.drive.android.settings.domain.usecase import me.proton.core.domain.entity.UserId +import me.proton.core.drive.base.domain.util.coRunCatching import me.proton.drive.android.settings.domain.entity.LayoutType import javax.inject.Inject class ToggleLayoutType @Inject constructor( private val updateLayoutType: UpdateLayoutType ) { - suspend operator fun invoke(userId: UserId, currentLayoutType: LayoutType) = + suspend operator fun invoke(userId: UserId, currentLayoutType: LayoutType) = coRunCatching { updateLayoutType( userId = userId, layoutType = if (currentLayoutType == LayoutType.GRID) { @@ -33,5 +34,6 @@ class ToggleLayoutType @Inject constructor( } else { LayoutType.GRID } - ) + ).getOrThrow() + } } diff --git a/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateLayoutType.kt b/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateLayoutType.kt index 6edadbf9..8ea8f3d0 100644 --- a/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateLayoutType.kt +++ b/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateLayoutType.kt @@ -19,6 +19,7 @@ package me.proton.drive.android.settings.domain.usecase import me.proton.core.domain.entity.UserId +import me.proton.core.drive.base.domain.util.coRunCatching import me.proton.drive.android.settings.domain.UiSettingsRepository import me.proton.drive.android.settings.domain.entity.LayoutType import javax.inject.Inject @@ -26,6 +27,7 @@ import javax.inject.Inject class UpdateLayoutType @Inject constructor( private val repository: UiSettingsRepository ) { - suspend operator fun invoke(userId: UserId, layoutType: LayoutType) = + suspend operator fun invoke(userId: UserId, layoutType: LayoutType) = coRunCatching { repository.updateLayoutType(userId, layoutType) + } } diff --git a/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateThemeStyle.kt b/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateThemeStyle.kt index 8ed1aebe..eb3999ab 100644 --- a/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateThemeStyle.kt +++ b/app-ui-settings/src/main/java/me/proton/drive/android/settings/domain/usecase/UpdateThemeStyle.kt @@ -19,6 +19,7 @@ package me.proton.drive.android.settings.domain.usecase import me.proton.core.domain.entity.UserId +import me.proton.core.drive.base.domain.util.coRunCatching import me.proton.drive.android.settings.domain.UiSettingsRepository import me.proton.drive.android.settings.domain.entity.ThemeStyle import javax.inject.Inject @@ -26,6 +27,7 @@ import javax.inject.Inject class UpdateThemeStyle @Inject constructor( private val repository: UiSettingsRepository ) { - suspend operator fun invoke(userId: UserId, themeStyle: ThemeStyle) = + suspend operator fun invoke(userId: UserId, themeStyle: ThemeStyle) = coRunCatching { repository.updateThemeStyle(userId, themeStyle) + } } diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9059c98e..ce436457 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,7 +27,6 @@ plugins { id("dagger.hilt.android.plugin") id("kotlin-parcelize") kotlin("android") - kotlin("kapt") id("me.proton.core.gradle-plugins.environment-config") version "1.3.0" } @@ -82,8 +81,7 @@ driveModule( implementation(libs.treessence) androidTestImplementation(libs.dagger.hilt.android.testing) - kapt(libs.dagger.hilt.android.compiler) - kaptAndroidTest(libs.dagger.hilt.android.compiler) + add("kspAndroidTest", libs.dagger.hilt.android.compiler) androidTestUtil(libs.androidx.test.orchestrator) androidTestUtil(libs.androidx.test.services) diff --git a/app/src/main/kotlin/me/proton/android/drive/di/ApplicationModule.kt b/app/src/main/kotlin/me/proton/android/drive/di/ApplicationModule.kt index a91bbbc2..d0068342 100644 --- a/app/src/main/kotlin/me/proton/android/drive/di/ApplicationModule.kt +++ b/app/src/main/kotlin/me/proton/android/drive/di/ApplicationModule.kt @@ -48,6 +48,7 @@ import me.proton.android.drive.provider.AppBuildConfigFieldsProvider import me.proton.android.drive.provider.BuildConfigurationProvider import me.proton.android.drive.provider.AppProtonDriveClientProvider import me.proton.android.drive.provider.AppProtonPhotosClientProvider +import me.proton.android.drive.provider.AppProtonSdkClientProvider import me.proton.android.drive.repository.BridgeFindDuplicatesRepository import me.proton.android.drive.repository.ClientUidRepositoryImpl import me.proton.android.drive.settings.DebugSettings @@ -75,6 +76,7 @@ import me.proton.core.drive.base.domain.usecase.DriveUrlBuilder import me.proton.core.drive.documentsprovider.domain.usecase.GetDocumentsProviderRoots import me.proton.core.drive.folder.create.domain.provider.OpenFolderActionProvider import me.proton.core.drive.key.domain.handler.PublicKeyEventHandler +import me.proton.core.drive.link.domain.provider.ProtonSdkClientProvider import me.proton.core.drive.log.domain.handler.LogEventHandler import me.proton.core.drive.messagequeue.domain.ActionProvider import me.proton.core.drive.notification.data.provider.NotificationBuilderProvider @@ -250,6 +252,10 @@ abstract class ApplicationBindsModule { @Singleton abstract fun bindsAppProtonPhotosClientProvider(impl: AppProtonPhotosClientProvider): ProtonPhotosClientProvider + @Binds + @Singleton + abstract fun bindsAppProtonSdkClientProvider(impl: AppProtonSdkClientProvider): ProtonSdkClientProvider + @Binds @IntoSet abstract fun bindsOpenFolderActionProvider(impl: OpenFolderActionProvider): ActionProvider diff --git a/app/src/main/kotlin/me/proton/android/drive/initializer/DownloadInitializer.kt b/app/src/main/kotlin/me/proton/android/drive/initializer/DownloadInitializer.kt index 2f1f1ae1..841b8a68 100644 --- a/app/src/main/kotlin/me/proton/android/drive/initializer/DownloadInitializer.kt +++ b/app/src/main/kotlin/me/proton/android/drive/initializer/DownloadInitializer.kt @@ -107,7 +107,9 @@ class DownloadInitializer : Initializer { downloadErrorHandlers.forEach { handler -> coRunCatching { handler.onError(downloadError) - }.getOrNull(LogTag.DOWNLOAD, "Failed to handle download error") + }.onFailure { error -> + error.log(LogTag.DOWNLOAD, "Failed to handle download error") + } } } diff --git a/app/src/main/kotlin/me/proton/android/drive/initializer/ProtonDriveSdkInitializer.kt b/app/src/main/kotlin/me/proton/android/drive/initializer/ProtonDriveSdkInitializer.kt index d8893e90..0c66c420 100644 --- a/app/src/main/kotlin/me/proton/android/drive/initializer/ProtonDriveSdkInitializer.kt +++ b/app/src/main/kotlin/me/proton/android/drive/initializer/ProtonDriveSdkInitializer.kt @@ -28,8 +28,7 @@ import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import me.proton.android.drive.provider.AppProtonDriveClientProvider -import me.proton.android.drive.provider.AppProtonPhotosClientProvider +import me.proton.android.drive.provider.AppProtonSdkClientProvider import me.proton.core.accountmanager.domain.AccountManager import me.proton.core.accountmanager.presentation.observe import me.proton.core.accountmanager.presentation.onAccountRemoved @@ -49,8 +48,7 @@ class ProtonDriveSdkInitializer : Initializer { ).run { accountManager.observe(appLifecycleProvider.lifecycle, Lifecycle.State.RESUMED) .onAccountRemoved { account -> - appProtonDriveClientProvider.remove(account.userId) - appProtonPhotosClientProvider.remove(account.userId) + appProtonSdkClientProvider.remove(account.userId) } } } @@ -66,6 +64,5 @@ interface SdkInitializerEntryPoint { val configurationProvider: ConfigurationProvider val accountManager: AccountManager val appLifecycleProvider: AppLifecycleProvider - val appProtonDriveClientProvider: AppProtonDriveClientProvider - val appProtonPhotosClientProvider: AppProtonPhotosClientProvider + val appProtonSdkClientProvider: AppProtonSdkClientProvider } diff --git a/app/src/main/kotlin/me/proton/android/drive/initializer/ShortcutInitializer.kt b/app/src/main/kotlin/me/proton/android/drive/initializer/ShortcutInitializer.kt index c027a8e9..f9250280 100644 --- a/app/src/main/kotlin/me/proton/android/drive/initializer/ShortcutInitializer.kt +++ b/app/src/main/kotlin/me/proton/android/drive/initializer/ShortcutInitializer.kt @@ -65,6 +65,12 @@ class ShortcutInitializer : Initializer { } .onAccountRemoved { updateDynamicShortcuts(emptyList()) + .onFailure { error -> + error.log( + tag = LogTag.DEFAULT, + message = "Failed to clear dynamic shortcuts", + ) + } } } } diff --git a/app/src/main/kotlin/me/proton/android/drive/initializer/TagsMigrationInitializer.kt b/app/src/main/kotlin/me/proton/android/drive/initializer/TagsMigrationInitializer.kt index 764a14e0..e8b52a86 100644 --- a/app/src/main/kotlin/me/proton/android/drive/initializer/TagsMigrationInitializer.kt +++ b/app/src/main/kotlin/me/proton/android/drive/initializer/TagsMigrationInitializer.kt @@ -133,7 +133,7 @@ class TagsMigrationInitializer : Initializer { driveLink = getDriveLink(fileId).toResult().getOrThrow(), retryable = true, networkType = NetworkType.UNMETERED, - ) + ).getOrThrow() }.onFailure { error -> error.log( PHOTO, diff --git a/app/src/main/kotlin/me/proton/android/drive/initializer/WebViewInitializer.kt b/app/src/main/kotlin/me/proton/android/drive/initializer/WebViewInitializer.kt index fa81e0da..fc648e84 100644 --- a/app/src/main/kotlin/me/proton/android/drive/initializer/WebViewInitializer.kt +++ b/app/src/main/kotlin/me/proton/android/drive/initializer/WebViewInitializer.kt @@ -29,6 +29,7 @@ import dagger.hilt.android.EntryPointAccessors import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import me.proton.android.drive.extension.log import me.proton.core.drive.base.domain.extension.getOrNull import me.proton.core.drive.base.domain.log.LogTag import me.proton.core.presentation.app.AppLifecycleProvider @@ -51,7 +52,9 @@ class WebViewInitializer : Initializer { CoreLogger.d(LogTag.WEBVIEW, "Start safe browsing: $isSuccess") } } - }.getOrNull(LogTag.WEBVIEW, "startSafeBrowsing failed") + }.onFailure { error -> + error.log(LogTag.WEBVIEW, "startSafeBrowsing failed") + } } } } diff --git a/app/src/main/kotlin/me/proton/android/drive/notification/AppNotificationBuilderProvider.kt b/app/src/main/kotlin/me/proton/android/drive/notification/AppNotificationBuilderProvider.kt index 8f2e7578..40cbc93a 100644 --- a/app/src/main/kotlin/me/proton/android/drive/notification/AppNotificationBuilderProvider.kt +++ b/app/src/main/kotlin/me/proton/android/drive/notification/AppNotificationBuilderProvider.kt @@ -21,6 +21,7 @@ package me.proton.android.drive.notification import androidx.core.app.NotificationCompat import me.proton.android.drive.usecase.notification.BackupNotificationBuilder import me.proton.android.drive.usecase.notification.DownloadNotificationBuilder +import me.proton.android.drive.usecase.notification.DownloadFileProgressNotificationBuilder import me.proton.android.drive.usecase.notification.ForcedSignOutNotificationBuilder import me.proton.android.drive.usecase.notification.NoSpaceLeftOnDeviceNotificationBuilder import me.proton.android.drive.usecase.notification.StorageFullNotificationBuilder @@ -35,6 +36,7 @@ class AppNotificationBuilderProvider @Inject constructor( private val storageFullBuilder: StorageFullNotificationBuilder, private val uploadNotificationBuilder: UploadNotificationBuilder, private val downloadNotificationBuilder: DownloadNotificationBuilder, + private val downloadFileProgressNotificationBuilder: DownloadFileProgressNotificationBuilder, private val forcedSignOutNotificationBuilder: ForcedSignOutNotificationBuilder, private val noSpaceLeftOnDeviceNotificationBuilder: NoSpaceLeftOnDeviceNotificationBuilder, private val backupNotificationBuilder: BackupNotificationBuilder, @@ -55,10 +57,15 @@ class AppNotificationBuilderProvider @Inject constructor( notificationId = requireIsInstance(notificationId), events = events as List, ) - events.size == 1 && events.first() is Event.Download -> + events.isNotEmpty() && events.all { it is Event.Download } -> downloadNotificationBuilder( notificationId = requireIsInstance(notificationId), - event = events.first() as Event.Download, + events = events as List, + ) + events.isNotEmpty() && events.all { it is Event.DownloadFileProgress } -> + downloadFileProgressNotificationBuilder( + notificationId = requireIsInstance(notificationId), + events = events as List, ) events.size == 1 && events.first() is Event.ForcedSignOut -> forcedSignOutNotificationBuilder( diff --git a/app/src/main/kotlin/me/proton/android/drive/provider/AppProtonSdkClientProvider.kt b/app/src/main/kotlin/me/proton/android/drive/provider/AppProtonSdkClientProvider.kt new file mode 100644 index 00000000..4d90620b --- /dev/null +++ b/app/src/main/kotlin/me/proton/android/drive/provider/AppProtonSdkClientProvider.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2026 Proton AG. + * This file is part of Proton Drive. + * + * Proton Drive is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Proton Drive is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Proton Drive. If not, see . + */ + +package me.proton.android.drive.provider + +import me.proton.core.domain.entity.UserId +import me.proton.core.drive.base.domain.extension.toResult +import me.proton.core.drive.base.domain.util.coRunCatching +import me.proton.core.drive.drivelink.domain.usecase.GetVolumeType +import me.proton.core.drive.link.domain.entity.LinkId +import me.proton.core.drive.link.domain.extension.userId +import me.proton.core.drive.link.domain.provider.ProtonSdkClientProvider +import me.proton.core.drive.volume.domain.entity.Volume +import me.proton.core.drive.volume.domain.entity.VolumeId +import me.proton.core.drive.volume.domain.usecase.GetVolume +import me.proton.drive.sdk.ProtonSdkClient +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AppProtonSdkClientProvider @Inject constructor( + private val getVolumeType: GetVolumeType, + private val getVolume: GetVolume, + private val driveClientProvider: AppProtonDriveClientProvider, + private val photosClientProvider: AppProtonPhotosClientProvider, +) : ProtonSdkClientProvider { + + override suspend fun getOrCreate( + linkId: LinkId, + ): Result = coRunCatching { + getOrCreate( + userId = linkId.userId, + volumeType = getVolumeType(linkId).getOrThrow(), + ).getOrThrow() + } + + override suspend fun getOrCreate( + userId: UserId, + volumeId: VolumeId + ): Result = coRunCatching { + getOrCreate( + userId = userId, + volumeType = getVolume(userId, volumeId).toResult().getOrThrow().type, + ).getOrThrow() + } + + override suspend fun getOrCreate( + userId: UserId, + volumeType: Volume.Type?, + ): Result = coRunCatching { + when (volumeType) { + null -> error("Cannot create sdk client for null volume type") + Volume.Type.UNKNOWN -> error("Cannot create sdk client for unknown volume type") + Volume.Type.REGULAR -> driveClientProvider.getOrCreate(userId).getOrThrow() + Volume.Type.PHOTO -> photosClientProvider.getOrCreate(userId).getOrThrow() + } + } + + fun remove(userId: UserId) { + driveClientProvider.remove(userId) + photosClientProvider.remove(userId) + } + + +} diff --git a/app/src/main/kotlin/me/proton/android/drive/receiver/NotificationBroadcastReceiver.kt b/app/src/main/kotlin/me/proton/android/drive/receiver/NotificationBroadcastReceiver.kt index dfd1966c..6bfb9d71 100644 --- a/app/src/main/kotlin/me/proton/android/drive/receiver/NotificationBroadcastReceiver.kt +++ b/app/src/main/kotlin/me/proton/android/drive/receiver/NotificationBroadcastReceiver.kt @@ -47,5 +47,6 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { private const val BASE_ACTION = "proton.android.intent.action" const val ACTION_DELETE = "$BASE_ACTION.DELETE" const val ACTION_CANCEL_ALL = "$BASE_ACTION.CANCEL_ALL" + const val ACTION_CANCEL_ALL_DOWNLOADS = "$BASE_ACTION.CANCEL_ALL_DOWNLOADS" } } diff --git a/app/src/main/kotlin/me/proton/android/drive/settings/DebugSettings.kt b/app/src/main/kotlin/me/proton/android/drive/settings/DebugSettings.kt index a9c21b0e..57867011 100644 --- a/app/src/main/kotlin/me/proton/android/drive/settings/DebugSettings.kt +++ b/app/src/main/kotlin/me/proton/android/drive/settings/DebugSettings.kt @@ -155,6 +155,7 @@ class DebugSettings( override val preferSdkForDownload: Boolean = true override val preferSdkForThumbnail: Boolean = true override val preferSdkForNodeOperation: Boolean = true + override val preferSdkForTrash: Boolean = true fun reset(coroutineScope: CoroutineScope) { coroutineScope.launch { diff --git a/app/src/main/kotlin/me/proton/android/drive/ui/options/Option.kt b/app/src/main/kotlin/me/proton/android/drive/ui/options/Option.kt index 3b1e2516..b04479bd 100644 --- a/app/src/main/kotlin/me/proton/android/drive/ui/options/Option.kt +++ b/app/src/main/kotlin/me/proton/android/drive/ui/options/Option.kt @@ -650,7 +650,7 @@ fun Iterable