From c4d20e7531a899cd752db6aa4daee8b455e507dc Mon Sep 17 00:00:00 2001 From: Pratik-canopas Date: Thu, 21 Nov 2024 15:13:26 +0530 Subject: [PATCH] Implement upload and download for dropbox --- .idea/libraries/Dart_Packages.xml | 136 ++-- .idea/libraries/Flutter_Plugins.xml | 55 +- .idea/modules.xml | 1 + data/.flutter-plugins | 3 + data/.flutter-plugins-dependencies | 2 +- .../dropbox/dropbox_content_endpoints.dart | 94 +++ .../dropbox_auth_interceptor.dart | 2 +- .../models/dropbox/token/token.freezed.dart | 297 --------- data/lib/models/dropbox/token/token.g.dart | 30 - data/lib/models/media/media.dart | 15 +- data/lib/models/media/media.g.dart | 1 + .../models/media_process/media_process.dart | 112 ++++ .../media_process/media_process.freezed.dart | 588 +++++++++++++----- .../models/media_process/media_process.g.dart | 73 +++ .../google_drive_process_repo.dart | 10 +- .../media_process_repository.dart | 368 +++++++++++ data/lib/services/auth_service.dart | 4 +- data/lib/services/cloud_provider_service.dart | 19 + data/lib/services/dropbox_services.dart | 54 +- data/lib/services/google_drive_service.dart | 106 ++-- data/lib/storage/app_preferences.dart | 4 +- data/pubspec.yaml | 1 + 22 files changed, 1312 insertions(+), 663 deletions(-) delete mode 100644 data/lib/models/dropbox/token/token.freezed.dart delete mode 100644 data/lib/models/dropbox/token/token.g.dart diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 411c718..3769f83 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -5,7 +5,6 @@ - @@ -69,7 +68,6 @@ - @@ -105,7 +103,6 @@ - @@ -127,7 +124,6 @@ - @@ -142,7 +138,6 @@ - @@ -192,7 +187,6 @@ - @@ -207,7 +201,6 @@ - @@ -222,7 +215,6 @@ - @@ -237,7 +229,6 @@ - @@ -245,7 +236,7 @@ - @@ -287,7 +278,6 @@ - @@ -330,7 +320,6 @@ - @@ -338,7 +327,6 @@ - @@ -367,7 +355,6 @@ - @@ -481,7 +468,6 @@ - @@ -510,7 +496,7 @@ - @@ -525,7 +511,6 @@ - @@ -533,7 +518,6 @@ - @@ -562,7 +546,6 @@ - @@ -570,7 +553,6 @@ - @@ -592,7 +574,7 @@ - @@ -634,7 +616,6 @@ - @@ -649,6 +630,7 @@ + @@ -692,7 +674,6 @@ - @@ -728,7 +709,6 @@ - @@ -771,7 +751,7 @@ - @@ -792,7 +772,6 @@ - @@ -814,7 +793,6 @@ - @@ -829,7 +807,7 @@ - @@ -843,14 +821,14 @@ - - @@ -885,7 +863,6 @@ - @@ -914,7 +891,6 @@ - @@ -929,6 +905,7 @@ + @@ -936,6 +913,7 @@ + @@ -1020,7 +998,6 @@ - @@ -1133,7 +1110,7 @@ - @@ -1154,7 +1131,7 @@ - @@ -1168,7 +1145,6 @@ - @@ -1176,7 +1152,6 @@ - @@ -1184,7 +1159,6 @@ - @@ -1192,7 +1166,6 @@ - @@ -1200,7 +1173,6 @@ - @@ -1208,7 +1180,6 @@ - @@ -1251,7 +1222,7 @@ - @@ -1279,28 +1250,28 @@ - - - - @@ -1342,7 +1313,6 @@ - @@ -1357,7 +1327,7 @@ - @@ -1378,7 +1348,6 @@ - @@ -1386,7 +1355,6 @@ - @@ -1408,7 +1376,6 @@ - @@ -1417,20 +1384,16 @@ - - - - @@ -1438,38 +1401,30 @@ - - - - - + - - - - @@ -1483,84 +1438,75 @@ - - + - - - - - + - + - - - + - - - + - - + + - - + + @@ -1572,7 +1518,6 @@ - @@ -1588,50 +1533,41 @@ - + - + - - - - - - - + - - - - + + + + - - + - - diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index f3628d3..8f5688c 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -1,52 +1,38 @@ - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + + + + @@ -57,6 +43,11 @@ + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml index adf7bca..3914824 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + diff --git a/data/.flutter-plugins b/data/.flutter-plugins index 2d28e59..8a3950d 100644 --- a/data/.flutter-plugins +++ b/data/.flutter-plugins @@ -16,6 +16,9 @@ shared_preferences_foundation=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sha shared_preferences_linux=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/ shared_preferences_web=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/ shared_preferences_windows=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/ +sqflite=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sqflite-2.4.1/ +sqflite_android=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sqflite_android-2.4.0/ +sqflite_darwin=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sqflite_darwin-2.4.1/ url_launcher=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher-6.3.1/ url_launcher_android=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.14/ url_launcher_ios=/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.1/ diff --git a/data/.flutter-plugins-dependencies b/data/.flutter-plugins-dependencies index 7132871..2bbe77a 100644 --- a/data/.flutter-plugins-dependencies +++ b/data/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.1/","native_build":true,"dependencies":[]}],"android":[{"name":"google_sign_in_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.33/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_android-2.2.12/","native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]},{"name":"url_launcher_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.14/","native_build":true,"dependencies":[]}],"macos":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_macos","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]},{"name":"url_launcher_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","native_build":true,"dependencies":[]}],"windows":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]},{"name":"url_launcher_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.3/","native_build":true,"dependencies":[]}],"web":[{"name":"google_sign_in_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.4+3/","dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]},{"name":"url_launcher_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_web-2.3.3/","dependencies":[]}]},"dependencyGraph":[{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"photo_manager","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2024-11-20 12:21:34.473093","version":"3.24.4","swift_package_manager_enabled":false} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite_darwin","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sqflite_darwin-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.1/","native_build":true,"dependencies":[]}],"android":[{"name":"google_sign_in_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_android-6.1.33/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_android-2.2.12/","native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]},{"name":"sqflite_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sqflite_android-2.4.0/","native_build":true,"dependencies":[]},{"name":"url_launcher_android","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.14/","native_build":true,"dependencies":[]}],"macos":[{"name":"google_sign_in_ios","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_ios-5.7.8/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"photo_manager","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/photo_manager-3.6.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite_darwin","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/sqflite_darwin-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"url_launcher_macos","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]},{"name":"url_launcher_linux","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","native_build":true,"dependencies":[]}],"windows":[{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]},{"name":"url_launcher_windows","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.3/","native_build":true,"dependencies":[]}],"web":[{"name":"google_sign_in_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.4+3/","dependencies":[]},{"name":"package_info_plus","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/package_info_plus-8.1.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]},{"name":"url_launcher_web","path":"/Users/pratikcanopas/.pub-cache/hosted/pub.dev/url_launcher_web-2.3.3/","dependencies":[]}]},"dependencyGraph":[{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"photo_manager","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"sqflite","dependencies":["sqflite_android","sqflite_darwin"]},{"name":"sqflite_android","dependencies":[]},{"name":"sqflite_darwin","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2024-11-20 16:08:04.940569","version":"3.24.5","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/data/lib/apis/dropbox/dropbox_content_endpoints.dart b/data/lib/apis/dropbox/dropbox_content_endpoints.dart index a5f0421..ffa5aa5 100644 --- a/data/lib/apis/dropbox/dropbox_content_endpoints.dart +++ b/data/lib/apis/dropbox/dropbox_content_endpoints.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; +import 'package:dio/dio.dart'; import '../network/endpoint.dart'; import '../network/urls.dart'; @@ -58,3 +60,95 @@ class DropboxListFolderEndpoint extends Endpoint { "recursive": recursive, }; } + +class DropboxUploadEndpoint extends Endpoint { + final String filePath; + final String mode; + final bool autoRename; + final bool mute; + final bool strictConflict; + final Stream> contentStream; + final void Function(int chunk, int length)? onProgress; + final CancelToken? cancellationToken; + + const DropboxUploadEndpoint({ + required this.filePath, + this.mode = 'add', + this.autoRename = true, + this.mute = false, + this.strictConflict = false, + this.cancellationToken, + this.onProgress, + required this.contentStream, + }); + + @override + String get baseUrl => BaseURL.dropboxContentV2; + + @override + HttpMethod get method => HttpMethod.post; + + @override + String get path => '/files/upload'; + + @override + Map get headers => { + 'Dropbox-API-Arg': jsonEncode({ + 'path': filePath, + 'mode': mode, + 'autorename': autoRename, + 'mute': mute, + 'strict_conflict': strictConflict, + }), + 'Content-Type': 'application/octet-stream', + }; + + @override + Object? get data => contentStream; + + @override + CancelToken? get cancelToken => cancellationToken; + + @override + void Function(int p1, int p2)? get onSendProgress => onProgress; +} + +class DropboxDownloadEndpoint extends DownloadEndpoint { + final String filePath; + final String storagePath; + final void Function(int chunk, int length)? onProgress; + final CancelToken? cancellationToken; + + const DropboxDownloadEndpoint({ + required this.filePath, + required this.storagePath, + this.cancellationToken, + this.onProgress, + }); + + @override + String get baseUrl => BaseURL.dropboxContentV2; + + @override + HttpMethod get method => HttpMethod.post; + + @override + String get path => '/files/download'; + + @override + Map get headers => { + 'Dropbox-API-Arg': jsonEncode({ + 'path': filePath, + }), + }; + + @override + CancelToken? get cancelToken => cancellationToken; + + @override + void Function(int p1, int p2)? get onReceiveProgress => onProgress; + + @override + String? get storePath => storagePath; +} + diff --git a/data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart b/data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart index 34d637c..582fff3 100644 --- a/data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart +++ b/data/lib/apis/network/interceptors/dropbox_auth_interceptor.dart @@ -1,7 +1,7 @@ import 'dart:async'; +import '../../../models/dropbox/token/dropbox_token.dart'; import '../../../services/auth_service.dart'; import 'package:dio/dio.dart'; -import '../../../models/token/token.dart'; import '../../../storage/provider/preferences_provider.dart'; class DropboxAuthInterceptor extends Interceptor { diff --git a/data/lib/models/dropbox/token/token.freezed.dart b/data/lib/models/dropbox/token/token.freezed.dart deleted file mode 100644 index 39879bc..0000000 --- a/data/lib/models/dropbox/token/token.freezed.dart +++ /dev/null @@ -1,297 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'token.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -DropboxToken _$DropboxTokenFromJson(Map json) { - return _DropboxToken.fromJson(json); -} - -/// @nodoc -mixin _$DropboxToken { - String get access_token => throw _privateConstructorUsedError; - String get token_type => throw _privateConstructorUsedError; - @ExpiresInJsonConverter() - DateTime get expires_in => throw _privateConstructorUsedError; - String get refresh_token => throw _privateConstructorUsedError; - String get account_id => throw _privateConstructorUsedError; - String get scope => throw _privateConstructorUsedError; - String get uid => throw _privateConstructorUsedError; - - /// Serializes this DropboxToken to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of DropboxToken - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $DropboxTokenCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $DropboxTokenCopyWith<$Res> { - factory $DropboxTokenCopyWith( - DropboxToken value, $Res Function(DropboxToken) then) = - _$DropboxTokenCopyWithImpl<$Res, DropboxToken>; - @useResult - $Res call( - {String access_token, - String token_type, - @ExpiresInJsonConverter() DateTime expires_in, - String refresh_token, - String account_id, - String scope, - String uid}); -} - -/// @nodoc -class _$DropboxTokenCopyWithImpl<$Res, $Val extends DropboxToken> - implements $DropboxTokenCopyWith<$Res> { - _$DropboxTokenCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of DropboxToken - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? access_token = null, - Object? token_type = null, - Object? expires_in = null, - Object? refresh_token = null, - Object? account_id = null, - Object? scope = null, - Object? uid = null, - }) { - return _then(_value.copyWith( - access_token: null == access_token - ? _value.access_token - : access_token // ignore: cast_nullable_to_non_nullable - as String, - token_type: null == token_type - ? _value.token_type - : token_type // ignore: cast_nullable_to_non_nullable - as String, - expires_in: null == expires_in - ? _value.expires_in - : expires_in // ignore: cast_nullable_to_non_nullable - as DateTime, - refresh_token: null == refresh_token - ? _value.refresh_token - : refresh_token // ignore: cast_nullable_to_non_nullable - as String, - account_id: null == account_id - ? _value.account_id - : account_id // ignore: cast_nullable_to_non_nullable - as String, - scope: null == scope - ? _value.scope - : scope // ignore: cast_nullable_to_non_nullable - as String, - uid: null == uid - ? _value.uid - : uid // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$DropboxTokenImplCopyWith<$Res> - implements $DropboxTokenCopyWith<$Res> { - factory _$$DropboxTokenImplCopyWith( - _$DropboxTokenImpl value, $Res Function(_$DropboxTokenImpl) then) = - __$$DropboxTokenImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String access_token, - String token_type, - @ExpiresInJsonConverter() DateTime expires_in, - String refresh_token, - String account_id, - String scope, - String uid}); -} - -/// @nodoc -class __$$DropboxTokenImplCopyWithImpl<$Res> - extends _$DropboxTokenCopyWithImpl<$Res, _$DropboxTokenImpl> - implements _$$DropboxTokenImplCopyWith<$Res> { - __$$DropboxTokenImplCopyWithImpl( - _$DropboxTokenImpl _value, $Res Function(_$DropboxTokenImpl) _then) - : super(_value, _then); - - /// Create a copy of DropboxToken - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? access_token = null, - Object? token_type = null, - Object? expires_in = null, - Object? refresh_token = null, - Object? account_id = null, - Object? scope = null, - Object? uid = null, - }) { - return _then(_$DropboxTokenImpl( - access_token: null == access_token - ? _value.access_token - : access_token // ignore: cast_nullable_to_non_nullable - as String, - token_type: null == token_type - ? _value.token_type - : token_type // ignore: cast_nullable_to_non_nullable - as String, - expires_in: null == expires_in - ? _value.expires_in - : expires_in // ignore: cast_nullable_to_non_nullable - as DateTime, - refresh_token: null == refresh_token - ? _value.refresh_token - : refresh_token // ignore: cast_nullable_to_non_nullable - as String, - account_id: null == account_id - ? _value.account_id - : account_id // ignore: cast_nullable_to_non_nullable - as String, - scope: null == scope - ? _value.scope - : scope // ignore: cast_nullable_to_non_nullable - as String, - uid: null == uid - ? _value.uid - : uid // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$DropboxTokenImpl implements _DropboxToken { - const _$DropboxTokenImpl( - {required this.access_token, - required this.token_type, - @ExpiresInJsonConverter() required this.expires_in, - required this.refresh_token, - required this.account_id, - required this.scope, - required this.uid}); - - factory _$DropboxTokenImpl.fromJson(Map json) => - _$$DropboxTokenImplFromJson(json); - - @override - final String access_token; - @override - final String token_type; - @override - @ExpiresInJsonConverter() - final DateTime expires_in; - @override - final String refresh_token; - @override - final String account_id; - @override - final String scope; - @override - final String uid; - - @override - String toString() { - return 'DropboxToken(access_token: $access_token, token_type: $token_type, expires_in: $expires_in, refresh_token: $refresh_token, account_id: $account_id, scope: $scope, uid: $uid)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$DropboxTokenImpl && - (identical(other.access_token, access_token) || - other.access_token == access_token) && - (identical(other.token_type, token_type) || - other.token_type == token_type) && - (identical(other.expires_in, expires_in) || - other.expires_in == expires_in) && - (identical(other.refresh_token, refresh_token) || - other.refresh_token == refresh_token) && - (identical(other.account_id, account_id) || - other.account_id == account_id) && - (identical(other.scope, scope) || other.scope == scope) && - (identical(other.uid, uid) || other.uid == uid)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, access_token, token_type, - expires_in, refresh_token, account_id, scope, uid); - - /// Create a copy of DropboxToken - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$DropboxTokenImplCopyWith<_$DropboxTokenImpl> get copyWith => - __$$DropboxTokenImplCopyWithImpl<_$DropboxTokenImpl>(this, _$identity); - - @override - Map toJson() { - return _$$DropboxTokenImplToJson( - this, - ); - } -} - -abstract class _DropboxToken implements DropboxToken { - const factory _DropboxToken( - {required final String access_token, - required final String token_type, - @ExpiresInJsonConverter() required final DateTime expires_in, - required final String refresh_token, - required final String account_id, - required final String scope, - required final String uid}) = _$DropboxTokenImpl; - - factory _DropboxToken.fromJson(Map json) = - _$DropboxTokenImpl.fromJson; - - @override - String get access_token; - @override - String get token_type; - @override - @ExpiresInJsonConverter() - DateTime get expires_in; - @override - String get refresh_token; - @override - String get account_id; - @override - String get scope; - @override - String get uid; - - /// Create a copy of DropboxToken - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$DropboxTokenImplCopyWith<_$DropboxTokenImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/data/lib/models/dropbox/token/token.g.dart b/data/lib/models/dropbox/token/token.g.dart deleted file mode 100644 index c8fa152..0000000 --- a/data/lib/models/dropbox/token/token.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'token.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$DropboxTokenImpl _$$DropboxTokenImplFromJson(Map json) => - _$DropboxTokenImpl( - access_token: json['access_token'] as String, - token_type: json['token_type'] as String, - expires_in: const ExpiresInJsonConverter() - .fromJson((json['expires_in'] as num).toInt()), - refresh_token: json['refresh_token'] as String, - account_id: json['account_id'] as String, - scope: json['scope'] as String, - uid: json['uid'] as String, - ); - -Map _$$DropboxTokenImplToJson(_$DropboxTokenImpl instance) => - { - 'access_token': instance.access_token, - 'token_type': instance.token_type, - 'expires_in': const ExpiresInJsonConverter().toJson(instance.expires_in), - 'refresh_token': instance.refresh_token, - 'account_id': instance.account_id, - 'scope': instance.scope, - 'uid': instance.uid, - }; diff --git a/data/lib/models/media/media.dart b/data/lib/models/media/media.dart index f955238..95d66ba 100644 --- a/data/lib/models/media/media.dart +++ b/data/lib/models/media/media.dart @@ -83,7 +83,8 @@ enum AppMediaOrientation { @JsonEnum(valueField: 'value') enum AppMediaSource { local('local'), - googleDrive('google_drive'); + googleDrive('google_drive'), + dropbox('dropbox'); final String value; @@ -93,6 +94,7 @@ enum AppMediaSource { @freezed class AppMedia with _$AppMedia { const AppMedia._(); + const factory AppMedia({ required String id, String? driveMediaRefId, @@ -191,4 +193,15 @@ class AppMedia with _$AppMedia { displayWidth: asset.size.width, ); } + + static Future fromDropboxJson(Map json) async { + return AppMedia( + id: json['id'], + path: json['path_display'], + name: json['name'], + size: json['size'], + type: AppMediaType.getType(location: json['path_display']), + sources: [AppMediaSource.dropbox], + ); + } } diff --git a/data/lib/models/media/media.g.dart b/data/lib/models/media/media.g.dart index 5e4dab0..e0a9317 100644 --- a/data/lib/models/media/media.g.dart +++ b/data/lib/models/media/media.g.dart @@ -79,6 +79,7 @@ const _$AppMediaOrientationEnumMap = { const _$AppMediaSourceEnumMap = { AppMediaSource.local: 'local', AppMediaSource.googleDrive: 'google_drive', + AppMediaSource.dropbox: 'dropbox', }; Json? _$JsonConverterToJson( diff --git a/data/lib/models/media_process/media_process.dart b/data/lib/models/media_process/media_process.dart index e69de29..12aadc7 100644 --- a/data/lib/models/media_process/media_process.dart +++ b/data/lib/models/media_process/media_process.dart @@ -0,0 +1,112 @@ +// ignore_for_file: non_constant_identifier_names + +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'media_process.freezed.dart'; + +part 'media_process.g.dart'; + +@JsonEnum(valueField: 'value') +enum MediaQueueProcessStatus { + waiting('waiting'), + uploading('uploading'), + deleting('deleting'), + downloading('downloading'), + completed('completed'), + terminated('terminated'), + failed('failed'); + + final String value; + + const MediaQueueProcessStatus(this.value); + + bool get isRunning => + this == MediaQueueProcessStatus.uploading || + this == MediaQueueProcessStatus.deleting || + this == MediaQueueProcessStatus.downloading; + + bool get isWaiting => this == MediaQueueProcessStatus.waiting; + + bool get isCompleted => this == MediaQueueProcessStatus.completed; + + bool get isFailed => this == MediaQueueProcessStatus.failed; + + bool get isTerminated => this == MediaQueueProcessStatus.terminated; +} + +@JsonEnum(valueField: 'value') +enum MediaProvider { + googleDrive('google-drive'), + dropbox('dropbox'); + + final String value; + + const MediaProvider(this.value); +} + +class LocalDatabaseBoolConverter extends JsonConverter { + const LocalDatabaseBoolConverter(); + + @override + bool fromJson(int json) { + return json == 1; + } + + @override + int toJson(bool object) { + return object ? 1 : 0; + } +} + +@freezed +class DownloadMediaProcess with _$DownloadMediaProcess { + const factory DownloadMediaProcess({ + required String id, + required String folder_id, + required MediaProvider provider, + @Default(MediaQueueProcessStatus.waiting) MediaQueueProcessStatus status, + @Default(1) int total, + required String extension, + @Default(0) int chunk, + }) = _MediaProcess; + + factory DownloadMediaProcess.fromJson(Map json) => + _$DownloadMediaProcessFromJson(json); +} + +@freezed +class UploadMediaProcess with _$UploadMediaProcess { + const factory UploadMediaProcess({ + required String id, + required String folder_id, + required MediaProvider provider, + required String path, + String? mime_type, + @Default(MediaQueueProcessStatus.waiting) MediaQueueProcessStatus status, + @LocalDatabaseBoolConverter() @Default(false) bool upload_using_auto_backup, + @Default(1) int total, + @Default(0) int chunk, + }) = _UploadMediaProcess; + + factory UploadMediaProcess.fromJson(Map json) => + _$UploadMediaProcessFromJson(json); +} + +@freezed +class MediaProcessProgress with _$MediaProcessProgress { + const MediaProcessProgress._(); + + const factory MediaProcessProgress({ + required int total, + required int chunk, + }) = _MediaProcessProgress; + + /// progress 0.0 - 1.0 + double get progress => total == 0 ? 0 : chunk / total; + + /// percentage of the progress 0 - 100 + double get progressPercentage => progress * 100; + + factory MediaProcessProgress.fromJson(Map json) => + _$MediaProcessProgressFromJson(json); +} diff --git a/data/lib/models/media_process/media_process.freezed.dart b/data/lib/models/media_process/media_process.freezed.dart index aa18fde..1f1fc61 100644 --- a/data/lib/models/media_process/media_process.freezed.dart +++ b/data/lib/models/media_process/media_process.freezed.dart @@ -14,66 +14,69 @@ T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); +DownloadMediaProcess _$DownloadMediaProcessFromJson(Map json) { + return _MediaProcess.fromJson(json); +} + /// @nodoc -mixin _$MediaProcess { +mixin _$DownloadMediaProcess { String get id => throw _privateConstructorUsedError; String get folder_id => throw _privateConstructorUsedError; - AppMediaSource get source => throw _privateConstructorUsedError; - String get local_path => throw _privateConstructorUsedError; + MediaProvider get provider => throw _privateConstructorUsedError; MediaQueueProcessStatus get status => throw _privateConstructorUsedError; - bool get uploading_using_auto_backup => throw _privateConstructorUsedError; - Object? get response => throw _privateConstructorUsedError; - MediaProcessProgress? get progress => throw _privateConstructorUsedError; + int get total => throw _privateConstructorUsedError; + String get extension => throw _privateConstructorUsedError; + int get chunk => throw _privateConstructorUsedError; - /// Create a copy of MediaProcess + /// Serializes this DownloadMediaProcess to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of DownloadMediaProcess /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - $MediaProcessCopyWith get copyWith => + $DownloadMediaProcessCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $MediaProcessCopyWith<$Res> { - factory $MediaProcessCopyWith( - MediaProcess value, $Res Function(MediaProcess) then) = - _$MediaProcessCopyWithImpl<$Res, MediaProcess>; +abstract class $DownloadMediaProcessCopyWith<$Res> { + factory $DownloadMediaProcessCopyWith(DownloadMediaProcess value, + $Res Function(DownloadMediaProcess) then) = + _$DownloadMediaProcessCopyWithImpl<$Res, DownloadMediaProcess>; @useResult $Res call( {String id, String folder_id, - AppMediaSource source, - String local_path, + MediaProvider provider, MediaQueueProcessStatus status, - bool uploading_using_auto_backup, - Object? response, - MediaProcessProgress? progress}); - - $MediaProcessProgressCopyWith<$Res>? get progress; + int total, + String extension, + int chunk}); } /// @nodoc -class _$MediaProcessCopyWithImpl<$Res, $Val extends MediaProcess> - implements $MediaProcessCopyWith<$Res> { - _$MediaProcessCopyWithImpl(this._value, this._then); +class _$DownloadMediaProcessCopyWithImpl<$Res, + $Val extends DownloadMediaProcess> + implements $DownloadMediaProcessCopyWith<$Res> { + _$DownloadMediaProcessCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; // ignore: unused_field final $Res Function($Val) _then; - /// Create a copy of MediaProcess + /// Create a copy of DownloadMediaProcess /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? id = null, Object? folder_id = null, - Object? source = null, - Object? local_path = null, + Object? provider = null, Object? status = null, - Object? uploading_using_auto_backup = null, - Object? response = freezed, - Object? progress = freezed, + Object? total = null, + Object? extension = null, + Object? chunk = null, }) { return _then(_value.copyWith( id: null == id @@ -84,48 +87,33 @@ class _$MediaProcessCopyWithImpl<$Res, $Val extends MediaProcess> ? _value.folder_id : folder_id // ignore: cast_nullable_to_non_nullable as String, - source: null == source - ? _value.source - : source // ignore: cast_nullable_to_non_nullable - as AppMediaSource, - local_path: null == local_path - ? _value.local_path - : local_path // ignore: cast_nullable_to_non_nullable - as String, + provider: null == provider + ? _value.provider + : provider // ignore: cast_nullable_to_non_nullable + as MediaProvider, status: null == status ? _value.status : status // ignore: cast_nullable_to_non_nullable as MediaQueueProcessStatus, - uploading_using_auto_backup: null == uploading_using_auto_backup - ? _value.uploading_using_auto_backup - : uploading_using_auto_backup // ignore: cast_nullable_to_non_nullable - as bool, - response: freezed == response ? _value.response : response, - progress: freezed == progress - ? _value.progress - : progress // ignore: cast_nullable_to_non_nullable - as MediaProcessProgress?, + total: null == total + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, + extension: null == extension + ? _value.extension + : extension // ignore: cast_nullable_to_non_nullable + as String, + chunk: null == chunk + ? _value.chunk + : chunk // ignore: cast_nullable_to_non_nullable + as int, ) as $Val); } - - /// Create a copy of MediaProcess - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $MediaProcessProgressCopyWith<$Res>? get progress { - if (_value.progress == null) { - return null; - } - - return $MediaProcessProgressCopyWith<$Res>(_value.progress!, (value) { - return _then(_value.copyWith(progress: value) as $Val); - }); - } } /// @nodoc abstract class _$$MediaProcessImplCopyWith<$Res> - implements $MediaProcessCopyWith<$Res> { + implements $DownloadMediaProcessCopyWith<$Res> { factory _$$MediaProcessImplCopyWith( _$MediaProcessImpl value, $Res Function(_$MediaProcessImpl) then) = __$$MediaProcessImplCopyWithImpl<$Res>; @@ -134,38 +122,33 @@ abstract class _$$MediaProcessImplCopyWith<$Res> $Res call( {String id, String folder_id, - AppMediaSource source, - String local_path, + MediaProvider provider, MediaQueueProcessStatus status, - bool uploading_using_auto_backup, - Object? response, - MediaProcessProgress? progress}); - - @override - $MediaProcessProgressCopyWith<$Res>? get progress; + int total, + String extension, + int chunk}); } /// @nodoc class __$$MediaProcessImplCopyWithImpl<$Res> - extends _$MediaProcessCopyWithImpl<$Res, _$MediaProcessImpl> + extends _$DownloadMediaProcessCopyWithImpl<$Res, _$MediaProcessImpl> implements _$$MediaProcessImplCopyWith<$Res> { __$$MediaProcessImplCopyWithImpl( _$MediaProcessImpl _value, $Res Function(_$MediaProcessImpl) _then) : super(_value, _then); - /// Create a copy of MediaProcess + /// Create a copy of DownloadMediaProcess /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? id = null, Object? folder_id = null, - Object? source = null, - Object? local_path = null, + Object? provider = null, Object? status = null, - Object? uploading_using_auto_backup = null, - Object? response = freezed, - Object? progress = freezed, + Object? total = null, + Object? extension = null, + Object? chunk = null, }) { return _then(_$MediaProcessImpl( id: null == id @@ -176,66 +159,66 @@ class __$$MediaProcessImplCopyWithImpl<$Res> ? _value.folder_id : folder_id // ignore: cast_nullable_to_non_nullable as String, - source: null == source - ? _value.source - : source // ignore: cast_nullable_to_non_nullable - as AppMediaSource, - local_path: null == local_path - ? _value.local_path - : local_path // ignore: cast_nullable_to_non_nullable - as String, + provider: null == provider + ? _value.provider + : provider // ignore: cast_nullable_to_non_nullable + as MediaProvider, status: null == status ? _value.status : status // ignore: cast_nullable_to_non_nullable as MediaQueueProcessStatus, - uploading_using_auto_backup: null == uploading_using_auto_backup - ? _value.uploading_using_auto_backup - : uploading_using_auto_backup // ignore: cast_nullable_to_non_nullable - as bool, - response: freezed == response ? _value.response : response, - progress: freezed == progress - ? _value.progress - : progress // ignore: cast_nullable_to_non_nullable - as MediaProcessProgress?, + total: null == total + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, + extension: null == extension + ? _value.extension + : extension // ignore: cast_nullable_to_non_nullable + as String, + chunk: null == chunk + ? _value.chunk + : chunk // ignore: cast_nullable_to_non_nullable + as int, )); } } /// @nodoc - +@JsonSerializable() class _$MediaProcessImpl implements _MediaProcess { const _$MediaProcessImpl( {required this.id, required this.folder_id, - required this.source, - required this.local_path, - required this.status, - this.uploading_using_auto_backup = false, - this.response, - this.progress = null}); + required this.provider, + this.status = MediaQueueProcessStatus.waiting, + this.total = 1, + required this.extension, + this.chunk = 0}); + + factory _$MediaProcessImpl.fromJson(Map json) => + _$$MediaProcessImplFromJson(json); @override final String id; @override final String folder_id; @override - final AppMediaSource source; - @override - final String local_path; + final MediaProvider provider; @override + @JsonKey() final MediaQueueProcessStatus status; @override @JsonKey() - final bool uploading_using_auto_backup; + final int total; @override - final Object? response; + final String extension; @override @JsonKey() - final MediaProcessProgress? progress; + final int chunk; @override String toString() { - return 'MediaProcess(id: $id, folder_id: $folder_id, source: $source, local_path: $local_path, status: $status, uploading_using_auto_backup: $uploading_using_auto_backup, response: $response, progress: $progress)'; + return 'DownloadMediaProcess(id: $id, folder_id: $folder_id, provider: $provider, status: $status, total: $total, extension: $extension, chunk: $chunk)'; } @override @@ -246,69 +229,65 @@ class _$MediaProcessImpl implements _MediaProcess { (identical(other.id, id) || other.id == id) && (identical(other.folder_id, folder_id) || other.folder_id == folder_id) && - (identical(other.source, source) || other.source == source) && - (identical(other.local_path, local_path) || - other.local_path == local_path) && + (identical(other.provider, provider) || + other.provider == provider) && (identical(other.status, status) || other.status == status) && - (identical(other.uploading_using_auto_backup, - uploading_using_auto_backup) || - other.uploading_using_auto_backup == - uploading_using_auto_backup) && - const DeepCollectionEquality().equals(other.response, response) && - (identical(other.progress, progress) || - other.progress == progress)); + (identical(other.total, total) || other.total == total) && + (identical(other.extension, extension) || + other.extension == extension) && + (identical(other.chunk, chunk) || other.chunk == chunk)); } + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( - runtimeType, - id, - folder_id, - source, - local_path, - status, - uploading_using_auto_backup, - const DeepCollectionEquality().hash(response), - progress); - - /// Create a copy of MediaProcess + runtimeType, id, folder_id, provider, status, total, extension, chunk); + + /// Create a copy of DownloadMediaProcess /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$MediaProcessImplCopyWith<_$MediaProcessImpl> get copyWith => __$$MediaProcessImplCopyWithImpl<_$MediaProcessImpl>(this, _$identity); + + @override + Map toJson() { + return _$$MediaProcessImplToJson( + this, + ); + } } -abstract class _MediaProcess implements MediaProcess { +abstract class _MediaProcess implements DownloadMediaProcess { const factory _MediaProcess( {required final String id, required final String folder_id, - required final AppMediaSource source, - required final String local_path, - required final MediaQueueProcessStatus status, - final bool uploading_using_auto_backup, - final Object? response, - final MediaProcessProgress? progress}) = _$MediaProcessImpl; + required final MediaProvider provider, + final MediaQueueProcessStatus status, + final int total, + required final String extension, + final int chunk}) = _$MediaProcessImpl; + + factory _MediaProcess.fromJson(Map json) = + _$MediaProcessImpl.fromJson; @override String get id; @override String get folder_id; @override - AppMediaSource get source; - @override - String get local_path; + MediaProvider get provider; @override MediaQueueProcessStatus get status; @override - bool get uploading_using_auto_backup; + int get total; @override - Object? get response; + String get extension; @override - MediaProcessProgress? get progress; + int get chunk; - /// Create a copy of MediaProcess + /// Create a copy of DownloadMediaProcess /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) @@ -316,6 +295,333 @@ abstract class _MediaProcess implements MediaProcess { throw _privateConstructorUsedError; } +UploadMediaProcess _$UploadMediaProcessFromJson(Map json) { + return _UploadMediaProcess.fromJson(json); +} + +/// @nodoc +mixin _$UploadMediaProcess { + String get id => throw _privateConstructorUsedError; + String get folder_id => throw _privateConstructorUsedError; + MediaProvider get provider => throw _privateConstructorUsedError; + String get path => throw _privateConstructorUsedError; + String? get mime_type => throw _privateConstructorUsedError; + MediaQueueProcessStatus get status => throw _privateConstructorUsedError; + @LocalDatabaseBoolConverter() + bool get upload_using_auto_backup => throw _privateConstructorUsedError; + int get total => throw _privateConstructorUsedError; + int get chunk => throw _privateConstructorUsedError; + + /// Serializes this UploadMediaProcess to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of UploadMediaProcess + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $UploadMediaProcessCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UploadMediaProcessCopyWith<$Res> { + factory $UploadMediaProcessCopyWith( + UploadMediaProcess value, $Res Function(UploadMediaProcess) then) = + _$UploadMediaProcessCopyWithImpl<$Res, UploadMediaProcess>; + @useResult + $Res call( + {String id, + String folder_id, + MediaProvider provider, + String path, + String? mime_type, + MediaQueueProcessStatus status, + @LocalDatabaseBoolConverter() bool upload_using_auto_backup, + int total, + int chunk}); +} + +/// @nodoc +class _$UploadMediaProcessCopyWithImpl<$Res, $Val extends UploadMediaProcess> + implements $UploadMediaProcessCopyWith<$Res> { + _$UploadMediaProcessCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of UploadMediaProcess + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? folder_id = null, + Object? provider = null, + Object? path = null, + Object? mime_type = freezed, + Object? status = null, + Object? upload_using_auto_backup = null, + Object? total = null, + Object? chunk = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + folder_id: null == folder_id + ? _value.folder_id + : folder_id // ignore: cast_nullable_to_non_nullable + as String, + provider: null == provider + ? _value.provider + : provider // ignore: cast_nullable_to_non_nullable + as MediaProvider, + path: null == path + ? _value.path + : path // ignore: cast_nullable_to_non_nullable + as String, + mime_type: freezed == mime_type + ? _value.mime_type + : mime_type // ignore: cast_nullable_to_non_nullable + as String?, + status: null == status + ? _value.status + : status // ignore: cast_nullable_to_non_nullable + as MediaQueueProcessStatus, + upload_using_auto_backup: null == upload_using_auto_backup + ? _value.upload_using_auto_backup + : upload_using_auto_backup // ignore: cast_nullable_to_non_nullable + as bool, + total: null == total + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, + chunk: null == chunk + ? _value.chunk + : chunk // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$UploadMediaProcessImplCopyWith<$Res> + implements $UploadMediaProcessCopyWith<$Res> { + factory _$$UploadMediaProcessImplCopyWith(_$UploadMediaProcessImpl value, + $Res Function(_$UploadMediaProcessImpl) then) = + __$$UploadMediaProcessImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String id, + String folder_id, + MediaProvider provider, + String path, + String? mime_type, + MediaQueueProcessStatus status, + @LocalDatabaseBoolConverter() bool upload_using_auto_backup, + int total, + int chunk}); +} + +/// @nodoc +class __$$UploadMediaProcessImplCopyWithImpl<$Res> + extends _$UploadMediaProcessCopyWithImpl<$Res, _$UploadMediaProcessImpl> + implements _$$UploadMediaProcessImplCopyWith<$Res> { + __$$UploadMediaProcessImplCopyWithImpl(_$UploadMediaProcessImpl _value, + $Res Function(_$UploadMediaProcessImpl) _then) + : super(_value, _then); + + /// Create a copy of UploadMediaProcess + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? folder_id = null, + Object? provider = null, + Object? path = null, + Object? mime_type = freezed, + Object? status = null, + Object? upload_using_auto_backup = null, + Object? total = null, + Object? chunk = null, + }) { + return _then(_$UploadMediaProcessImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + folder_id: null == folder_id + ? _value.folder_id + : folder_id // ignore: cast_nullable_to_non_nullable + as String, + provider: null == provider + ? _value.provider + : provider // ignore: cast_nullable_to_non_nullable + as MediaProvider, + path: null == path + ? _value.path + : path // ignore: cast_nullable_to_non_nullable + as String, + mime_type: freezed == mime_type + ? _value.mime_type + : mime_type // ignore: cast_nullable_to_non_nullable + as String?, + status: null == status + ? _value.status + : status // ignore: cast_nullable_to_non_nullable + as MediaQueueProcessStatus, + upload_using_auto_backup: null == upload_using_auto_backup + ? _value.upload_using_auto_backup + : upload_using_auto_backup // ignore: cast_nullable_to_non_nullable + as bool, + total: null == total + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, + chunk: null == chunk + ? _value.chunk + : chunk // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$UploadMediaProcessImpl implements _UploadMediaProcess { + const _$UploadMediaProcessImpl( + {required this.id, + required this.folder_id, + required this.provider, + required this.path, + this.mime_type, + this.status = MediaQueueProcessStatus.waiting, + @LocalDatabaseBoolConverter() this.upload_using_auto_backup = false, + this.total = 1, + this.chunk = 0}); + + factory _$UploadMediaProcessImpl.fromJson(Map json) => + _$$UploadMediaProcessImplFromJson(json); + + @override + final String id; + @override + final String folder_id; + @override + final MediaProvider provider; + @override + final String path; + @override + final String? mime_type; + @override + @JsonKey() + final MediaQueueProcessStatus status; + @override + @JsonKey() + @LocalDatabaseBoolConverter() + final bool upload_using_auto_backup; + @override + @JsonKey() + final int total; + @override + @JsonKey() + final int chunk; + + @override + String toString() { + return 'UploadMediaProcess(id: $id, folder_id: $folder_id, provider: $provider, path: $path, mime_type: $mime_type, status: $status, upload_using_auto_backup: $upload_using_auto_backup, total: $total, chunk: $chunk)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UploadMediaProcessImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.folder_id, folder_id) || + other.folder_id == folder_id) && + (identical(other.provider, provider) || + other.provider == provider) && + (identical(other.path, path) || other.path == path) && + (identical(other.mime_type, mime_type) || + other.mime_type == mime_type) && + (identical(other.status, status) || other.status == status) && + (identical( + other.upload_using_auto_backup, upload_using_auto_backup) || + other.upload_using_auto_backup == upload_using_auto_backup) && + (identical(other.total, total) || other.total == total) && + (identical(other.chunk, chunk) || other.chunk == chunk)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, id, folder_id, provider, path, + mime_type, status, upload_using_auto_backup, total, chunk); + + /// Create a copy of UploadMediaProcess + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$UploadMediaProcessImplCopyWith<_$UploadMediaProcessImpl> get copyWith => + __$$UploadMediaProcessImplCopyWithImpl<_$UploadMediaProcessImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$UploadMediaProcessImplToJson( + this, + ); + } +} + +abstract class _UploadMediaProcess implements UploadMediaProcess { + const factory _UploadMediaProcess( + {required final String id, + required final String folder_id, + required final MediaProvider provider, + required final String path, + final String? mime_type, + final MediaQueueProcessStatus status, + @LocalDatabaseBoolConverter() final bool upload_using_auto_backup, + final int total, + final int chunk}) = _$UploadMediaProcessImpl; + + factory _UploadMediaProcess.fromJson(Map json) = + _$UploadMediaProcessImpl.fromJson; + + @override + String get id; + @override + String get folder_id; + @override + MediaProvider get provider; + @override + String get path; + @override + String? get mime_type; + @override + MediaQueueProcessStatus get status; + @override + @LocalDatabaseBoolConverter() + bool get upload_using_auto_backup; + @override + int get total; + @override + int get chunk; + + /// Create a copy of UploadMediaProcess + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$UploadMediaProcessImplCopyWith<_$UploadMediaProcessImpl> get copyWith => + throw _privateConstructorUsedError; +} + MediaProcessProgress _$MediaProcessProgressFromJson(Map json) { return _MediaProcessProgress.fromJson(json); } diff --git a/data/lib/models/media_process/media_process.g.dart b/data/lib/models/media_process/media_process.g.dart index de391bb..e78e22a 100644 --- a/data/lib/models/media_process/media_process.g.dart +++ b/data/lib/models/media_process/media_process.g.dart @@ -6,6 +6,79 @@ part of 'media_process.dart'; // JsonSerializableGenerator // ************************************************************************** +_$MediaProcessImpl _$$MediaProcessImplFromJson(Map json) => + _$MediaProcessImpl( + id: json['id'] as String, + folder_id: json['folder_id'] as String, + provider: $enumDecode(_$MediaProviderEnumMap, json['provider']), + status: $enumDecodeNullable( + _$MediaQueueProcessStatusEnumMap, json['status']) ?? + MediaQueueProcessStatus.waiting, + total: (json['total'] as num?)?.toInt() ?? 1, + extension: json['extension'] as String, + chunk: (json['chunk'] as num?)?.toInt() ?? 0, + ); + +Map _$$MediaProcessImplToJson(_$MediaProcessImpl instance) => + { + 'id': instance.id, + 'folder_id': instance.folder_id, + 'provider': _$MediaProviderEnumMap[instance.provider]!, + 'status': _$MediaQueueProcessStatusEnumMap[instance.status]!, + 'total': instance.total, + 'extension': instance.extension, + 'chunk': instance.chunk, + }; + +const _$MediaProviderEnumMap = { + MediaProvider.googleDrive: 'google-drive', + MediaProvider.dropbox: 'dropbox', +}; + +const _$MediaQueueProcessStatusEnumMap = { + MediaQueueProcessStatus.waiting: 'waiting', + MediaQueueProcessStatus.uploading: 'uploading', + MediaQueueProcessStatus.deleting: 'deleting', + MediaQueueProcessStatus.downloading: 'downloading', + MediaQueueProcessStatus.completed: 'completed', + MediaQueueProcessStatus.terminated: 'terminated', + MediaQueueProcessStatus.failed: 'failed', +}; + +_$UploadMediaProcessImpl _$$UploadMediaProcessImplFromJson( + Map json) => + _$UploadMediaProcessImpl( + id: json['id'] as String, + folder_id: json['folder_id'] as String, + provider: $enumDecode(_$MediaProviderEnumMap, json['provider']), + path: json['path'] as String, + mime_type: json['mime_type'] as String?, + status: $enumDecodeNullable( + _$MediaQueueProcessStatusEnumMap, json['status']) ?? + MediaQueueProcessStatus.waiting, + upload_using_auto_backup: json['upload_using_auto_backup'] == null + ? false + : const LocalDatabaseBoolConverter() + .fromJson((json['upload_using_auto_backup'] as num).toInt()), + total: (json['total'] as num?)?.toInt() ?? 1, + chunk: (json['chunk'] as num?)?.toInt() ?? 0, + ); + +Map _$$UploadMediaProcessImplToJson( + _$UploadMediaProcessImpl instance) => + { + 'id': instance.id, + 'folder_id': instance.folder_id, + 'provider': _$MediaProviderEnumMap[instance.provider]!, + 'path': instance.path, + 'mime_type': instance.mime_type, + 'status': _$MediaQueueProcessStatusEnumMap[instance.status]!, + 'upload_using_auto_backup': const LocalDatabaseBoolConverter() + .toJson(instance.upload_using_auto_backup), + 'total': instance.total, + 'chunk': instance.chunk, + }; + _$MediaProcessProgressImpl _$$MediaProcessProgressImplFromJson( Map json) => _$MediaProcessProgressImpl( diff --git a/data/lib/repositories/google_drive_process_repo.dart b/data/lib/repositories/google_drive_process_repo.dart index b56a52c..673832c 100644 --- a/data/lib/repositories/google_drive_process_repo.dart +++ b/data/lib/repositories/google_drive_process_repo.dart @@ -116,9 +116,11 @@ class GoogleDriveProcessRepo extends ChangeNotifier { final cancelToken = CancelToken(); - final res = await _googleDriveService.uploadInGoogleDrive( - folderID: _backUpFolderID!, - media: process.media, + final res = await _googleDriveService.uploadMedia( + folderId: _backUpFolderID!, + path: process.media.path, + description: process.media.id, + mimeType: process.media.mimeType, onProgress: (chunk, total) { if (_uploadQueue .firstWhereOrNull((element) => element.id == process.id) @@ -248,7 +250,7 @@ class GoogleDriveProcessRepo extends ChangeNotifier { final cancelToken = CancelToken(); - await _googleDriveService.downloadFromGoogleDrive( + await _googleDriveService.downloadMedia( id: process.media.driveMediaRefId!, saveLocation: tempFileLocation, onProgress: (received, total) { diff --git a/data/lib/repositories/media_process_repository.dart b/data/lib/repositories/media_process_repository.dart index e69de29..e457596 100644 --- a/data/lib/repositories/media_process_repository.dart +++ b/data/lib/repositories/media_process_repository.dart @@ -0,0 +1,368 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:sqflite/sqflite.dart'; +import '../errors/app_error.dart'; +import '../models/media/media.dart'; +import '../models/media/media_extension.dart'; +import '../models/media_process/media_process.dart'; +import '../services/dropbox_services.dart'; +import '../services/google_drive_service.dart'; +import '../services/local_media_service.dart'; + +class LocalDatabaseConstants { + static const String databaseName = 'cloud-gallery.db'; + static const String uploadQueueTable = 'UploadQueue'; + static const String downloadQueueTable = 'DownloadQueue'; +} + +class MediaProcessRepo extends ChangeNotifier { + final GoogleDriveService _googleDriveService; + final DropboxService _dropboxService; + final LocalMediaService _localMediaService; + + late Database database; + + List _uploadQueue = []; + List _downloadQueue = []; + + List get uploadQueue => _uploadQueue; + + List get downloadQueue => _downloadQueue; + + bool _uploadQueueIsRunning = false; + bool _downloadQueueIsRunning = false; + + MediaProcessRepo( + this._googleDriveService, + this._dropboxService, + this._localMediaService, + ) { + initializeLocalDatabase(); + } + + Future initializeLocalDatabase() async { + database = await openDatabase( + LocalDatabaseConstants.databaseName, + version: 1, + onCreate: (Database db, int version) async { + await db.execute( + 'CREATE TABLE ${LocalDatabaseConstants.uploadQueueTable} (' + 'id TEXT PRIMARY KEY, ' + 'folder_id TEXT NOT NULL, ' + 'provider TEXT NOT NULL, ' + 'path TEXT NOT NULL, ' + 'status TEXT NOT NULL, ' + 'upload_using_auto_backup INTEGER NOT NULL, ' + 'mime_type TEXT, ' + 'total INTEGER NOT NULL, ' + 'chunk INTEGER NOT NULL' + ')', + ); + await db.execute( + 'CREATE TABLE ${LocalDatabaseConstants.downloadQueueTable} (' + 'id TEXT PRIMARY KEY, ' + 'folder_id TEXT NOT NULL, ' + 'provider TEXT NOT NULL, ' + 'local_path TEXT NOT NULL, ' + 'status TEXT NOT NULL, ' + 'extension TEXT NOT NULL, ' + 'total INTEGER NOT NULL, ' + 'chunk INTEGER NOT NULL' + ')', + ); + }, + onOpen: updateQueue, + ); + } + + void uploadMedia({ + required List medias, + required String folderId, + required MediaProvider provider, + bool uploadUsingAutoBackup = false, + }) async { + for (AppMedia media in medias) { + await database.insert( + LocalDatabaseConstants.uploadQueueTable, + UploadMediaProcess( + id: media.id, + folder_id: folderId, + provider: provider, + path: media.path, + upload_using_auto_backup: uploadUsingAutoBackup, + mime_type: media.mimeType, + ).toJson(), + ); + } + + updateQueue(database); + runUploadQueue(); + } + + void downloadMedia({ + required List medias, + required String folderId, + required MediaProvider provider, + }) async { + for (AppMedia media in medias) { + await database.insert( + LocalDatabaseConstants.downloadQueueTable, + DownloadMediaProcess( + id: media.id, + folder_id: folderId, + provider: provider, + extension: media.extension, + ).toJson(), + ); + } + updateQueue(database); + runDownloadQueue(); + } + + Future updateQueue(Database db) async { + db.query(LocalDatabaseConstants.uploadQueueTable).then((value) { + _uploadQueue = value.map((e) => UploadMediaProcess.fromJson(e)).toList(); + }); + db.query(LocalDatabaseConstants.downloadQueueTable).then((value) { + _downloadQueue = + value.map((e) => DownloadMediaProcess.fromJson(e)).toList(); + }); + + notifyListeners(); + } + + Future runUploadQueue() async { + if (_uploadQueueIsRunning) return; + _uploadQueueIsRunning = true; + while (_uploadQueue.firstOrNull != null) { + if (_uploadQueue.first.provider == MediaProvider.googleDrive) { + _uploadInGoogleDrive(_uploadQueue.first); + } else if (_uploadQueue.first.provider == MediaProvider.dropbox) { + _uploadInDropbox(_uploadQueue.first); + } else { + _updateUploadQueue( + _uploadQueue.first.copyWith(status: MediaQueueProcessStatus.failed), + ); + } + } + _uploadQueueIsRunning = false; + } + + Future _updateUploadQueue( + UploadMediaProcess process, + ) async { + await database.update( + LocalDatabaseConstants.uploadQueueTable, + process.toJson(), + where: 'id = ?', + whereArgs: [process.id], + ); + await updateQueue(database); + return _uploadQueue.firstWhere((element) => element.id == process.id); + } + + void _uploadInGoogleDrive(UploadMediaProcess uploadProcess) async { + UploadMediaProcess process = uploadProcess; + try { + process = process.copyWith(status: MediaQueueProcessStatus.uploading); + process = await _updateUploadQueue(process); + + final cancelToken = CancelToken(); + + await _googleDriveService.uploadMedia( + folderId: process.folder_id, + path: process.path, + mimeType: process.mime_type, + description: process.id, + onProgress: (chunk, total) async { + if (process.status.isTerminated) { + cancelToken.cancel(); + } + process = process.copyWith(total: total, chunk: chunk); + process = await _updateUploadQueue(process); + }, + cancelToken: cancelToken, + ); + process = process.copyWith(status: MediaQueueProcessStatus.completed); + process = await _updateUploadQueue(process); + } catch (e) { + if (e is RequestCancelledByUser) return; + process = process.copyWith(status: MediaQueueProcessStatus.failed); + process = await _updateUploadQueue(process); + return; + } + } + + void _uploadInDropbox(UploadMediaProcess uploadProcess) async { + UploadMediaProcess process = uploadProcess; + try { + process = process.copyWith(status: MediaQueueProcessStatus.uploading); + process = await _updateUploadQueue(process); + + final cancelToken = CancelToken(); + + await _dropboxService.uploadMedia( + folderId: process.folder_id, + path: process.path, + mimeType: process.mime_type, + description: process.id, + onProgress: (chunk, total) async { + if (process.status.isTerminated) { + cancelToken.cancel(); + } + process = process.copyWith(total: total, chunk: chunk); + process = await _updateUploadQueue(process); + }, + cancelToken: cancelToken, + ); + process = process.copyWith(status: MediaQueueProcessStatus.completed); + process = await _updateUploadQueue(process); + } catch (e) { + if (e is RequestCancelledByUser) return; + process = process.copyWith(status: MediaQueueProcessStatus.failed); + process = await _updateUploadQueue(process); + return; + } + } + + Future runDownloadQueue() async { + if (_downloadQueueIsRunning) return; + _downloadQueueIsRunning = true; + while (_downloadQueue.firstOrNull != null) { + if (_downloadQueue.first.provider == MediaProvider.googleDrive) { + _downloadFromGoogleDrive(_downloadQueue.first); + } else if (_downloadQueue.first.provider == MediaProvider.dropbox) { + _downloadFromDropbox(_downloadQueue.first); + } else { + await _updateDownloadQueue( + _downloadQueue.first.copyWith(status: MediaQueueProcessStatus.failed), + ); + } + } + _downloadQueueIsRunning = false; + } + + Future _updateDownloadQueue( + DownloadMediaProcess process, + ) async { + await database.update( + LocalDatabaseConstants.downloadQueueTable, + process.toJson(), + where: 'id = ?', + whereArgs: [process.id], + ); + await updateQueue(database); + return _downloadQueue.firstWhere((element) => element.id == process.id); + } + + Future _downloadFromGoogleDrive( + DownloadMediaProcess downloadProcess, + ) async { + DownloadMediaProcess process = downloadProcess; + String? tempFileLocation; + try { + process = process.copyWith(status: MediaQueueProcessStatus.downloading); + process = await _updateDownloadQueue(process); + + final tempDir = await getTemporaryDirectory(); + tempFileLocation = "${tempDir.path}/${process.id}.${process.extension}"; + + final cancelToken = CancelToken(); + + await _googleDriveService.downloadMedia( + id: process.id, + saveLocation: tempFileLocation, + onProgress: (received, total) async { + if (process.status.isTerminated) { + cancelToken.cancel(); + } + process = process.copyWith(total: total, chunk: received); + process = await _updateDownloadQueue(process); + }, + cancelToken: cancelToken, + ); + + final localMedia = await _localMediaService.saveInGallery( + saveFromLocation: tempFileLocation, + type: AppMediaType.fromLocation(location: tempFileLocation), + ); + + if (localMedia == null) { + process = process.copyWith(status: MediaQueueProcessStatus.failed); + process = await _updateDownloadQueue(process); + } + + //TODO: update local ref in media + + process = process.copyWith(status: MediaQueueProcessStatus.completed); + process = await _updateDownloadQueue(process); + } catch (error) { + if (error is RequestCancelledByUser) { + return; + } + process = process.copyWith(status: MediaQueueProcessStatus.failed); + process = await _updateDownloadQueue(process); + } finally { + if (tempFileLocation != null) { + await File(tempFileLocation).delete(); + } + } + } + + Future _downloadFromDropbox( + DownloadMediaProcess downloadProcess, + ) async { + DownloadMediaProcess process = downloadProcess; + String? tempFileLocation; + try { + process = process.copyWith(status: MediaQueueProcessStatus.downloading); + process = await _updateDownloadQueue(process); + + final tempDir = await getTemporaryDirectory(); + tempFileLocation = "${tempDir.path}/${process.id}.${process.extension}"; + + final cancelToken = CancelToken(); + + await _dropboxService.downloadMedia( + id: process.id, + saveLocation: tempFileLocation, + onProgress: (received, total) async { + if (process.status.isTerminated) { + cancelToken.cancel(); + } + process = process.copyWith(total: total, chunk: received); + process = await _updateDownloadQueue(process); + }, + cancelToken: cancelToken, + ); + + final localMedia = await _localMediaService.saveInGallery( + saveFromLocation: tempFileLocation, + type: AppMediaType.fromLocation(location: tempFileLocation), + ); + + if (localMedia == null) { + process = process.copyWith(status: MediaQueueProcessStatus.failed); + process = await _updateDownloadQueue(process); + } + + //TODO: update local ref in media + + process = process.copyWith(status: MediaQueueProcessStatus.completed); + process = await _updateDownloadQueue(process); + } catch (error) { + if (error is RequestCancelledByUser) { + return; + } + process = process.copyWith(status: MediaQueueProcessStatus.failed); + process = await _updateDownloadQueue(process); + } finally { + if (tempFileLocation != null) { + await File(tempFileLocation).delete(); + } + } + } +} diff --git a/data/lib/services/auth_service.dart b/data/lib/services/auth_service.dart index 85e5f05..ccda41c 100644 --- a/data/lib/services/auth_service.dart +++ b/data/lib/services/auth_service.dart @@ -2,7 +2,8 @@ import 'dart:async'; import '../apis/network/client.dart'; import '../apis/network/oauth2.dart'; import '../errors/app_error.dart'; -import '../models/dropbox_account/dropbox_account.dart'; +import '../models/dropbox/account/dropbox_account.dart'; +import '../models/dropbox/token/dropbox_token.dart'; import '../storage/app_preferences.dart'; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -12,7 +13,6 @@ import 'package:url_launcher/url_launcher.dart'; import '../apis/dropbox/dropbox_auth_endpoints.dart'; import '../apis/network/secrets.dart'; import '../apis/network/urls.dart'; -import '../models/token/token.dart'; import '../storage/provider/preferences_provider.dart'; final googleUserAccountProvider = StateProvider((ref) { diff --git a/data/lib/services/cloud_provider_service.dart b/data/lib/services/cloud_provider_service.dart index f0a7383..8b879d0 100644 --- a/data/lib/services/cloud_provider_service.dart +++ b/data/lib/services/cloud_provider_service.dart @@ -1,7 +1,26 @@ +import 'package:dio/dio.dart'; +import '../models/media/media.dart'; + abstract class CloudProviderService { const CloudProviderService(); Future createFolder(String folderName); Future getBackUpFolderId(); + + Future uploadMedia({ + required String folderId, + required String path, + String? mimeType, + String? description, + CancelToken? cancelToken, + void Function(int sent, int total)? onProgress, + }); + + Future downloadMedia({ + required String id, + required String saveLocation, + CancelToken? cancelToken, + void Function(int sent, int total)? onProgress, + }); } diff --git a/data/lib/services/dropbox_services.dart b/data/lib/services/dropbox_services.dart index ae12c0a..adec212 100644 --- a/data/lib/services/dropbox_services.dart +++ b/data/lib/services/dropbox_services.dart @@ -1,9 +1,11 @@ +import 'dart:io'; import 'package:collection/collection.dart'; import '../apis/dropbox/dropbox_content_endpoints.dart'; import '../apis/network/client.dart'; import '../domain/config.dart'; import '../errors/app_error.dart'; -import '../models/dropbox_account/dropbox_account.dart'; +import '../models/dropbox/account/dropbox_account.dart'; +import '../models/media/media.dart'; import '../storage/app_preferences.dart'; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -68,4 +70,54 @@ class DropboxService extends CloudProviderService { throw AppError.fromError(response.statusMessage ?? ''); } + + @override + Future uploadMedia({ + required String folderId, + required String path, + String? mimeType, + String? description, + CancelToken? cancelToken, + void Function(int sent, int total)? onProgress, + }) async { + final localFile = File(path); + try { + final res = await _dropboxAuthenticatedDio.req( + DropboxUploadEndpoint( + contentStream: localFile.openRead(), + filePath: + "/${FolderPath.backupFolderName}/${localFile.path.split('/').last}", + onProgress: onProgress, + cancellationToken: cancelToken, + ), + ); + if (res.statusCode == 200) { + return AppMedia.fromDropboxJson(res.data); + } + throw AppError.fromError(res.statusMessage ?? ''); + } catch (error) { + throw AppError.fromError(error); + } + } + + @override + Future downloadMedia({ + required String id, + required String saveLocation, + CancelToken? cancelToken, + void Function(int sent, int total)? onProgress, + }) async { + try { + await _dropboxAuthenticatedDio.downloadReq( + DropboxDownloadEndpoint( + filePath: "id:$id", + storagePath: saveLocation, + cancellationToken: cancelToken, + onProgress: onProgress, + ), + ); + } catch (e) { + throw AppError.fromError(e); + } + } } diff --git a/data/lib/services/google_drive_service.dart b/data/lib/services/google_drive_service.dart index 841a205..3d290f9 100644 --- a/data/lib/services/google_drive_service.dart +++ b/data/lib/services/google_drive_service.dart @@ -79,19 +79,62 @@ class GoogleDriveService extends CloudProviderService { } } - Future uploadInGoogleDrive({ - required String folderID, - required AppMedia media, + @override + Future getBackUpFolderId() async { + try { + final driveApi = await _getGoogleDriveAPI(); + + final response = await driveApi.files.list( + q: "name='${FolderPath.backupFolderName}' and trashed=false and mimeType='application/vnd.google-apps.folder'", + ); + + if (response.files?.isNotEmpty ?? false) { + return response.files?.first.id; + } else { + final folder = drive.File( + name: FolderPath.backupFolderName, + mimeType: 'application/vnd.google-apps.folder', + ); + final googleDriveFolder = await driveApi.files.create(folder); + return googleDriveFolder.id; + } + } catch (e) { + throw AppError.fromError(e); + } + } + + @override + Future createFolder(String folderName) async { + try { + final driveApi = await _getGoogleDriveAPI(); + + final folder = drive.File( + name: folderName, + mimeType: 'application/vnd.google-apps.folder', + ); + final googleDriveFolder = await driveApi.files.create(folder); + return googleDriveFolder.id; + } catch (e) { + throw AppError.fromError(e); + } + } + + @override + Future uploadMedia({ + required String folderId, + required String path, + String? description, + String? mimeType, CancelToken? cancelToken, - void Function(int chunk, int total)? onProgress, + void Function(int sent, int total)? onProgress, }) async { - final localFile = File(media.path); + final localFile = File(path); try { final file = drive.File( - name: media.name ?? localFile.path.split('/').last, - mimeType: media.mimeType, - description: media.id, - parents: [folderID], + name: localFile.path.split('/').last, + mimeType: mimeType, + description: description, + parents: [folderId], ); final res = await _client.req( @@ -116,11 +159,12 @@ class GoogleDriveService extends CloudProviderService { } } - Future downloadFromGoogleDrive({ + @override + Future downloadMedia({ required String id, required String saveLocation, - void Function(int chunk, int total)? onProgress, CancelToken? cancelToken, + void Function(int sent, int total)? onProgress, }) async { try { await _client.downloadReq( @@ -135,44 +179,4 @@ class GoogleDriveService extends CloudProviderService { throw AppError.fromError(e); } } - - @override - Future getBackUpFolderId() async { - try { - final driveApi = await _getGoogleDriveAPI(); - - final response = await driveApi.files.list( - q: "name='${FolderPath.backupFolderName}' and trashed=false and mimeType='application/vnd.google-apps.folder'", - ); - - if (response.files?.isNotEmpty ?? false) { - return response.files?.first.id; - } else { - final folder = drive.File( - name: FolderPath.backupFolderName, - mimeType: 'application/vnd.google-apps.folder', - ); - final googleDriveFolder = await driveApi.files.create(folder); - return googleDriveFolder.id; - } - } catch (e) { - throw AppError.fromError(e); - } - } - - @override - Future createFolder(String folderName) async { - try { - final driveApi = await _getGoogleDriveAPI(); - - final folder = drive.File( - name: folderName, - mimeType: 'application/vnd.google-apps.folder', - ); - final googleDriveFolder = await driveApi.files.create(folder); - return googleDriveFolder.id; - } catch (e) { - throw AppError.fromError(e); - } - } } diff --git a/data/lib/storage/app_preferences.dart b/data/lib/storage/app_preferences.dart index c468e7b..da37642 100644 --- a/data/lib/storage/app_preferences.dart +++ b/data/lib/storage/app_preferences.dart @@ -1,5 +1,5 @@ -import '../models/dropbox_account/dropbox_account.dart'; -import '../models/token/token.dart'; +import '../models/dropbox/account/dropbox_account.dart'; +import '../models/dropbox/token/dropbox_token.dart'; import 'provider/preferences_provider.dart'; class AppPreferences { diff --git a/data/pubspec.yaml b/data/pubspec.yaml index a47b004..5c52092 100644 --- a/data/pubspec.yaml +++ b/data/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: # storage shared_preferences: ^2.3.3 + sqflite: ^2.4.1 # code generation freezed: ^2.5.7