feat: deepLink

This commit is contained in:
guozhigq
2024-10-13 23:00:32 +08:00
parent be316f1c69
commit 47ec38b465
12 changed files with 235 additions and 121 deletions

View File

@ -47,13 +47,14 @@
<activity <activity
android:name="com.guozhigq.pilipala.MainActivity" android:name="com.guozhigq.pilipala.MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTask"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:supportsPictureInPicture="true" android:supportsPictureInPicture="true"
android:resizeableActivity="true" android:resizeableActivity="true"
android:autoVerify="true"
> >
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
@ -63,10 +64,21 @@
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
<intent-filter> <intent-filter>
<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>
<!-- Deep Link -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="pili"/>
<data android:scheme="pilipala"/>
</intent-filter>
<!-- App Link -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEARCH" /> <action android:name="android.intent.action.SEARCH" />

View File

@ -1,4 +1,6 @@
PODS: PODS:
- app_links (0.0.2):
- Flutter
- appscheme (1.0.4): - appscheme (1.0.4):
- Flutter - Flutter
- audio_service (0.0.1): - audio_service (0.0.1):
@ -66,6 +68,7 @@ PODS:
- Flutter - Flutter
DEPENDENCIES: DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
- appscheme (from `.symlinks/plugins/appscheme/ios`) - appscheme (from `.symlinks/plugins/appscheme/ios`)
- audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_service (from `.symlinks/plugins/audio_service/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`)
@ -102,6 +105,8 @@ SPEC REPOS:
- Toast - Toast
EXTERNAL SOURCES: EXTERNAL SOURCES:
app_links:
:path: ".symlinks/plugins/app_links/ios"
appscheme: appscheme:
:path: ".symlinks/plugins/appscheme/ios" :path: ".symlinks/plugins/appscheme/ios"
audio_service: audio_service:
@ -160,6 +165,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios" :path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8 appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8
audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_service: f509d65da41b9521a61f1c404dd58651f265a567
audio_session: 4f3e461722055d21515cf3261b64c973c062f345 audio_session: 4f3e461722055d21515cf3261b64c973c062f345

View File

@ -65,44 +65,29 @@
<array> <array>
<dict> <dict>
<key>CFBundleURLName</key> <key>CFBundleURLName</key>
<string></string> <string>bilibili</string>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>http</string> <string>http</string>
<string>https</string> <string>https</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>m.bilibili.com</string>
<string>bilibili.com</string>
<string>www.bilibili.com</string>
<string>bangumi.bilibili.com</string>
<string>bilibili.cn</string>
<string>www.bilibili.cn</string>
<string>bangumi.bilibili.cn</string>
<string>bilibili.tv</string>
<string>www.bilibili.tv</string>
<string>bangumi.bilibili.tv</string>
<string>miniapp.bilibili.com</string>
<string>live.bilibili.com</string>
</array>
</dict>
</array>
</dict>
<!-- 当其他应用程序或系统通过 bilibili -->
<dict>
<key>CFBundleURLName</key>
<string>bilibili</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bilibili</string> <string>bilibili</string>
<string>m.bilibili.com</string>
<string>bilibili.com</string>
<string>www.bilibili.com</string>
<string>bangumi.bilibili.com</string>
<string>bilibili.cn</string>
<string>www.bilibili.cn</string>
<string>bangumi.bilibili.cn</string>
<string>bilibili.tv</string>
<string>www.bilibili.tv</string>
<string>bangumi.bilibili.tv</string>
<string>miniapp.bilibili.com</string>
<string>live.bilibili.com</string>
<string>pili</string>
<string>pilipala</string>
</array> </array>
<key>FlutterDeepLinkingEnabled</key>
<false/>
</dict> </dict>
</array> </array>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>

View File

