blob: b0c7fece0e7c6f16c1294a62143139d59e59d6a9 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/url_launcher_linux/url_launcher_plugin.h"
#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
#include <cstring>
#include "url_launcher_plugin_private.h"
// See url_launcher_channel.dart for documentation.
const char kChannelName[] = "plugins.flutter.io/url_launcher_linux";
const char kBadArgumentsError[] = "Bad Arguments";
const char kLaunchError[] = "Launch Error";
const char kCanLaunchMethod[] = "canLaunch";
const char kLaunchMethod[] = "launch";
const char kUrlKey[] = "url";
struct _FlUrlLauncherPlugin {
GObject parent_instance;
FlPluginRegistrar* registrar;
// Connection to Flutter engine.
FlMethodChannel* channel;
};
G_DEFINE_TYPE(FlUrlLauncherPlugin, fl_url_launcher_plugin, g_object_get_type())
// Gets the URL from the arguments or generates an error.
static gchar* get_url(FlValue* args, GError** error) {
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
g_set_error(error, 0, 0, "Argument map missing or malformed");
return nullptr;
}
FlValue* url_value = fl_value_lookup_string(args, kUrlKey);
if (url_value == nullptr) {
g_set_error(error, 0, 0, "Missing URL");
return nullptr;
}
return g_strdup(fl_value_get_string(url_value));
}
// Checks if URI has launchable file resource.
static gboolean can_launch_uri_with_file_resource(FlUrlLauncherPlugin* self,
const gchar* url) {
g_autoptr(GError) error = nullptr;
g_autoptr(GFile) file = g_file_new_for_uri(url);
g_autoptr(GAppInfo) app_info =
g_file_query_default_handler(file, NULL, &error);
return app_info != nullptr;
}
// Called to check if a URL can be launched.
FlMethodResponse* can_launch(FlUrlLauncherPlugin* self, FlValue* args) {
g_autoptr(GError) error = nullptr;
g_autofree gchar* url = get_url(args, &error);
if (url == nullptr) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
kBadArgumentsError, error->message, nullptr));
}
gboolean is_launchable = FALSE;
g_autofree gchar* scheme = g_uri_parse_scheme(url);
if (scheme != nullptr) {
g_autoptr(GAppInfo) app_info =
g_app_info_get_default_for_uri_scheme(scheme);
is_launchable = app_info != nullptr;
if (!is_launchable) {
is_launchable = can_launch_uri_with_file_resource(self, url);
}
}
g_autoptr(FlValue) result = fl_value_new_bool(is_launchable);
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
// Called when a URL should launch.
static FlMethodResponse* launch(FlUrlLauncherPlugin* self, FlValue* args) {
g_autoptr(GError) error = nullptr;
g_autofree gchar* url = get_url(args, &error);
if (url == nullptr) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
kBadArgumentsError, error->message, nullptr));
}
FlView* view = fl_plugin_registrar_get_view(self->registrar);
gboolean launched;
if (view != nullptr) {
GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
launched = gtk_show_uri_on_window(window, url, GDK_CURRENT_TIME, &error);
} else {
launched = g_app_info_launch_default_for_uri(url, nullptr, &error);
}
if (!launched) {
g_autofree gchar* message =
g_strdup_printf("Failed to launch URL: %s", error->message);
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kLaunchError, message, nullptr));
}
g_autoptr(FlValue) result = fl_value_new_bool(TRUE);
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
// Called when a method call is received from Flutter.
static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,
gpointer user_data) {
FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(user_data);
const gchar* method = fl_method_call_get_name(method_call);
FlValue* args = fl_method_call_get_args(method_call);
g_autoptr(FlMethodResponse) response = nullptr;
if (strcmp(method, kCanLaunchMethod) == 0)
response = can_launch(self, args);
else if (strcmp(method, kLaunchMethod) == 0)
response = launch(self, args);
else
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
g_autoptr(GError) error = nullptr;
if (!fl_method_call_respond(method_call, response, &error))
g_warning("Failed to send method call response: %s", error->message);
}
static void fl_url_launcher_plugin_dispose(GObject* object) {
FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(object);
g_clear_object(&self->registrar);
g_clear_object(&self->channel);
G_OBJECT_CLASS(fl_url_launcher_plugin_parent_class)->dispose(object);
}
static void fl_url_launcher_plugin_class_init(FlUrlLauncherPluginClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_url_launcher_plugin_dispose;
}
FlUrlLauncherPlugin* fl_url_launcher_plugin_new(FlPluginRegistrar* registrar) {
FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(
g_object_new(fl_url_launcher_plugin_get_type(), nullptr));
self->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
self->channel =
fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
kChannelName, FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(self->channel, method_call_cb,
g_object_ref(self), g_object_unref);
return self;
}
static void fl_url_launcher_plugin_init(FlUrlLauncherPlugin* self) {}
void url_launcher_plugin_register_with_registrar(FlPluginRegistrar* registrar) {
FlUrlLauncherPlugin* plugin = fl_url_launcher_plugin_new(registrar);
g_object_unref(plugin);
}