From 9f5872317d007da5053e73f48f4a9e4ec3364d97 Mon Sep 17 00:00:00 2001 From: SliverAppBar Date: Sat, 14 Oct 2023 15:49:25 +0900 Subject: [PATCH 1/4] init: use isolate and reviver for smooth initialization --- lib/component/hitomi/population.dart | 28 ++++++++++--- lib/component/hitomi/related.dart | 32 ++++++++++---- lib/component/hitomi/tag_translate.dart | 55 ++++++++++++++++++------- 3 files changed, 88 insertions(+), 27 deletions(-) diff --git a/lib/component/hitomi/population.dart b/lib/component/hitomi/population.dart index 3f0e386bc..1a9ae7348 100644 --- a/lib/component/hitomi/population.dart +++ b/lib/component/hitomi/population.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; import 'package:flutter/services.dart'; import 'package:violet/database/query.dart'; @@ -15,18 +16,35 @@ class Population { String data; if (Platform.environment.containsKey('FLUTTER_TEST')) { - var file = File('/home/ubuntu/violet/assets/rank/population.json'); + final file = File('/home/ubuntu/violet/assets/rank/population.json'); data = await file.readAsString(); } else { data = await rootBundle.loadString('assets/rank/population.json'); } - List dataPopulation = json.decode(data); - population = {}; + Future> decodeJsonData() async { + final population = {}; - for (int i = 0; i < dataPopulation.length; i++) { - population[dataPopulation[i] as int] = i; + json.decode( + data, + reviver: (keyObject, valueObject) { + if (keyObject == null) { + return null; + } + + int key = keyObject as int; + int value = (valueObject as num).toInt(); + + population[value] = key; + + return null; + }, + ); + + return population; } + + population = await Isolate.run(decodeJsonData); } static void sortByPopulation(List qr) { diff --git a/lib/component/hitomi/related.dart b/lib/component/hitomi/related.dart index c4f51e590..1dd8d9041 100644 --- a/lib/component/hitomi/related.dart +++ b/lib/component/hitomi/related.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; import 'package:flutter/services.dart'; @@ -13,20 +14,35 @@ class Related { String data; if (Platform.environment.containsKey('FLUTTER_TEST')) { - var file = File('/home/ubuntu/violet/assets/rank/related.json'); + final file = File('/home/ubuntu/violet/assets/rank/related.json'); data = await file.readAsString(); } else { data = await rootBundle.loadString('assets/rank/related.json'); } - Map dataMap = json.decode(data); - - related = >{}; + Future>> decodeJsonData() async { + final related = >{}; + + json.decode( + data, + reviver: (key, value) { + if (key == null) { + return value; + } + + if (key is String) { + related[int.parse(key)] = (value as List).cast(); + return null; + } else { + return value; + } + }, + ); + + return related; + } - dataMap.entries.forEach((element) { - related[int.parse(element.key)] = - (element.value as List).map((e) => e as int).toList(); - }); + related = await Isolate.run(decodeJsonData); } static bool existsRelated(int articleId) { diff --git a/lib/component/hitomi/tag_translate.dart b/lib/component/hitomi/tag_translate.dart index 2c50b4077..ddc9657b2 100644 --- a/lib/component/hitomi/tag_translate.dart +++ b/lib/component/hitomi/tag_translate.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; import 'package:flutter/services.dart'; import 'package:tuple/tuple.dart'; @@ -21,25 +22,51 @@ class TagTranslate { String data; if (Platform.environment.containsKey('FLUTTER_TEST')) { - var file = File('/home/ubuntu/violet/assets/locale/tag/korean.json'); + final file = File('/home/ubuntu/violet/assets/locale/tag/korean.json'); data = await file.readAsString(); } else { data = await rootBundle .loadString('assets/locale/tag/$defaultLanguage.json'); } - Map result = json.decode(data); - - _translateMap = {}; - _reverseAndroMap = {}; - - result.entries.forEach((element) { - if (element.value.toString().trim() == '') return; - if (_translateMap.containsKey(element.key)) return; - _translateMap[element.key] = element.value as String; - _reverseAndroMap[disassembly((element.value as String) - .replaceAll('female:', '') - .replaceAll('male:', ''))] = element.key; - }); + + Future<(Map, Map)> decodeJsonData() async { + final translateMap = {}; + final reverseAndroMap = {}; + + json.decode( + data, + reviver: (keyObject, valueObject) { + if (keyObject == null) { + return null; + } + + final key = keyObject.toString(); + final value = valueObject.toString(); + + if (value.trim().isEmpty) { + return null; + } + + if (!translateMap.containsKey(key)) { + return null; + } + + translateMap[key] = value; + reverseAndroMap[disassembly(value) + .replaceAll('female:', '') + .replaceAll('male:', '')] = key; + + return null; + }, + ); + + return (translateMap, reverseAndroMap); + } + + final (translateMap, reverseAndroMap) = await Isolate.run(decodeJsonData); + + _translateMap = translateMap; + _reverseAndroMap = reverseAndroMap; } static String of(String classification, String key) { From 679ad88a59bc5cb8285f642d9f43aaaadc586899 Mon Sep 17 00:00:00 2001 From: SliverAppBar Date: Sat, 14 Oct 2023 15:55:48 +0900 Subject: [PATCH 2/4] chore: fix typo --- lib/pages/database_download/database_download_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/database_download/database_download_page.dart b/lib/pages/database_download/database_download_page.dart index abc618279..b0578883b 100644 --- a/lib/pages/database_download/database_download_page.dart +++ b/lib/pages/database_download/database_download_page.dart @@ -35,10 +35,10 @@ class DataBaseDownloadPage extends StatefulWidget { }) : super(key: key); @override - State createState() => DataBaseDownloadPagepState(); + State createState() => DataBaseDownloadPageState(); } -class DataBaseDownloadPagepState extends State { +class DataBaseDownloadPageState extends State { bool downloading = false; var baseString = ''; var progressString = ''; From 47b28dfb4694cb2018fb3189e16bf74f22193d93 Mon Sep 17 00:00:00 2001 From: SliverAppBar Date: Sat, 14 Oct 2023 15:57:50 +0900 Subject: [PATCH 3/4] download: show numeric values properly --- .../database_download_page.dart | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/pages/database_download/database_download_page.dart b/lib/pages/database_download/database_download_page.dart index b0578883b..3e0fb4957 100644 --- a/lib/pages/database_download/database_download_page.dart +++ b/lib/pages/database_download/database_download_page.dart @@ -95,7 +95,8 @@ class DataBaseDownloadPageState extends State { Timer timer = Timer.periodic( const Duration(seconds: 1), (Timer timer) => setState(() { - speedString = '${tlatest / 1024} KB/S'; + final speed = tlatest / 1024; + speedString = '${_formatNumberWithComma(speed)} KB/s'; tlatest = tnu; tnu = 0; })); @@ -113,8 +114,10 @@ class DataBaseDownloadPageState extends State { setState( () { downloading = true; - progressString = '${((rec / total) * 100).toStringAsFixed(0)}%'; - downString = '[${numberWithComma(rec)}/${numberWithComma(total)}]'; + final progressPercent = (rec / total) * 100; + progressString = '${_formatNumberWithComma(progressPercent)}%'; + downString = + '[${_formatNumberWithComma(rec)}/${_formatNumberWithComma(total)}]'; }, ); }); @@ -194,7 +197,8 @@ class DataBaseDownloadPageState extends State { Timer timer = Timer.periodic( const Duration(seconds: 1), (Timer timer) => setState(() { - speedString = '${tlatest / 1024} KB/S'; + final speed = tlatest / 1024; + speedString = '${_formatNumberWithComma(speed)} KB/s'; tlatest = tnu; tnu = 0; })); @@ -212,8 +216,10 @@ class DataBaseDownloadPageState extends State { setState( () { downloading = true; - progressString = '${((rec / total) * 100).toStringAsFixed(0)}%'; - downString = '[${numberWithComma(rec)}/${numberWithComma(total)}]'; + final progressPercent = (rec / total) * 100; + progressString = '${_formatNumberWithComma(progressPercent)}%'; + downString = + '[${_formatNumberWithComma(rec)}/${_formatNumberWithComma(total)}]'; }, ); }); @@ -493,8 +499,10 @@ class DataBaseDownloadPageState extends State { } } - String numberWithComma(int param) { - return NumberFormat('###,###,###,###').format(param).replaceAll(' ', ''); + static final _commaFormatter = NumberFormat('#,###.#'); + + String _formatNumberWithComma(num param) { + return _commaFormatter.format(param).replaceAll(' ', ''); } @override From 27c44dfd5b3798a3d6adc3140ab2ace3dc3c4aba Mon Sep 17 00:00:00 2001 From: SliverAppBar Date: Sat, 14 Oct 2023 16:26:42 +0900 Subject: [PATCH 4/4] download: add button to exit the app --- .../kotlin/xyz/project/violet/MainActivity.kt | 14 ++++++++ assets/locale/en.json | 3 +- assets/locale/eo.json | 3 +- assets/locale/it.json | 3 +- assets/locale/ja.json | 3 +- assets/locale/ko.json | 3 +- assets/locale/pt.json | 3 +- assets/locale/zh.json | 3 +- assets/locale/zh_Hans.json | 3 +- assets/locale/zh_Hant.json | 3 +- .../database_download_page.dart | 32 ++++++++++++++++++- lib/platform/misc.dart | 19 +++++++++++ 12 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 lib/platform/misc.dart diff --git a/android/app/src/main/kotlin/xyz/project/violet/MainActivity.kt b/android/app/src/main/kotlin/xyz/project/violet/MainActivity.kt index d2ecae8e4..a9348adba 100644 --- a/android/app/src/main/kotlin/xyz/project/violet/MainActivity.kt +++ b/android/app/src/main/kotlin/xyz/project/violet/MainActivity.kt @@ -9,6 +9,7 @@ import android.view.WindowManager import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugins.GeneratedPluginRegistrant @@ -17,6 +18,7 @@ class MainActivity : FlutterFragmentActivity() { private val VOLUME_CHANNEL = "xyz.project.violet/volume" private val NATIVELIBDIR_CHANNEL = "xyz.project.violet/nativelibdir" private val EXTERNAL_STORAGE_DIRECTORY_CHANNEL = "xyz.project.violet/externalStorageDirectory" + private val MISC_CHANNEL = "xyz.project.violet/misc" private val EXTERNAL_STORAGE_DIRECTORY_METHODS = mapOf( "getExternalStorageDirectory" to MethodCallHandler { call, result -> @@ -76,6 +78,13 @@ class MainActivity : FlutterFragmentActivity() { result.notImplemented() } } + + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, MISC_CHANNEL).setMethodCallHandler { call, result -> + when (call.method) { + "finishMainActivity" -> finishMainActivity(call, result) + else -> result.notImplemented() + } + } } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { @@ -86,4 +95,9 @@ class MainActivity : FlutterFragmentActivity() { return super.onKeyDown(keyCode, event) } + + private fun finishMainActivity(call: MethodCall, result: MethodChannel.Result) { + finish() + result.success(null) + } } diff --git a/assets/locale/en.json b/assets/locale/en.json index c706b7054..6480d776f 100644 --- a/assets/locale/en.json +++ b/assets/locale/en.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/assets/locale/eo.json b/assets/locale/eo.json index d8b64da18..c6e315c79 100644 --- a/assets/locale/eo.json +++ b/assets/locale/eo.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/assets/locale/it.json b/assets/locale/it.json index cdcaf6afb..eb271b286 100644 --- a/assets/locale/it.json +++ b/assets/locale/it.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/assets/locale/ja.json b/assets/locale/ja.json index f3009274c..cbd2907a4 100644 --- a/assets/locale/ja.json +++ b/assets/locale/ja.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/assets/locale/ko.json b/assets/locale/ko.json index 2158012d3..dfda64f38 100644 --- a/assets/locale/ko.json +++ b/assets/locale/ko.json @@ -305,5 +305,6 @@ "lab": "실험실", "realtimeuserrecord": "실시간 유저 레코드", "manualupdate": "수동 업데이트", - "cannotuseios": "iOS에선 사용할 수 없어요 :(" + "cannotuseios": "iOS에선 사용할 수 없어요 :(", + "exitTheApp": "앱 종료하기" } diff --git a/assets/locale/pt.json b/assets/locale/pt.json index 2a42c0073..61d8e90f6 100644 --- a/assets/locale/pt.json +++ b/assets/locale/pt.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/assets/locale/zh.json b/assets/locale/zh.json index fa6c0983f..3a6683edc 100644 --- a/assets/locale/zh.json +++ b/assets/locale/zh.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/assets/locale/zh_Hans.json b/assets/locale/zh_Hans.json index d8822bfd5..85029def9 100644 --- a/assets/locale/zh_Hans.json +++ b/assets/locale/zh_Hans.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/assets/locale/zh_Hant.json b/assets/locale/zh_Hant.json index 6a9123087..8d0c87000 100644 --- a/assets/locale/zh_Hant.json +++ b/assets/locale/zh_Hant.json @@ -305,5 +305,6 @@ "lab": "Laboratory", "realtimeuserrecord": "Real Time User Record", "manualupdate": "Manual Update", - "cannotuseios": "You cannot use update on iOS :(" + "cannotuseios": "You cannot use update on iOS :(", + "exitTheApp": "Exit the app" } diff --git a/lib/pages/database_download/database_download_page.dart b/lib/pages/database_download/database_download_page.dart index 3e0fb4957..f9116dd87 100644 --- a/lib/pages/database_download/database_download_page.dart +++ b/lib/pages/database_download/database_download_page.dart @@ -4,10 +4,12 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:ui'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -18,6 +20,7 @@ import 'package:violet/database/query.dart'; import 'package:violet/locale/locale.dart'; import 'package:violet/log/log.dart'; import 'package:violet/pages/database_download/decompress.dart'; +import 'package:violet/platform/misc.dart'; import 'package:violet/settings/settings.dart'; import 'package:violet/variables.dart'; import 'package:violet/version/sync.dart'; @@ -45,6 +48,8 @@ class DataBaseDownloadPageState extends State { var downString = ''; var speedString = ''; + var _showCloseAppButton = false; + @override void initState() { super.initState(); @@ -166,6 +171,7 @@ class DataBaseDownloadPageState extends State { } else { setState(() { baseString = Translations.instance!.trans('dbdcomplete'); + _showCloseAppButton = true; }); } @@ -245,6 +251,7 @@ class DataBaseDownloadPageState extends State { } else { setState(() { baseString = Translations.instance!.trans('dbdcomplete'); + _showCloseAppButton = true; }); } @@ -492,6 +499,7 @@ class DataBaseDownloadPageState extends State { setState(() { baseString = Translations.instance!.trans('dbdcomplete'); + _showCloseAppButton = true; }); break; } @@ -505,6 +513,16 @@ class DataBaseDownloadPageState extends State { return _commaFormatter.format(param).replaceAll(' ', ''); } + Future _exitApplication() async { + if (Platform.isAndroid) { + await PlatformMiscMethods.instance.finishMainActivity(); + } else if (Platform.isIOS) { + exit(0); + } else { + ServicesBinding.instance.exitApplication(AppExitType.required); + } + } + @override Widget build(BuildContext context) { if (baseString == '') { @@ -551,7 +569,19 @@ class DataBaseDownloadPageState extends State { ), ), ) - : Text(baseString), + : Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(baseString), + if (_showCloseAppButton) ...[ + const SizedBox(height: 32.0), + ElevatedButton( + onPressed: _exitApplication, + child: Text(Translations.of(context).trans('exitTheApp')), + ), + ], + ], + ), ), ); } diff --git a/lib/platform/misc.dart b/lib/platform/misc.dart new file mode 100644 index 000000000..886c33a7c --- /dev/null +++ b/lib/platform/misc.dart @@ -0,0 +1,19 @@ +import 'dart:io'; + +import 'package:flutter/services.dart'; + +class PlatformMiscMethods { + const PlatformMiscMethods._(); + + static const instance = PlatformMiscMethods._(); + + final _methodChannel = const MethodChannel('xyz.project.violet/misc'); + + Future finishMainActivity() async { + if (!Platform.isAndroid) { + throw UnsupportedError('Android only'); + } + + await _methodChannel.invokeMethod('finishMainActivity'); + } +}