mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-03-11 17:25:32 +00:00
add chapter download module
This commit is contained in:
parent
054fa0642c
commit
35b01b51c3
23 changed files with 1021 additions and 100 deletions
|
|
@ -47,7 +47,7 @@ android {
|
|||
applicationId "com.kodjodevf.mangayomi"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion 21
|
||||
minSdkVersion 24
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.kodjodevf.mangayomi">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||
<application
|
||||
android:label="mangayomi"
|
||||
android:name="${applicationName}"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
ext.kotlin_version = '1.8.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:mangayomi/models/manga_history.dart';
|
|||
import 'package:mangayomi/models/model_manga.dart';
|
||||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/source/source_model.dart';
|
||||
import 'package:mangayomi/views/manga/download/download_model.dart';
|
||||
import 'package:mangayomi/views/manga/reader/providers/reader_controller_provider.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/providers/blend_level_state_provider.dart';
|
||||
import 'views/more/settings/appearance/providers/flex_scheme_color_state_provider.dart';
|
||||
|
|
@ -35,10 +36,12 @@ void main() async {
|
|||
Hive.registerAdapter(SourceModelAdapter());
|
||||
Hive.registerAdapter(ReaderModeAdapter());
|
||||
Hive.registerAdapter(TypeSourceAdapter());
|
||||
Hive.registerAdapter(DownloadModelAdapter());
|
||||
await Hive.openBox<ModelManga>(HiveConstant.hiveBoxManga);
|
||||
await Hive.openBox<MangaHistoryModel>(HiveConstant.hiveBoxMangaHistory);
|
||||
await Hive.openBox<ReaderMode>(HiveConstant.hiveBoxReaderMode);
|
||||
await Hive.openBox<SourceModel>(HiveConstant.hiveBoxMangaSource);
|
||||
await Hive.openBox<DownloadModel>(HiveConstant.hiveBoxDownloads);
|
||||
await Hive.openBox(HiveConstant.hiveBoxMangaInfo);
|
||||
await Hive.openBox(HiveConstant.hiveBoxMangaFilter);
|
||||
await Hive.openBox(HiveConstant.hiveBoxAppSettings);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:mangayomi/utils/constant.dart';
|
|||
import 'package:mangayomi/models/manga_history.dart';
|
||||
import 'package:mangayomi/models/model_manga.dart';
|
||||
import 'package:mangayomi/source/source_model.dart';
|
||||
import 'package:mangayomi/views/manga/download/download_model.dart';
|
||||
import 'package:mangayomi/views/manga/reader/providers/reader_controller_provider.dart';
|
||||
|
||||
final hiveBoxManga = Provider<Box<ModelManga>>((ref) {
|
||||
|
|
@ -27,6 +28,10 @@ final hiveBoxMangaFilterProvider = Provider<Box>((ref) {
|
|||
final hiveBoxMangaSourceProvider = Provider<Box<SourceModel>>((ref) {
|
||||
return Hive.box<SourceModel>(HiveConstant.hiveBoxMangaSource);
|
||||
});
|
||||
final hiveBoxMangaDownloads = Provider<Box<DownloadModel>>((ref) {
|
||||
return Hive.box<DownloadModel>(HiveConstant.hiveBoxDownloads);
|
||||
});
|
||||
|
||||
final hiveBoxSettings = Provider<Box>((ref) {
|
||||
return Hive.box(HiveConstant.hiveBoxAppSettings);
|
||||
});
|
||||
|
|
|
|||
49
lib/providers/storage_provider.dart
Normal file
49
lib/providers/storage_provider.dart
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// ignore_for_file: depend_on_referenced_packages
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:mangayomi/models/model_manga.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class StorageProvider {
|
||||
Future<bool> requestPermission() async {
|
||||
Permission permission = Permission.manageExternalStorage;
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
if (await permission.isGranted) {
|
||||
return true;
|
||||
} else {
|
||||
final result = await permission.request();
|
||||
if (result == PermissionStatus.granted) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<Directory?> getDirectory() async {
|
||||
Directory? directory;
|
||||
if (Platform.isAndroid) {
|
||||
directory = Directory("/storage/emulated/0/Mangayomi/");
|
||||
} else {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
directory = Directory("${dir.path}/Mangayomi");
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
|
||||
Future<Directory?> getMangaChapterDirectory(
|
||||
ModelManga modelManga, index) async {
|
||||
final dir = await getDirectory();
|
||||
return Directory(
|
||||
"${dir!.path}/downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${modelManga.chapterTitle![index].replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
|
||||
}
|
||||
|
||||
Future<Directory?> getMangaMainDirectory(ModelManga modelManga, index) async {
|
||||
final dir = await getDirectory();
|
||||
return Directory(
|
||||
"${dir!.path}/downloads/${modelManga.source} (${modelManga.lang!.toUpperCase()})/${modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
|
||||
}
|
||||
}
|
||||
|
|
@ -8,11 +8,11 @@ import 'package:html/dom.dart' as dom;
|
|||
import 'package:mangayomi/models/comick/chapter_page_comick.dart';
|
||||
import 'package:mangayomi/models/model_manga.dart';
|
||||
import 'package:mangayomi/providers/hive_provider.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/services/get_popular_manga.dart';
|
||||
import 'package:mangayomi/services/http_res_to_dom_html.dart';
|
||||
import 'package:mangayomi/source/source_model.dart';
|
||||
import 'package:mangayomi/views/more/settings/providers/incognito_mode_state_provider.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
part 'get_manga_chapter_url.g.dart';
|
||||
|
|
@ -36,15 +36,7 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
|
|||
"${modelManga.source}/${modelManga.name}/${modelManga.chapterTitle![index]}-pageurl",
|
||||
defaultValue: []);
|
||||
final incognitoMode = ref.watch(incognitoModeStateProvider);
|
||||
Directory? pathh;
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
pathh = await getExternalStorageDirectory();
|
||||
} else {
|
||||
pathh = await getApplicationDocumentsDirectory();
|
||||
}
|
||||
|
||||
path = Directory(
|
||||
"${pathh!.path}/${modelManga.source}/${modelManga.name}/${modelManga.chapterTitle![index]}/");
|
||||
path = await StorageProvider().getMangaChapterDirectory(modelManga, index);
|
||||
|
||||
if (hiveUrl.isNotEmpty) {
|
||||
urll = hiveUrl;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'search_manga.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$searchMangaHash() => r'e84374580686773aa67deb76ab91c00e2e6fab8b';
|
||||
String _$searchMangaHash() => r'6cb4c0eaa232a0c2b54a2c8f4841d3acfffacd40';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ class HiveConstant {
|
|||
static String get hiveBoxMangaSource => "_manga_box_source_";
|
||||
static String get hiveBoxMangaFilter => "_manga_box_filter_";
|
||||
static String get hiveBoxAppSettings => "_app_box_settings_";
|
||||
static String get hiveBoxDownloads => "_manga_box_downloads_";
|
||||
static String get hiveBoxReaderSettings => "_reader_box_settings_";
|
||||
static String get hiveBoxReaderMode =>
|
||||
"_readerMode_box_settings_";
|
||||
static String get hiveBoxReaderMode => "_readerMode_box_settings_";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Map<String, String>? headers(String source) {
|
||||
Map<String, String> headers(String source) {
|
||||
return source == 'mangakawaii'
|
||||
? {
|
||||
'Referer': 'https://www.mangakawaii.io/',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/source/source_model.dart';
|
||||
import 'package:mangayomi/views/browse/extension/extension_screen.dart';
|
||||
import 'package:mangayomi/views/browse/migrate_screen.dart';
|
||||
|
|
@ -22,16 +23,22 @@ class _BrowseScreenState extends State<BrowseScreen>
|
|||
_tabBarController = TabController(length: 3, vsync: this);
|
||||
_tabBarController.animateTo(0);
|
||||
_tabBarController.addListener(() {
|
||||
_chekPermission();
|
||||
setState(() {
|
||||
_textEditingController.clear();
|
||||
_entriesFilter = [];
|
||||
_isSearch = false;
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
List<SourceModel> entries = [];
|
||||
List<SourceModel> entriesFilter = [];
|
||||
_chekPermission() async {
|
||||
await StorageProvider().requestPermission();
|
||||
}
|
||||
|
||||
List<SourceModel> _entries = [];
|
||||
List<SourceModel> _entriesFilter = [];
|
||||
final _textEditingController = TextEditingController();
|
||||
bool _isSearch = false;
|
||||
@override
|
||||
|
|
@ -52,7 +59,7 @@ class _BrowseScreenState extends State<BrowseScreen>
|
|||
? SeachFormTextField(
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
entriesFilter = entries
|
||||
_entriesFilter = _entries
|
||||
.where((element) => element.sourceName
|
||||
.toLowerCase()
|
||||
.contains(value.toLowerCase()))
|
||||
|
|
@ -67,7 +74,7 @@ class _BrowseScreenState extends State<BrowseScreen>
|
|||
_isSearch = false;
|
||||
});
|
||||
_textEditingController.clear();
|
||||
entriesFilter = entries;
|
||||
_entriesFilter = _entries;
|
||||
},
|
||||
controller: _textEditingController,
|
||||
)
|
||||
|
|
@ -122,9 +129,9 @@ class _BrowseScreenState extends State<BrowseScreen>
|
|||
const SourcesScreen(),
|
||||
ExtensionScreen(
|
||||
entriesData: (val) {
|
||||
entries = val as List<SourceModel>;
|
||||
_entries = val as List<SourceModel>;
|
||||
},
|
||||
entriesFilter: entriesFilter,
|
||||
entriesFilter: _entriesFilter,
|
||||
),
|
||||
const MigrateScreen()
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import 'package:draggable_scrollbar/draggable_scrollbar.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:mangayomi/models/manga_reader.dart';
|
||||
import 'package:mangayomi/models/model_manga.dart';
|
||||
import 'package:mangayomi/utils/cached_network.dart';
|
||||
import 'package:mangayomi/utils/media_query.dart';
|
||||
import 'package:mangayomi/views/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/views/manga/detail/readmore.dart';
|
||||
import 'package:mangayomi/views/manga/download/download_page_widget.dart';
|
||||
|
||||
class MangaDetailView extends ConsumerStatefulWidget {
|
||||
final Function(bool) isExtended;
|
||||
|
|
@ -169,13 +169,12 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
modelManga: widget.modelManga!,
|
||||
index: reverse ? reverseIndex : finalIndex);
|
||||
},
|
||||
trailing: const Icon(
|
||||
FontAwesomeIcons.circleDown,
|
||||
size: 20,
|
||||
),
|
||||
trailing: ref.watch(ChapterPageDownloadsProvider(
|
||||
index: reverse ? reverseIndex : finalIndex,
|
||||
modelManga: widget.modelManga!)),
|
||||
subtitle: Text(
|
||||
chapterDate[finalIndex],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
title: Text(
|
||||
chapterTitle[finalIndex],
|
||||
|
|
|
|||
32
lib/views/manga/download/download_model.dart
Normal file
32
lib/views/manga/download/download_model.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:mangayomi/models/model_manga.dart';
|
||||
part 'download_model.g.dart';
|
||||
|
||||
@HiveType(typeId: 6)
|
||||
class DownloadModel {
|
||||
@HiveField(0)
|
||||
final ModelManga modelManga;
|
||||
@HiveField(1)
|
||||
final int index;
|
||||
@HiveField(2)
|
||||
final int succeeded;
|
||||
@HiveField(3)
|
||||
final int failed;
|
||||
@HiveField(4)
|
||||
final int total;
|
||||
@HiveField(6)
|
||||
final bool isDownload;
|
||||
@HiveField(7)
|
||||
final List taskIds;
|
||||
@HiveField(8)
|
||||
final bool isStartDownload;
|
||||
DownloadModel(
|
||||
{required this.modelManga,
|
||||
required this.succeeded,
|
||||
required this.failed,
|
||||
required this.index,
|
||||
required this.total,
|
||||
required this.isDownload,
|
||||
required this.taskIds,
|
||||
required this.isStartDownload});
|
||||
}
|
||||
62
lib/views/manga/download/download_model.g.dart
Normal file
62
lib/views/manga/download/download_model.g.dart
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'download_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class DownloadModelAdapter extends TypeAdapter<DownloadModel> {
|
||||
@override
|
||||
final int typeId = 6;
|
||||
|
||||
@override
|
||||
DownloadModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return DownloadModel(
|
||||
modelManga: fields[0] as ModelManga,
|
||||
succeeded: fields[2] as int,
|
||||
failed: fields[3] as int,
|
||||
index: fields[1] as int,
|
||||
total: fields[4] as int,
|
||||
isDownload: fields[6] as bool,
|
||||
taskIds: (fields[7] as List).cast<dynamic>(),
|
||||
isStartDownload: fields[8] as bool,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, DownloadModel obj) {
|
||||
writer
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.modelManga)
|
||||
..writeByte(1)
|
||||
..write(obj.index)
|
||||
..writeByte(2)
|
||||
..write(obj.succeeded)
|
||||
..writeByte(3)
|
||||
..write(obj.failed)
|
||||
..writeByte(4)
|
||||
..write(obj.total)
|
||||
..writeByte(6)
|
||||
..write(obj.isDownload)
|
||||
..writeByte(7)
|
||||
..write(obj.taskIds)
|
||||
..writeByte(8)
|
||||
..write(obj.isStartDownload);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DownloadModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
489
lib/views/manga/download/download_page_widget.dart
Normal file
489
lib/views/manga/download/download_page_widget.dart
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
// ignore_for_file: implementation_imports, depend_on_referenced_packages
|
||||
import 'dart:io';
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:mangayomi/models/model_manga.dart';
|
||||
import 'package:mangayomi/providers/hive_provider.dart';
|
||||
import 'package:mangayomi/services/get_manga_chapter_url.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
import 'package:mangayomi/views/manga/download/download_model.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'download_page_widget.g.dart';
|
||||
|
||||
@riverpod
|
||||
class ChapterPageDownloads extends _$ChapterPageDownloads {
|
||||
@override
|
||||
Widget build({required ModelManga modelManga, required int index}) {
|
||||
return ChapterPageDownload(
|
||||
index: index,
|
||||
modelManga: modelManga,
|
||||
);
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
class ChapterPageDownload extends ConsumerStatefulWidget {
|
||||
final ModelManga modelManga;
|
||||
final int index;
|
||||
const ChapterPageDownload(
|
||||
{super.key, required this.modelManga, required this.index});
|
||||
|
||||
@override
|
||||
ConsumerState createState() => _ChapterPageDownloadState();
|
||||
}
|
||||
|
||||
class _ChapterPageDownloadState extends ConsumerState<ChapterPageDownload>
|
||||
with AutomaticKeepAliveClientMixin<ChapterPageDownload> {
|
||||
List _urll = [];
|
||||
List<DownloadTask> tasks = [];
|
||||
final StorageProvider _storageProvider = StorageProvider();
|
||||
_startDownload() async {
|
||||
await _storageProvider.requestPermission();
|
||||
Directory? path;
|
||||
bool isOk = false;
|
||||
final path1 = await _storageProvider.getDirectory();
|
||||
|
||||
final finalPath =
|
||||
"downloads/${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${widget.modelManga.chapterTitle![widget.index].replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}";
|
||||
path = Directory(
|
||||
"${path1!.path}downloads/${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${widget.modelManga.chapterTitle![widget.index].replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
|
||||
ref
|
||||
.read(getMangaChapterUrlProvider(
|
||||
modelManga: widget.modelManga,
|
||||
index: widget.index,
|
||||
).future)
|
||||
.then((value) {
|
||||
if (value.urll.isNotEmpty) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_urll = value.urll;
|
||||
isOk = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
await Future.doWhile(() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (isOk == true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (_urll.isNotEmpty) {
|
||||
for (var index = 0; index < _urll.length; index++) {
|
||||
final path2 = Directory("${path1.path}downloads/");
|
||||
final path4 = Directory(
|
||||
"${path2.path}${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/");
|
||||
final path3 = Directory(
|
||||
"${path2.path}${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/");
|
||||
final path5 = Directory(
|
||||
"${path2.path}${widget.modelManga.source} (${widget.modelManga.lang!.toUpperCase()})/${widget.modelManga.name!.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}/${widget.modelManga.chapterTitle![widget.index].replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_')}");
|
||||
|
||||
if (!(await path1.exists())) {
|
||||
path1.create();
|
||||
}
|
||||
if (!(await path2.exists())) {
|
||||
path2.create();
|
||||
}
|
||||
if (!(await path4.exists())) {
|
||||
path4.create();
|
||||
}
|
||||
if (!(await path3.exists())) {
|
||||
path3.create();
|
||||
}
|
||||
if (!(await path5.exists())) {
|
||||
path5.create();
|
||||
}
|
||||
|
||||
if ((await path.exists())) {
|
||||
if (await File("${path.path}" "${index + 1}.jpg").exists()) {
|
||||
} else {
|
||||
tasks.add(DownloadTask(
|
||||
taskId: _urll[index],
|
||||
headers: headers(widget.modelManga.source!),
|
||||
url: _urll[index],
|
||||
filename: "${index + 1}.jpg",
|
||||
baseDirectory:
|
||||
Platform.isWindows || Platform.isMacOS || Platform.isLinux
|
||||
? BaseDirectory.applicationDocuments
|
||||
: BaseDirectory.temporary,
|
||||
directory:
|
||||
Platform.isWindows || Platform.isMacOS || Platform.isLinux
|
||||
? 'Mangayomi/$finalPath'
|
||||
: finalPath,
|
||||
updates: Updates.statusAndProgress,
|
||||
allowPause: true,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
path.create();
|
||||
if (await File("${path.path}" "${index + 1}.jpg").exists()) {
|
||||
} else {
|
||||
tasks.add(DownloadTask(
|
||||
taskId: _urll[index],
|
||||
headers: headers(widget.modelManga.source!),
|
||||
url: _urll[index],
|
||||
filename: "${index + 1}.jpg",
|
||||
baseDirectory:
|
||||
Platform.isWindows || Platform.isMacOS || Platform.isLinux
|
||||
? BaseDirectory.applicationDocuments
|
||||
: BaseDirectory.temporary,
|
||||
directory:
|
||||
Platform.isWindows || Platform.isMacOS || Platform.isLinux
|
||||
? 'Mangayomi/$finalPath'
|
||||
: finalPath,
|
||||
updates: Updates.statusAndProgress,
|
||||
allowPause: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tasks.isEmpty && _urll.isNotEmpty) {
|
||||
final model = DownloadModel(
|
||||
modelManga: widget.modelManga,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
index: widget.index,
|
||||
total: 0,
|
||||
isDownload: true,
|
||||
taskIds: _urll,
|
||||
isStartDownload: false);
|
||||
|
||||
ref
|
||||
.watch(hiveBoxMangaDownloads)
|
||||
.put(widget.modelManga.chapterTitle![widget.index], model);
|
||||
} else {
|
||||
await FileDownloader().downloadBatch(
|
||||
tasks,
|
||||
batchProgressCallback: (succeeded, failed) {
|
||||
final model = DownloadModel(
|
||||
modelManga: widget.modelManga,
|
||||
succeeded: succeeded,
|
||||
failed: failed,
|
||||
index: widget.index,
|
||||
total: tasks.length,
|
||||
isDownload: (succeeded == tasks.length) ? true : false,
|
||||
taskIds: _urll,
|
||||
isStartDownload: true);
|
||||
|
||||
Hive.box<DownloadModel>(HiveConstant.hiveBoxDownloads)
|
||||
.put(widget.modelManga.chapterTitle![widget.index], model);
|
||||
},
|
||||
taskProgressCallback: (task, progress) async {
|
||||
if (progress == 1.0) {
|
||||
final downloadTask = DownloadTask(
|
||||
creationTime: task.creationTime,
|
||||
taskId: task.taskId,
|
||||
headers: task.headers,
|
||||
url: task.url,
|
||||
filename: task.filename,
|
||||
baseDirectory: task.baseDirectory,
|
||||
directory: task.directory,
|
||||
updates: task.updates,
|
||||
allowPause: task.allowPause,
|
||||
);
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
await FileDownloader().moveToSharedStorage(
|
||||
downloadTask, SharedStorage.external,
|
||||
directory: finalPath);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_deleteFile(List pageUrl) async {
|
||||
final path = await _storageProvider.getMangaChapterDirectory(
|
||||
widget.modelManga, widget.index);
|
||||
|
||||
try {
|
||||
path!.deleteSync(recursive: true);
|
||||
ref.watch(hiveBoxMangaDownloads).delete(
|
||||
widget.modelManga.chapterTitle![widget.index],
|
||||
);
|
||||
} catch (e) {
|
||||
ref.watch(hiveBoxMangaDownloads).delete(
|
||||
widget.modelManga.chapterTitle![widget.index],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool _isStarted = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return SizedBox(
|
||||
height: 41,
|
||||
width: 35,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
||||
child: ValueListenableBuilder<Box<DownloadModel>>(
|
||||
valueListenable: ref.watch(hiveBoxMangaDownloads).listenable(),
|
||||
builder: (context, val, child) {
|
||||
final entries = val.values
|
||||
.where((element) =>
|
||||
element.modelManga.chapterTitle![element.index] ==
|
||||
widget.modelManga.chapterTitle![widget.index])
|
||||
.toList();
|
||||
|
||||
if (entries.isNotEmpty) {
|
||||
return entries.first.isDownload
|
||||
? PopupMenuButton(
|
||||
child: Icon(
|
||||
size: 25,
|
||||
Icons.check_circle,
|
||||
color:
|
||||
Theme.of(context).iconTheme.color!.withOpacity(0.7),
|
||||
),
|
||||
onSelected: (value) {
|
||||
if (value.toString() == 'Delete') {
|
||||
setState(() {
|
||||
_isStarted = false;
|
||||
});
|
||||
_deleteFile(entries.first.taskIds);
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(value: 'Send', child: Text("Send")),
|
||||
const PopupMenuItem(
|
||||
value: 'Delete', child: Text('Delete')),
|
||||
],
|
||||
)
|
||||
: entries.first.isStartDownload &&
|
||||
entries.first.succeeded == 0
|
||||
? SizedBox(
|
||||
height: 41,
|
||||
width: 35,
|
||||
child: PopupMenuButton(
|
||||
child: _downloadWidget(context, false),
|
||||
onSelected: (value) {
|
||||
if (value.toString() == 'Cancel') {
|
||||
setState(() {
|
||||
_isStarted = false;
|
||||
});
|
||||
List<String> taskIds = [];
|
||||
for (var id in entries.first.taskIds) {
|
||||
taskIds.add(id);
|
||||
}
|
||||
FileDownloader()
|
||||
.cancelTasksWithIds(taskIds)
|
||||
.then((value) async {
|
||||
await Future.delayed(
|
||||
const Duration(seconds: 2));
|
||||
ref.watch(hiveBoxMangaDownloads).delete(
|
||||
widget.modelManga
|
||||
.chapterTitle![widget.index],
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'Cancel', child: Text("Cancel")),
|
||||
],
|
||||
))
|
||||
: entries.first.succeeded != 0
|
||||
? SizedBox(
|
||||
height: 41,
|
||||
width: 35,
|
||||
child: PopupMenuButton(
|
||||
child: Stack(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.arrow_downward_sharp,
|
||||
color: Theme.of(context)
|
||||
.iconTheme
|
||||
.color!
|
||||
.withOpacity(0.7),
|
||||
)),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: TweenAnimationBuilder<double>(
|
||||
duration:
|
||||
const Duration(milliseconds: 250),
|
||||
curve: Curves.easeInOut,
|
||||
tween: Tween<double>(
|
||||
begin: 0,
|
||||
end: (entries.first.succeeded /
|
||||
entries.first.total),
|
||||
),
|
||||
builder: (context, value, _) =>
|
||||
SizedBox(
|
||||
height: 2,
|
||||
width: 2,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 19,
|
||||
value: value,
|
||||
color: Theme.of(context)
|
||||
.iconTheme
|
||||
.color!
|
||||
.withOpacity(0.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.arrow_downward_sharp,
|
||||
color: Theme.of(context)
|
||||
.scaffoldBackgroundColor,
|
||||
)),
|
||||
],
|
||||
),
|
||||
onSelected: (value) {
|
||||
if (value.toString() == 'Cancel') {
|
||||
setState(() {
|
||||
_isStarted = false;
|
||||
});
|
||||
List<String> taskIds = [];
|
||||
for (var id in entries.first.taskIds) {
|
||||
taskIds.add(id);
|
||||
}
|
||||
FileDownloader()
|
||||
.cancelTasksWithIds(taskIds)
|
||||
.then((value) async {
|
||||
await Future.delayed(
|
||||
const Duration(seconds: 2));
|
||||
ref.watch(hiveBoxMangaDownloads).delete(
|
||||
widget.modelManga
|
||||
.chapterTitle![widget.index],
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'Cancel', child: Text("Cancel")),
|
||||
],
|
||||
))
|
||||
: entries.first.succeeded == 0
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
// _startDownload();
|
||||
setState(() {
|
||||
_isStarted = true;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
FontAwesomeIcons.circleDown,
|
||||
color: Theme.of(context)
|
||||
.iconTheme
|
||||
.color!
|
||||
.withOpacity(0.7),
|
||||
size: 25,
|
||||
))
|
||||
: SizedBox(
|
||||
height: 50,
|
||||
width: 50,
|
||||
child: PopupMenuButton(
|
||||
child: const Icon(
|
||||
Icons.error_outline_outlined,
|
||||
color: Colors.red,
|
||||
size: 25,
|
||||
),
|
||||
onSelected: (value) {
|
||||
if (value.toString() == 'Retry') {
|
||||
ref.watch(hiveBoxMangaDownloads).delete(
|
||||
widget.modelManga
|
||||
.chapterTitle![widget.index],
|
||||
);
|
||||
_startDownload();
|
||||
setState(() {
|
||||
_isStarted = true;
|
||||
});
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'Retry', child: Text("Retry")),
|
||||
],
|
||||
));
|
||||
}
|
||||
return _isStarted
|
||||
? SizedBox(
|
||||
height: 50,
|
||||
width: 50,
|
||||
child: PopupMenuButton(
|
||||
child: _downloadWidget(context, true),
|
||||
onSelected: (value) {
|
||||
if (value.toString() == 'Cancel') {
|
||||
setState(() {
|
||||
_isStarted = false;
|
||||
});
|
||||
List<String> taskIds = [];
|
||||
for (var id in _urll) {
|
||||
taskIds.add(id);
|
||||
}
|
||||
FileDownloader()
|
||||
.cancelTasksWithIds(taskIds)
|
||||
.then((value) async {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
ref.watch(hiveBoxMangaDownloads).delete(
|
||||
widget.modelManga.chapterTitle![widget.index],
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'Cancel', child: Text("Cancel")),
|
||||
],
|
||||
))
|
||||
: IconButton(
|
||||
splashRadius: 5,
|
||||
iconSize: 17,
|
||||
onPressed: () {
|
||||
_startDownload();
|
||||
setState(() {
|
||||
_isStarted = true;
|
||||
});
|
||||
},
|
||||
icon: _downloadWidget(context, false),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
|
||||
Widget _downloadWidget(BuildContext context, bool isLoading) {
|
||||
return Stack(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
size: 18,
|
||||
Icons.arrow_downward_sharp,
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.7),
|
||||
)),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
value: isLoading ? null : 1,
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.7),
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
140
lib/views/manga/download/download_page_widget.g.dart
Normal file
140
lib/views/manga/download/download_page_widget.g.dart
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'download_page_widget.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$chapterPageDownloadsHash() =>
|
||||
r'0b3eaf9a3ca4786287616a87e5de62af24259b68';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$ChapterPageDownloads
|
||||
extends BuildlessAutoDisposeNotifier<Widget> {
|
||||
late final ModelManga modelManga;
|
||||
late final int index;
|
||||
|
||||
Widget build({
|
||||
required ModelManga modelManga,
|
||||
required int index,
|
||||
});
|
||||
}
|
||||
|
||||
/// See also [ChapterPageDownloads].
|
||||
@ProviderFor(ChapterPageDownloads)
|
||||
const chapterPageDownloadsProvider = ChapterPageDownloadsFamily();
|
||||
|
||||
/// See also [ChapterPageDownloads].
|
||||
class ChapterPageDownloadsFamily extends Family<Widget> {
|
||||
/// See also [ChapterPageDownloads].
|
||||
const ChapterPageDownloadsFamily();
|
||||
|
||||
/// See also [ChapterPageDownloads].
|
||||
ChapterPageDownloadsProvider call({
|
||||
required ModelManga modelManga,
|
||||
required int index,
|
||||
}) {
|
||||
return ChapterPageDownloadsProvider(
|
||||
modelManga: modelManga,
|
||||
index: index,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ChapterPageDownloadsProvider getProviderOverride(
|
||||
covariant ChapterPageDownloadsProvider provider,
|
||||
) {
|
||||
return call(
|
||||
modelManga: provider.modelManga,
|
||||
index: provider.index,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'chapterPageDownloadsProvider';
|
||||
}
|
||||
|
||||
/// See also [ChapterPageDownloads].
|
||||
class ChapterPageDownloadsProvider
|
||||
extends AutoDisposeNotifierProviderImpl<ChapterPageDownloads, Widget> {
|
||||
/// See also [ChapterPageDownloads].
|
||||
ChapterPageDownloadsProvider({
|
||||
required this.modelManga,
|
||||
required this.index,
|
||||
}) : super.internal(
|
||||
() => ChapterPageDownloads()
|
||||
..modelManga = modelManga
|
||||
..index = index,
|
||||
from: chapterPageDownloadsProvider,
|
||||
name: r'chapterPageDownloadsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$chapterPageDownloadsHash,
|
||||
dependencies: ChapterPageDownloadsFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
ChapterPageDownloadsFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final ModelManga modelManga;
|
||||
final int index;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ChapterPageDownloadsProvider &&
|
||||
other.modelManga == modelManga &&
|
||||
other.index == index;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, modelManga.hashCode);
|
||||
hash = _SystemHash.combine(hash, index.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget runNotifierBuild(
|
||||
covariant ChapterPageDownloads notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
modelManga: modelManga,
|
||||
index: index,
|
||||
);
|
||||
}
|
||||
}
|
||||
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
|
||||
|
|
@ -36,21 +36,53 @@ class ImageViewHorizontal extends StatefulWidget {
|
|||
typedef DoubleClickAnimationListener = void Function();
|
||||
|
||||
class _ImageViewHorizontalState extends State<ImageViewHorizontal> {
|
||||
@override
|
||||
void initState() {
|
||||
_localCheck();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_localCheck() async {
|
||||
if (await File("${widget.path.path}" "${widget.index + 1}.jpg").exists()) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLocale = true;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLocale = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _isLocale = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ExtendedImage.network(
|
||||
widget.url,
|
||||
cache: true,
|
||||
clearMemoryCacheWhenDispose: true,
|
||||
enableMemoryCache: false,
|
||||
cacheMaxAge: const Duration(days: 7),
|
||||
headers: headers(widget.source),
|
||||
mode: ExtendedImageMode.gesture,
|
||||
initGestureConfigHandler: widget.initGestureConfigHandler,
|
||||
onDoubleTap: widget.onDoubleTap,
|
||||
handleLoadingProgress: true,
|
||||
loadStateChanged: widget.loadStateChanged,
|
||||
);
|
||||
return _isLocale
|
||||
? ExtendedImage.file(
|
||||
File("${widget.path.path}" "${widget.index + 1}.jpg"),
|
||||
clearMemoryCacheWhenDispose: true,
|
||||
enableMemoryCache: false,
|
||||
mode: ExtendedImageMode.gesture,
|
||||
initGestureConfigHandler: widget.initGestureConfigHandler,
|
||||
onDoubleTap: widget.onDoubleTap,
|
||||
loadStateChanged: widget.loadStateChanged,
|
||||
)
|
||||
: ExtendedImage.network(
|
||||
widget.url,
|
||||
cache: true,
|
||||
clearMemoryCacheWhenDispose: true,
|
||||
enableMemoryCache: false,
|
||||
cacheMaxAge: const Duration(days: 7),
|
||||
headers: headers(widget.source),
|
||||
mode: ExtendedImageMode.gesture,
|
||||
initGestureConfigHandler: widget.initGestureConfigHandler,
|
||||
onDoubleTap: widget.onDoubleTap,
|
||||
handleLoadingProgress: true,
|
||||
loadStateChanged: widget.loadStateChanged,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,29 @@ class ImageViewVertical extends ConsumerStatefulWidget {
|
|||
class _ImageViewVerticalState extends ConsumerState<ImageViewVertical>
|
||||
with AutomaticKeepAliveClientMixin<ImageViewVertical> {
|
||||
@override
|
||||
void initState() {
|
||||
_localCheck();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_localCheck() async {
|
||||
if (await File("${widget.path.path}" "${widget.index + 1}.jpg").exists()) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLocale = true;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLocale = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _isLocale = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Container(
|
||||
|
|
@ -44,44 +67,57 @@ class _ImageViewVerticalState extends ConsumerState<ImageViewVertical>
|
|||
SizedBox(
|
||||
height: MediaQuery.of(context).padding.top,
|
||||
),
|
||||
ExtendedImage.network(widget.url,
|
||||
headers: headers(widget.source),
|
||||
handleLoadingProgress: true,
|
||||
fit: BoxFit.contain,
|
||||
cacheMaxAge: const Duration(days: 7),
|
||||
clearMemoryCacheWhenDispose: true,
|
||||
enableMemoryCache: false,
|
||||
loadStateChanged: (ExtendedImageState state) {
|
||||
if (state.extendedImageLoadState == LoadState.loading) {
|
||||
final ImageChunkEvent? loadingProgress = state.loadingProgress;
|
||||
final double? progress =
|
||||
loadingProgress?.expectedTotalBytes != null
|
||||
? loadingProgress!.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null;
|
||||
return SizedBox(
|
||||
height: mediaHeight(context, 0.5),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: progress,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (state.extendedImageLoadState == LoadState.failed) {
|
||||
return Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
state.reLoadImage();
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.replay_outlined,
|
||||
size: 30,
|
||||
)),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
_isLocale
|
||||
? ExtendedImage.file(
|
||||
fit: BoxFit.contain,
|
||||
clearMemoryCacheWhenDispose: true,
|
||||
enableMemoryCache: false,
|
||||
File('${widget.path.path}${widget.index + 1}.jpg'))
|
||||
: ExtendedImage.network(widget.url,
|
||||
headers: headers(widget.source),
|
||||
handleLoadingProgress: true,
|
||||
fit: BoxFit.contain,
|
||||
cacheMaxAge: const Duration(days: 7),
|
||||
clearMemoryCacheWhenDispose: true,
|
||||
enableMemoryCache: false,
|
||||
loadStateChanged: (ExtendedImageState state) {
|
||||
if (state.extendedImageLoadState == LoadState.loading) {
|
||||
final ImageChunkEvent? loadingProgress =
|
||||
state.loadingProgress;
|
||||
final double? progress =
|
||||
loadingProgress?.expectedTotalBytes != null
|
||||
? loadingProgress!.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null;
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
height: mediaHeight(context, 0.8),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: progress,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (state.extendedImageLoadState == LoadState.failed) {
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
height: mediaHeight(context, 0.8),
|
||||
child: Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
state.reLoadImage();
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.replay_outlined,
|
||||
size: 30,
|
||||
)),
|
||||
],
|
||||
));
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
if (widget.index + 1 == widget.length)
|
||||
Column(
|
||||
children: [
|
||||
|
|
|
|||
|
|
@ -169,20 +169,20 @@ class _MangaChapterPageGalleryState
|
|||
widget.readerController.setPageIndex(index);
|
||||
}
|
||||
|
||||
void _onAddButtonTapped(int ok, bool isPrev, {bool isSlide = false}) {
|
||||
void _onAddButtonTapped(int index, bool isPrev, {bool isSlide = false}) {
|
||||
if (isPrev) {
|
||||
if (_selectedValue == ReaderMode.verticalContinuous ||
|
||||
_selectedValue == ReaderMode.webtoon) {
|
||||
if (ok != -1) {
|
||||
if (index != -1) {
|
||||
_itemScrollController.scrollTo(
|
||||
curve: Curves.ease,
|
||||
index: ok,
|
||||
index: index,
|
||||
duration: Duration(milliseconds: isSlide ? 2 : 150));
|
||||
}
|
||||
} else {
|
||||
if (ok != -1) {
|
||||
if (index != -1) {
|
||||
if (_extendedController.hasClients) {
|
||||
_extendedController.animateToPage(ok.toInt(),
|
||||
_extendedController.animateToPage(index,
|
||||
duration: Duration(milliseconds: isSlide ? 2 : 150),
|
||||
curve: Curves.ease);
|
||||
}
|
||||
|
|
@ -191,16 +191,16 @@ class _MangaChapterPageGalleryState
|
|||
} else {
|
||||
if (_selectedValue == ReaderMode.verticalContinuous ||
|
||||
_selectedValue == ReaderMode.webtoon) {
|
||||
if (widget.readerController.getPageLength(widget.url) != ok) {
|
||||
if (widget.readerController.getPageLength(widget.url) != index) {
|
||||
_itemScrollController.scrollTo(
|
||||
curve: Curves.ease,
|
||||
index: ok,
|
||||
index: index,
|
||||
duration: Duration(milliseconds: isSlide ? 2 : 150));
|
||||
}
|
||||
} else {
|
||||
if (widget.readerController.getPageLength(widget.url) != ok) {
|
||||
if (widget.readerController.getPageLength(widget.url) != index) {
|
||||
if (_extendedController.hasClients) {
|
||||
_extendedController.animateToPage(ok.toInt(),
|
||||
_extendedController.animateToPage(index.toInt(),
|
||||
duration: Duration(milliseconds: isSlide ? 2 : 150),
|
||||
curve: Curves.ease);
|
||||
}
|
||||
|
|
@ -923,7 +923,9 @@ class _MangaChapterPageGalleryState
|
|||
reverse: _isReversHorizontal,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
canScrollPage: (GestureDetails? gestureDetails) {
|
||||
return !(gestureDetails!.totalScale! > 1.0);
|
||||
return gestureDetails != null
|
||||
? !(gestureDetails.totalScale! > 1.0)
|
||||
: true;
|
||||
},
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return ImageViewHorizontal(
|
||||
|
|
@ -981,16 +983,21 @@ class _MangaChapterPageGalleryState
|
|||
}
|
||||
if (state.extendedImageLoadState ==
|
||||
LoadState.failed) {
|
||||
return Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
state.reLoadImage();
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.replay_outlined,
|
||||
size: 30,
|
||||
)),
|
||||
);
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
height: mediaHeight(context, 0.8),
|
||||
child: Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
state.reLoadImage();
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.replay_outlined,
|
||||
size: 30,
|
||||
)),
|
||||
],
|
||||
));
|
||||
}
|
||||
return Container();
|
||||
},
|
||||
|
|
|
|||
57
pubspec.lock
57
pubspec.lock
|
|
@ -41,6 +41,15 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
background_downloader:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "7b778222546d53fcd41a84f6aee90b87c2593930"
|
||||
url: "https://github.com/kodjodevf/background_downloader.git"
|
||||
source: git
|
||||
version: "5.4.5"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -544,6 +553,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
localstore:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: localstore
|
||||
sha256: "42a0afb7696cfab1b4bd7d08355b4ee01f975fd364553b28d51496eccaf11cce"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.5"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -688,6 +705,46 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.2.0"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.2.0"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.8"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.9.0"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
photo_view:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ dependencies:
|
|||
# draggable_menu: ^0.2.0
|
||||
url_launcher: ^6.1.10
|
||||
package_info_plus: ^3.0.2
|
||||
background_downloader:
|
||||
git:
|
||||
url: https://github.com/kodjodevf/background_downloader.git
|
||||
permission_handler: ^10.2.0
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
|
|
|||
|
|
@ -7,11 +7,14 @@
|
|||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flutter_js/flutter_js_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FlutterJsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_js
|
||||
permission_handler_windows
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue