docs: added new feature graphics
fix: updated folder handling on ios (#155)
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 359 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 359 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 118 KiB |
@@ -1,46 +1,4 @@
|
||||
PODS:
|
||||
- AEXML (4.6.0)
|
||||
- DKImagePickerController/Core (4.3.9):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
- DKImagePickerController/ImageDataManager (4.3.9)
|
||||
- DKImagePickerController/PhotoGallery (4.3.9):
|
||||
- DKImagePickerController/Core
|
||||
- DKPhotoGallery
|
||||
- DKImagePickerController/Resource (4.3.9)
|
||||
- DKPhotoGallery (0.0.19):
|
||||
- DKPhotoGallery/Core (= 0.0.19)
|
||||
- DKPhotoGallery/Model (= 0.0.19)
|
||||
- DKPhotoGallery/Preview (= 0.0.19)
|
||||
- DKPhotoGallery/Resource (= 0.0.19)
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Core (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Preview
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Model (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Preview (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Resource
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Resource (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- EpubViewerKit (0.1.3):
|
||||
- AEXML (= 4.6.0)
|
||||
- FontBlaster (= 5.1.1)
|
||||
- MenuItemKit (= 4.0.1)
|
||||
- RealmSwift (= 5.5.1)
|
||||
- SSZipArchive (= 2.2.3)
|
||||
- ZFDragableModalTransition (= 0.6)
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_inappwebview_ios (0.0.1):
|
||||
- Flutter
|
||||
@@ -51,123 +9,42 @@ PODS:
|
||||
- OrderedSet (~> 6.0.3)
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- FontBlaster (5.1.1)
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- MenuItemKit (4.0.1)
|
||||
- open_file_ios (0.0.1):
|
||||
- isar_community_flutter_libs (1.0.0):
|
||||
- Flutter
|
||||
- OrderedSet (6.0.3)
|
||||
- package_info_plus (0.4.5):
|
||||
- screen_brightness_ios (0.1.0):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Realm (5.5.1):
|
||||
- Realm/Headers (= 5.5.1)
|
||||
- Realm/Headers (5.5.1)
|
||||
- RealmSwift (5.5.1):
|
||||
- Realm (= 5.5.1)
|
||||
- SDWebImage (5.21.5):
|
||||
- SDWebImage/Core (= 5.21.5)
|
||||
- SDWebImage/Core (5.21.5)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- SSZipArchive (2.2.3)
|
||||
- SwiftyGif (5.4.5)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- vocsy_epub_viewer (2.0.0):
|
||||
- EpubViewerKit (~> 0.1.3)
|
||||
- Flutter
|
||||
- ZFDragableModalTransition (0.6)
|
||||
|
||||
DEPENDENCIES:
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- open_file_ios (from `.symlinks/plugins/open_file_ios/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- vocsy_epub_viewer (from `.symlinks/plugins/vocsy_epub_viewer/ios`)
|
||||
- isar_community_flutter_libs (from `.symlinks/plugins/isar_community_flutter_libs/ios`)
|
||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- AEXML
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- EpubViewerKit
|
||||
- FontBlaster
|
||||
- MenuItemKit
|
||||
- OrderedSet
|
||||
- Realm
|
||||
- RealmSwift
|
||||
- SDWebImage
|
||||
- SSZipArchive
|
||||
- SwiftyGif
|
||||
- ZFDragableModalTransition
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_inappwebview_ios:
|
||||
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
||||
fluttertoast:
|
||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
open_file_ios:
|
||||
:path: ".symlinks/plugins/open_file_ios/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
sqflite_darwin:
|
||||
:path: ".symlinks/plugins/sqflite_darwin/darwin"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
vocsy_epub_viewer:
|
||||
:path: ".symlinks/plugins/vocsy_epub_viewer/ios"
|
||||
isar_community_flutter_libs:
|
||||
:path: ".symlinks/plugins/isar_community_flutter_libs/ios"
|
||||
screen_brightness_ios:
|
||||
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AEXML: 2fbb73d8724797ed2cde888731e176fa4d44dd83
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
EpubViewerKit: 5f6c8ff9161bba18d6742c59b3c575c19f0623a9
|
||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
FontBlaster: 8897ebdbb4b6ace3ab78c92b665d88c954f19e4a
|
||||
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||
MenuItemKit: 1459920e984d823e1bcc858b7a964f94e6bfa0f6
|
||||
open_file_ios: 5ff7526df64e4394b4fe207636b67a95e83078bb
|
||||
isar_community_flutter_libs: bede843185a61a05ff364a05c9b23209523f7e0d
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
||||
Realm: c2ffe0667f9c98c9ba65f608ba85684d39826145
|
||||
RealmSwift: 5a35c1c35c3e1925e6fcd092c881d5cc4a826bff
|
||||
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||
vocsy_epub_viewer: f6b70ce4776ac1871caee8e6bc7a108d847cad79
|
||||
ZFDragableModalTransition: 0d294eaaba6edfcb9839595de765f9ca06a4b524
|
||||
screen_brightness_ios: 28c5fbdb40634de44f86025d84470158ad4df48c
|
||||
|
||||
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
FCBD8B4FECDED30E88FAD71B /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F1D761D07B3CD346428E2F /* Pods_RunnerTests.framework */; };
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -65,6 +66,7 @@
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -80,6 +82,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
|
||||
955B95705D44B98F16C785A6 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -98,6 +101,7 @@
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
@@ -188,6 +192,9 @@
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||
packageProductDependencies = (
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
|
||||
);
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
@@ -213,6 +220,9 @@
|
||||
|
||||
/* Begin PBXProject section */
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
packageReferences = (
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
|
||||
);
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
@@ -724,6 +734,18 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCLocalSwiftPackageReference section */
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "dkcamera",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zhangao0086/DKCamera",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "5c691d11014b910aff69f960475d70e65d9dcc96"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "dkimagepickercontroller",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zhangao0086/DKImagePickerController",
|
||||
"state" : {
|
||||
"branch" : "4.3.9",
|
||||
"revision" : "0bdfeacefa308545adde07bef86e349186335915"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "dkphotogallery",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zhangao0086/DKPhotoGallery",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "311c1bc7a94f1538f82773a79c84374b12a2ef3d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "sdwebimage",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SDWebImage/SDWebImage",
|
||||
"state" : {
|
||||
"revision" : "2de3a496eaf6df9a1312862adcfd54acd73c39c0",
|
||||
"version" : "5.21.7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftygif",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kirualex/SwiftyGif.git",
|
||||
"state" : {
|
||||
"revision" : "4430cbc148baa3907651d40562d96325426f409a",
|
||||
"version" : "5.4.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "tocropviewcontroller",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/TimOliver/TOCropViewController",
|
||||
"state" : {
|
||||
"revision" : "d4a6d8100f4b886fdbc8ae399bf144ff3e9afb7e",
|
||||
"version" : "2.8.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
@@ -5,6 +5,24 @@
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Prepare Flutter Framework Script"
|
||||
scriptText = "/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "dkcamera",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zhangao0086/DKCamera",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "5c691d11014b910aff69f960475d70e65d9dcc96"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "dkimagepickercontroller",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zhangao0086/DKImagePickerController",
|
||||
"state" : {
|
||||
"branch" : "4.3.9",
|
||||
"revision" : "0bdfeacefa308545adde07bef86e349186335915"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "dkphotogallery",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zhangao0086/DKPhotoGallery",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "311c1bc7a94f1538f82773a79c84374b12a2ef3d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "sdwebimage",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SDWebImage/SDWebImage",
|
||||
"state" : {
|
||||
"revision" : "2de3a496eaf6df9a1312862adcfd54acd73c39c0",
|
||||
"version" : "5.21.7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftygif",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kirualex/SwiftyGif.git",
|
||||
"state" : {
|
||||
"revision" : "4430cbc148baa3907651d40562d96325426f409a",
|
||||
"version" : "5.4.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "tocropviewcontroller",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/TimOliver/TOCropViewController",
|
||||
"state" : {
|
||||
"revision" : "d4a6d8100f4b886fdbc8ae399bf144ff3e9afb7e",
|
||||
"version" : "2.8.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
@@ -282,15 +282,26 @@ class BookDetailsBloc extends Bloc<BookDetailsEvent, BookDetailsState> {
|
||||
|
||||
final schema = event.schema;
|
||||
|
||||
final filePath = await repository.downloadBook(
|
||||
state.bookDetails!,
|
||||
event.directory,
|
||||
schema,
|
||||
format: event.format,
|
||||
progressCallback: (progress) {
|
||||
emit(state.copyWith(downloadProgress: progress));
|
||||
},
|
||||
);
|
||||
final String filePath;
|
||||
if (event.directory == null) {
|
||||
filePath = await repository.downloadBookToDevice(
|
||||
state.bookDetails!,
|
||||
format: event.format,
|
||||
progressCallback: (progress) {
|
||||
emit(state.copyWith(downloadProgress: progress));
|
||||
},
|
||||
);
|
||||
} else {
|
||||
filePath = await repository.downloadBook(
|
||||
state.bookDetails!,
|
||||
event.directory!,
|
||||
schema,
|
||||
format: event.format,
|
||||
progressCallback: (progress) {
|
||||
emit(state.copyWith(downloadProgress: progress));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (state.bookViewModel != null) {
|
||||
await downloadManager.registerDownload(
|
||||
|
||||
@@ -67,7 +67,7 @@ class DownloadBook extends BookDetailsEvent {
|
||||
final String author;
|
||||
final String series;
|
||||
final int seriesIndex;
|
||||
final DocumentFile directory;
|
||||
final DocumentFile? directory;
|
||||
final DownloadSchema schema;
|
||||
|
||||
const DownloadBook({
|
||||
@@ -77,7 +77,7 @@ class DownloadBook extends BookDetailsEvent {
|
||||
required this.author,
|
||||
required this.series,
|
||||
required this.seriesIndex,
|
||||
required this.directory,
|
||||
this.directory,
|
||||
required this.schema,
|
||||
});
|
||||
|
||||
@@ -112,13 +112,10 @@ class SendBookByEmail extends BookDetailsEvent {
|
||||
}
|
||||
|
||||
class OpenBookInReader extends BookDetailsEvent {
|
||||
final DocumentFile selectedDirectory;
|
||||
final DocumentFile? selectedDirectory;
|
||||
final DownloadSchema schema;
|
||||
|
||||
const OpenBookInReader({
|
||||
required this.selectedDirectory,
|
||||
required this.schema,
|
||||
});
|
||||
const OpenBookInReader({this.selectedDirectory, required this.schema});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [selectedDirectory, schema];
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:http/http.dart' as http;
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@@ -573,7 +574,7 @@ class BookDetailsRemoteDatasource {
|
||||
|
||||
Future<bool> openInReader(
|
||||
BookDetailsModel book,
|
||||
DocumentFile selectedDirectory,
|
||||
DocumentFile? selectedDirectory,
|
||||
DownloadSchema schema, {
|
||||
Function(int)? progressCallback,
|
||||
}) async {
|
||||
@@ -585,29 +586,39 @@ class BookDetailsRemoteDatasource {
|
||||
format = book.formats.first.toLowerCase();
|
||||
}
|
||||
|
||||
final filePath = await downloadBookToPath(
|
||||
book: book,
|
||||
selectedDirectory: selectedDirectory,
|
||||
schema: schema,
|
||||
format: format,
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
String localPath;
|
||||
if (selectedDirectory == null) {
|
||||
localPath = await downloadBookToDevice(
|
||||
book,
|
||||
format: format,
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
} else {
|
||||
final filePath = await downloadBookToPath(
|
||||
book: book,
|
||||
selectedDirectory: selectedDirectory,
|
||||
schema: schema,
|
||||
format: format,
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
|
||||
DocumentFile? file =
|
||||
filePath.isNotEmpty ? await DocumentFile.fromUri(filePath) : null;
|
||||
final file =
|
||||
filePath.isNotEmpty ? await DocumentFile.fromUri(filePath) : null;
|
||||
|
||||
if (file == null || !file.isFile) {
|
||||
logger.e('Downloaded file is not a valid file: $filePath');
|
||||
return false;
|
||||
if (file == null || !file.isFile) {
|
||||
logger.e('Downloaded file is not a valid file: $filePath');
|
||||
return false;
|
||||
}
|
||||
|
||||
final cachedFile = await file.cache();
|
||||
if (cachedFile == null) {
|
||||
logger.e('Could not cache file for opening');
|
||||
return false;
|
||||
}
|
||||
localPath = cachedFile.path;
|
||||
}
|
||||
|
||||
final cachedFile = await file.cache();
|
||||
if (cachedFile == null) {
|
||||
logger.e('Could not cache file for opening');
|
||||
return false;
|
||||
}
|
||||
|
||||
final result = await OpenFile.open(cachedFile.path);
|
||||
final result = await OpenFile.open(localPath);
|
||||
|
||||
if (result.type != ResultType.done) {
|
||||
logger.e('Error while opening the file: ${result.message}');
|
||||
@@ -729,6 +740,29 @@ class BookDetailsRemoteDatasource {
|
||||
return Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
Future<String> downloadBookToDevice(
|
||||
BookDetailsModel book, {
|
||||
String format = 'epub',
|
||||
Function(int)? progressCallback,
|
||||
}) async {
|
||||
logger.i('Downloading "${book.title}" to app sandbox, format: $format');
|
||||
final bytes = await streamBookBytes(
|
||||
book,
|
||||
format: format,
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final safeTitle =
|
||||
book.title.replaceAll(RegExp(r'[\\/:*?"<>|.]'), '').trim();
|
||||
final fileName = '${safeTitle.isEmpty ? 'book' : safeTitle}.$format';
|
||||
final file = File('${dir.path}/$fileName');
|
||||
await file.writeAsBytes(bytes, flush: true);
|
||||
|
||||
logger.i('Saved book to ${file.path}');
|
||||
return file.path;
|
||||
}
|
||||
|
||||
Future<Uint8List?> readLocalEpubBytes(String path) async {
|
||||
try {
|
||||
String name;
|
||||
|
||||
@@ -52,7 +52,7 @@ class BookDetailsRepository {
|
||||
|
||||
Future<bool> openInReader(
|
||||
BookDetailsModel book,
|
||||
DocumentFile selectedDirectory,
|
||||
DocumentFile? selectedDirectory,
|
||||
DownloadSchema schema, {
|
||||
Function(int)? progressCallback,
|
||||
}) async {
|
||||
@@ -190,6 +190,16 @@ class BookDetailsRepository {
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
|
||||
Future<String> downloadBookToDevice(
|
||||
BookDetailsModel book, {
|
||||
String format = 'epub',
|
||||
Function(int)? progressCallback,
|
||||
}) => datasource.downloadBookToDevice(
|
||||
book,
|
||||
format: format,
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
|
||||
Future<Uint8List?> readLocalEpubBytes(String path) =>
|
||||
datasource.readLocalEpubBytes(path);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
|
||||
@@ -1108,33 +1109,35 @@ class _BookDetailsPageState extends State<BookDetailsPage> {
|
||||
: () async {
|
||||
DocumentFile? selectedDirectory;
|
||||
|
||||
if (settingsState.defaultDownloadPath.isEmpty) {
|
||||
selectedDirectory = await DocMan.pick.directory();
|
||||
if (selectedDirectory == null) {
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context);
|
||||
if (Platform.isAndroid) {
|
||||
if (settingsState.defaultDownloadPath.isEmpty) {
|
||||
selectedDirectory = await DocMan.pick.directory();
|
||||
if (selectedDirectory == null) {
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context);
|
||||
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
final uri = settingsState.defaultDownloadPath;
|
||||
selectedDirectory =
|
||||
uri.isNotEmpty
|
||||
? await DocumentFile.fromUri(uri)
|
||||
: null;
|
||||
if (selectedDirectory == null ||
|
||||
!selectedDirectory.isDirectory) {
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
return;
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
final uri = settingsState.defaultDownloadPath;
|
||||
selectedDirectory =
|
||||
uri.isNotEmpty
|
||||
? await DocumentFile.fromUri(uri)
|
||||
: null;
|
||||
if (selectedDirectory == null ||
|
||||
!selectedDirectory.isDirectory) {
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:docman/docman.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
|
||||
import 'package:calibre_web_companion/features/book_details/bloc/book_details_bloc.dart';
|
||||
import 'package:calibre_web_companion/features/book_details/bloc/book_details_event.dart';
|
||||
@@ -126,40 +129,61 @@ class DownloadToDeviceWidget extends StatelessWidget {
|
||||
) async {
|
||||
final settingsState = context.read<SettingsBloc>().state;
|
||||
DocumentFile? selectedDirectory;
|
||||
if (settingsState.defaultDownloadPath.isEmpty) {
|
||||
_showDownloadStatusSheet(
|
||||
// ignore: use_build_context_synchronously
|
||||
context,
|
||||
localizations,
|
||||
DownloadState.selectingDestination,
|
||||
null,
|
||||
0,
|
||||
() {
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
if (settingsState.defaultDownloadPath.isEmpty) {
|
||||
_showDownloadStatusSheet(
|
||||
// ignore: use_build_context_synchronously
|
||||
context,
|
||||
localizations,
|
||||
DownloadState.selectingDestination,
|
||||
null,
|
||||
0,
|
||||
() {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
|
||||
selectedDirectory = await DocMan.pick.directory();
|
||||
if (selectedDirectory == null) {
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
|
||||
selectedDirectory = await DocMan.pick.directory();
|
||||
if (selectedDirectory == null) {
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context);
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
final uri = settingsState.defaultDownloadPath;
|
||||
selectedDirectory =
|
||||
uri.isNotEmpty ? await DocumentFile.fromUri(uri) : null;
|
||||
if (selectedDirectory == null || !selectedDirectory.isDirectory) {
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(localizations.noFolderWasSelected, isError: true);
|
||||
return;
|
||||
_showDownloadStatusSheet(
|
||||
// ignore: use_build_context_synchronously
|
||||
context,
|
||||
localizations,
|
||||
DownloadState.downloading,
|
||||
null,
|
||||
0,
|
||||
() {
|
||||
context.read<BookDetailsBloc>().add(CancelDownload());
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
final uri = settingsState.defaultDownloadPath;
|
||||
selectedDirectory =
|
||||
uri.isNotEmpty ? await DocumentFile.fromUri(uri) : null;
|
||||
if (selectedDirectory == null || !selectedDirectory.isDirectory) {
|
||||
// ignore: use_build_context_synchronously
|
||||
context.showSnackBar(localizations.noFolderWasSelected, isError: true);
|
||||
return;
|
||||
}
|
||||
|
||||
_showDownloadStatusSheet(
|
||||
// ignore: use_build_context_synchronously
|
||||
context,
|
||||
localizations,
|
||||
DownloadState.downloading,
|
||||
@@ -214,8 +238,11 @@ class DownloadToDeviceWidget extends StatelessWidget {
|
||||
current.downloadErrorMessage;
|
||||
},
|
||||
listener: (context, state) {
|
||||
if (state.downloadState == DownloadState.success ||
|
||||
state.downloadState == DownloadState.failed) {}
|
||||
if (state.downloadState == DownloadState.success &&
|
||||
!Platform.isAndroid &&
|
||||
state.downloadFilePath != null) {
|
||||
OpenFile.open(state.downloadFilePath!);
|
||||
}
|
||||
},
|
||||
buildWhen: (previous, current) {
|
||||
return previous.downloadState != current.downloadState ||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:docman/docman.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -84,42 +86,44 @@ class SendToEreaderWidget extends StatelessWidget {
|
||||
AppLocalizations localizations,
|
||||
SettingsState settingsState,
|
||||
) async {
|
||||
if (settingsState.storeReadNowAndSendToEReaderOnDevice &&
|
||||
settingsState.defaultDownloadPath.isEmpty) {
|
||||
if (context.mounted) {
|
||||
context.showSnackBar(
|
||||
localizations.pleaseSetDefaultDownloadFolderFirst,
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DocumentFile? selectedDirectory;
|
||||
|
||||
if (settingsState.defaultDownloadPath.isEmpty) {
|
||||
selectedDirectory = await DocMan.pick.directory();
|
||||
if (selectedDirectory == null) {
|
||||
if (Platform.isAndroid) {
|
||||
if (settingsState.storeReadNowAndSendToEReaderOnDevice &&
|
||||
settingsState.defaultDownloadPath.isEmpty) {
|
||||
if (context.mounted) {
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
localizations.pleaseSetDefaultDownloadFolderFirst,
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
final uri = settingsState.defaultDownloadPath;
|
||||
selectedDirectory =
|
||||
uri.isNotEmpty ? await DocumentFile.fromUri(uri) : null;
|
||||
if (selectedDirectory == null || !selectedDirectory.isDirectory) {
|
||||
if (context.mounted) {
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
|
||||
if (settingsState.defaultDownloadPath.isEmpty) {
|
||||
selectedDirectory = await DocMan.pick.directory();
|
||||
if (selectedDirectory == null) {
|
||||
if (context.mounted) {
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
final uri = settingsState.defaultDownloadPath;
|
||||
selectedDirectory =
|
||||
uri.isNotEmpty ? await DocumentFile.fromUri(uri) : null;
|
||||
if (selectedDirectory == null || !selectedDirectory.isDirectory) {
|
||||
if (context.mounted) {
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,7 +476,13 @@ class SendToEreaderWidget extends StatelessWidget {
|
||||
final settingsState = context.read<SettingsBloc>().state;
|
||||
DocumentFile? selectedDirectory;
|
||||
|
||||
if (settingsState.storeReadNowAndSendToEReaderOnDevice) {
|
||||
// "Store on device first" needs a SAF folder, which iOS doesn't have — there
|
||||
// the bytes are streamed straight to the upload instead.
|
||||
final storeOnDevice =
|
||||
Platform.isAndroid &&
|
||||
settingsState.storeReadNowAndSendToEReaderOnDevice;
|
||||
|
||||
if (storeOnDevice) {
|
||||
final uri = settingsState.defaultDownloadPath;
|
||||
if (uri.isEmpty) {
|
||||
context.showSnackBar(
|
||||
@@ -507,8 +517,7 @@ class SendToEreaderWidget extends StatelessWidget {
|
||||
isKindle: isKindle,
|
||||
title: book.title,
|
||||
send2ereaderUrl: context.read<SettingsBloc>().state.send2ereaderUrl,
|
||||
downloadToDeviceFirst:
|
||||
settingsState.storeReadNowAndSendToEReaderOnDevice,
|
||||
downloadToDeviceFirst: storeOnDevice,
|
||||
selectedDirectory: selectedDirectory,
|
||||
schema: settingsState.downloadSchema,
|
||||
),
|
||||
|
||||
@@ -846,81 +846,85 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.download_for_offline_rounded,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
localizations.storeReadNowAndSendToEReaderOnDevice,
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
localizations
|
||||
.storeReadNowAndSendToEReaderOnDeviceDescription,
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
if (Platform.isAndroid) ...[
|
||||
const SizedBox(height: 16),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.download_for_offline_rounded,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
value: state.storeReadNowAndSendToEReaderOnDevice,
|
||||
onChanged: (value) async {
|
||||
if (!value) {
|
||||
context.read<SettingsBloc>().add(
|
||||
const SetStoreReadNowAndSendToEReaderOnDevice(false),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.defaultDownloadPath.isEmpty) {
|
||||
String? selectedPath;
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
final selectedDirectory =
|
||||
await DocMan.pick.directory();
|
||||
selectedPath = selectedDirectory?.uri;
|
||||
} else {
|
||||
selectedPath = await FilePicker.getDirectoryPath();
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
localizations.storeReadNowAndSendToEReaderOnDevice,
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
localizations
|
||||
.storeReadNowAndSendToEReaderOnDeviceDescription,
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
value: state.storeReadNowAndSendToEReaderOnDevice,
|
||||
onChanged: (value) async {
|
||||
if (!value) {
|
||||
context.read<SettingsBloc>().add(
|
||||
const SetStoreReadNowAndSendToEReaderOnDevice(
|
||||
false,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedPath == null) {
|
||||
if (state.defaultDownloadPath.isEmpty) {
|
||||
String? selectedPath;
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
final selectedDirectory =
|
||||
await DocMan.pick.directory();
|
||||
selectedPath = selectedDirectory?.uri;
|
||||
} else {
|
||||
selectedPath = await FilePicker.getDirectoryPath();
|
||||
}
|
||||
|
||||
if (selectedPath == null) {
|
||||
if (context.mounted) {
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
context.showSnackBar(
|
||||
localizations.noFolderWasSelected,
|
||||
isError: true,
|
||||
context.read<SettingsBloc>().add(
|
||||
SetDownloadFolder(selectedPath),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
context.read<SettingsBloc>().add(
|
||||
SetDownloadFolder(selectedPath),
|
||||
const SetStoreReadNowAndSendToEReaderOnDevice(true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
context.read<SettingsBloc>().add(
|
||||
const SetStoreReadNowAndSendToEReaderOnDevice(true),
|
||||
);
|
||||
}
|
||||
},
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
},
|
||||
activeThumbColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@@ -21,8 +21,10 @@ class DownloadOptionsWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildSelectingDownloadFolder(context),
|
||||
_buildSelectingDownloadSchema(context),
|
||||
if (Platform.isAndroid) ...[
|
||||
_buildSelectingDownloadFolder(context),
|
||||
_buildSelectingDownloadSchema(context),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||