diff --git a/lib/component/download_list_item_component.dart b/lib/component/download_list_item_component.dart new file mode 100644 index 0000000..53899fc --- /dev/null +++ b/lib/component/download_list_item_component.dart @@ -0,0 +1,182 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_font_icons/flutter_font_icons.dart'; +import 'package:nowser/const/base.dart'; +import 'package:nowser/data/download_log.dart'; +import 'package:nowser/main.dart'; +import 'package:nowser/util/file_size_util.dart'; +import 'package:path/path.dart' as path; + +class DownloadListItemComponent extends StatelessWidget { + DownloadLog downloadLog; + + DownloadListItemComponent(this.downloadLog); + + @override + Widget build(BuildContext context) { + var themeData = Theme.of(context); + + Widget fileIconWidget = const Icon( + AntDesign.file1, + size: 40, + ); + if (downloadLog.fileName != null) { + var mimeType = getFileType(downloadLog.fileName!); + if (mimeType.contains("image")) { + fileIconWidget = const Icon( + Icons.image, + size: 40, + ); + } else if (mimeType.contains("markdown")) { + fileIconWidget = const Icon( + AntDesign.file_markdown, + size: 40, + ); + } else if (mimeType.contains("document")) { + fileIconWidget = const Icon( + AntDesign.filetext1, + size: 40, + ); + } else if (mimeType.contains("video")) { + fileIconWidget = const Icon( + Icons.movie, + size: 40, + ); + } else if (mimeType.contains("audio")) { + fileIconWidget = const Icon( + Icons.music_note, + size: 40, + ); + } else if (mimeType.contains("archive")) { + fileIconWidget = const Icon( + Icons.folder_zip, + size: 40, + ); + } else if (mimeType.contains("apk")) { + fileIconWidget = const Icon( + AntDesign.android, + size: 40, + ); + } + } + + Widget rightIcon = GestureDetector( + onTap: () {}, + child: const Icon( + Icons.more_horiz, + ), + ); + if (downloadLog.progress != null) { + rightIcon = GestureDetector( + onTap: () {}, + child: const Icon( + Icons.stop_circle_outlined, + ), + ); + } + + Widget fileStatusWidget = Container(); + if (downloadLog.progress != null) { + fileStatusWidget = Text( + "${(downloadLog.progress! * 100).toStringAsFixed(1)}%", + style: TextStyle( + fontSize: themeData.textTheme.bodySmall!.fontSize, + color: themeData.hintColor, + ), + ); + } else if (downloadLog.fileSize != null) { + fileStatusWidget = Text( + FileSizeUtil.getFileSize(downloadLog.fileSize!), + style: TextStyle( + fontSize: themeData.textTheme.bodySmall!.fontSize, + color: themeData.hintColor, + ), + ); + } + + return Container( + padding: const EdgeInsets.only( + left: Base.BASE_PADDING_HALF, + right: Base.BASE_PADDING_HALF, + top: 2, + bottom: 2, + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(6), + child: fileIconWidget, + ), + Expanded( + child: Container( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + downloadLog.fileName ?? "unknow_file", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + fileStatusWidget, + ], + ), + ), + ), + Container( + padding: const EdgeInsets.all(6), + child: rightIcon, + ), + ], + ), + ); + } + + String getFileType(String filePath) { + // 获取扩展名(含点,如 '.jpg') + String extension = path.extension(filePath).toLowerCase(); + + // 处理无扩展名的情况 + if (extension.isEmpty) return 'other'; + + // 去掉点并检查映射 + String cleanedExtension = + extension.startsWith('.') ? extension.substring(1) : extension; + + return _fileExtensionToType[cleanedExtension] ?? 'other'; + } + + Map _fileExtensionToType = { + // 图片类型 + 'jpg': 'image', + 'jpeg': 'image', + 'png': 'image', + 'gif': 'image', + 'webp': 'image', + 'bmp': 'image', + 'mp4': 'video', + 'mov': 'video', + 'avi': 'video', + 'mkv': 'video', + 'flv': 'video', + 'pdf': 'document', + 'doc': 'document', + 'docx': 'document', + 'xls': 'document', + 'xlsx': 'document', + 'ppt': 'document', + 'pptx': 'document', + 'txt': 'document', + 'md': 'markdown', + 'markdown': 'markdown', + 'mp3': 'audio', + 'wav': 'audio', + 'aac': 'audio', + 'ogg': 'audio', + 'zip': 'archive', + 'rar': 'archive', + '7z': 'archive', + 'tar': 'archive', + 'gz': 'archive', + 'apk': 'apk', + }; +} diff --git a/lib/component/download_task_dialog.dart b/lib/component/download_task_dialog.dart new file mode 100644 index 0000000..de38dfb --- /dev/null +++ b/lib/component/download_task_dialog.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:nostr_sdk/utils/platform_util.dart'; +import 'package:nowser/provider/download_provider.dart'; +import 'package:nowser/util/router_util.dart'; + +import '../const/base.dart'; +import '../generated/l10n.dart'; +import '../main.dart'; +import '../util/table_mode_util.dart'; + +class DownloadTaskDialog extends StatefulWidget { + String downloadUrl; + + DownloadTaskDialog(this.downloadUrl); + + static Future show(BuildContext context, String downloadUrl) async { + await showDialog( + context: context, + builder: (_context) { + return DownloadTaskDialog(downloadUrl); + }, + ); + } + + @override + State createState() { + return _DownloadTaskDialog(); + } +} + +class _DownloadTaskDialog extends State { + TextEditingController urlTextController = TextEditingController(); + + TextEditingController fileNameTextController = TextEditingController(); + + late S s; + + @override + void initState() { + super.initState(); + String fileName = + widget.downloadUrl.substring(widget.downloadUrl.lastIndexOf('/') + 1); + + urlTextController.text = widget.downloadUrl; + fileNameTextController.text = fileName; + } + + @override + Widget build(BuildContext context) { + var themeData = Theme.of(context); + s = S.of(context); + + List list = []; + list.add(Container( + margin: EdgeInsets.only( + bottom: Base.BASE_PADDING, + ), + child: Text( + s.Downloads, + style: TextStyle( + fontSize: themeData.textTheme.bodyLarge!.fontSize, + fontWeight: FontWeight.bold, + ), + ), + )); + + list.add(Container( + child: TextField( + controller: urlTextController, + decoration: InputDecoration( + labelText: s.Url, + ), + ), + )); + + list.add(Container( + child: TextField( + controller: fileNameTextController, + decoration: InputDecoration( + labelText: s.Name, + ), + ), + )); + + list.add(Container( + margin: EdgeInsets.only( + top: Base.BASE_PADDING * 2, + bottom: Base.BASE_PADDING, + ), + width: double.infinity, + child: FilledButton(onPressed: confirm, child: Text(s.Confirm)), + )); + + Widget main = Container( + child: Column( + mainAxisSize: MainAxisSize.min, + children: list, + ), + ); + if (PlatformUtil.isPC() || TableModeUtil.isTableMode()) { + main = Container( + width: mediaDataCache.size.width / 2, + child: main, + ); + } + + return Dialog( + child: Container( + padding: EdgeInsets.all(Base.BASE_PADDING * 2), + child: main, + ), + ); + } + + void confirm() { + var url = urlTextController.text; + var fileName = fileNameTextController.text; + downloadProvider.startDownload(url, fileName); + RouterUtil.back(context); + } +} diff --git a/lib/component/webview/long_press_dialog.dart b/lib/component/webview/long_press_dialog.dart index 391c875..a4a51e2 100644 --- a/lib/component/webview/long_press_dialog.dart +++ b/lib/component/webview/long_press_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:nostr_sdk/utils/string_util.dart'; +import 'package:nowser/component/download_task_dialog.dart'; import 'package:nowser/main.dart'; import '../../const/base.dart'; @@ -98,7 +99,9 @@ class _LongPressDialog extends State { )); list.add(LongPressDialogItem( s.Download_image, - onTap: () {}, + onTap: () { + DownloadTaskDialog.show(context, src); + }, showBottomLine: false, )); } @@ -180,10 +183,10 @@ class LongPressDialogItem extends StatelessWidget { return GestureDetector( onTap: () { + RouterUtil.back(context); if (onTap != null) { onTap!(); } - RouterUtil.back(context); }, child: Container( decoration: BoxDecoration( diff --git a/lib/component/webview/webview_component.dart b/lib/component/webview/webview_component.dart index c30f9fc..e1b5460 100644 --- a/lib/component/webview/webview_component.dart +++ b/lib/component/webview/webview_component.dart @@ -7,6 +7,7 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:nostr_sdk/event.dart'; import 'package:nostr_sdk/utils/platform_util.dart'; import 'package:nostr_sdk/utils/string_util.dart'; +import 'package:nowser/component/download_task_dialog.dart'; import 'package:nowser/component/webview/long_press_dialog.dart'; import 'package:nowser/component/webview/web_info.dart'; import 'package:nowser/const/app_type.dart'; @@ -224,6 +225,11 @@ class _WebViewComponent extends State } } }, + onDownloadStartRequest: (InAppWebViewController controller, + DownloadStartRequest downloadStartRequest) { + String downloadUrl = downloadStartRequest.url.path; + DownloadTaskDialog.show(context, downloadUrl); + }, ), ); } diff --git a/lib/const/router_path.dart b/lib/const/router_path.dart index 30da56a..c8d5789 100644 --- a/lib/const/router_path.dart +++ b/lib/const/router_path.dart @@ -12,4 +12,5 @@ class RouterPath { static const String AUTH_LOGS = "/authLogs"; static const String SETTING = "/setting"; static const String ABOUT_ME = "/aboutMe"; + static const String DOWNLOADS = "/downloads"; } diff --git a/lib/data/browser_history_db.dart b/lib/data/browser_history_db.dart index 98db09c..764172c 100644 --- a/lib/data/browser_history_db.dart +++ b/lib/data/browser_history_db.dart @@ -29,14 +29,18 @@ class BrowserHistoryDB { } static Future deleteByIds(List ids, {DatabaseExecutor? db}) async { - var sql = "delete from browser_history where id in("; - for (var id in ids) { - sql += "?,"; - } - sql = sql.substring(0, sql.length - 1); - sql += ")"; - - db = await DB.getDB(db); - await db.execute(sql, ids); + await DB.deleteByIds("browser_history", ids, db: db); } + + // static Future deleteByIds(List ids, {DatabaseExecutor? db}) async { + // var sql = "delete from browser_history where id in("; + // for (var id in ids) { + // sql += "?,"; + // } + // sql = sql.substring(0, sql.length - 1); + // sql += ")"; + + // db = await DB.getDB(db); + // await db.execute(sql, ids); + // } } diff --git a/lib/data/db.dart b/lib/data/db.dart index acc2b64..9848b4d 100644 --- a/lib/data/db.dart +++ b/lib/data/db.dart @@ -2,7 +2,7 @@ import 'package:nostr_sdk/utils/db_util.dart'; import 'package:sqflite/sqflite.dart'; class DB { - static const _VERSION = 2; + static const _VERSION = 3; static const _dbName = "nowser.db"; @@ -41,14 +41,22 @@ class DB { "create table bookmark(id integer not null constraint bookmark_pk primary key autoincrement,title text,url text not null,favicon text,weight integer,added_to_index integer, added_to_qa integer,created_at integer);"); db.execute( "create table browser_history(id integer not null constraint browser_history_pk primary key autoincrement,title text,url text not null,favicon text,created_at integer);"); + + db.execute( + "create table download_log(id integer constraint download_log_pk primary key autoincrement,url text,file_path text,file_name TEXT,file_size integer,created_at integer);"); } static Future _onUpgrade( Database db, int oldVersion, int newVersion) async { - if (oldVersion == 1 && newVersion == 2) { + if (oldVersion == 1) { db.execute( "alter table bookmark add added_to_qa integer after added_to_index"); } + + if (oldVersion <= 2) { + db.execute( + "create table download_log(id integer constraint download_log_pk primary key autoincrement,url text,file_path text,file_name TEXT,file_size integer,created_at integer);"); + } } static Future getCurrentDatabase() async { @@ -69,4 +77,17 @@ class DB { _database?.close(); _database = null; } + + static Future deleteByIds(String tableName, List ids, + {DatabaseExecutor? db}) async { + var sql = "delete from $tableName where id in("; + for (var id in ids) { + sql += "?,"; + } + sql = sql.substring(0, sql.length - 1); + sql += ")"; + + db = await DB.getDB(db); + await db.execute(sql, ids); + } } diff --git a/lib/data/download_log.dart b/lib/data/download_log.dart new file mode 100644 index 0000000..2aa40b9 --- /dev/null +++ b/lib/data/download_log.dart @@ -0,0 +1,48 @@ +class DownloadLog { + int? id; + + String? url; + + String? filePath; + + String? fileName; + + int? fileSize; + + int? createdAt; + + String? taskId; + + double? progress; + + DownloadLog({ + this.id, + this.url, + this.filePath, + this.fileName, + this.fileSize, + this.createdAt, + this.taskId, + this.progress, + }); + + DownloadLog.fromJson(Map json) { + id = json['id']; + url = json['url']; + filePath = json['file_path']; + fileName = json['file_name']; + fileSize = json['file_size']; + createdAt = json['created_at']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['url'] = url; + data['file_path'] = filePath; + data['file_name'] = fileName; + data['file_size'] = fileSize; + data['created_at'] = createdAt; + return data; + } +} diff --git a/lib/data/download_log_db.dart b/lib/data/download_log_db.dart new file mode 100644 index 0000000..a14c463 --- /dev/null +++ b/lib/data/download_log_db.dart @@ -0,0 +1,34 @@ +import 'package:nowser/data/download_log.dart'; +import 'package:sqflite/sqflite.dart'; + +import 'db.dart'; + +class DownloadLogDB { + static Future total({DatabaseExecutor? db}) async { + db = await DB.getDB(db); + var sql = "select count(1) from download_log"; + return Sqflite.firstIntValue(await db.rawQuery(sql)); + } + + static Future insert(DownloadLog o, {DatabaseExecutor? db}) async { + db = await DB.getDB(db); + return await db.insert("download_log", o.toJson()); + } + + static Future> all({DatabaseExecutor? db}) async { + List objs = []; + List? arguments = []; + db = await DB.getDB(db); + var sql = "select * from download_log order by created_at desc"; + List> list = await db.rawQuery(sql, arguments); + for (var i = 0; i < list.length; i++) { + var json = list[i]; + objs.add(DownloadLog.fromJson(json)); + } + return objs; + } + + static Future deleteByIds(List ids, {DatabaseExecutor? db}) async { + await DB.deleteByIds("download_log", ids, db: db); + } +} diff --git a/lib/main.dart b/lib/main.dart index 712bbb0..f35ab7d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,6 +15,7 @@ import 'package:nowser/provider/android_signer_mixin.dart'; import 'package:nowser/provider/app_provider.dart'; import 'package:nowser/provider/bookmark_provider.dart'; import 'package:nowser/provider/build_in_relay_provider.dart'; +import 'package:nowser/provider/download_provider.dart'; import 'package:nowser/provider/key_provider.dart'; import 'package:nowser/provider/permission_check_mixin.dart'; import 'package:nowser/provider/web_provider.dart'; @@ -24,6 +25,7 @@ import 'package:nowser/router/apps/add_remote_app_router.dart'; import 'package:nowser/router/apps/apps_router.dart'; import 'package:nowser/router/auth_log/auth_logs_router.dart'; import 'package:nowser/router/bookmark/bookmark_router.dart'; +import 'package:nowser/router/downloads/downloads_router.dart'; import 'package:nowser/router/history/history_router.dart'; import 'package:nowser/router/index/index_router.dart'; import 'package:nowser/router/keys/keys_router.dart'; @@ -65,6 +67,8 @@ late RootIsolateToken rootIsolateToken; late BuildInRelayProvider buildInRelayProvider; +late DownloadProvider downloadProvider; + const QuickActions quickActions = QuickActions(); BookmarkProvider bookmarkProvider = BookmarkProvider(); @@ -135,6 +139,7 @@ Future doInit() async { settingProvider = futureResultList[0] as SettingProvider; webProvider = WebProvider(); remoteSigningProvider = RemoteSigningProvider(); + downloadProvider = DownloadProvider(); } class MyApp extends StatefulWidget { @@ -197,6 +202,7 @@ class _MyApp extends State { RouterPath.AUTH_LOGS: (context) => AuthLogsRouter(), RouterPath.SETTING: (context) => SettingRouter(indexReload: reload), RouterPath.ABOUT_ME: (context) => AboutMeRouter(), + RouterPath.DOWNLOADS: (context) => DownloadsRouter(), }; SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( @@ -229,6 +235,9 @@ class _MyApp extends State { ListenableProvider.value( value: bookmarkProvider, ), + ListenableProvider.value( + value: downloadProvider, + ), ], child: MaterialApp( builder: BotToastInit(), diff --git a/lib/provider/download_provider.dart b/lib/provider/download_provider.dart new file mode 100644 index 0000000..1fa8580 --- /dev/null +++ b/lib/provider/download_provider.dart @@ -0,0 +1,100 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:nowser/data/download_log_db.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:background_downloader/background_downloader.dart'; + +import '../data/download_log.dart'; + +class DownloadProvider extends ChangeNotifier { + FileDownloader fileDownloader = FileDownloader(); + + List currentDownloadLogs = []; + + Map taskMap = {}; + + // Future init() async { + // await _reloadData(); + // } + + // Future _reloadData() async { + // // _downloadLogs = await BookmarkDB.all(); + // } + + // Future reload() async { + // await _reloadData(); + // notifyListeners(); + // } + + void pauseDownload(String taskId) { + fileDownloader.database.allRecords(); + var downloadTask = taskMap[taskId]; + if (downloadTask != null) { + fileDownloader.pause(downloadTask); + } + } + + void resumeDownload(String taskId) { + var downloadTask = taskMap[taskId]; + if (downloadTask != null) { + fileDownloader.resume(downloadTask); + } + } + + Future startDownload(String url, String fileName) async { + var downloadDir = await getApplicationDocumentsDirectory(); + + var downloadDirPath = downloadDir.absolute.path; + + print("url $url fileName $fileName downloadDirPath $downloadDirPath"); + + final task = DownloadTask( + url: url, + filename: fileName, + baseDirectory: BaseDirectory.applicationDocuments, + directory: 'downloads', + updates: Updates.statusAndProgress, // request status and progress updates + requiresWiFi: false, + retries: 5, + allowPause: true, + ); + + var downloadLog = DownloadLog( + url: url, + filePath: + "$downloadDirPath${Platform.pathSeparator}downloads${Platform.pathSeparator}$fileName", + fileName: fileName, + taskId: task.taskId, + progress: 0, + ); + + currentDownloadLogs.add(downloadLog); + taskMap[task.taskId] = task; + + // Start download, and wait for result. Show progress and status changes + // while downloading + var downloadResult = await fileDownloader.download( + task, + onProgress: (progress) { + print('Progress: ${progress * 100}%'); + downloadLog.progress = progress; + notifyListeners(); + }, + onStatus: (status) { + print('Status: $status'); + }, + ); + + if (downloadResult.status == TaskStatus.complete) { + var file = File(downloadLog.filePath!); + downloadLog.fileSize = file.lengthSync(); + downloadLog.createdAt = DateTime.now().millisecondsSinceEpoch ~/ 1000; + + DownloadLogDB.insert(downloadLog); + } + + currentDownloadLogs.remove(downloadLog); + notifyListeners(); + } +} diff --git a/lib/router/bookmark/bookmark_router.dart b/lib/router/bookmark/bookmark_router.dart index 3ccae83..9c1d6aa 100644 --- a/lib/router/bookmark/bookmark_router.dart +++ b/lib/router/bookmark/bookmark_router.dart @@ -105,6 +105,10 @@ class _BookmarkRouter extends CustState setState(() { selectedIds.add(bookmark.id!); }); + } else { + setState(() { + selectedIds.remove(bookmark.id!); + }); } }); diff --git a/lib/router/downloads/downloads_router.dart b/lib/router/downloads/downloads_router.dart new file mode 100644 index 0000000..11eddab --- /dev/null +++ b/lib/router/downloads/downloads_router.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:nowser/component/cust_state.dart'; +import 'package:nowser/component/download_list_item_component.dart'; +import 'package:nowser/data/download_log.dart'; +import 'package:nowser/data/download_log_db.dart'; +import 'package:nowser/provider/download_provider.dart'; +import 'package:provider/provider.dart'; + +import '../../component/appbar_back_btn_component.dart'; +import '../../component/deletable_list_mixin.dart'; +import '../../const/base.dart'; +import '../../generated/l10n.dart'; +import '../../util/router_util.dart'; + +class DownloadsRouter extends StatefulWidget { + @override + State createState() { + return _DownloadsRouter(); + } +} + +class _DownloadsRouter extends CustState + with DeletableListMixin { + List selectedIds = []; + + List completedLogs = []; + + @override + Future onReady(BuildContext context) async { + var allList = await DownloadLogDB.all(); + print(allList); + setState(() { + completedLogs = allList; + }); + } + + @override + Widget doBuild(BuildContext context) { + var themeData = Theme.of(context); + var s = S.of(context); + + var _downloadProvider = Provider.of(context); + List currentDownloadLogs = + _downloadProvider.currentDownloadLogs; + + List list = []; + for (var logItem in currentDownloadLogs) { + list.add(wrapItem(DownloadListItemComponent(logItem), logItem)); + } + + int? lastMonth; + int? lastDay; + + for (var logItem in completedLogs) { + if (logItem.createdAt != null) { + var date = + DateTime.fromMillisecondsSinceEpoch(logItem.createdAt! * 1000); + if (date.month != lastMonth || date.day != lastDay) { + var dateStr = DateFormat.yMd().format(date); + list.add(Container( + margin: EdgeInsets.only( + left: Base.BASE_PADDING, top: Base.BASE_PADDING), + child: Text( + dateStr, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + )); + } + } + + list.add(wrapItem(DownloadListItemComponent(logItem), logItem)); + } + + return Scaffold( + appBar: AppBar( + leading: AppbarBackBtnComponent(), + title: Text( + s.Downloads, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: themeData.textTheme.bodyLarge!.fontSize, + ), + ), + actions: genAppBarActions(context), + ), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: list, + ), + ), + ); + } + + Widget wrapItem(Widget child, DownloadLog downloadLog) { + return wrapListItem(child, onTap: () { + // RouterUtil.back(context, history.url); + }, onSelect: () { + if (!selectedIds.contains(downloadLog.id)) { + setState(() { + selectedIds.add(downloadLog.id!); + }); + } else { + setState(() { + selectedIds.remove(downloadLog.id!); + }); + } + }); + } +} diff --git a/lib/router/history/history_router.dart b/lib/router/history/history_router.dart index dc011d6..2b6a4d6 100644 --- a/lib/router/history/history_router.dart +++ b/lib/router/history/history_router.dart @@ -86,6 +86,10 @@ class _HistoryRouter extends CustState with DeletableListMixin { setState(() { selectedIds.add(history.id!); }); + } else { + setState(() { + selectedIds.remove(history.id!); + }); } }); diff --git a/lib/router/index/web_control_component.dart b/lib/router/index/web_control_component.dart index e55dbce..0880566 100644 --- a/lib/router/index/web_control_component.dart +++ b/lib/router/index/web_control_component.dart @@ -155,7 +155,7 @@ class _WebControlComponent extends State { size: 30, ), onTap: () { - BotToast.showText(text: s.Comming_soon); + RouterUtil.router(context, RouterPath.DOWNLOADS); }, ), ), diff --git a/lib/router/me/me_router.dart b/lib/router/me/me_router.dart index 4b1e75e..f199e49 100644 --- a/lib/router/me/me_router.dart +++ b/lib/router/me/me_router.dart @@ -9,6 +9,7 @@ import 'package:nowser/const/router_path.dart'; import 'package:nowser/data/auth_log_db.dart'; import 'package:nowser/data/bookmark_db.dart'; import 'package:nowser/data/browser_history_db.dart'; +import 'package:nowser/data/download_log_db.dart'; import 'package:nowser/main.dart'; import 'package:nowser/provider/app_provider.dart'; import 'package:nowser/provider/key_provider.dart'; @@ -53,6 +54,7 @@ class _MeRouter extends CustState { Future updateNumber() async { bookmarkNum = await BookmarkDB.total(); historyNum = await BrowserHistoryDB.total(); + downloadNum = await DownloadLogDB.total(); setState(() {}); } @@ -186,7 +188,7 @@ class _MeRouter extends CustState { name: s.Downloads, iconData: Icons.download, onTap: () { - BotToast.showText(text: s.Comming_soon); + RouterUtil.router(context, RouterPath.DOWNLOADS); }, )); // webItemList.add(MeRouterWebItemComponent( diff --git a/lib/util/file_size_util.dart b/lib/util/file_size_util.dart new file mode 100644 index 0000000..1d82ac8 --- /dev/null +++ b/lib/util/file_size_util.dart @@ -0,0 +1,12 @@ +class FileSizeUtil { + static String getFileSize(int fileSize) { + if (fileSize > 1024 * 1024 * 1024) { + return "${fileSize ~/ (1024 * 1024 * 1024)} GB"; + } else if (fileSize > 1024 * 1024) { + return "${fileSize ~/ (1024 * 1024)} MB"; + } else if (fileSize > 1024) { + return "${fileSize ~/ 1024} KB"; + } + return "$fileSize B"; + } +} diff --git a/pubspec.lock b/pubspec.lock index 60a9f38..683f8cc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + background_downloader: + dependency: "direct main" + description: + name: background_downloader + sha256: "3a796f29f0834b3e5698e7ac2a39e04939734395420349ff22e1ac2c73a0f73d" + url: "https://pub.dev" + source: hosted + version: "9.2.1" base58check: dependency: transitive description: @@ -580,6 +588,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -605,7 +621,7 @@ packages: source: hosted version: "1.15.0" mime: - dependency: transitive + dependency: "direct main" description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" @@ -651,13 +667,13 @@ packages: source: hosted version: "1.9.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ec9e9a9..a8c5e98 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,9 @@ dependencies: searchfield: ^1.2.7 # webview_cef: ^0.2.2 flutter_font_icons: ^2.2.7 + path_provider: ^2.1.5 + background_downloader: ^9.2.1 + mime: ^2.0.0 dev_dependencies: flutter_launcher_icons: ^0.13.1