mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-03-11 17:25:32 +00:00
added deep links for repo urls
This commit is contained in:
parent
011f62e157
commit
f00b82ddb9
23 changed files with 353 additions and 9 deletions
|
|
@ -29,6 +29,12 @@
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter android:label="Add source repository">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="mangayomi" android:host="add-repo" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
|
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
|
||||||
|
|
@ -39,6 +45,7 @@
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="mangayomi" />
|
<data android:scheme="mangayomi" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Flutter
|
import Flutter
|
||||||
import Libmtorrentserver
|
import Libmtorrentserver
|
||||||
|
import app_links
|
||||||
|
|
||||||
@main
|
@main
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
|
|
@ -29,6 +30,12 @@ import Libmtorrentserver
|
||||||
})
|
})
|
||||||
|
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
|
||||||
|
if let url = AppLinks.shared.getLink(launchOptions: launchOptions) {
|
||||||
|
AppLinks.shared.handleLink(url: url)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,5 +62,7 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>UISupportsDocumentBrowser</key>
|
<key>UISupportsDocumentBrowser</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>FlutterDeepLinkingEnabled</key>
|
||||||
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -416,5 +416,6 @@
|
||||||
"remove_extensions_repo": "Repository-Link entfernen",
|
"remove_extensions_repo": "Repository-Link entfernen",
|
||||||
"manage_manga_repo_url": "Verwalte Repository-Links für Manga",
|
"manage_manga_repo_url": "Verwalte Repository-Links für Manga",
|
||||||
"manage_anime_repo_url": "Verwalte Repository-Links für Anime",
|
"manage_anime_repo_url": "Verwalte Repository-Links für Anime",
|
||||||
"manage_novel_repo_url": "Verwalte Repository-Links für Novellen"
|
"manage_novel_repo_url": "Verwalte Repository-Links für Novellen",
|
||||||
|
"repo_added": "Erweiterungs-Repository hinzugefügt!"
|
||||||
}
|
}
|
||||||
|
|
@ -428,5 +428,6 @@
|
||||||
"invalid_url_format": "Invalid URL format",
|
"invalid_url_format": "Invalid URL format",
|
||||||
"clear_all_sources": "Clear all sources",
|
"clear_all_sources": "Clear all sources",
|
||||||
"clear_all_sources_msg": "This will completely erase all sources of the application. Are you sure you want to continue?",
|
"clear_all_sources_msg": "This will completely erase all sources of the application. Are you sure you want to continue?",
|
||||||
"sources_cleared": "Sources cleared!!!"
|
"sources_cleared": "Sources cleared!!!",
|
||||||
|
"repo_added": "Source repository added!"
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||||
|
|
@ -10,8 +12,12 @@ import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||||
|
import 'package:mangayomi/models/manga.dart';
|
||||||
|
import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/modules/more/data_and_storage/providers/storage_usage.dart';
|
import 'package:mangayomi/modules/more/data_and_storage/providers/storage_usage.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/appearance/providers/app_font_family.dart';
|
import 'package:mangayomi/modules/more/settings/appearance/providers/app_font_family.dart';
|
||||||
|
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
|
||||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||||
import 'package:mangayomi/providers/storage_provider.dart';
|
import 'package:mangayomi/providers/storage_provider.dart';
|
||||||
import 'package:mangayomi/router/router.dart';
|
import 'package:mangayomi/router/router.dart';
|
||||||
|
|
@ -21,6 +27,7 @@ import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_
|
||||||
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart';
|
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:mangayomi/src/rust/frb_generated.dart';
|
import 'package:mangayomi/src/rust/frb_generated.dart';
|
||||||
|
import 'package:mangayomi/utils/url_protocol/api.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
@ -40,6 +47,9 @@ void main(List<String> args) async {
|
||||||
if (!(Platform.isAndroid || Platform.isIOS)) {
|
if (!(Platform.isAndroid || Platform.isIOS)) {
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
}
|
}
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
registerProtocolHandler("mangayomi");
|
||||||
|
}
|
||||||
if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) {
|
if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) {
|
||||||
final availableVersion = await WebViewEnvironment.getAvailableVersion();
|
final availableVersion = await WebViewEnvironment.getAvailableVersion();
|
||||||
if (availableVersion != null) {
|
if (availableVersion != null) {
|
||||||
|
|
@ -73,9 +83,13 @@ class MyApp extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyAppState extends ConsumerState<MyApp> {
|
class _MyAppState extends ConsumerState<MyApp> {
|
||||||
|
late AppLinks _appLinks;
|
||||||
|
StreamSubscription<Uri>? _linkSubscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
iniDateFormatting();
|
iniDateFormatting();
|
||||||
|
initDeepLinks();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (ref.read(clearChapterCacheOnAppLaunchStateProvider)) {
|
if (ref.read(clearChapterCacheOnAppLaunchStateProvider)) {
|
||||||
ref
|
ref
|
||||||
|
|
@ -145,4 +159,55 @@ class _MyAppState extends ConsumerState<MyApp> {
|
||||||
title: 'MangaYomi',
|
title: 'MangaYomi',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_linkSubscription?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initDeepLinks() async {
|
||||||
|
final l10n = l10nLocalizations(context);
|
||||||
|
_appLinks = AppLinks();
|
||||||
|
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
|
||||||
|
switch (uri.host) {
|
||||||
|
case "add-repo":
|
||||||
|
final repoName = uri.queryParameters["repo_name"];
|
||||||
|
final repoUrl = uri.queryParameters["repo_url"];
|
||||||
|
final mangaRepoUrls = uri.queryParametersAll["manga_url"];
|
||||||
|
final animeRepoUrls = uri.queryParametersAll["anime_url"];
|
||||||
|
final novelRepoUrls = uri.queryParametersAll["novel_url"];
|
||||||
|
if (mangaRepoUrls != null) {
|
||||||
|
final mangaRepos =
|
||||||
|
ref.read(extensionsRepoStateProvider(ItemType.manga)).toList();
|
||||||
|
mangaRepos.addAll(mangaRepoUrls.map(
|
||||||
|
(e) => Repo(name: repoName, jsonUrl: e, website: repoUrl)));
|
||||||
|
ref
|
||||||
|
.read(extensionsRepoStateProvider(ItemType.manga).notifier)
|
||||||
|
.set(mangaRepos);
|
||||||
|
}
|
||||||
|
if (animeRepoUrls != null) {
|
||||||
|
final animeRepos =
|
||||||
|
ref.read(extensionsRepoStateProvider(ItemType.anime)).toList();
|
||||||
|
animeRepos.addAll(animeRepoUrls.map(
|
||||||
|
(e) => Repo(name: repoName, jsonUrl: e, website: repoUrl)));
|
||||||
|
ref
|
||||||
|
.read(extensionsRepoStateProvider(ItemType.anime).notifier)
|
||||||
|
.set(animeRepos);
|
||||||
|
}
|
||||||
|
if (novelRepoUrls != null) {
|
||||||
|
final novelRepos =
|
||||||
|
ref.read(extensionsRepoStateProvider(ItemType.novel)).toList();
|
||||||
|
novelRepos.addAll(novelRepoUrls.map(
|
||||||
|
(e) => Repo(name: repoName, jsonUrl: e, website: repoUrl)));
|
||||||
|
ref
|
||||||
|
.read(extensionsRepoStateProvider(ItemType.novel).notifier)
|
||||||
|
.set(novelRepos);
|
||||||
|
}
|
||||||
|
botToast(l10n.repo_added);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:mangayomi/main.dart';
|
import 'package:mangayomi/main.dart';
|
||||||
|
import 'package:mangayomi/models/category.dart';
|
||||||
import 'package:mangayomi/models/history.dart';
|
import 'package:mangayomi/models/history.dart';
|
||||||
import 'package:mangayomi/models/manga.dart';
|
import 'package:mangayomi/models/manga.dart';
|
||||||
import 'package:mangayomi/models/source.dart';
|
import 'package:mangayomi/models/source.dart';
|
||||||
|
|
@ -12,6 +13,8 @@ part 'migration.g.dart';
|
||||||
Future<void> migration(Ref ref) async {
|
Future<void> migration(Ref ref) async {
|
||||||
final mangas =
|
final mangas =
|
||||||
isar.mangas.filter().idIsNotNull().isMangaIsNotNull().findAllSync();
|
isar.mangas.filter().idIsNotNull().isMangaIsNotNull().findAllSync();
|
||||||
|
final categories =
|
||||||
|
isar.categorys.filter().idIsNotNull().forMangaIsNotNull().findAllSync();
|
||||||
|
|
||||||
final histories = isar.historys
|
final histories = isar.historys
|
||||||
.filter()
|
.filter()
|
||||||
|
|
@ -43,6 +46,9 @@ Future<void> migration(Ref ref) async {
|
||||||
for (var manga in mangas) {
|
for (var manga in mangas) {
|
||||||
isar.mangas.putSync(manga..itemType = _convertToItemType(manga.isManga!));
|
isar.mangas.putSync(manga..itemType = _convertToItemType(manga.isManga!));
|
||||||
}
|
}
|
||||||
|
for (var category in categories) {
|
||||||
|
isar.categorys.putSync(category..forItemType = _convertToItemType(category.forManga!));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ ItemType _convertToItemType(Map<String, dynamic> backup) {
|
||||||
ItemType _convertToItemTypeCategory(Map<String, dynamic> backup) {
|
ItemType _convertToItemTypeCategory(Map<String, dynamic> backup) {
|
||||||
final forManga = backup['forManga'];
|
final forManga = backup['forManga'];
|
||||||
return forManga == null
|
return forManga == null
|
||||||
? ItemType.values[backup['itemType'] ?? 0]
|
? ItemType.values[backup['forItemType'] ?? 0]
|
||||||
: forManga
|
: forManga
|
||||||
? ItemType.manga
|
? ItemType.manga
|
||||||
: ItemType.anime;
|
: ItemType.anime;
|
||||||
|
|
|
||||||
42
lib/utils/url_protocol/api.dart
Normal file
42
lib/utils/url_protocol/api.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import 'windows_protocol.dart'
|
||||||
|
if (dart.library.js_interop) 'web_url_protocol.dart';
|
||||||
|
|
||||||
|
/// Registers a protocol by [scheme] to allow for links in the form `<scheme>://...`
|
||||||
|
/// to be processed by this application. By default, opening a link will open
|
||||||
|
/// the executable that was used to register the scheme with the URL as the first
|
||||||
|
/// argument passed to the executable.
|
||||||
|
///
|
||||||
|
/// If a protocol is already registered for the given scheme, this function will
|
||||||
|
/// attempt to overwrite the previous handler with the current executable information.
|
||||||
|
/// However, note that depending on process permissions, this operation may be
|
||||||
|
/// disallowed by the underlying platform.
|
||||||
|
///
|
||||||
|
/// You may pass an [executable] to override the path to the executable to run
|
||||||
|
/// when accessing the URL.
|
||||||
|
///
|
||||||
|
/// [arguments] is a list of arguments to be used when running the executable.
|
||||||
|
/// If passed, the list must contain at least one element, and at least one of
|
||||||
|
/// those elements must contain the literal value `%s` to denote the URL to open.
|
||||||
|
/// Quoting arguments is not necessary, as this will be handled for you.
|
||||||
|
/// Escaping the `%s` as an unprocessed literal is currently unsupported.
|
||||||
|
void registerProtocolHandler(
|
||||||
|
String scheme, {
|
||||||
|
String? executable,
|
||||||
|
List<String>? arguments,
|
||||||
|
}) {
|
||||||
|
WindowsProtocolHandler().register(
|
||||||
|
scheme,
|
||||||
|
executable: executable,
|
||||||
|
arguments: arguments,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unregisters the protocol handler with the underlying platform. The provided
|
||||||
|
/// [scheme] will no longer be used in links.
|
||||||
|
///
|
||||||
|
/// Note that this will unregister a protocol by scheme regardless of which process
|
||||||
|
/// had registered it. Unregistering a scheme that was not registered by this
|
||||||
|
/// application is undefined and depends on platform-specific restrictions.
|
||||||
|
void unregisterProtocolHandler(String scheme) {
|
||||||
|
WindowsProtocolHandler().unregister(scheme);
|
||||||
|
}
|
||||||
15
lib/utils/url_protocol/protocol.dart
Normal file
15
lib/utils/url_protocol/protocol.dart
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
abstract class ProtocolHandler {
|
||||||
|
void register(String scheme, {String? executable, List<String>? arguments});
|
||||||
|
|
||||||
|
void unregister(String scheme);
|
||||||
|
|
||||||
|
List<String> getArguments(List<String>? arguments) {
|
||||||
|
if (arguments == null) return ['%s'];
|
||||||
|
|
||||||
|
if (arguments.isEmpty && !arguments.any((e) => e.contains('%s'))) {
|
||||||
|
throw ArgumentError('arguments must contain at least 1 instance of "%s"');
|
||||||
|
}
|
||||||
|
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
lib/utils/url_protocol/web_url_protocol.dart
Normal file
9
lib/utils/url_protocol/web_url_protocol.dart
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import './protocol.dart';
|
||||||
|
|
||||||
|
class WindowsProtocolHandler extends ProtocolHandler {
|
||||||
|
@override
|
||||||
|
void register(String scheme, {String? executable, List<String>? arguments}) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void unregister(String scheme) {}
|
||||||
|
}
|
||||||
65
lib/utils/url_protocol/windows_protocol.dart
Normal file
65
lib/utils/url_protocol/windows_protocol.dart
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:win32/win32.dart';
|
||||||
|
|
||||||
|
import './protocol.dart';
|
||||||
|
|
||||||
|
const _hive = HKEY_CURRENT_USER;
|
||||||
|
|
||||||
|
class WindowsProtocolHandler extends ProtocolHandler {
|
||||||
|
@override
|
||||||
|
void register(String scheme, {String? executable, List<String>? arguments}) {
|
||||||
|
if (defaultTargetPlatform != TargetPlatform.windows) return;
|
||||||
|
|
||||||
|
final prefix = _regPrefix(scheme);
|
||||||
|
final capitalized = scheme[0].toUpperCase() + scheme.substring(1);
|
||||||
|
final args = getArguments(arguments).map((a) => _sanitize(a));
|
||||||
|
final cmd =
|
||||||
|
'${executable ?? Platform.resolvedExecutable} ${args.join(' ')}';
|
||||||
|
|
||||||
|
_regCreateStringKey(_hive, prefix, '', 'URL:$capitalized');
|
||||||
|
_regCreateStringKey(_hive, prefix, 'URL Protocol', '');
|
||||||
|
_regCreateStringKey(_hive, '$prefix\\shell\\open\\command', '', cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void unregister(String scheme) {
|
||||||
|
if (defaultTargetPlatform != TargetPlatform.windows) return;
|
||||||
|
|
||||||
|
final txtKey = TEXT(_regPrefix(scheme));
|
||||||
|
try {
|
||||||
|
RegDeleteTree(HKEY_CURRENT_USER, txtKey);
|
||||||
|
} finally {
|
||||||
|
free(txtKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _regPrefix(String scheme) => 'SOFTWARE\\Classes\\$scheme';
|
||||||
|
|
||||||
|
int _regCreateStringKey(int hKey, String key, String valueName, String data) {
|
||||||
|
final txtKey = TEXT(key);
|
||||||
|
final txtValue = TEXT(valueName);
|
||||||
|
final txtData = TEXT(data);
|
||||||
|
try {
|
||||||
|
return RegSetKeyValue(
|
||||||
|
hKey,
|
||||||
|
txtKey,
|
||||||
|
txtValue,
|
||||||
|
REG_VALUE_TYPE.REG_SZ,
|
||||||
|
txtData,
|
||||||
|
txtData.length * 2 + 2,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
free(txtKey);
|
||||||
|
free(txtValue);
|
||||||
|
free(txtData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _sanitize(String value) {
|
||||||
|
value = value.replaceAll(r'%s', '%1').replaceAll(r'"', '\\"');
|
||||||
|
return '"$value"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <flutter_qjs/flutter_qjs_plugin.h>
|
#include <flutter_qjs/flutter_qjs_plugin.h>
|
||||||
|
#include <gtk/gtk_plugin.h>
|
||||||
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
|
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
|
||||||
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
||||||
#include <media_kit_video/media_kit_video_plugin.h>
|
#include <media_kit_video/media_kit_video_plugin.h>
|
||||||
|
|
@ -23,6 +24,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) flutter_qjs_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_qjs_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterQjsPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterQjsPlugin");
|
||||||
flutter_qjs_plugin_register_with_registrar(flutter_qjs_registrar);
|
flutter_qjs_plugin_register_with_registrar(flutter_qjs_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) gtk_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
|
||||||
|
gtk_plugin_register_with_registrar(gtk_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =
|
g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin");
|
||||||
isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);
|
isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
desktop_webview_window
|
desktop_webview_window
|
||||||
flutter_qjs
|
flutter_qjs
|
||||||
|
gtk
|
||||||
isar_flutter_libs
|
isar_flutter_libs
|
||||||
media_kit_libs_linux
|
media_kit_libs_linux
|
||||||
media_kit_video
|
media_kit_video
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,13 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
||||||
// Implements GApplication::activate.
|
// Implements GApplication::activate.
|
||||||
static void my_application_activate(GApplication* application) {
|
static void my_application_activate(GApplication* application) {
|
||||||
MyApplication* self = MY_APPLICATION(application);
|
MyApplication* self = MY_APPLICATION(application);
|
||||||
|
|
||||||
|
GList* windows = gtk_application_get_windows(GTK_APPLICATION(application));
|
||||||
|
if (windows) {
|
||||||
|
gtk_window_present(GTK_WINDOW(windows->data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
GtkWindow* window =
|
GtkWindow* window =
|
||||||
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
||||||
|
|
||||||
|
|
@ -78,7 +85,7 @@ static gboolean my_application_local_command_line(GApplication* application, gch
|
||||||
g_application_activate(application);
|
g_application_activate(application);
|
||||||
*exit_status = 0;
|
*exit_status = 0;
|
||||||
|
|
||||||
return TRUE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements GObject::dispose.
|
// Implements GObject::dispose.
|
||||||
|
|
@ -99,6 +106,6 @@ static void my_application_init(MyApplication* self) {}
|
||||||
MyApplication* my_application_new() {
|
MyApplication* my_application_new() {
|
||||||
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
||||||
"application-id", APPLICATION_ID,
|
"application-id", APPLICATION_ID,
|
||||||
"flags", G_APPLICATION_NON_UNIQUE,
|
"flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
|
||||||
nullptr));
|
nullptr));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import app_links
|
||||||
import audio_session
|
import audio_session
|
||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
import flutter_inappwebview_macos
|
import flutter_inappwebview_macos
|
||||||
|
|
@ -28,6 +29,7 @@ import window_manager
|
||||||
import window_to_front
|
import window_to_front
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,19 @@
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
|
import app_links
|
||||||
|
|
||||||
@main
|
@main
|
||||||
class AppDelegate: FlutterAppDelegate {
|
class AppDelegate: FlutterAppDelegate {
|
||||||
|
public override func application(_ application: NSApplication,
|
||||||
|
continue userActivity: NSUserActivity,
|
||||||
|
restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void) -> Bool {
|
||||||
|
guard let url = AppLinks.shared.getUniversalLink(userActivity) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
AppLinks.shared.handleLink(link: url.absoluteString)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,5 +33,16 @@
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>mangayomi</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>mangayomi</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
46
pubspec.lock
46
pubspec.lock
|
|
@ -38,6 +38,38 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.13.2"
|
version: "4.13.2"
|
||||||
|
app_links:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: app_links
|
||||||
|
sha256: "433df2e61b10519407475d7f69e470789d23d593f28224c38ba1068597be7950"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.3"
|
||||||
|
app_links_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_linux
|
||||||
|
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
app_links_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_platform_interface
|
||||||
|
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
|
app_links_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_web
|
||||||
|
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -796,6 +828,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.0.0"
|
||||||
|
gtk:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gtk
|
||||||
|
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
html:
|
html:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -2060,13 +2100,13 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.16.3"
|
version: "3.16.3"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "154360849a56b7b67331c21f09a386562d88903f90a1099c5987afc1912e1f29"
|
sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.10.0"
|
version: "5.10.1"
|
||||||
window_manager:
|
window_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,8 @@ dependencies:
|
||||||
flutter_widget_from_html: ^0.15.3
|
flutter_widget_from_html: ^0.15.3
|
||||||
convert: ^3.1.2
|
convert: ^3.1.2
|
||||||
connectivity_plus: ^6.1.2
|
connectivity_plus: ^6.1.2
|
||||||
|
app_links: ^6.3.3
|
||||||
|
win32: ^5.10.1
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
http: ^1.2.2
|
http: ^1.2.2
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <app_links/app_links_plugin_c_api.h>
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
|
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
|
||||||
#include <flutter_qjs/flutter_qjs_plugin.h>
|
#include <flutter_qjs/flutter_qjs_plugin.h>
|
||||||
|
|
@ -21,6 +22,8 @@
|
||||||
#include <window_to_front/window_to_front_plugin.h>
|
#include <window_to_front/window_to_front_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
AppLinksPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
||||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
|
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
app_links
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
flutter_inappwebview_windows
|
flutter_inappwebview_windows
|
||||||
flutter_qjs
|
flutter_qjs
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,51 @@
|
||||||
|
|
||||||
#include "flutter_window.h"
|
#include "flutter_window.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "app_links/app_links_plugin_c_api.h"
|
||||||
|
|
||||||
|
bool SendAppLinkToInstance(const std::wstring& title) {
|
||||||
|
// Find our exact window
|
||||||
|
HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", title.c_str());
|
||||||
|
|
||||||
|
if (hwnd) {
|
||||||
|
// Dispatch new link to current window
|
||||||
|
SendAppLink(hwnd);
|
||||||
|
|
||||||
|
// (Optional) Restore our window to front in same state
|
||||||
|
WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) };
|
||||||
|
GetWindowPlacement(hwnd, &place);
|
||||||
|
|
||||||
|
switch(place.showCmd) {
|
||||||
|
case SW_SHOWMAXIMIZED:
|
||||||
|
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
|
||||||
|
break;
|
||||||
|
case SW_SHOWMINIMIZED:
|
||||||
|
ShowWindow(hwnd, SW_RESTORE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ShowWindow(hwnd, SW_NORMAL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
// END (Optional) Restore
|
||||||
|
|
||||||
|
// Window has been found, don't create another one.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||||
_In_ wchar_t *command_line, _In_ int show_command) {
|
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||||
|
// Replace "example" with the generated title found as parameter of `window.Create` in this file.
|
||||||
|
// You may ignore the result if you need to create another window.
|
||||||
|
if (SendAppLinkToInstance(L"Mangayomi")) {
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// Attach to console when present (e.g., 'flutter run') or create a
|
// Attach to console when present (e.g., 'flutter run') or create a
|
||||||
// new console when running with a debugger.
|
// new console when running with a debugger.
|
||||||
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
|
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
|
||||||
|
|
@ -40,4 +82,4 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||||
|
|
||||||
::CoUninitialize();
|
::CoUninitialize();
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue