From c00bcbcff70182a6b49f028acc3da6485411d527 Mon Sep 17 00:00:00 2001 From: TaYaKi71751 Date: Sun, 10 Dec 2023 23:27:41 +0900 Subject: [PATCH] Add linux --- .github/workflows/build.yml | 77 +++++- .gitignore | 5 +- .metadata | 16 +- .vscode/settings.json | 3 +- AppImageBuilder.yml | 76 ++++++ assets/db/null.db | Bin 0 -> 4096 bytes lib/checker/checker.dart | 9 + lib/component/eh/eh_headers.dart | 13 +- lib/component/eh/eh_provider.dart | 4 +- lib/component/hentai.dart | 2 +- lib/database/database.dart | 17 +- lib/database/user/settings.dart | 128 +++++++++ lib/database/user/user.dart | 15 +- lib/downloader/isolate_downloader.dart | 5 +- lib/log/log.dart | 1 + lib/main.dart | 27 +- lib/pages/article_info/article_info_page.dart | 8 +- .../group/group_article_list_page.dart | 6 +- lib/pages/community/community_page.dart | 18 +- lib/pages/community/user_status_card.dart | 4 +- .../community/user_status_card_dead.dart | 18 +- .../database_download_page.dart | 142 +++++++++- lib/pages/download/download_page.dart | 4 +- lib/pages/lock/lock_screen.dart | 6 +- lib/pages/main/info/lab_page.dart | 6 +- lib/pages/main/main_page.dart | 8 +- lib/pages/search/search_page.dart | 6 +- lib/pages/settings/lock_setting_page.dart | 4 +- lib/pages/settings/route.dart | 4 +- lib/pages/settings/settings_page.dart | 18 +- lib/pages/splash/splash_page.dart | 13 +- lib/server/community/anon.dart | 5 +- lib/server/violet.dart | 4 +- lib/settings/settings.dart | 249 ++++++++++++++++-- lib/update/update_manager.dart | 4 +- lib/version/sync.dart | 8 +- linux/.gitignore | 1 + linux/CMakeLists.txt | 145 ++++++++++ linux/flutter/CMakeLists.txt | 88 +++++++ linux/flutter/generated_plugin_registrant.cc | 19 ++ linux/flutter/generated_plugin_registrant.h | 15 ++ linux/flutter/generated_plugins.cmake | 25 ++ linux/main.cc | 6 + linux/my_application.cc | 104 ++++++++ linux/my_application.h | 18 ++ preprocess-ios.py | 2 +- preprocess-linux.py | 112 ++++++++ pubspec.lock | 40 +-- pubspec.yaml | 3 +- 49 files changed, 1353 insertions(+), 158 deletions(-) create mode 100644 AppImageBuilder.yml create mode 100644 assets/db/null.db create mode 100644 lib/database/user/settings.dart create mode 100644 linux/.gitignore create mode 100644 linux/CMakeLists.txt create mode 100644 linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 linux/main.cc create mode 100644 linux/my_application.cc create mode 100644 linux/my_application.h create mode 100644 preprocess-linux.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cfa87166a..6c50c1569 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,10 +21,16 @@ jobs: python-version: '3.8' - name: Preprocess run: | - cd lib/server - wget -q ${{ secrets.SECRET_SALT }} - wget -q ${{ secrets.SECRET_WSALT }} - cd ../.. + # cd lib/server + # wget -q ${{ secrets.SECRET_SALT }} + # wget -q ${{ secrets.SECRET_WSALT }} + # cd ../.. + cat >> lib/server/salt.dart << EOF + String getValid(foo) {return foo;} + EOF + cat >> lib/server/wsalt.dart << EOF + String getValid(foo) {return foo;} + EOF python3 preprocess-ios.py - name: Podfile run: | @@ -48,6 +54,55 @@ jobs: name: ipa-build path: Payload.ipa + # https://github.com/AppImageCrafters/appimage-builder-flutter-example/blob/main/.github/workflows/appimage.yml + linux-build: + runs-on: Ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: subosito/flutter-action@v1 + # with: + # flutter-version: '1.22.4' + - run: flutter channel beta + - run: flutter upgrade + - run: flutter config --enable-linux-desktop + - name: "Install dependencies" + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build build-essential pkg-config curl file git unzip xz-utils zip libgtk-3-dev + - name: Build flutter app + run: | + # cd lib/server + # wget -q ${{ secrets.SECRET_SALT }} + # wget -q ${{ secrets.SECRET_WSALT }} + # cd ../.. + python3 preprocess-linux.py + cat << EOF > lib/server/salt.dart + String getValid(foo) {return foo;} + EOF + cat << EOF > lib/server/wsalt.dart + String getValid(foo) {return foo;} + EOF + flutter build linux + - name: Build AppImage unsing appimage-builder + uses: docker://appimagecrafters/appimage-builder:0.8.5 + with: + entrypoint: appimage-builder + args: --recipe ./AppImageBuilder.yml --skip-test + - name: Save build Artifact + uses: actions/upload-artifact@v2 + with: + name: AppImage + path: './*.AppImage*' + - name: Release AppImage + uses: marvinpinto/action-automatic-releases@latest + with: + title: Continuous build + automatic_release_tag: 'continuous' + prerelease: true + draft: false + files: './*.AppImage*' + repo_token: ${{ secrets.GITHUB_TOKEN }} + android-build: runs-on: ubuntu-latest steps: @@ -66,10 +121,16 @@ jobs: python-version: '3.8' - name: Preprocess run: | - cd lib/server - wget -q ${{ secrets.SECRET_SALT }} - wget -q ${{ secrets.SECRET_WSALT }} - cd ../.. + # cd lib/server + # wget -q ${{ secrets.SECRET_SALT }} + # wget -q ${{ secrets.SECRET_WSALT }} + # cd ../.. + cat << EOF > lib/server/salt.dart + String getValid(foo) {return foo;} + EOF + cat << EOF > lib/server/wsalt.dart + String getValid(foo) {return foo;} + EOF python3 preprocess-android.py - name: Build run: | diff --git a/.gitignore b/.gitignore index 0efcc6c57..a4ae24dcc 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ migrate_working_dir/ .pub-cache/ .pub/ /build/ +/AppDir/ +*.AppImage +*.appimage # Web related lib/generated_plugin_registrant.dart @@ -62,4 +65,4 @@ emulator.bat img/* test/db test/*.7z -test/rawdata-korean \ No newline at end of file +test/rawdata-korean diff --git a/.metadata b/.metadata index d4114470d..3deb92fa5 100644 --- a/.metadata +++ b/.metadata @@ -1,11 +1,11 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled. +# This file should be version controlled and should not be manually edited. version: - revision: 18a827f3933c19f51862dde3fa472197683249d6 - channel: stable + revision: "7f20e5d18ce4cb80c621533090a7c5113f5bdc52" + channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 18a827f3933c19f51862dde3fa472197683249d6 - base_revision: 18a827f3933c19f51862dde3fa472197683249d6 - - platform: windows - create_revision: 18a827f3933c19f51862dde3fa472197683249d6 - base_revision: 18a827f3933c19f51862dde3fa472197683249d6 + create_revision: 7f20e5d18ce4cb80c621533090a7c5113f5bdc52 + base_revision: 7f20e5d18ce4cb80c621533090a7c5113f5bdc52 + - platform: linux + create_revision: 7f20e5d18ce4cb80c621533090a7c5113f5bdc52 + base_revision: 7f20e5d18ce4cb80c621533090a7c5113f5bdc52 # User provided section diff --git a/.vscode/settings.json b/.vscode/settings.json index 404da7585..fdcf9ab1f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "files.associations": { "ostream": "cpp" - } + }, + "java.compile.nullAnalysis.mode": "disabled" } \ No newline at end of file diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml new file mode 100644 index 000000000..eb822fc01 --- /dev/null +++ b/AppImageBuilder.yml @@ -0,0 +1,76 @@ +# appimage-builder recipe see https://appimage-builder.readthedocs.io for details +version: 1 +script: + - rm -rf AppDir || true + - cp -r build/linux/x64/release/bundle AppDir + - mkdir -p AppDir/usr/share/icons/hicolor/64x64/apps/ + - cp assets/images/logo.png AppDir/usr/share/icons/hicolor/64x64/apps/ +AppDir: + path: ./AppDir + app_info: + id: xyz.project.violet + name: Violet + icon: logo + version: latest + exec: violet + exec_args: $@ + apt: + arch: amd64 + allow_unauthenticated: true + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse + - sourceline: deb http://security.ubuntu.com/ubuntu focal-security main restricted + - sourceline: deb http://security.ubuntu.com/ubuntu focal-security universe + - sourceline: deb http://security.ubuntu.com/ubuntu focal-security multiverse + include: + - libgtk-3-0 + exclude: + - humanity-icon-theme + - hicolor-icon-theme + - adwaita-icon-theme + - ubuntu-mono + files: + include: + - lib/*.so + - data + exclude: + - usr/share/man + - usr/share/doc/*/README.* + - usr/share/doc/*/changelog.* + - usr/share/doc/*/NEWS.* + - usr/share/doc/*/TODO.* + runtime: + env: + GIO_MODULE_DIR: $APPDIR/usr/lib/x86_64-linux-gnu/gio/modules/ + test: + fedora: + image: appimagecrafters/tests-env:fedora-30 + command: ./violet + use_host_x: true + debian: + image: appimagecrafters/tests-env:debian-stable + command: ./violet + use_host_x: true + arch: + image: appimagecrafters/tests-env:archlinux-latest + command: ./violet + use_host_x: true + centos: + image: appimagecrafters/tests-env:centos-7 + command: ./violet + use_host_x: true + ubuntu: + image: appimagecrafters/tests-env:ubuntu-xenial + command: ./violet + use_host_x: true +AppImage: + arch: x86_64 + update-information: guess + sign-key: None + diff --git a/assets/db/null.db b/assets/db/null.db new file mode 100644 index 0000000000000000000000000000000000000000..76b623b198ee85153780294e191b64fc17cedc19 GIT binary patch literal 4096 zcmWFz^vNtqRY=P(%1ta$FlG>7U}R))P*7lCU|@t|AO!{>KB<6_L9b{LFG!aFsai+X qkA}c#2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S0iD+B requestString(String url) async { - final prefs = await SharedPreferences.getInstance(); - var cookie = prefs.getString('eh_cookies'); + final prefs = await MultiPreferences.getInstance(); + var cookie = await prefs.getString('eh_cookies'); return (await http.get(url, headers: {'Cookie': cookie ?? ''})).body; } static Future requestRedirect(String url) async { - final prefs = await SharedPreferences.getInstance(); - var cookie = prefs.getString('eh_cookies'); + final prefs = await MultiPreferences.getInstance(); + var cookie = await prefs.getString('eh_cookies'); Request req = Request('Get', Uri.parse(url))..followRedirects = false; req.headers['Cookie'] = cookie ?? ''; Client baseClient = Client(); @@ -27,8 +28,8 @@ class EHSession { } static Future postComment(String url, String content) async { - final prefs = await SharedPreferences.getInstance(); - var cookie = prefs.getString('eh_cookies'); + final prefs = await MultiPreferences.getInstance(); + var cookie = await prefs.getString('eh_cookies'); return (await http.post(url, headers: {'Cookie': cookie ?? ''}, body: 'commenttext_new=${Uri.encodeFull(content)}')) diff --git a/lib/component/eh/eh_provider.dart b/lib/component/eh/eh_provider.dart index 1467c2712..09889e870 100644 --- a/lib/component/eh/eh_provider.dart +++ b/lib/component/eh/eh_provider.dart @@ -53,8 +53,8 @@ class EHentaiImageProvider extends VioletImageProvider { @override Future> getHeader(int page) async { - final prefs = await SharedPreferences.getInstance(); - var cookie = prefs.getString('eh_cookies'); + final prefs = await MultiPreferences.getInstance(); + var cookie = await prefs.getString('eh_cookies'); return {'Cookie': cookie ?? ''}; } diff --git a/lib/component/hentai.dart b/lib/component/hentai.dart index 401d48eab..69a658320 100644 --- a/lib/component/hentai.dart +++ b/lib/component/hentai.dart @@ -296,7 +296,7 @@ class HentaiManager { 'https://e${exh ? 'x' : '-'}hentai.org/?page=$page&f_cats=993&f_search=$search&advsearch=1&f_sname=on&f_stags=on&f_sh=on&f_spf=&f_spt='; final cookie = - (await SharedPreferences.getInstance()).getString('eh_cookies') ?? ''; + (await (await MultiPreferences.getInstance()).getString('eh_cookies')) ?? ''; final html = (await http.get(url, headers: {'Cookie': '$cookie;sl=dm_2'})).body; diff --git a/lib/database/database.dart b/lib/database/database.dart index 80a7c6101..5582c1761 100644 --- a/lib/database/database.dart +++ b/lib/database/database.dart @@ -33,9 +33,19 @@ class DataBaseManager { if (Platform.environment.containsKey('FLUTTER_TEST')) { dbPath = join(Directory.current.path, 'test/db/data.db'); } else { - dbPath = Platform.isAndroid - ? '${(await getApplicationDocumentsDirectory()).path}/data/data.db' - : '${await getDatabasesPath()}/data.db'; + if(Platform.isAndroid || Platform.isIOS){ + dbPath = Platform.isAndroid + ? '${(await getApplicationDocumentsDirectory()).path}/data/data.db' + : '${await getDatabasesPath()}/data.db'; + } else if(Platform.isLinux){ + var home = ''; + Platform.environment.forEach((key, value) { + if(key == 'HOME'){ + home = value; + } + }); + dbPath = '${home}/.violet/data.db'; + } } _instance = create(dbPath); await _instance!.open(); @@ -46,7 +56,6 @@ class DataBaseManager { static Future reloadInstance() async { final db = _instance?.db; _instance?.db = null; - await db?.close(); } Future open() async { diff --git a/lib/database/user/settings.dart b/lib/database/user/settings.dart new file mode 100644 index 000000000..fef0992b3 --- /dev/null +++ b/lib/database/user/settings.dart @@ -0,0 +1,128 @@ +// This source code is a part of Project Violet. +// Copyright (C) 2020-2023. violet-team. Licensed under the Apache-2.0 License. + +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:sqflite/sqflite.dart'; +import 'package:flutter/services.dart'; + +class SettingsManager { + String? dbPath; + Database? db; + static SettingsManager? _instance; + + SettingsManager({this.dbPath}); + + static SettingsManager create(String dbPath) { + return SettingsManager(dbPath: dbPath); + } + + @protected + @mustCallSuper + void dispose() async { + print('close: ${dbPath!}'); + if (db != null) db!.close(); + } + + static Future getInstance() async { + if (_instance == null) { + var home = ''; + if(Platform.isLinux){ + Platform.environment.forEach((key, value) => { + if(key == 'HOME'){ + home = value + } + }); + } + var dbPath = (Platform.isLinux) + ? '${home}/.violet/settings.db' + : ''; + if(!(await File(dbPath).exists())){ + await File(dbPath).create(recursive: true); + final sharedLibraryPath = 'assets/db/null.db'; + final sharedLibraryContent = await rootBundle.load(sharedLibraryPath); + + final libraryFile = File('${dbPath}'); + final createdFile = await libraryFile.create(); + final openFile = await createdFile.open(mode: FileMode.write); + final writtenFile = + await openFile.writeFrom(Uint8List.view(sharedLibraryContent.buffer)); + await writtenFile.close(); + + + } + _instance = create(dbPath); + await _instance!.open(); + } + return _instance!; + } + + static Future reloadInstance() async { + var home = ''; + if(Platform.isLinux){ + Platform.environment.forEach((key, value) => { + if(key == 'HOME'){ + home = value + } + }); + } + var dbPath = (Platform.isLinux) + ? '${home}/.violet/settings.db' + : ''; + _instance = create(dbPath); + } + + Future open() async { + db ??= await openDatabase(dbPath!); + } + + Future checkOpen() async { + if(db != null){ + if (!db!.isOpen) db = await openDatabase(dbPath!); + } else if(db == null){ + db = await openDatabase(dbPath!); + } + } + + Future>> query(String str) async { + List> result = []; + await checkOpen(); + result = await db!.rawQuery(str); + return result; + } + + Future execute(String str) async { + await checkOpen(); + await db!.execute(str); + } + + Future insert(String name, Map wh) async { + int result = -1; + await checkOpen(); + result = await db!.insert(name, wh); + return result; + } + + Future update(String name, Map wh, String where, + List args) async { + await checkOpen(); + await db!.update(name, wh, where: where, whereArgs: args); + } + + Future swap(String name, String key, String what, int key1, int key2, + int s1, int s2) async { + await checkOpen(); + await db!.transaction((txn) async { + await txn.rawUpdate('UPDATE $name SET $what=? WHERE $key=?', [s2, key1]); + await txn.rawUpdate('UPDATE $name SET $what=? WHERE $key=?', [s1, key2]); + }); + } + + Future delete(String name, String where, List args) async { + await checkOpen(); + await db!.delete(name, where: where, whereArgs: args); + } +} diff --git a/lib/database/user/user.dart b/lib/database/user/user.dart index 1b4c81acb..2d070b1af 100644 --- a/lib/database/user/user.dart +++ b/lib/database/user/user.dart @@ -2,6 +2,7 @@ // Copyright (C) 2020-2023. violet-team. Licensed under the Apache-2.0 License. import 'dart:async'; +import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:violet/database/database.dart'; @@ -11,8 +12,18 @@ class CommonUserDatabase extends DataBaseManager { static Future getInstance() async { if (_instance == null) { - var dir = await getApplicationDocumentsDirectory(); - _instance = DataBaseManager.create('${dir.path}/user.db'); + var home = ''; + if(Platform.isAndroid || Platform.isIOS){ + var dir = await getApplicationDocumentsDirectory(); + _instance = DataBaseManager.create('${dir.path}/user.db'); + } else if(Platform.isLinux){ + Platform.environment.forEach((key, value) => { + if(key == 'HOME'){ + home = value + } + }); + _instance = DataBaseManager.create('${home}/.violet/user.db'); + } await _instance!.open(); } return _instance!; diff --git a/lib/downloader/isolate_downloader.dart b/lib/downloader/isolate_downloader.dart index 5f2ec29f1..ddc87d484 100644 --- a/lib/downloader/isolate_downloader.dart +++ b/lib/downloader/isolate_downloader.dart @@ -10,6 +10,7 @@ import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:violet/component/downloadable.dart'; import 'package:violet/log/log.dart'; +import 'package:violet/settings/settings.dart'; part './isolate/core.dart'; @@ -84,8 +85,8 @@ class IsolateDownloader { } Future _initThreadCount() async { - final prefs = await SharedPreferences.getInstance(); - var tc = prefs.getInt('thread_count'); + final prefs = await MultiPreferences.getInstance(); + var tc = await prefs.getInt('thread_count'); if (tc == null) { tc = 4; await prefs.setInt('thread_count', 4); diff --git a/lib/log/log.dart b/lib/log/log.dart index d5bb7a2dd..d68fb3ece 100644 --- a/lib/log/log.dart +++ b/lib/log/log.dart @@ -62,6 +62,7 @@ class Logger { if (!Platform.environment.containsKey('FLUTTER_TEST')) { await lock.synchronized(() async { + await init(); await logFile.writeAsString('[${DateTime.now().toUtc()}] $msg\n', mode: FileMode.append); }); diff --git a/lib/main.dart b/lib/main.dart index e73a69cc5..a82751b31 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:dynamic_theme/dynamic_theme.dart'; @@ -28,24 +29,34 @@ import 'package:violet/pages/lock/lock_screen.dart'; import 'package:violet/pages/splash/splash_page.dart'; import 'package:violet/settings/settings.dart'; import 'package:violet/style/palette.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:sqflite/sqflite.dart'; Future main() async { runZonedGuarded>(() async { + // https://stackoverflow.com/questions/66971604/sqlite-with-flutter-desktop-windows + if (Platform.isWindows || Platform.isLinux) { + // Initialize FFI + sqfliteFfiInit(); + // Change the default factory + databaseFactory = databaseFactoryFfi; + } + WidgetsFlutterBinding.ensureInitialized(); - await FlutterDownloader.initialize(); // @dependent: android + if(Platform.isAndroid || Platform.isIOS) await FlutterDownloader.initialize(); // @dependent: android FlareCache.doesPrune = false; FlutterError.onError = recordFlutterError; - await initFirebase(); + if(Platform.isAndroid || Platform.isIOS) await initFirebase(); await Settings.initFirst(); - await warmupFlare(); + if(Platform.isAndroid || Platform.isIOS) await warmupFlare(); runApp(const MyApp()); }, (exception, stack) async { Logger.error('[async-error] E: $exception\n$stack'); - await FirebaseCrashlytics.instance.recordError(exception, stack); + if(Platform.isAndroid || Platform.isIOS) await FirebaseCrashlytics.instance.recordError(exception, stack); }); } @@ -67,15 +78,15 @@ Future recordFlutterError(FlutterErrorDetails flutterErrorDetails) async { '[unhandled-error] E: ${flutterErrorDetails.exceptionAsString()}\n' '${flutterErrorDetails.stack}'); - await FirebaseCrashlytics.instance.recordFlutterError(flutterErrorDetails); + if(Platform.isAndroid || Platform.isIOS) FirebaseCrashlytics.instance.recordFlutterError(flutterErrorDetails); } Future initFirebase() async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); // check user-id is set - final prefs = await SharedPreferences.getInstance(); - var id = prefs.getString('fa_userid'); + final prefs = await MultiPreferences.getInstance(); + var id = await prefs.getString('fa_userid'); if (id == null) { id = sha1.convert(utf8.encode(DateTime.now().toString())).toString(); prefs.setString('fa_userid', id); @@ -153,7 +164,7 @@ class MyApp extends StatelessWidget { Settings.useLockScreen ? const LockScreen() : const SplashPage(); final navigatorObservers = [ - FirebaseAnalyticsObserver(analytics: FirebaseAnalytics.instance), + if(Platform.isAndroid || Platform.isIOS) FirebaseAnalyticsObserver(analytics: FirebaseAnalytics.instance), ]; return GetMaterialApp( diff --git a/lib/pages/article_info/article_info_page.dart b/lib/pages/article_info/article_info_page.dart index 796811078..8c9855082 100644 --- a/lib/pages/article_info/article_info_page.dart +++ b/lib/pages/article_info/article_info_page.dart @@ -601,8 +601,8 @@ class __CommentAreaState extends State<_CommentArea> { Future.delayed(const Duration(milliseconds: 100)).then((value) async { if (widget.queryResult.ehash() != null) { - final prefs = await SharedPreferences.getInstance(); - var cookie = prefs.getString('eh_cookies'); + final prefs = await MultiPreferences.getInstance(); + var cookie = await prefs.getString('eh_cookies'); if (cookie != null) { try { final html = await EHSession.requestString( @@ -776,8 +776,8 @@ class __InfoAreaWidgetState extends State<_InfoAreaWidget> { return; } - final prefs = await SharedPreferences.getInstance(); - var cookie = prefs.getString('eh_cookies'); + final prefs = await MultiPreferences.getInstance(); + var cookie = await prefs.getString('eh_cookies'); if (cookie == null || !cookie.contains('ipb_pass_hash')) { await showOkDialog(context, 'Please, Login First!'); return; diff --git a/lib/pages/bookmark/group/group_article_list_page.dart b/lib/pages/bookmark/group/group_article_list_page.dart index 1f2499260..7bac4ce0e 100644 --- a/lib/pages/bookmark/group/group_article_list_page.dart +++ b/lib/pages/bookmark/group/group_article_list_page.dart @@ -93,8 +93,8 @@ class _GroupArticleListPageState extends State { } Future _loadBookmarkAlignType() async { - final prefs = await SharedPreferences.getInstance(); - nowType = prefs.getInt('bookmark_${widget.groupId}') ?? 3; + final prefs = await MultiPreferences.getInstance(); + nowType = await prefs.getInt('bookmark_${widget.groupId}') ?? 3; } Future _refreshAsync() async { @@ -355,7 +355,7 @@ class _GroupArticleListPageState extends State { nowType = value; itemKeys.clear(); - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setInt('bookmark_${widget.groupId}', value); await Future.delayed(const Duration(milliseconds: 50), () { _shouldRebuild = true; diff --git a/lib/pages/community/community_page.dart b/lib/pages/community/community_page.dart index a0cbc5027..1319d1c2d 100644 --- a/lib/pages/community/community_page.dart +++ b/lib/pages/community/community_page.dart @@ -36,12 +36,12 @@ class _CommunityPageState extends State // load boards Future.delayed(const Duration(milliseconds: 100)).then((value) async { - final prefs = await SharedPreferences.getInstance(); - var id = prefs.getString('saved_community_id'); - var pw = prefs.getString('saved_community_pw'); + final prefs = await MultiPreferences.getInstance(); + var id = await prefs.getString('saved_community_id'); + var pw = await prefs.getString('saved_community_pw'); _userId = id ?? 'None'; - _userAppId = prefs.getString('fa_userid')!; + _userAppId = (await prefs.getString('fa_userid'))!; setState(() {}); if (id != null && pw != null) { @@ -66,12 +66,12 @@ class _CommunityPageState extends State } Future _trylogin() async { - final prefs = await SharedPreferences.getInstance(); - var id = prefs.getString('saved_community_id'); - var pw = prefs.getString('saved_community_pw'); + final prefs = await MultiPreferences.getInstance(); + var id = await prefs.getString('saved_community_id'); + var pw = await prefs.getString('saved_community_pw'); _userId = id ?? 'None'; - _userAppId = prefs.getString('fa_userid')!; + _userAppId = (await prefs.getString('fa_userid'))!; setState(() {}); if (id != null && pw != null) { @@ -275,7 +275,7 @@ class _CommunityPageState extends State } } - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setString('saved_community_id', id); await prefs.setString('saved_community_pw', pw); diff --git a/lib/pages/community/user_status_card.dart b/lib/pages/community/user_status_card.dart index 6a3977b6e..2c73891db 100644 --- a/lib/pages/community/user_status_card.dart +++ b/lib/pages/community/user_status_card.dart @@ -45,8 +45,8 @@ class _UserStatusCardState extends ThemeSwitchableState // load boards Future.delayed(const Duration(milliseconds: 100)).then((value) async { - final prefs = await SharedPreferences.getInstance(); - _userAppId = prefs.getString('fa_userid')!; + final prefs = await MultiPreferences.getInstance(); + _userAppId = (await prefs.getString('fa_userid'))!; setState(() {}); // if (id != null && pw != null) { diff --git a/lib/pages/community/user_status_card_dead.dart b/lib/pages/community/user_status_card_dead.dart index 49fb68ca5..45ba2374f 100644 --- a/lib/pages/community/user_status_card_dead.dart +++ b/lib/pages/community/user_status_card_dead.dart @@ -42,11 +42,11 @@ class _UserStatusCardState extends State // load boards Future.delayed(const Duration(milliseconds: 100)).then((value) async { - final prefs = await SharedPreferences.getInstance(); - var id = prefs.getString('saved_community_id'); - var pw = prefs.getString('saved_community_pw'); + final prefs = await MultiPreferences.getInstance(); + var id = await prefs.getString('saved_community_id'); + var pw = await prefs.getString('saved_community_pw'); - _userAppId = prefs.getString('fa_userid')!; + _userAppId = (await prefs.getString('fa_userid'))!; setState(() {}); if (id != null && pw != null) { @@ -71,12 +71,12 @@ class _UserStatusCardState extends State } Future _trylogin() async { - final prefs = await SharedPreferences.getInstance(); - var id = prefs.getString('saved_community_id'); - var pw = prefs.getString('saved_community_pw'); + final prefs = await MultiPreferences.getInstance(); + var id = await prefs.getString('saved_community_id'); + var pw = await prefs.getString('saved_community_pw'); _userId = id ?? 'None'; - _userAppId = prefs.getString('fa_userid')!; + _userAppId = (await prefs.getString('fa_userid'))!; setState(() {}); if (id != null && pw != null) { @@ -325,7 +325,7 @@ class _UserStatusCardState extends State } } - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setString('saved_community_id', id); await prefs.setString('saved_community_pw', pw); diff --git a/lib/pages/database_download/database_download_page.dart b/lib/pages/database_download/database_download_page.dart index ff66f4ad0..c5e3ab443 100644 --- a/lib/pages/database_download/database_download_page.dart +++ b/lib/pages/database_download/database_download_page.dart @@ -59,11 +59,20 @@ class DataBaseDownloadPageState extends State { Future checkDownload() async { try { - final prefs = await SharedPreferences.getInstance(); - if (prefs.getInt('db_exists') == 1) { + final prefs = await MultiPreferences.getInstance(); + if ((await prefs.getInt('db_exists')) == 1) { var dbPath = Platform.isAndroid ? '${(await getApplicationDocumentsDirectory()).path}/data/data.db' : '${await getDatabasesPath()}/data.db'; + if(Platform.isLinux){ + var home = ''; + Platform.environment.forEach((key, value) { + if(key == 'HOME'){ + home = value; + } + }); + dbPath = '${home}/.violet/data.db'; + } if (await File(dbPath).exists()) await File(dbPath).delete(); var dir = await getApplicationDocumentsDirectory(); if (await Directory('${dir.path}/data').exists()) { @@ -76,11 +85,13 @@ class DataBaseDownloadPageState extends State { } if (Platform.isAndroid) { - downloadFileAndroid(); + await downloadFileAndroid(); } else if (Platform.isIOS) { // p7zip is not supported on IOS. // So, download raw database. - downloadFileIOS(); + await downloadFileIOS(); + } else if (Platform.isLinux){ + await downloadFileLinux(); } } @@ -99,6 +110,14 @@ class DataBaseDownloadPageState extends State { } } + Future downloadFileLinux() async { + try { + await downloadFileLinuxWith('latest', true); + } catch(e){ + await downloadFileLinuxWith('old', false); + } + } + Future downloadFileAndroidWith(String target, bool _throw) async { Dio dio = Dio(); int oneMega = 1024 * 1024; @@ -179,7 +198,7 @@ class DataBaseDownloadPageState extends State { await File('${dir.path}/db.sql.7z').delete(); - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setInt('db_exists', 1); await prefs.setString('databasetype', widget.dbType!); await prefs.setString( @@ -281,7 +300,118 @@ class DataBaseDownloadPageState extends State { }); timer.cancel(); - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); + await prefs.setInt('db_exists', 1); + await prefs.setString('databasetype', widget.dbType!); + await prefs.setString( + 'databasesync', SyncManager.getLatestDB().getDateTime().toString()); + await prefs.setInt('synclatest', SyncManager.getLatestDB().timestamp); + + setState(() { + downloading = false; + }); + + await DataBaseManager.reloadInstance(); + + try { + if (Settings.useOptimizeDatabase) await deleteUnused(); + } catch(e1,st1){ + Logger.error('[deleteUnused] E: $e1\n' + '$st1'); + } + try { + await indexing(); + } catch(e1,st1){ + Logger.error('[indexing] E: $e1\n' + '$st1'); + } + + if (widget.isSync == true) { + Navigator.pop(context); + } else { + setState(() { + baseString = Translations.instance!.trans('dbdcomplete'); + _showCloseAppButton = true; + }); + } + + return; + } catch (e, st) { + Logger.error('[DBDownload] E: $e\n' + '$st'); + if(_throw) throw e; + } + + setState(() { + downloading = false; + baseString = Translations.instance!.trans('dbdretry'); + }); + } + Future downloadFileLinuxWith(String target, bool _throw) async { + Dio dio = Dio(); + int oneMega = 1024 * 1024; + int nu = 0; + int latest = 0; + int tlatest = 0; + int tnu = 0; + + try { + var dir = ''; + if(Platform.isLinux){ + Platform.environment.forEach((key, value) => { + if(key == 'HOME'){ + dir = value + } + }); + dir = '${dir}/.violet'; + } + if (await File('$dir/data.db').exists()) { + await File('$dir/data.db').delete(); + } + switch(target){ + case 'latest': await SyncManager.checkSyncLatest(_throw); break; + case 'old': await SyncManager.checkSyncOld(_throw); break; + default: { + try { + await downloadFileLinuxWith('latest', true); + } catch(e){ + await downloadFileLinuxWith('old', false); + } + return; + } + } + + Timer timer = Timer.periodic( + const Duration(seconds: 1), + (Timer timer) => setState(() { + final speed = tlatest / 1024; + speedString = '${_formatNumberWithComma(speed)} KB/s'; + tlatest = tnu; + tnu = 0; + })); + await dio.download( + SyncManager.getLatestDB().getDBDownloadUrliOS(widget.dbType!), + '$dir/data.db', onReceiveProgress: (rec, total) { + nu += rec - latest; + tnu += rec - latest; + latest = rec; + if (nu <= oneMega) return; + + nu = 0; + + setState( + () { + downloading = true; + final progressPercent = (rec / total) * 100; + progressString = '${_formatNumberWithComma(progressPercent)}%'; + downString = + '[${_formatNumberWithComma(rec)}/${_formatNumberWithComma(total)}]'; + }, + ); + }); + timer.cancel(); + + final prefs = await MultiPreferences.getInstance(); await prefs.setInt('db_exists', 1); await prefs.setString('databasetype', widget.dbType!); await prefs.setString( diff --git a/lib/pages/download/download_page.dart b/lib/pages/download/download_page.dart index df0984347..a32a12b03 100644 --- a/lib/pages/download/download_page.dart +++ b/lib/pages/download/download_page.dart @@ -433,9 +433,9 @@ class _DownloadPageState extends ThemeSwitchableState type: MaterialType.transparency, child: InkWell( onTap: () async { - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); if (!Settings.useInnerStorage && - prefs.getBool('checkauthalready') == null) { + (await prefs.getBool('checkauthalready')) == null) { await prefs.setBool('checkauthalready', true); if (await Permission.manageExternalStorage.request() == PermissionStatus.denied) { diff --git a/lib/pages/lock/lock_screen.dart b/lib/pages/lock/lock_screen.dart index d4a8d8274..03ee8f05b 100644 --- a/lib/pages/lock/lock_screen.dart +++ b/lib/pages/lock/lock_screen.dart @@ -288,8 +288,8 @@ class _LockScreenState extends State with TickerProviderStateMixin { } Future _checkPasswordIsCorret() async { - final prefs = await SharedPreferences.getInstance(); - final pinPass = prefs.getString('pinPass'); + final prefs = await MultiPreferences.getInstance(); + final pinPass = await prefs.getString('pinPass'); if (!widget.isRegisterMode && _pin.join() == pinPass) { if (widget.isSecureMode) { @@ -330,7 +330,7 @@ class _LockScreenState extends State with TickerProviderStateMixin { }); } - Future passwordRegisterInteraction(SharedPreferences prefs) async { + Future passwordRegisterInteraction(MultiPreferences prefs) async { await prefs.setString('pinPass', _pin.join()); Future.delayed(const Duration(milliseconds: 300)).then((value) { fToast.showToast( diff --git a/lib/pages/main/info/lab_page.dart b/lib/pages/main/info/lab_page.dart index c5af39942..2334d57fd 100644 --- a/lib/pages/main/info/lab_page.dart +++ b/lib/pages/main/info/lab_page.dart @@ -221,7 +221,7 @@ class _LaboratoryPageState extends State { if (getValid('${text.text}saltff') == '605f372') { await showOkDialog(context, 'Successful!'); - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setString('labmasterkey', text.text); } else { await showOkDialog(context, 'Fail!'); @@ -368,8 +368,8 @@ class _LaboratoryPageState extends State { } Future _checkMaterKey() async { - final prefs = await SharedPreferences.getInstance(); - var key = prefs.getString('labmasterkey'); + final prefs = await MultiPreferences.getInstance(); + var key = await prefs.getString('labmasterkey'); if (key != null && getValid('${key}saltff') == '605f372') { return true; } diff --git a/lib/pages/main/main_page.dart b/lib/pages/main/main_page.dart index 495ea5d81..b5e94e007 100644 --- a/lib/pages/main/main_page.dart +++ b/lib/pages/main/main_page.dart @@ -284,8 +284,8 @@ class _MainPageState extends ThemeSwitchableState }).then((value) async { if (updateContinued) return; - final prefs = await SharedPreferences.getInstance(); - if (prefs.getBool('usevioletserver_check') != null) return; + final prefs = await MultiPreferences.getInstance(); + if ((await prefs.getBool('usevioletserver_check')) != null) return; var bb = await showYesNoDialog( context, Translations.of(context).trans('violetservermsg')); @@ -475,9 +475,9 @@ class _VersionAreaWidgetState extends State<_VersionAreaWidget> { } _onSyncPressed() async { - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); var latestDB = SyncManager.getLatestDB().getDateTime(); - var lastDB = prefs.getString('databasesync'); + var lastDB = await prefs.getString('databasesync'); if (lastDB != null && latestDB.difference(DateTime.parse(lastDB)).inHours < 1) { diff --git a/lib/pages/search/search_page.dart b/lib/pages/search/search_page.dart index 28e6d7c98..476c81588 100644 --- a/lib/pages/search/search_page.dart +++ b/lib/pages/search/search_page.dart @@ -106,10 +106,10 @@ class _SearchPageState extends ThemeSwitchableState } welcomeMessage() async { - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); - if (prefs.getBool('litemode_welcome_message') == null) { - prefs.setBool('litemode_welcome_message', true); + if ((await prefs.getBool('litemode_welcome_message')) == null) { + await prefs.setBool('litemode_welcome_message', true); showOkDialog(context, '라이트 모드가 활성화되었습니다! 설정에서 라이트 모드를 끌 수 있습니다.'); } } diff --git a/lib/pages/settings/lock_setting_page.dart b/lib/pages/settings/lock_setting_page.dart index 973679cd2..985801b1b 100644 --- a/lib/pages/settings/lock_setting_page.dart +++ b/lib/pages/settings/lock_setting_page.dart @@ -109,8 +109,8 @@ class _LockSettingPageState extends State { } Future _toggleAppLock() async { - final prefs = await SharedPreferences.getInstance(); - if (prefs.getString('pinPass') == null) { + final prefs = await MultiPreferences.getInstance(); + if ((await prefs.getString('pinPass')) == null) { showOkDialog(context, Translations.of(context).trans('registerfinbeforeuseapplock')); return; diff --git a/lib/pages/settings/route.dart b/lib/pages/settings/route.dart index 16d31e4c5..e284ff723 100644 --- a/lib/pages/settings/route.dart +++ b/lib/pages/settings/route.dart @@ -37,7 +37,7 @@ class _RouteDialogState extends State { Settings.searchRule[newIndex - 1] = old; } - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setString('searchrule', Settings.searchRule.join('|')); setState(() {}); }, @@ -102,7 +102,7 @@ class _ImageRouteDialogState extends State { Settings.routingRule[newIndex - 1] = old; } - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setString( 'routingrule', Settings.routingRule.join('|')); setState(() {}); diff --git a/lib/pages/settings/settings_page.dart b/lib/pages/settings/settings_page.dart index 6023511a2..9078a6223 100644 --- a/lib/pages/settings/settings_page.dart +++ b/lib/pages/settings/settings_page.dart @@ -1212,9 +1212,9 @@ class _SettingsPageState extends State onTap: Variables.databaseDecompressed ? null : () async { - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); var latestDB = SyncManager.getLatestDB().getDateTime(); - var lastDB = prefs.getString('databasesync'); + var lastDB = await prefs.getString('databasesync'); if (lastDB != null && latestDB.difference(DateTime.parse(lastDB)).inHours < @@ -1503,9 +1503,9 @@ class _SettingsPageState extends State ), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () async { - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); // 32개 => 50mb/s - var tc = prefs.getInt('thread_count'); + var tc = await prefs.getInt('thread_count'); TextEditingController text = TextEditingController(text: tc.toString()); @@ -1797,8 +1797,8 @@ class _SettingsPageState extends State Translations.of(context).trans('restorebookmarkmsg'), Translations.of(context).trans('warning')); - final prefs = await SharedPreferences.getInstance(); - var myappid = prefs.getString('fa_userid'); + final prefs = await MultiPreferences.getInstance(); + var myappid = await prefs.getString('fa_userid'); // 1. 북마크 유저 아이디 선택 TextEditingController text = @@ -2014,7 +2014,7 @@ class _SettingsPageState extends State trailing: const Icon(Icons.keyboard_arrow_right), ), onTap: () async { - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); var ehc = prefs.getString('eh_cookies'); if (ehc == null || ehc == '') { @@ -2251,7 +2251,7 @@ class _SettingsPageState extends State if (dialog == null) return; - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); if (dialog == 1) { var result = await Navigator.of(context).push(MaterialPageRoute( builder: (context) => const LoginScreen())); @@ -2269,7 +2269,7 @@ class _SettingsPageState extends State ); } } else if (dialog == 2) { - var cookie = prefs.getString('eh_cookies'); + var cookie = await prefs.getString('eh_cookies'); var iController = TextEditingController( text: diff --git a/lib/pages/splash/splash_page.dart b/lib/pages/splash/splash_page.dart index eb9504348..0352952a1 100644 --- a/lib/pages/splash/splash_page.dart +++ b/lib/pages/splash/splash_page.dart @@ -166,8 +166,8 @@ class _SplashPageState extends State { }; Future checkAuth() async { - final prefs = await SharedPreferences.getInstance(); - if (prefs.getBool('checkauthalready') == null) { + final prefs = await MultiPreferences.getInstance(); + if ((await prefs.getBool('checkauthalready')) == null) { await prefs.setBool('checkauthalready', true); if (await Permission.manageExternalStorage.request() == PermissionStatus.denied) { @@ -217,8 +217,9 @@ class _SplashPageState extends State { // this may be slow down to loading _changeMessage('check network...'); - var connectivityResult = await (Connectivity().checkConnectivity()); - if (connectivityResult != ConnectivityResult.none) { + var connectivityResult; + if(!Platform.isLinux) connectivityResult = await (Connectivity().checkConnectivity()); + if (Platform.isLinux || connectivityResult != ConnectivityResult.none) { _changeMessage('loading script...'); await ScriptManager.init(); } @@ -238,8 +239,8 @@ class _SplashPageState extends State { // await Logger.exportLog(); // } catch (_) {} - final prefs = await SharedPreferences.getInstance(); - if (prefs.getInt('db_exists') == 1 && !widget.switching) { + final prefs = await MultiPreferences.getInstance(); + if (((await prefs.getInt('db_exists')) == 1) && !widget.switching) { if (connectivityResult != ConnectivityResult.none) { try { _changeMessage('check sync...'); diff --git a/lib/server/community/anon.dart b/lib/server/community/anon.dart index cea88f18e..24806c52d 100644 --- a/lib/server/community/anon.dart +++ b/lib/server/community/anon.dart @@ -8,6 +8,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:violet/log/log.dart'; import 'package:violet/server/violet.dart'; import 'package:violet/server/wsalt.dart'; +import 'package:violet/settings/settings.dart'; class VioletCommunityAnonymous { static Future _getV(String api, String params) async { @@ -50,8 +51,8 @@ class VioletCommunityAnonymous { static String? _userAppId; static Future _getUserAppId() async { if (_userAppId == null) { - final prefs = await SharedPreferences.getInstance(); - _userAppId = prefs.getString('fa_userid'); + final prefs = await MultiPreferences.getInstance(); + _userAppId = await prefs.getString('fa_userid'); } return _userAppId!; } diff --git a/lib/server/violet.dart b/lib/server/violet.dart index c28e228b5..005fd2695 100644 --- a/lib/server/violet.dart +++ b/lib/server/violet.dart @@ -279,8 +279,8 @@ class VioletServer { static String? _userId; static Future _getUserAppId() async { if (_userId == null) { - final prefs = await SharedPreferences.getInstance(); - _userId = prefs.getString('fa_userid'); + final prefs = await MultiPreferences.getInstance(); + _userId = await prefs.getString('fa_userid'); } return _userId!; } diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index e19dd08f3..dc068befa 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -7,18 +7,228 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_windowmanager/flutter_windowmanager.dart'; +import 'package:get/get.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:violet/component/hitomi/shielder.dart'; +import 'package:violet/database/database.dart'; import 'package:violet/database/user/download.dart'; +import 'package:violet/database/user/settings.dart'; import 'package:violet/log/log.dart'; import 'package:violet/platform/android_external_storage_directory.dart'; import 'package:violet/settings/device_type.dart'; +class MultiPreferences { + static late SharedPreferences shared_prefs; + static late SettingsManager db_prefs; + + static MultiPreferences? _instance; + + static MultiPreferences create() { + return MultiPreferences(); + } + + @protected + @mustCallSuper + void dispose() async { + if(Platform.isLinux){ + db_prefs.dispose(); + } + } + + static Future getInstance() async { + // switch(true){ + // case Platform.isAndroid: + // case Platform.isIOS: + // shared_prefs = await SharedPreferences.getInstance(); + // break; + // case Platform.isLinux: + // case Platform.isMacOS: + // db_prefs = await SettingsManager.getInstance(); + // break; + // } + if(Platform.isAndroid || Platform.isIOS){ + shared_prefs = await SharedPreferences.getInstance(); + } else if(Platform.isLinux){ + db_prefs = await SettingsManager.getInstance(); + } + if (_instance == null) { + _instance = create(); + } + return _instance!; + } + Future init() async { + if(Platform.isLinux){ + await db_prefs.execute('CREATE TABLE IF NOT EXISTS SharedPreferences (key STRING, value STRING,type STRING );'); + } + } + + Future getString(String key) async { + if(Platform.isAndroid || Platform.isIOS){ + return Future.value(shared_prefs.getString(key)); + } else if(Platform.isLinux){ + try { + var res = await db_prefs.query('SELECT value FROM SharedPreferences WHERE key="${key}" AND type="STRING";'); + if(res.isEmpty) return Future.value(null); + var a = res.first; + } catch(e){ + return Future.value(null); + } + } else { + throw Error(); + } + } + + Future setString(String key,String value) async { + if(Platform.isAndroid || Platform.isIOS){ + shared_prefs.setString(key, value); + } else if(Platform.isLinux){ + String? result = null; + try { + result = await getString(key); + } catch(e){ + result = null; + } + if(result != null){ + await db_prefs.execute('UPDATE SharedPreferences SET value="${value}" WHERE key="${key}" AND type="STRING";'); + } else { + await db_prefs.execute('INSERT INTO SharedPreferences (key,value,type) VALUES ("${key}","${value}","STRING");'); + } + } + } + + Future getBool(String key) async { + if(Platform.isAndroid || Platform.isIOS){ + return Future.value(shared_prefs.getBool(key)); + } else if(Platform.isLinux){ + try { + var res = await db_prefs.query('SELECT value FROM SharedPreferences WHERE key="${key}" AND type="BOOL";'); + if(res.isEmpty) return Future.value(null); + var a = res.first; + if(res != null){ + if(res.first != null){ + if(res.first.values != null){ + if(res.first.values.first != null){ + return bool.parse(res.first.values.first); + } + } + } + } + return Future.value(null); + } catch(e){ + return Future.value(null); + } + } else { + throw Error(); + } + } + Future setBool(String key,bool value) async { + if(Platform.isAndroid || Platform.isIOS){ + await shared_prefs.setBool(key, value); + } else if(Platform.isLinux){ + bool? result = null; + try { + result = await getBool(key); + } catch(e){ + result = null; + } + if(result != null){ + await db_prefs.execute('UPDATE SharedPreferences SET value="${value}" WHERE key="${key}" AND type="BOOL";'); + } else { + await db_prefs.execute('INSERT INTO SharedPreferences(key,value,type) VALUES("${key}","${value}","BOOL");'); + } + } + } + + Future getDouble(String key) async { + if(Platform.isAndroid || Platform.isIOS){ + return Future.value(shared_prefs.getDouble(key)); + } else if(Platform.isLinux){ + try { + var res = await db_prefs.query('SELECT value FROM SharedPreferences WHERE key="${key}" AND type="DOUBLE";'); + if(res.isEmpty) return Future.value(null); + if(res != null){ + if(res.first != null){ + if(res.first.values != null){ + if(res.first.values.first != null){ + return double.parse(res.first.values.first); + } + } + } + } + return Future.value(null); + } catch(e){ + return Future.value(null); + } + } else { + throw Error(); + } + } + Future setDouble(String key,double value) async { + if(Platform.isAndroid || Platform.isIOS){ + await shared_prefs.setDouble(key, value); + } else if(Platform.isLinux){ + double? result = null; + try { + result = await getDouble(key); + } catch(e){ + result = null; + } + if(result != null){ + await db_prefs.execute('UPDATE SharedPreferences SET value="${value}" WHERE key="${key}" AND type="DOUBLE";'); + } else { + await db_prefs.execute('INSERT INTO SharedPreferences(key,value,type) VALUES("${key}","${value}","DOUBLE");'); + } + } + } + + Future getInt(String key) async { + if(Platform.isAndroid || Platform.isIOS){ + return Future.value(shared_prefs.getInt(key)); + } else if(Platform.isLinux){ + try{ + var res = await db_prefs.query('SELECT value FROM SharedPreferences WHERE key="${key}" AND TYPE="INT";'); + if(res.isEmpty) return Future.value(null); + if(res != null){ + if(res.first != null){ + if(res.first.values != null){ + if(res.first.values.first != null){ + return int.parse(res.first.values.first); + } + } + } + } + return Future.value(null); + } catch(e){ + return Future.value(null); + } + } else { + throw Error(); + } + } + Future setInt(String key,int value) async { + if(Platform.isAndroid || Platform.isIOS){ + await shared_prefs.setInt(key, value); + } else if(Platform.isLinux){ + int? result = null; + try { + result = await getInt(key); + } catch(e){ + result = null; + } + if(result != null){ + await db_prefs.execute('UPDATE SharedPreferences SET value="${value}" WHERE key="${key}" AND type="INT";'); + } else { + await db_prefs.execute('INSERT INTO SharedPreferences(key,value,type) VALUES("${key}","${value}","INT");'); + } + } + } +} + class Settings { - static late final SharedPreferences prefs; + static late final MultiPreferences prefs; // Color Settings static late Color themeColor; // default light @@ -109,7 +319,8 @@ class Settings { static late bool useSecureMode; static Future initFirst() async { - prefs = await SharedPreferences.getInstance(); + prefs = await MultiPreferences.getInstance(); + await prefs.init(); final mc = await _getInt('majorColor', Colors.purple.value); final mac = await _getInt('majorAccentColor', Colors.purpleAccent.value); @@ -124,7 +335,7 @@ class Settings { liteMode = await _getBool('liteMode', true); - language = prefs.getString('language'); + language = await prefs.getString('language'); useLockScreen = await _getBool('useLockScreen'); useSecureMode = await _getBool('useSecureMode'); @@ -148,9 +359,9 @@ class Settings { downloadResultType = await _getInt('downloadResultType', 3); downloadAlignType = await _getInt('downloadAlignType', 0); - var includetags = prefs.getString('includetags'); - var excludetags = prefs.getString('excludetags'); - var blurredtags = prefs.getString('blurredtags'); + var includetags = await prefs.getString('includetags'); + var excludetags = await prefs.getString('excludetags'); + var blurredtags = await prefs.getString('blurredtags'); if (includetags == null) { var language = 'lang:english'; @@ -188,7 +399,7 @@ class Settings { await prefs.setString('routingrule', routingRule.join('|')); } - var databasetype = prefs.getString('databasetype'); + var databasetype = await prefs.getString('databasetype'); if (databasetype == null) { var langcode = Platform.localeName.split('_')[0]; var acclc = ['ko', 'ja', 'en', 'ru', 'zh']; @@ -219,7 +430,7 @@ class Settings { showPageNumberIndicator = await _getBool('showPageNumberIndicator', true); showRecordJumpMessage = await _getBool('showRecordJumpMessage', true); - var tUseInnerStorage = prefs.getBool('useinnerstorage'); + var tUseInnerStorage = await prefs.getBool('useinnerstorage'); if (tUseInnerStorage == null) { tUseInnerStorage = Platform.isIOS; if (Platform.isAndroid) { @@ -234,14 +445,14 @@ class Settings { String? tDownloadBasePath; if (Platform.isAndroid) { - tDownloadBasePath = prefs.getString('downloadbasepath'); + tDownloadBasePath = await prefs.getString('downloadbasepath'); final String path = await AndroidExternalStorageDirectory.instance .getExternalStorageDirectory(); var androidInfo = await DeviceInfoPlugin().androidInfo; var sdkInt = androidInfo.version.sdkInt; - if (sdkInt >= 30 && prefs.getBool('android30downpath') == null) { + if (sdkInt >= 30 && await prefs.getBool('android30downpath') == null) { await prefs.setBool('android30downpath', true); var ext = await getExternalStorageDirectory(); tDownloadBasePath = ext!.path; @@ -292,6 +503,8 @@ class Settings { } } else if (Platform.isIOS) { tDownloadBasePath = await _getString('downloadbasepath', 'not supported'); + } else if(Platform.isLinux){ + tDownloadBasePath = await _getString('downloadbasepath', 'not_supported'); } downloadBasePath = tDownloadBasePath!; @@ -314,7 +527,7 @@ class Settings { searchPure = await _getBool('searchPure'); // main에서 셋팅됨 - userAppId = prefs.getString('fa_userid')!; + if(Platform.isAndroid || Platform.isIOS) userAppId = ((await prefs.getString('fa_userid')))!; autobackupBookmark = await _getBool('autobackupbookmark', false); @@ -358,16 +571,20 @@ class Settings { } static Future _getBool(String key, [bool defaultValue = false]) async { - var nn = prefs.getBool(key); + var nn = await prefs.getBool(key); if (nn == null) { nn = defaultValue; - await prefs.setBool(key, nn); + if(Platform.isAndroid || Platform.isIOS){ + await prefs.setBool(key, nn); + } else if(Platform.isLinux){ + + } } return nn; } static Future _getInt(String key, [int defaultValue = 0]) async { - var nn = prefs.getInt(key); + var nn = await prefs.getInt(key); if (nn == null) { nn = defaultValue; await prefs.setInt(key, nn); @@ -377,7 +594,7 @@ class Settings { static Future _getString(String key, [String defaultValue = '']) async { - var nn = prefs.getString(key); + var nn = await prefs.getString(key); if (nn == null) { nn = defaultValue; await prefs.setString(key, nn); @@ -387,7 +604,7 @@ class Settings { static Future _getDouble(String key, [double defaultValue = 0.0]) async { - var nn = prefs.getDouble(key); + var nn = await prefs.getDouble(key); if (nn == null) { nn = defaultValue; await prefs.setDouble(key, nn); diff --git a/lib/update/update_manager.dart b/lib/update/update_manager.dart index e3274ec3d..c836cbae6 100644 --- a/lib/update/update_manager.dart +++ b/lib/update/update_manager.dart @@ -92,8 +92,8 @@ class UpdateManager { }).then((value) async { if (updateContinued) return; - final prefs = await SharedPreferences.getInstance(); - if (prefs.getBool('usevioletserver_check') != null) return; + final prefs = await MultiPreferences.getInstance(); + if ((await prefs.getBool('usevioletserver_check')) != null) return; final bb = await showYesNoDialog( context, Translations.of(context).trans('violetservermsg')); diff --git a/lib/version/sync.dart b/lib/version/sync.dart index 61bf28d54..fcaab3330 100644 --- a/lib/version/sync.dart +++ b/lib/version/sync.dart @@ -67,9 +67,9 @@ class SyncManager { var infoRaw = (await http.get(syncInfoURL(branch))).body; var lines = ls.convert(infoRaw); - final prefs = await SharedPreferences.getInstance(); - var latest = prefs.getInt('synclatest'); - var lastDB = prefs.getString('databasesync'); + final prefs = await MultiPreferences.getInstance(); + var latest = await prefs.getInt('synclatest'); + var lastDB = await prefs.getString('databasesync'); if (latest == null) { syncRequire = firstSync = true; @@ -228,7 +228,7 @@ class SyncManager { }); await dbraw.close(); - final prefs = await SharedPreferences.getInstance(); + final prefs = await MultiPreferences.getInstance(); await prefs.setInt('synclatest', row.timestamp); } diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 000000000..178c8489c --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "violet") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "xyz.project.violet") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..d5bd01648 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..7a9609beb --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_js_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterJsPlugin"); + flutter_js_plugin_register_with_registrar(flutter_js_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..e0f0a47bc --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..fb0b405e1 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,25 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + flutter_js + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 000000000..c2fbe22ec --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "violet"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "violet"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/preprocess-ios.py b/preprocess-ios.py index 961247eae..8b29715ed 100644 --- a/preprocess-ios.py +++ b/preprocess-ios.py @@ -109,4 +109,4 @@ def create_dummy_valid(path): process_yaml(root + '/' + filename) # create_dummy_valid('./lib/server/salt.dart') -# create_dummy_valid('./lib/server/wsalt.dart') \ No newline at end of file +# create_dummy_valid('./lib/server/wsalt.dart') diff --git a/preprocess-linux.py b/preprocess-linux.py new file mode 100644 index 000000000..92d132722 --- /dev/null +++ b/preprocess-linux.py @@ -0,0 +1,112 @@ +# This source code is a part of Project Violet. +# Copyright (C) 2021.violet-team. Licensed under the Apache-2.0 License. + +import sys +import os +import os.path +import re + +target = 'linux' + +def process_dart(path): + f = open(path, 'r') + w = [] + nl = False + op = False + for line in f.readlines(): + if '//' in line: + annote = re.split(r': |, | ',line.split('//')[-1].strip()) + + if not annote[0].startswith('@dependent'): + if nl or op: + nl = False + else: + w.append(line) + continue + + if annote[1] == target: + w.append(line) + continue + + if len(annote) == 2: + continue + + if annote[2] == '=>': + nl = True + continue + + if annote[2] == '[': + op = True + continue + + if annote[2] == ']': + op = False + continue + else: + if nl or op: + nl = False + else: + w.append(line) + f.close() + f = open(path, 'w+') + f.writelines(w) + f.close() + +def process_yaml(path): + f = open(path, 'r') + w = [] + nl = False + op = False + for line in f.readlines(): + if '#' in line: + annote = re.split(r': |, | ', line.split('#')[-1].strip()) + + if not annote[0].startswith('@dependent'): + if nl or op: + nl = False + else: + w.append(line) + continue + + if annote[1] == target: + w.append(line) + continue + + if len(annote) == 2: + continue + + if annote[2] == '=>': + nl = True + continue + + if annote[2] == '[': + op = True + continue + + if annote[2] == ']': + op = False + continue + else: + if nl or op: + nl = False + else: + w.append(line) + f.close() + f = open(path, 'w+') + f.writelines(w) + f.close() + +def create_dummy_valid(path): + f = open(path, 'w') + f.writelines(['String getValid(foo) {return foo;}']) + f.close() + +for root, subdirs, files in os.walk('./'): + for filename in files: + if filename.endswith(".dart"): + process_dart(root + '/' + filename) + elif filename.endswith(".yaml"): + process_yaml(root + '/' + filename) + +# create_dummy_valid('./lib/server/salt.dart') +# create_dummy_valid('./lib/server/wsalt.dart') diff --git a/pubspec.lock b/pubspec.lock index d41f33258..8260cd786 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -253,10 +253,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" community_charts_common: dependency: transitive description: @@ -1083,10 +1083,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: @@ -1473,42 +1473,42 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.5.0+2" sqflite_common_ffi: - dependency: "direct dev" + dependency: "direct main" description: name: sqflite_common_ffi - sha256: "0d5cc1be2eb18400ac6701c31211d44164393aa75886093002ecdd947be04f93" + sha256: "873677ee78738a723d1ded4ccb23980581998d873d30ee9c331f6a81748663ff" url: "https://pub.dev" source: hosted - version: "2.3.0+2" + version: "2.3.1" sqlite3: dependency: transitive description: name: sqlite3 - sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb + sha256: "8922805564b78eb7aa9386c10056d377a541ac7270dc6a1589176277ebb4d15d" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1553,10 +1553,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" timeago: dependency: "direct main" description: @@ -1713,10 +1713,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1798,5 +1798,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index 489db5eb4..d8f08ea8d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -124,6 +124,7 @@ dependencies: webview_flutter: ^3.0.0 collection: ^1.16.0 octo_image: ^2.0.0 + sqflite_common_ffi: ^2.3.1 dev_dependencies: flutter_test: @@ -132,7 +133,6 @@ dev_dependencies: json_serializable: ^6.7.0 freezed: ^2.3.5 drift_sqflite: ^2.0.0 - sqflite_common_ffi: flutter_lints: ^3.0.0 # For information on the generic Dart part of this file, see the @@ -183,6 +183,7 @@ flutter: - assets/p7zip/x86_64/lib7zr.so # @dependent: android - assets/webview/ + - assets/db/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware.