mirror of https://github.com/Wox-launcher/Wox
Add support for deep linking
Deep linking capability was added to the application along with handling for custom 'wox' scheme. This change enables the application to receive and process deep links, allowing navigation to specific parts of the application from schema-formatted URLs. This will let users to directly install a new plugin from plugin store, and also improve inter-app and website-app interactions.
This commit is contained in:
parent
25dbcae518
commit
048b1363f7
|
@ -97,4 +97,11 @@ class WoxApi {
|
|||
"query": query.toJson(),
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> onProtocolUrlReceived(String command, Map<String, String> arguments) async {
|
||||
await WoxHttpUtil.instance.postData("/deeplink", {
|
||||
"command": command,
|
||||
"arguments": arguments,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ import 'dart:io';
|
|||
|
||||
import 'package:chinese_font_library/chinese_font_library.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:protocol_handler/protocol_handler.dart';
|
||||
import 'package:uuid/v4.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:wox/api/wox_api.dart';
|
||||
|
@ -23,6 +23,7 @@ import 'package:wox/utils/wox_websocket_msg_util.dart';
|
|||
void main(List<String> arguments) async {
|
||||
await initialServices(arguments);
|
||||
await initWindow();
|
||||
await initDeepLink();
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
|
@ -58,6 +59,12 @@ Future<void> initialServices(List<String> arguments) async {
|
|||
Get.put(WoxSettingController());
|
||||
}
|
||||
|
||||
Future<void> initDeepLink() async {
|
||||
// Register a custom protocol
|
||||
// For macOS platform needs to declare the scheme in ios/Runner/Info.plist
|
||||
await protocolHandler.register('wox');
|
||||
}
|
||||
|
||||
Future<void> initWindow() async {
|
||||
await windowManager.ensureInitialized();
|
||||
await Window.initialize();
|
||||
|
@ -99,11 +106,13 @@ class WoxApp extends StatefulWidget {
|
|||
State<WoxApp> createState() => _WoxAppState();
|
||||
}
|
||||
|
||||
class _WoxAppState extends State<WoxApp> with WindowListener {
|
||||
class _WoxAppState extends State<WoxApp> with WindowListener, ProtocolListener {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
protocolHandler.addListener(this);
|
||||
|
||||
setAcrylicEffect();
|
||||
|
||||
var launcherController = Get.find<WoxLauncherController>();
|
||||
|
@ -144,8 +153,28 @@ class _WoxAppState extends State<WoxApp> with WindowListener {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onProtocolUrlReceived(String url) {
|
||||
Logger.instance.info(const UuidV4().generate(), "deep link received: $url");
|
||||
//replace %20 with space in the url
|
||||
url = url.replaceAll("%20", " ");
|
||||
// split the command and argument
|
||||
// wox://command?argument=value&argument2=value2
|
||||
var command = url.split("?")[0].split("//")[1];
|
||||
var arguments = url.split("?")[1].split("&");
|
||||
var argumentMap = <String, String>{};
|
||||
for (var argument in arguments) {
|
||||
var key = argument.split("=")[0];
|
||||
var value = argument.split("=")[1];
|
||||
argumentMap[key] = value;
|
||||
}
|
||||
|
||||
WoxApi.instance.onProtocolUrlReceived(command, argumentMap);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
protocolHandler.removeListener(this);
|
||||
windowManager.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import device_info_plus
|
|||
import hotkey_manager_macos
|
||||
import macos_window_utils
|
||||
import path_provider_foundation
|
||||
import protocol_handler_macos
|
||||
import screen_retriever
|
||||
import syncfusion_pdfviewer_macos
|
||||
import url_launcher_macos
|
||||
|
@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
|
||||
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ProtocolHandlerMacosPlugin.register(with: registry.registrar(forPlugin: "ProtocolHandlerMacosPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
|
|
|
@ -13,6 +13,8 @@ PODS:
|
|||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- protocol_handler_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- screen_retriever (0.0.1):
|
||||
- FlutterMacOS
|
||||
- syncfusion_pdfviewer_macos (0.0.1):
|
||||
|
@ -29,6 +31,7 @@ DEPENDENCIES:
|
|||
- hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`)
|
||||
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- protocol_handler_macos (from `Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos`)
|
||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||
- syncfusion_pdfviewer_macos (from `Flutter/ephemeral/.symlinks/plugins/syncfusion_pdfviewer_macos/macos`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
|
@ -51,6 +54,8 @@ EXTERNAL SOURCES:
|
|||
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
|
||||
path_provider_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
protocol_handler_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos
|
||||
screen_retriever:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||
syncfusion_pdfviewer_macos:
|
||||
|
@ -68,6 +73,7 @@ SPEC CHECKSUMS:
|
|||
hotkey_manager_macos: 1e2edb0c7ae4fe67108af44a9d3445de41404160
|
||||
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
protocol_handler_macos: d10a6c01d6373389ffd2278013ab4c47ed6d6daa
|
||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||
syncfusion_pdfviewer_macos: e9194851581cad04b28b53913d0636d39a4ed4b2
|
||||
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
|
||||
|
|
|
@ -26,6 +26,19 @@
|
|||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>wox</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
|
|
|
@ -517,6 +517,54 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
protocol_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: protocol_handler
|
||||
sha256: dc2e2dcb1e0e313c3f43827ec3fa6d98adee6e17edc0c3923ac67efee87479a9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
protocol_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protocol_handler_android
|
||||
sha256: "82eb860ca42149e400328f54b85140329a1766d982e94705b68271f6ca73895c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
protocol_handler_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protocol_handler_ios
|
||||
sha256: "0d3a56b8c1926002cb1e32b46b56874759f4dcc8183d389b670864ac041b6ec2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
protocol_handler_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protocol_handler_macos
|
||||
sha256: "6eb8687a84e7da3afbc5660ce046f29d7ecf7976db45a9dadeae6c87147dd710"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
protocol_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protocol_handler_platform_interface
|
||||
sha256: "53776b10526fdc25efdf1abcf68baf57fdfdb75342f4101051db521c9e3f3e5b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
protocol_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protocol_handler_windows
|
||||
sha256: d8f3a58938386aca2c76292757392f4d059d09f11439d6d896d876ebe997f2c4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
recase:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -56,6 +56,7 @@ dependencies:
|
|||
url_launcher: ^6.2.5
|
||||
flutter_image_slideshow: ^0.1.6
|
||||
dynamic_tabbar: ^1.0.6
|
||||
protocol_handler: ^0.2.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <desktop_drop/desktop_drop_plugin.h>
|
||||
#include <flutter_acrylic/flutter_acrylic_plugin.h>
|
||||
#include <hotkey_manager_windows/hotkey_manager_windows_plugin_c_api.h>
|
||||
#include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <syncfusion_pdfviewer_windows/syncfusion_pdfviewer_windows_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
@ -21,6 +22,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||
registry->GetRegistrarForPlugin("FlutterAcrylicPlugin"));
|
||||
HotkeyManagerWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("HotkeyManagerWindowsPluginCApi"));
|
||||
ProtocolHandlerWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ProtocolHandlerWindowsPluginCApi"));
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
SyncfusionPdfviewerWindowsPluginRegisterWithRegistrar(
|
||||
|
|
|
@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||
desktop_drop
|
||||
flutter_acrylic
|
||||
hotkey_manager_windows
|
||||
protocol_handler_windows
|
||||
screen_retriever
|
||||
syncfusion_pdfviewer_windows
|
||||
url_launcher_windows
|
||||
|
|
|
@ -5,8 +5,21 @@
|
|||
#include "flutter_window.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.h>
|
||||
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||
|
||||
// Replace protocol_handler_example with your_window_title.
|
||||
HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", L"wox");
|
||||
if (hwnd != NULL) {
|
||||
DispatchToProtocolHandler(hwnd);
|
||||
|
||||
::ShowWindow(hwnd, SW_NORMAL);
|
||||
::SetForegroundWindow(hwnd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Attach to console when present (e.g., 'flutter run') or create a
|
||||
// new console when running with a debugger.
|
||||
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
|
||||
|
|
|
@ -479,3 +479,18 @@ func (m *Manager) SetActiveWindowName(name string) {
|
|||
func (m *Manager) GetActiveWindowName() string {
|
||||
return m.activeWindowName
|
||||
}
|
||||
|
||||
func (m *Manager) PostDeeplink(ctx context.Context, command string, arguments map[string]string) {
|
||||
logger.Info(ctx, fmt.Sprintf("deeplink: %s, %v", command, arguments))
|
||||
|
||||
if command == "query" {
|
||||
query := arguments["q"]
|
||||
if query != "" {
|
||||
m.ui.ChangeQuery(ctx, share.PlainQuery{
|
||||
QueryType: plugin.QueryTypeInput,
|
||||
QueryText: query,
|
||||
})
|
||||
m.ui.ShowApp(ctx, share.ShowContext{SelectAll: false})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ var routers = map[string]func(w http.ResponseWriter, r *http.Request){
|
|||
"/backup/all": handleBackupAll,
|
||||
"/hotkey/available": handleHotkeyAvailable,
|
||||
"/query/icon": handleQueryIcon,
|
||||
"/deeplink": handleDeeplink,
|
||||
}
|
||||
|
||||
func handleHome(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -718,3 +719,29 @@ func handleQueryIcon(w http.ResponseWriter, r *http.Request) {
|
|||
iconImage := plugin.ConvertIcon(ctx, iconImg, pluginInstance.PluginDirectory)
|
||||
writeSuccessResponse(w, iconImage)
|
||||
}
|
||||
|
||||
func handleDeeplink(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := util.NewTraceContext()
|
||||
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
commandResult := gjson.GetBytes(body, "command")
|
||||
if !commandResult.Exists() {
|
||||
writeErrorResponse(w, "command is empty")
|
||||
return
|
||||
}
|
||||
|
||||
// arguments is map[string]string
|
||||
argumentsResult := gjson.GetBytes(body, "arguments")
|
||||
var arguments = make(map[string]string)
|
||||
if argumentsResult.Exists() {
|
||||
err := json.Unmarshal([]byte(argumentsResult.String()), &arguments)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
GetUIManager().PostDeeplink(ctx, commandResult.String(), arguments)
|
||||
|
||||
writeSuccessResponse(w, "")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ fetch('https://raw.githubusercontent.com/Wox-launcher/Wox/v2/plugin-store.json')
|
|||
|
||||
let thead = document.createElement('thead');
|
||||
let headerRow = document.createElement('tr');
|
||||
let headers = ['Icon', 'Name', 'Description', 'Author', 'Version'];
|
||||
let headers = ['Icon', 'Name', 'Description', 'Author', 'Version', 'Install'];
|
||||
headers.forEach(header => {
|
||||
let th = document.createElement('th');
|
||||
if (header === 'Icon') {
|
||||
|
@ -45,6 +45,7 @@ fetch('https://raw.githubusercontent.com/Wox-launcher/Wox/v2/plugin-store.json')
|
|||
plugin.Description,
|
||||
plugin.Author,
|
||||
`v${plugin.Version}`,
|
||||
`<a href="wox://query?q=wpm install ${plugin.Name}" target="_blank">Install</a>`
|
||||
];
|
||||
cells.forEach(cell => {
|
||||
let td = document.createElement('td');
|
||||
|
|
Loading…
Reference in New Issue