@ -730,7 +730,7 @@ InlineSpan buildContent(
source: '', source: '',
dataString: matchStr, dataString: matchStr,
); );
PiliSchame.fullPathPush(scheme); PiliSchame.httpsScheme(scheme);
} }
} else { } else {
if (appUrlSchema.startsWith('bilibili://search')) { if (appUrlSchema.startsWith('bilibili://search')) {

View File

@ -1,3 +1,4 @@
import 'package:app_links/app_links.dart';
import 'package:appscheme/appscheme.dart'; import 'package:appscheme/appscheme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -10,22 +11,29 @@ import 'url_utils.dart';
import 'utils.dart'; import 'utils.dart';
class PiliSchame { class PiliSchame {
static late AppLinks appLinks;
static AppScheme appScheme = AppSchemeImpl.getInstance()!; static AppScheme appScheme = AppSchemeImpl.getInstance()!;
static Future<void> init() async { static Future<void> init() async {
/// appLinks = AppLinks();
final SchemeEntity? value = await appScheme.getInitScheme(); appLinks.uriLinkStream.listen((Uri uri) {
if (value != null) { final String scheme = uri.scheme;
_routePush(value); if (RegExp(r'^pili', caseSensitive: false).hasMatch(scheme)) {
} piliScheme(uri);
}
});
appScheme.getInitScheme().then((SchemeEntity? value) {
if (value != null) {
_routePush(value);
}
});
/// 完整链接进入 b23.无效
appScheme.getLatestScheme().then((SchemeEntity? value) { appScheme.getLatestScheme().then((SchemeEntity? value) {
if (value != null) { if (value != null) {
_routePush(value); _routePush(value);
} }
}); });
/// 注册从外部打开的Scheme监听信息 #
appScheme.registerSchemeListener().listen((SchemeEntity? event) { appScheme.registerSchemeListener().listen((SchemeEntity? event) {
if (event != null) { if (event != null) {
_routePush(event); _routePush(event);
@ -36,88 +44,11 @@ class PiliSchame {
/// 路由跳转 /// 路由跳转
static void _routePush(value) async { static void _routePush(value) async {
final String scheme = value.scheme; final String scheme = value.scheme;
final String host = value.host;
final String path = value.path;
if (scheme == 'bilibili') { if (scheme == 'bilibili') {
switch (host) { biliScheme(value);
case 'root':
Navigator.popUntil(
Get.context!, (Route<dynamic> route) => route.isFirst);
break;
case 'space':
final String mid = path.split('/').last;
Get.toNamed<dynamic>(
'/member?mid=$mid',
arguments: <String, dynamic>{'face': null},
);
break;
case 'video':
String pathQuery = path.split('/').last;
final numericRegex = RegExp(r'^[0-9]+$');
if (numericRegex.hasMatch(pathQuery)) {
pathQuery = 'AV$pathQuery';
}
Map map = IdUtils.matchAvorBv(input: pathQuery);
if (map.containsKey('AV')) {
_videoPush(map['AV'], null);
} else if (map.containsKey('BV')) {
_videoPush(null, map['BV']);
} else {
SmartDialog.showToast('投稿匹配失败');
}
break;
case 'live':
final String roomId = path.split('/').last;
Get.toNamed<dynamic>(
'/liveRoom?roomid=$roomId',
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
);
break;
case 'bangumi':
if (path.startsWith('/season')) {
final String seasonId = path.split('/').last;
RoutePush.bangumiPush(int.parse(seasonId), null);
}
break;
case 'opus':
if (path.startsWith('/detail')) {
var opusId = path.split('/').last;
Get.toNamed('/opus', parameters: {
'title': '',
'id': opusId,
'articleType': 'opus',
});
}
break;
case 'search':
Get.toNamed('/searchResult', parameters: {'keyword': ''});
break;
case 'article':
final String id = path.split('/').last.split('?').first;
Get.toNamed(
'/read',
parameters: {
'title': 'cv$id',
'id': id,
'dynamicType': 'read',
},
);
break;
case 'pgc':
if (path.contains('ep')) {
final String lastPathSegment = path.split('/').last;
RoutePush.bangumiPush(
null, int.parse(lastPathSegment.split('?').first));
}
break;
default:
SmartDialog.showToast('未匹配地址,请联系开发者');
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
break;
}
} }
if (scheme == 'https') { if (scheme == 'https') {
fullPathPush(value); httpsScheme(value);
} }
} }
@ -148,7 +79,7 @@ class PiliSchame {
} }
} }
static Future<void> fullPathPush(SchemeEntity value) async { static Future<void> httpsScheme(SchemeEntity value) async {
// https://m.bilibili.com/bangumi/play/ss39708 // https://m.bilibili.com/bangumi/play/ss39708
// https | m.bilibili.com | /bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708
// final String scheme = value.scheme!; // final String scheme = value.scheme!;
@ -281,6 +212,134 @@ class PiliSchame {
} }
} }
static Future<void> biliScheme(SchemeEntity value) async {
final String host = value.host!;
final String path = value.path!;
switch (host) {
case 'root':
Navigator.popUntil(
Get.context!, (Route<dynamic> route) => route.isFirst);
break;
case 'space':
final String mid = path.split('/').last;
Get.toNamed<dynamic>(
'/member?mid=$mid',
arguments: <String, dynamic>{'face': null},
);
break;
case 'video':
String pathQuery = path.split('/').last;
final numericRegex = RegExp(r'^[0-9]+$');
if (numericRegex.hasMatch(pathQuery)) {
pathQuery = 'AV$pathQuery';
}
Map map = IdUtils.matchAvorBv(input: pathQuery);
if (map.containsKey('AV')) {
_videoPush(map['AV'], null);
} else if (map.containsKey('BV')) {
_videoPush(null, map['BV']);
} else {
SmartDialog.showToast('投稿匹配失败');
}
break;
case 'live':
final String roomId = path.split('/').last;
Get.toNamed<dynamic>(
'/liveRoom?roomid=$roomId',
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
);
break;
case 'bangumi':
if (path.startsWith('/season')) {
final String seasonId = path.split('/').last;
RoutePush.bangumiPush(int.parse(seasonId), null);
}
break;
case 'opus':
if (path.startsWith('/detail')) {
var opusId = path.split('/').last;
Get.toNamed('/opus', parameters: {
'title': '',
'id': opusId,
'articleType': 'opus',
});
}
break;
case 'search':
Get.toNamed('/searchResult', parameters: {'keyword': ''});
break;
case 'article':
final String id = path.split('/').last.split('?').first;
Get.toNamed(
'/read',
parameters: {
'title': 'cv$id',
'id': id,
'dynamicType': 'read',
},
);
break;
case 'pgc':
if (path.contains('ep')) {
final String lastPathSegment = path.split('/').last;
RoutePush.bangumiPush(
null, int.parse(lastPathSegment.split('?').first));
}
break;
default:
SmartDialog.showToast('未匹配地址,请联系开发者');
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
break;
}
}
static Future<void> piliScheme(Uri value) async {
final String host = value.host;
final String path = value.path;
final String arg = path.split('/').last;
switch (host) {
case 'home':
case 'root':
Get.toNamed('/');
break;
case 'member':
if (arg != '') {
Get.toNamed<dynamic>(
'/member?mid=$arg',
arguments: <String, dynamic>{'face': null},
);
} else {
Get.toNamed('/mine');
}
break;
case 'search':
if (arg != '') {
Get.toNamed('/searchResult', parameters: {'keyword': arg});
} else {
Get.toNamed('/search');
}
break;
case 'setting':
Get.toNamed('/setting');
break;
case 'fav':
Get.toNamed('/fav');
break;
case 'history':
Get.toNamed('/history');
break;
case 'later':
Get.toNamed('/later');
break;
case 'msg':
Get.toNamed('/whisper');
break;
default:
Get.toNamed('/');
break;
}
}
static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { static void _handleEpisodePath(String lastPathSegment, String redirectUrl) {
final String seasonId = _extractIdFromPath(lastPathSegment); final String seasonId = _extractIdFromPath(lastPathSegment);
RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first); RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first);

View File

@ -8,6 +8,7 @@
#include <dynamic_color/dynamic_color_plugin.h> #include <dynamic_color/dynamic_color_plugin.h>
#include <flutter_volume_controller/flutter_volume_controller_plugin.h> #include <flutter_volume_controller/flutter_volume_controller_plugin.h>
#include <gtk/gtk_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>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
@ -19,6 +20,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar = g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin");
flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar); flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_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) media_kit_libs_linux_registrar = g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color dynamic_color
flutter_volume_controller flutter_volume_controller
gtk
media_kit_libs_linux media_kit_libs_linux
media_kit_video media_kit_video
url_launcher_linux url_launcher_linux

View File

@ -5,6 +5,7 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import app_links
import audio_service import audio_service
import audio_session import audio_session
import connectivity_plus import connectivity_plus
@ -22,6 +23,7 @@ import url_launcher_macos
import wakelock_plus import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
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"))

View File

@ -17,6 +17,38 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.2.0" version: "6.2.0"
app_links:
dependency: "direct main"
description:
name: app_links
sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.2"
app_links_linux:
dependency: transitive
description:
name: app_links_linux
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
app_links_platform_interface:
dependency: transitive
description:
name: app_links_platform_interface
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
app_links_web:
dependency: transitive
description:
name: app_links_web
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
appscheme: appscheme:
dependency: "direct main" dependency: "direct main"
description: description:
@ -686,6 +718,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.1.0" version: "0.1.0"
gtk:
dependency: transitive
description:
name: gtk
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
hive: hive:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -112,6 +112,7 @@ dependencies:
flutter_displaymode: ^0.6.0 flutter_displaymode: ^0.6.0
# scheme跳转 # scheme跳转
appscheme: ^1.0.8 appscheme: ^1.0.8
app_links: ^6.3.2
# 弹幕 # 弹幕
ns_danmaku: ns_danmaku:
git: git:

View File

@ -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 <dynamic_color/dynamic_color_plugin_c_api.h> #include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <flutter_volume_controller/flutter_volume_controller_plugin_c_api.h> #include <flutter_volume_controller/flutter_volume_controller_plugin_c_api.h>
@ -17,6 +18,8 @@
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
AppLinksPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar( ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
DynamicColorPluginCApiRegisterWithRegistrar( DynamicColorPluginCApiRegisterWithRegistrar(

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
app_links
connectivity_plus connectivity_plus
dynamic_color dynamic_color
flutter_volume_controller flutter_volume_controller