From 839e4819f62b8b44b8de24746b0f06e28350d76d Mon Sep 17 00:00:00 2001
From: jigar-f <132374182+jigar-f@users.noreply.github.com>
Date: Thu, 26 Dec 2024 12:19:33 +0530
Subject: [PATCH] Deeplink Support for Desktop (#1272)
* implemented deeplink for macos
* Setup for windows.
* Setup for Linux.
* Fix compile issue and other changes.
* Update checksum.
* Remove entitlements from debug, added swift format in macos.
---
.run/desktop.run.xml | 7 --
Makefile | 3 +-
lib/app.dart | 3 -
linux/my_application.cc | 149 +++++++++++++-----------
macos/Runner.xcodeproj/project.pbxproj | 8 +-
macos/Runner/AppDelegate.swift | 18 +++
macos/Runner/Info.plist | 8 +-
macos/Runner/Release.entitlements | 5 +
pubspec.lock | 44 ++++++-
pubspec.yaml | 17 ++-
windows/packaging/msix/make_config.yaml | 2 +
windows/runner/main.cpp | 44 +++++++
12 files changed, 218 insertions(+), 90 deletions(-)
delete mode 100644 .run/desktop.run.xml
diff --git a/.run/desktop.run.xml b/.run/desktop.run.xml
deleted file mode 100644
index c9e939683d..0000000000
--- a/.run/desktop.run.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Makefile b/Makefile
index c53efdf46b..a70d79f1ce 100644
--- a/Makefile
+++ b/Makefile
@@ -591,6 +591,7 @@ macos: macos-arm64
${DESKTOP_LIB_NAME}_amd64.dylib \
-output ${DARWIN_LIB_NAME}
install_name_tool -id "@rpath/${DARWIN_LIB_NAME}" ${DARWIN_LIB_NAME}
+ rm -Rf ${DESKTOP_LIB_NAME}_arm64.dylib ${DESKTOP_LIB_NAME}_amd64.dylib
$(INSTALLER_NAME).dmg: require-version require-appdmg require-retry require-magick
@echo "Generating distribution package for darwin/amd64..." && \
@@ -733,7 +734,7 @@ assert-go-version:
.PHONY: swift-format
swift-format:
- swift-format --in-place --recursive DBModule ios/Runner ios/Tunnel ios/LanternTests
+ swift-format --in-place --recursive DBModule ios/Runner ios/Tunnel ios/LanternTests macos/Runner
clean:
rm -f liblantern*.aar && \
diff --git a/lib/app.dart b/lib/app.dart
index 3dafd90ee0..d54f759347 100644
--- a/lib/app.dart
+++ b/lib/app.dart
@@ -204,9 +204,6 @@ class _LanternAppState extends State
}
DeepLink navigateToDeepLink(PlatformDeepLink deepLink) {
- if (!Platform.isAndroid) {
- return DeepLink.defaultPath;
- }
logger.d("DeepLink configuration: ${deepLink.configuration.toString()}");
if (deepLink.path.toLowerCase().startsWith('/report-issue')) {
logger.d("DeepLink uri: ${deepLink.uri.toString()}");
diff --git a/linux/my_application.cc b/linux/my_application.cc
index 35209eccb5..6a43c2b5aa 100644
--- a/linux/my_application.cc
+++ b/linux/my_application.cc
@@ -1,6 +1,7 @@
#include "my_application.h"
#include
+
#ifdef GDK_WINDOWING_X11
#include
#endif
@@ -8,95 +9,105 @@
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
- GtkApplication parent_instance;
- char** dart_entrypoint_arguments;
+ GtkApplication parent_instance;
+ char **dart_entrypoint_arguments;
};
-G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+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;
+static void my_application_activate(GApplication *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 = 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;
+ 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, "Lantern");
- 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, "Lantern");
- }
+ 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, "Lantern");
+ 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, "Lantern");
+ }
- gtk_window_set_default_size(window, 360, 712);
- gtk_widget_show(GTK_WIDGET(window));
+ gtk_window_set_default_size(window, 360, 712);
+ 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);
+ 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));
+ 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));
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
- gtk_widget_grab_focus(GTK_WIDGET(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;
+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 FALSE;
}
// 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_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_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) {}
+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,
- nullptr)); }
+MyApplication *my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ "flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
+ nullptr));
+}
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
index 1cbcd726a2..efedbbd434 100644
--- a/macos/Runner.xcodeproj/project.pbxproj
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -570,10 +570,10 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = "";
+ DEVELOPMENT_TEAM = ACZRKC3LQ9;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Lantern;
LD_RUNPATH_SEARCH_PATHS = (
@@ -724,10 +724,10 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
- "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = "";
+ DEVELOPMENT_TEAM = ACZRKC3LQ9;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Lantern;
LD_RUNPATH_SEARCH_PATHS = (
diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift
index a6f73a80ad..b92cebadd4 100644
--- a/macos/Runner/AppDelegate.swift
+++ b/macos/Runner/AppDelegate.swift
@@ -1,9 +1,27 @@
import Cocoa
import FlutterMacOS
+import app_links
@main
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 {
return false
}
+
}
diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist
index e277023973..5fdc29e8d7 100644
--- a/macos/Runner/Info.plist
+++ b/macos/Runner/Info.plist
@@ -2,6 +2,10 @@
+ FlutterDeepLinkingEnabled
+
+ LSApplicationCategoryType
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
@@ -28,7 +32,7 @@
MainMenu
NSPrincipalClass
NSApplication
- com.apple.security.network.client
-
+ com.apple.security.network.client
+
diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements
index 852fa1a472..a17c81038a 100644
--- a/macos/Runner/Release.entitlements
+++ b/macos/Runner/Release.entitlements
@@ -2,6 +2,11 @@
+ com.apple.developer.associated-domains
+
+ webcredentials:lantern.io
+ applinks:lantern.io
+
com.apple.security.app-sandbox
diff --git a/pubspec.lock b/pubspec.lock
index b3261af03c..54129718bf 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -70,6 +70,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.2"
args:
dependency: transitive
description:
@@ -350,6 +358,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
+ console:
+ dependency: transitive
+ description:
+ name: console
+ sha256: e04e7824384c5b39389acdd6dc7d33f3efe6b232f6f16d7626f194f6a01ad69a
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.0"
convert:
dependency: transitive
description:
@@ -880,10 +896,10 @@ packages:
dependency: "direct main"
description:
name: get_it
- sha256: f126a3e286b7f5b578bf436d5592968706c4c1de28a228b870ce375d9f743103
+ sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1
url: "https://pub.dev"
source: hosted
- version: "8.0.3"
+ version: "7.7.0"
gettext_parser:
dependency: transitive
description:
@@ -988,6 +1004,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.0.6"
+ image:
+ dependency: transitive
+ description:
+ name: image
+ sha256: "8346ad4b5173924b5ddddab782fc7d8a6300178c8b1dc427775405a01701c4a6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.5.2"
in_app_purchase:
dependency: "direct main"
description:
@@ -1193,6 +1217,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.4.4"
+ msix:
+ dependency: "direct dev"
+ description:
+ name: msix
+ sha256: c50d6bd1aafe0d071a3c1e5a5ccb056404502935cb0a549e3178c4aae16caf33
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.16.8"
nested:
dependency: transitive
description:
@@ -1401,6 +1433,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
+ posix:
+ dependency: transitive
+ description:
+ name: posix
+ sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.1"
process:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 6674b34047..1fbb5adf1f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -132,7 +132,7 @@ dependencies:
# Deeplink handling
app_links: ^6.3.3
# Service Locator
- get_it: ^8.0.3
+ get_it: ^7.7.0
#Loading
animated_loading_border: ^0.0.2
shimmer: ^3.0.0
@@ -149,6 +149,7 @@ dev_dependencies:
mockito: ^5.4.4
auto_route_generator: ^9.0.0
build_runner: ^2.4.13
+ msix: ^3.16.8
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -206,4 +207,16 @@ flutter:
# weight: 700
#
# For details regarding fonts from package dependencies,
- # see https://flutter.dev/custom-fonts/#from-packages
\ No newline at end of file
+ # see https://flutter.dev/custom-fonts/#from-packages
+
+
+# Windows related setup
+msix_config:
+ display_name: Lantern
+ publisher_display_name: lantern
+ identity_name: org.getlantern.lantern
+ msix_version: 3.0.0.0
+ protocol_activation: https
+ app_uri_handler_hosts: www.lantern.io, lantern.io # Add the app uri handler hosts. You
+ logo_path: windows/runner/resources/app_icon.ico
+ capabilities: internetClient, internetClientServer, privateNetworkClientServer
\ No newline at end of file
diff --git a/windows/packaging/msix/make_config.yaml b/windows/packaging/msix/make_config.yaml
index d3ab30c377..1808b84f2d 100644
--- a/windows/packaging/msix/make_config.yaml
+++ b/windows/packaging/msix/make_config.yaml
@@ -3,5 +3,7 @@ msix_version: 2.1.5.0
publisher_display_name: lantern
identity_name: org.getlantern.lantern
logo_path: windows/runner/resources/app_icon.ico
+protocol_activation: https
+app_uri_handler_hosts: www.lantern.io, lantern.io # Add the app uri handler hosts. You
capabilities: internetClient, internetClientServer, privateNetworkClientServer
install_certificate: "false"
\ No newline at end of file
diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp
index f228a5058e..fd56925ca3 100644
--- a/windows/runner/main.cpp
+++ b/windows/runner/main.cpp
@@ -4,9 +4,52 @@
#include "flutter_window.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,
_In_ wchar_t *command_line, _In_ int show_command) {
+ // You may ignore the result if you need to create another window.
+ if (SendAppLinkToInstance(L"Lantern")) {
+ return EXIT_SUCCESS;
+ }
+
// 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()) {
@@ -41,3 +84,4 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
::CoUninitialize();
return EXIT_SUCCESS;
}
+