fix: Récupérer l'opération active depuis la table operations

- Corrige l'erreur SQL 'Unknown column fk_operation in users'
- L'opération active est récupérée depuis operations.chk_active = 1
- Jointure avec users pour filtrer par entité de l'admin créateur
- Query: SELECT o.id FROM operations o INNER JOIN users u ON u.fk_entite = o.fk_entite WHERE u.id = ? AND o.chk_active = 1
This commit is contained in:
2026-01-26 16:57:08 +01:00
parent c24a3afe6a
commit 0687900564
3040 changed files with 77204 additions and 1578 deletions

View File

@@ -1 +0,0 @@
/home/pierre/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/

View File

@@ -0,0 +1,66 @@
# Below is a list of people and organizations that have contributed
# to the Flutter project. Names should be added to the list like so:
#
# Name/Organization <email address>
Google Inc.
The Chromium Authors
German Saprykin <saprykin.h@gmail.com>
Benjamin Sauer <sauer.benjamin@gmail.com>
larsenthomasj@gmail.com
Ali Bitek <alibitek@protonmail.ch>
Pol Batlló <pol.batllo@gmail.com>
Anatoly Pulyaevskiy
Hayden Flinner <haydenflinner@gmail.com>
Stefano Rodriguez <hlsroddy@gmail.com>
Salvatore Giordano <salvatoregiordanoo@gmail.com>
Brian Armstrong <brian@flutter.institute>
Paul DeMarco <paulmdemarco@gmail.com>
Fabricio Nogueira <feufeu@gmail.com>
Simon Lightfoot <simon@devangels.london>
Ashton Thomas <ashton@acrinta.com>
Thomas Danner <thmsdnnr@gmail.com>
Diego Velásquez <diego.velasquez.lopez@gmail.com>
Hajime Nakamura <nkmrhj@gmail.com>
Tuyển Vũ Xuân <netsoft1985@gmail.com>
Miguel Ruivo <miguel@miguelruivo.com>
Sarthak Verma <sarthak@artiosys.com>
Mike Diarmid <mike@invertase.io>
Invertase <oss@invertase.io>
Elliot Hesp <elliot@invertase.io>
Vince Varga <vince.varga@smaho.com>
Aawaz Gyawali <awazgyawali@gmail.com>
EUI Limited <ian.evans3@admiralgroup.co.uk>
Katarina Sheremet <katarina@sheremet.ch>
Thomas Stockx <thomas@stockxit.com>
Sarbagya Dhaubanjar <sarbagyastha@gmail.com>
Ozkan Eksi <ozeksi@gmail.com>
Rishab Nayak <rishab@bu.edu>
ko2ic <ko2ic.dev@gmail.com>
Jonathan Younger <jonathan@daikini.com>
Jose Sanchez <josesm82@gmail.com>
Debkanchan Samadder <debu.samadder@gmail.com>
Audrius Karosevicius <audrius.karosevicius@gmail.com>
Lukasz Piliszczuk <lukasz@intheloup.io>
SoundReply Solutions GmbH <ch@soundreply.com>
Rafal Wachol <rwachol@gmail.com>
Pau Picas <pau.picas@gmail.com>
Christian Weder <chrstian.weder@yapeal.ch>
Alexandru Tuca <salexandru.tuca@outlook.com>
Christian Weder <chrstian.weder@yapeal.ch>
Rhodes Davis Jr. <rody.davis.jr@gmail.com>
Luigi Agosti <luigi@tengio.com>
Quentin Le Guennec <quentin@tengio.com>
Koushik Ravikumar <koushik@tengio.com>
Nissim Dsilva <nissim@tengio.com>
Giancarlo Rocha <giancarloiff@gmail.com>
Ryo Miyake <ryo@miyake.id>
Théo Champion <contact.theochampion@gmail.com>
Kazuki Yamaguchi <y.kazuki0614n@gmail.com>
Eitan Schwartz <eshvartz@gmail.com>
Chris Rutkowski <chrisrutkowski89@gmail.com>
Juan Alvarez <juan.alvarez@resideo.com>
Aleksandr Yurkovskiy <sanekyy@gmail.com>
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>

View File

@@ -0,0 +1,105 @@
## 3.2.1
* Updates Pigeon to resolve a compilation failure with some versions of glib.
## 3.2.0
* Updates platform channels to use Pigeon.
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.
## 3.1.1
* Implements `launchUrl`.
* Simplifies method channel interface by removing unused elements.
* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.
## 3.1.0
* Implements `supportsMode` and `supportsCloseForMode`.
## 3.0.6
* Adds pub topics to package metadata.
* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19.
## 3.0.5
* Sets a cmake_policy compatibility version to fix build warnings.
## 3.0.4
* Clarifies explanation of endorsement in README.
* Aligns Dart and Flutter SDK constraints.
## 3.0.3
* Updates links for the merge of flutter/plugins into flutter/packages.
* Updates minimum Flutter version to 3.0.
## 3.0.2
* Updates code for stricter lint checks.
* Updates minimum Flutter version to 2.10.
## 3.0.1
* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors
lint warnings.
## 3.0.0
* Changes the major version since, due to a typo in `default_package` in
existing versions of `url_launcher`, requiring Dart registration in this
package is in practice a breaking change.
* Does not include any API changes; clients can allow both 2.x or 3.x.
## 2.0.4
* **\[Retracted\]** Switches to an in-package method channel implementation.
## 2.0.3
* Updates code for new analysis options.
* Fix minor memory leak in Linux url_launcher tests.
* Fixes canLaunch detection for URIs addressing on local or network file systems
## 2.0.2
* Replaced reference to `shared_preferences` plugin with the `url_launcher` in the README.
## 2.0.1
* Updated installation instructions in README.
## 2.0.0
* Migrate to null safety.
* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets.
* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276))
* Set `implementation` in pubspec.yaml
## 0.0.2+1
* Update Flutter SDK constraint.
## 0.0.2
* Update integration test examples to use `testWidgets` instead of `test`.
## 0.0.1+4
* Update Dart SDK constraint in example.
## 0.0.1+3
* Add a missing include.
## 0.0.1+2
* Check in linux/ directory for example/
# 0.0.1+1
* README update for endorsement by url_launcher.
# 0.0.1
* The initial implementation of url_launcher for Linux

View File

@@ -0,0 +1,25 @@
Copyright 2013 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,15 @@
# url\_launcher\_linux
The Linux implementation of [`url_launcher`][1].
## Usage
This package is [endorsed][2], which means you can simply use `url_launcher`
normally. This package will be automatically included in your app when you do,
so you do not need to add it to your `pubspec.yaml`.
However, if you `import` this package to use any of its APIs directly, you
should add it to your `pubspec.yaml` as usual.
[1]: https://pub.dev/packages/url_launcher
[2]: https://flutter.dev/to/endorsed-federated-plugin

View File

@@ -0,0 +1,9 @@
# Platform Implementation Test App
This is a test app for manual testing and automated integration testing
of this platform implementation. It is not intended to demonstrate actual use of
this package, since the intent is that plugin clients use the app-facing
package.
Unless you are making changes to this implementation package, this example is
very unlikely to be relevant.

View File

@@ -0,0 +1,20 @@
// 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.
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('canLaunch', (WidgetTester _) async {
final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance;
expect(await launcher.canLaunch('randomstring'), false);
// Generally all devices should have some default browser.
expect(await launcher.canLaunch('http://flutter.dev'), true);
});
}

View File

@@ -0,0 +1,95 @@
// 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.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'URL Launcher',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'URL Launcher'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<void>? _launched;
Future<void> _launchInBrowser(String url) async {
if (await UrlLauncherPlatform.instance.canLaunch(url)) {
await UrlLauncherPlatform.instance.launch(
url,
useSafariVC: false,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: <String, String>{},
);
} else {
throw Exception('Could not launch $url');
}
}
Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return const Text('');
}
}
@override
Widget build(BuildContext context) {
const String toLaunch = 'https://www.cylog.org/headers/';
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(toLaunch),
),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInBrowser(toLaunch);
}),
child: const Text('Launch in browser'),
),
const Padding(padding: EdgeInsets.all(16.0)),
FutureBuilder<void>(future: _launched, builder: _launchStatus),
],
),
],
),
);
}
}

View File

@@ -0,0 +1,100 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "example")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
# Application build
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
apply_standard_settings(${BINARY_NAME})
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)
# Enable the test target.
set(include_url_launcher_linux_tests TRUE)
# Provide an alias for the test target using the name expected by repo tooling.
add_custom_target(unit_tests DEPENDS url_launcher_linux_test)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View File

@@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

View File

@@ -0,0 +1,24 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@@ -0,0 +1,10 @@
// 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 "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}

View File

@@ -0,0 +1,48 @@
// 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 "my_application.h"
#include <flutter_linux/flutter_linux.h>
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "example");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
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));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(), nullptr));
}

View File

@@ -0,0 +1,22 @@
// 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.
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_

View File

@@ -0,0 +1,28 @@
name: url_launcher_example
description: Demonstrates how to use the url_launcher plugin.
publish_to: none
environment:
sdk: ^3.3.0
flutter: ">=3.19.0"
dependencies:
flutter:
sdk: flutter
url_launcher_linux:
# When depending on this package from a real application you should use:
# url_launcher_linux: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
url_launcher_platform_interface: ^2.2.0
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
flutter:
uses-material-design: true

View File

@@ -0,0 +1,7 @@
// 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.
import 'package:integration_test/integration_test_driver.dart';
Future<void> main() => integrationDriver();

View File

@@ -0,0 +1,75 @@
// 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.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher_platform_interface/link.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'src/messages.g.dart';
/// An implementation of [UrlLauncherPlatform] for Linux.
class UrlLauncherLinux extends UrlLauncherPlatform {
/// Creates a new URL launcher instance.
UrlLauncherLinux({@visibleForTesting UrlLauncherApi? api})
: _hostApi = api ?? UrlLauncherApi();
/// Registers this class as the default instance of [UrlLauncherPlatform].
static void registerWith() {
UrlLauncherPlatform.instance = UrlLauncherLinux();
}
final UrlLauncherApi _hostApi;
@override
final LinkDelegate? linkDelegate = null;
@override
Future<bool> canLaunch(String url) async {
return _hostApi.canLaunchUrl(url);
}
@override
Future<bool> launch(
String url, {
required bool useSafariVC,
required bool useWebView,
required bool enableJavaScript,
required bool enableDomStorage,
required bool universalLinksOnly,
required Map<String, String> headers,
String? webOnlyWindowName,
}) {
// None of the options are supported, so they don't need to be converted to
// LaunchOptions.
return launchUrl(url, const LaunchOptions());
}
@override
Future<bool> launchUrl(String url, LaunchOptions options) async {
final String? error = await _hostApi.launchUrl(url);
if (error != null) {
// TODO(stuartmorgan): Standardize errors across the entire plugin,
// instead of using PlatformException. This preserves the pre-Pigeon
// behavior of the C code returning this error response.
throw PlatformException(
code: 'Launch Error', message: 'Failed to launch URL: $error');
}
return true;
}
@override
Future<bool> supportsMode(PreferredLaunchMode mode) async {
return mode == PreferredLaunchMode.platformDefault ||
mode == PreferredLaunchMode.externalApplication;
}
@override
Future<bool> supportsCloseForMode(PreferredLaunchMode mode) async {
// No supported mode is closeable.
return false;
}
}

View File

@@ -0,0 +1,65 @@
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME "url_launcher_linux")
project(${PROJECT_NAME} LANGUAGES CXX)
cmake_policy(VERSION 3.10...3.24)
set(PLUGIN_NAME "${PROJECT_NAME}_plugin")
list(APPEND PLUGIN_SOURCES
"messages.g.cc"
"url_launcher_plugin.cc"
)
add_library(${PLUGIN_NAME} SHARED
${PLUGIN_SOURCES}
)
apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
# === Tests ===
if (${include_${PROJECT_NAME}_tests})
if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
message("Unit tests require CMake 3.11.0 or later")
else()
set(TEST_RUNNER "${PROJECT_NAME}_test")
enable_testing()
# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest
# instance rather than downloading for each plugin. This approach makes sense
# for a template, but not for a monorepo with many plugins.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
)
# Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Disable install commands for gtest so it doesn't end up in the bundle.
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
FetchContent_MakeAvailable(googletest)
# The plugin's exported API is not very useful for unit testing, so build the
# sources directly into the test binary rather than using the shared library.
add_executable(${TEST_RUNNER}
test/url_launcher_linux_test.cc
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter)
target_link_libraries(${TEST_RUNNER} PRIVATE PkgConfig::GTK)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif() # CMake version check
endif() # include_${PROJECT_NAME}_tests

View File

@@ -0,0 +1,31 @@
// 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.
#ifndef PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_
#define PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_
// A plugin to launch URLs.
#include <flutter_linux/flutter_linux.h>
G_BEGIN_DECLS
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define FLUTTER_PLUGIN_EXPORT
#endif
G_DECLARE_FINAL_TYPE(FlUrlLauncherPlugin, fl_url_launcher_plugin, FL,
URL_LAUNCHER_PLUGIN, GObject)
FLUTTER_PLUGIN_EXPORT FlUrlLauncherPlugin* fl_url_launcher_plugin_new(
FlPluginRegistrar* registrar);
FLUTTER_PLUGIN_EXPORT void url_launcher_plugin_register_with_registrar(
FlPluginRegistrar* registrar);
G_END_DECLS
#endif // PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_

View File

@@ -0,0 +1,300 @@
// 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.
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#include "messages.g.h"
struct _FulMessageCodec {
FlStandardMessageCodec parent_instance;
};
G_DEFINE_TYPE(FulMessageCodec, ful_message_codec,
fl_standard_message_codec_get_type())
static gboolean ful_message_codec_write_value(FlStandardMessageCodec* codec,
GByteArray* buffer,
FlValue* value, GError** error) {
if (fl_value_get_type(value) == FL_VALUE_TYPE_CUSTOM) {
switch (fl_value_get_custom_type(value)) {}
}
return FL_STANDARD_MESSAGE_CODEC_CLASS(ful_message_codec_parent_class)
->write_value(codec, buffer, value, error);
}
static FlValue* ful_message_codec_read_value_of_type(
FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, int type,
GError** error) {
switch (type) {
default:
return FL_STANDARD_MESSAGE_CODEC_CLASS(ful_message_codec_parent_class)
->read_value_of_type(codec, buffer, offset, type, error);
}
}
static void ful_message_codec_init(FulMessageCodec* self) {}
static void ful_message_codec_class_init(FulMessageCodecClass* klass) {
FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->write_value =
ful_message_codec_write_value;
FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->read_value_of_type =
ful_message_codec_read_value_of_type;
}
static FulMessageCodec* ful_message_codec_new() {
FulMessageCodec* self =
FUL_MESSAGE_CODEC(g_object_new(ful_message_codec_get_type(), nullptr));
return self;
}
struct _FulUrlLauncherApiCanLaunchUrlResponse {
GObject parent_instance;
FlValue* value;
};
G_DEFINE_TYPE(FulUrlLauncherApiCanLaunchUrlResponse,
ful_url_launcher_api_can_launch_url_response, G_TYPE_OBJECT)
static void ful_url_launcher_api_can_launch_url_response_dispose(
GObject* object) {
FulUrlLauncherApiCanLaunchUrlResponse* self =
FUL_URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE(object);
g_clear_pointer(&self->value, fl_value_unref);
G_OBJECT_CLASS(ful_url_launcher_api_can_launch_url_response_parent_class)
->dispose(object);
}
static void ful_url_launcher_api_can_launch_url_response_init(
FulUrlLauncherApiCanLaunchUrlResponse* self) {}
static void ful_url_launcher_api_can_launch_url_response_class_init(
FulUrlLauncherApiCanLaunchUrlResponseClass* klass) {
G_OBJECT_CLASS(klass)->dispose =
ful_url_launcher_api_can_launch_url_response_dispose;
}
FulUrlLauncherApiCanLaunchUrlResponse*
ful_url_launcher_api_can_launch_url_response_new(gboolean return_value) {
FulUrlLauncherApiCanLaunchUrlResponse* self =
FUL_URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE(g_object_new(
ful_url_launcher_api_can_launch_url_response_get_type(), nullptr));
self->value = fl_value_new_list();
fl_value_append_take(self->value, fl_value_new_bool(return_value));
return self;
}
FulUrlLauncherApiCanLaunchUrlResponse*
ful_url_launcher_api_can_launch_url_response_new_error(const gchar* code,
const gchar* message,
FlValue* details) {
FulUrlLauncherApiCanLaunchUrlResponse* self =
FUL_URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE(g_object_new(
ful_url_launcher_api_can_launch_url_response_get_type(), nullptr));
self->value = fl_value_new_list();
fl_value_append_take(self->value, fl_value_new_string(code));
fl_value_append_take(self->value,
fl_value_new_string(message != nullptr ? message : ""));
fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details)
: fl_value_new_null());
return self;
}
struct _FulUrlLauncherApiLaunchUrlResponse {
GObject parent_instance;
FlValue* value;
};
G_DEFINE_TYPE(FulUrlLauncherApiLaunchUrlResponse,
ful_url_launcher_api_launch_url_response, G_TYPE_OBJECT)
static void ful_url_launcher_api_launch_url_response_dispose(GObject* object) {
FulUrlLauncherApiLaunchUrlResponse* self =
FUL_URL_LAUNCHER_API_LAUNCH_URL_RESPONSE(object);
g_clear_pointer(&self->value, fl_value_unref);
G_OBJECT_CLASS(ful_url_launcher_api_launch_url_response_parent_class)
->dispose(object);
}
static void ful_url_launcher_api_launch_url_response_init(
FulUrlLauncherApiLaunchUrlResponse* self) {}
static void ful_url_launcher_api_launch_url_response_class_init(
FulUrlLauncherApiLaunchUrlResponseClass* klass) {
G_OBJECT_CLASS(klass)->dispose =
ful_url_launcher_api_launch_url_response_dispose;
}
FulUrlLauncherApiLaunchUrlResponse*
ful_url_launcher_api_launch_url_response_new(const gchar* return_value) {
FulUrlLauncherApiLaunchUrlResponse* self =
FUL_URL_LAUNCHER_API_LAUNCH_URL_RESPONSE(g_object_new(
ful_url_launcher_api_launch_url_response_get_type(), nullptr));
self->value = fl_value_new_list();
fl_value_append_take(self->value, return_value != nullptr
? fl_value_new_string(return_value)
: fl_value_new_null());
return self;
}
FulUrlLauncherApiLaunchUrlResponse*
ful_url_launcher_api_launch_url_response_new_error(const gchar* code,
const gchar* message,
FlValue* details) {
FulUrlLauncherApiLaunchUrlResponse* self =
FUL_URL_LAUNCHER_API_LAUNCH_URL_RESPONSE(g_object_new(
ful_url_launcher_api_launch_url_response_get_type(), nullptr));
self->value = fl_value_new_list();
fl_value_append_take(self->value, fl_value_new_string(code));
fl_value_append_take(self->value,
fl_value_new_string(message != nullptr ? message : ""));
fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details)
: fl_value_new_null());
return self;
}
struct _FulUrlLauncherApi {
GObject parent_instance;
const FulUrlLauncherApiVTable* vtable;
gpointer user_data;
GDestroyNotify user_data_free_func;
};
G_DEFINE_TYPE(FulUrlLauncherApi, ful_url_launcher_api, G_TYPE_OBJECT)
static void ful_url_launcher_api_dispose(GObject* object) {
FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API(object);
if (self->user_data != nullptr) {
self->user_data_free_func(self->user_data);
}
self->user_data = nullptr;
G_OBJECT_CLASS(ful_url_launcher_api_parent_class)->dispose(object);
}
static void ful_url_launcher_api_init(FulUrlLauncherApi* self) {}
static void ful_url_launcher_api_class_init(FulUrlLauncherApiClass* klass) {
G_OBJECT_CLASS(klass)->dispose = ful_url_launcher_api_dispose;
}
static FulUrlLauncherApi* ful_url_launcher_api_new(
const FulUrlLauncherApiVTable* vtable, gpointer user_data,
GDestroyNotify user_data_free_func) {
FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API(
g_object_new(ful_url_launcher_api_get_type(), nullptr));
self->vtable = vtable;
self->user_data = user_data;
self->user_data_free_func = user_data_free_func;
return self;
}
static void ful_url_launcher_api_can_launch_url_cb(
FlBasicMessageChannel* channel, FlValue* message_,
FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) {
FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API(user_data);
if (self->vtable == nullptr || self->vtable->can_launch_url == nullptr) {
return;
}
FlValue* value0 = fl_value_get_list_value(message_, 0);
const gchar* url = fl_value_get_string(value0);
g_autoptr(FulUrlLauncherApiCanLaunchUrlResponse) response =
self->vtable->can_launch_url(url, self->user_data);
if (response == nullptr) {
g_warning("No response returned to %s.%s", "UrlLauncherApi",
"canLaunchUrl");
return;
}
g_autoptr(GError) error = NULL;
if (!fl_basic_message_channel_respond(channel, response_handle,
response->value, &error)) {
g_warning("Failed to send response to %s.%s: %s", "UrlLauncherApi",
"canLaunchUrl", error->message);
}
}
static void ful_url_launcher_api_launch_url_cb(
FlBasicMessageChannel* channel, FlValue* message_,
FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) {
FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API(user_data);
if (self->vtable == nullptr || self->vtable->launch_url == nullptr) {
return;
}
FlValue* value0 = fl_value_get_list_value(message_, 0);
const gchar* url = fl_value_get_string(value0);
g_autoptr(FulUrlLauncherApiLaunchUrlResponse) response =
self->vtable->launch_url(url, self->user_data);
if (response == nullptr) {
g_warning("No response returned to %s.%s", "UrlLauncherApi", "launchUrl");
return;
}
g_autoptr(GError) error = NULL;
if (!fl_basic_message_channel_respond(channel, response_handle,
response->value, &error)) {
g_warning("Failed to send response to %s.%s: %s", "UrlLauncherApi",
"launchUrl", error->message);
}
}
void ful_url_launcher_api_set_method_handlers(
FlBinaryMessenger* messenger, const gchar* suffix,
const FulUrlLauncherApiVTable* vtable, gpointer user_data,
GDestroyNotify user_data_free_func) {
g_autofree gchar* dot_suffix =
suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup("");
g_autoptr(FulUrlLauncherApi) api_data =
ful_url_launcher_api_new(vtable, user_data, user_data_free_func);
g_autoptr(FulMessageCodec) codec = ful_message_codec_new();
g_autofree gchar* can_launch_url_channel_name = g_strdup_printf(
"dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.canLaunchUrl%s",
dot_suffix);
g_autoptr(FlBasicMessageChannel) can_launch_url_channel =
fl_basic_message_channel_new(messenger, can_launch_url_channel_name,
FL_MESSAGE_CODEC(codec));
fl_basic_message_channel_set_message_handler(
can_launch_url_channel, ful_url_launcher_api_can_launch_url_cb,
g_object_ref(api_data), g_object_unref);
g_autofree gchar* launch_url_channel_name = g_strdup_printf(
"dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.launchUrl%s",
dot_suffix);
g_autoptr(FlBasicMessageChannel) launch_url_channel =
fl_basic_message_channel_new(messenger, launch_url_channel_name,
FL_MESSAGE_CODEC(codec));
fl_basic_message_channel_set_message_handler(
launch_url_channel, ful_url_launcher_api_launch_url_cb,
g_object_ref(api_data), g_object_unref);
}
void ful_url_launcher_api_clear_method_handlers(FlBinaryMessenger* messenger,
const gchar* suffix) {
g_autofree gchar* dot_suffix =
suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup("");
g_autoptr(FulMessageCodec) codec = ful_message_codec_new();
g_autofree gchar* can_launch_url_channel_name = g_strdup_printf(
"dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.canLaunchUrl%s",
dot_suffix);
g_autoptr(FlBasicMessageChannel) can_launch_url_channel =
fl_basic_message_channel_new(messenger, can_launch_url_channel_name,
FL_MESSAGE_CODEC(codec));
fl_basic_message_channel_set_message_handler(can_launch_url_channel, nullptr,
nullptr, nullptr);
g_autofree gchar* launch_url_channel_name = g_strdup_printf(
"dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.launchUrl%s",
dot_suffix);
g_autoptr(FlBasicMessageChannel) launch_url_channel =
fl_basic_message_channel_new(messenger, launch_url_channel_name,
FL_MESSAGE_CODEC(codec));
fl_basic_message_channel_set_message_handler(launch_url_channel, nullptr,
nullptr, nullptr);
}

View File

@@ -0,0 +1,121 @@
// 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.
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#ifndef PIGEON_MESSAGES_G_H_
#define PIGEON_MESSAGES_G_H_
#include <flutter_linux/flutter_linux.h>
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(FulMessageCodec, ful_message_codec, FUL, MESSAGE_CODEC,
FlStandardMessageCodec)
G_DECLARE_FINAL_TYPE(FulUrlLauncherApi, ful_url_launcher_api, FUL,
URL_LAUNCHER_API, GObject)
G_DECLARE_FINAL_TYPE(FulUrlLauncherApiCanLaunchUrlResponse,
ful_url_launcher_api_can_launch_url_response, FUL,
URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE, GObject)
/**
* ful_url_launcher_api_can_launch_url_response_new:
*
* Creates a new response to UrlLauncherApi.canLaunchUrl.
*
* Returns: a new #FulUrlLauncherApiCanLaunchUrlResponse
*/
FulUrlLauncherApiCanLaunchUrlResponse*
ful_url_launcher_api_can_launch_url_response_new(gboolean return_value);
/**
* ful_url_launcher_api_can_launch_url_response_new_error:
* @code: error code.
* @message: error message.
* @details: (allow-none): error details or %NULL.
*
* Creates a new error response to UrlLauncherApi.canLaunchUrl.
*
* Returns: a new #FulUrlLauncherApiCanLaunchUrlResponse
*/
FulUrlLauncherApiCanLaunchUrlResponse*
ful_url_launcher_api_can_launch_url_response_new_error(const gchar* code,
const gchar* message,
FlValue* details);
G_DECLARE_FINAL_TYPE(FulUrlLauncherApiLaunchUrlResponse,
ful_url_launcher_api_launch_url_response, FUL,
URL_LAUNCHER_API_LAUNCH_URL_RESPONSE, GObject)
/**
* ful_url_launcher_api_launch_url_response_new:
*
* Creates a new response to UrlLauncherApi.launchUrl.
*
* Returns: a new #FulUrlLauncherApiLaunchUrlResponse
*/
FulUrlLauncherApiLaunchUrlResponse*
ful_url_launcher_api_launch_url_response_new(const gchar* return_value);
/**
* ful_url_launcher_api_launch_url_response_new_error:
* @code: error code.
* @message: error message.
* @details: (allow-none): error details or %NULL.
*
* Creates a new error response to UrlLauncherApi.launchUrl.
*
* Returns: a new #FulUrlLauncherApiLaunchUrlResponse
*/
FulUrlLauncherApiLaunchUrlResponse*
ful_url_launcher_api_launch_url_response_new_error(const gchar* code,
const gchar* message,
FlValue* details);
/**
* FulUrlLauncherApiVTable:
*
* Table of functions exposed by UrlLauncherApi to be implemented by the API
* provider.
*/
typedef struct {
FulUrlLauncherApiCanLaunchUrlResponse* (*can_launch_url)(const gchar* url,
gpointer user_data);
FulUrlLauncherApiLaunchUrlResponse* (*launch_url)(const gchar* url,
gpointer user_data);
} FulUrlLauncherApiVTable;
/**
* ful_url_launcher_api_set_method_handlers:
*
* @messenger: an #FlBinaryMessenger.
* @suffix: (allow-none): a suffix to add to the API or %NULL for none.
* @vtable: implementations of the methods in this API.
* @user_data: (closure): user data to pass to the functions in @vtable.
* @user_data_free_func: (allow-none): a function which gets called to free
* @user_data, or %NULL.
*
* Connects the method handlers in the UrlLauncherApi API.
*/
void ful_url_launcher_api_set_method_handlers(
FlBinaryMessenger* messenger, const gchar* suffix,
const FulUrlLauncherApiVTable* vtable, gpointer user_data,
GDestroyNotify user_data_free_func);
/**
* ful_url_launcher_api_clear_method_handlers:
*
* @messenger: an #FlBinaryMessenger.
* @suffix: (allow-none): a suffix to add to the API or %NULL for none.
*
* Clears the method handlers in the UrlLauncherApi API.
*/
void ful_url_launcher_api_clear_method_handlers(FlBinaryMessenger* messenger,
const gchar* suffix);
G_END_DECLS
#endif // PIGEON_MESSAGES_G_H_

View File

@@ -0,0 +1,85 @@
// 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 <flutter_linux/flutter_linux.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include "include/url_launcher_linux/url_launcher_plugin.h"
#include "url_launcher_plugin_private.h"
// Re-declare the opaque struct as a temporary workaround for the lack of
// APIs for reading host API response objects.
// TODO(stuartmorgan): Remove this once the following is fixed:
// https://github.com/flutter/flutter/issues/152166.
struct _FulUrlLauncherApiCanLaunchUrlResponse {
GObject parent_instance;
FlValue* value;
};
namespace url_launcher_plugin {
namespace test {
TEST(UrlLauncherPlugin, CanLaunchSuccess) {
g_autoptr(FulUrlLauncherApiCanLaunchUrlResponse) response =
handle_can_launch_url("https://flutter.dev", nullptr);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(fl_value_get_type(response->value) == FL_VALUE_TYPE_LIST);
ASSERT_TRUE(fl_value_get_length(response->value) == 1);
g_autoptr(FlValue) expected = fl_value_new_bool(true);
EXPECT_TRUE(
fl_value_equal(fl_value_get_list_value(response->value, 0), expected));
}
TEST(UrlLauncherPlugin, CanLaunchFailureUnhandled) {
g_autoptr(FulUrlLauncherApiCanLaunchUrlResponse) response =
handle_can_launch_url("madeup:scheme", nullptr);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(fl_value_get_type(response->value) == FL_VALUE_TYPE_LIST);
ASSERT_TRUE(fl_value_get_length(response->value) == 1);
g_autoptr(FlValue) expected = fl_value_new_bool(false);
EXPECT_TRUE(
fl_value_equal(fl_value_get_list_value(response->value, 0), expected));
}
TEST(UrlLauncherPlugin, CanLaunchFileSuccess) {
g_autoptr(FulUrlLauncherApiCanLaunchUrlResponse) response =
handle_can_launch_url("file:///", nullptr);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(fl_value_get_type(response->value) == FL_VALUE_TYPE_LIST);
ASSERT_TRUE(fl_value_get_length(response->value) == 1);
g_autoptr(FlValue) expected = fl_value_new_bool(true);
EXPECT_TRUE(
fl_value_equal(fl_value_get_list_value(response->value, 0), expected));
}
TEST(UrlLauncherPlugin, CanLaunchFailureInvalidFileExtension) {
g_autoptr(FulUrlLauncherApiCanLaunchUrlResponse) response =
handle_can_launch_url("file:///madeup.madeupextension", nullptr);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(fl_value_get_type(response->value) == FL_VALUE_TYPE_LIST);
ASSERT_TRUE(fl_value_get_length(response->value) == 1);
g_autoptr(FlValue) expected = fl_value_new_bool(false);
EXPECT_TRUE(
fl_value_equal(fl_value_get_list_value(response->value, 0), expected));
}
// For consistency with the established mobile implementations,
// an invalid URL should return false, not an error.
TEST(UrlLauncherPlugin, CanLaunchFailureInvalidUrl) {
g_autoptr(FulUrlLauncherApiCanLaunchUrlResponse) response =
handle_can_launch_url("", nullptr);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(fl_value_get_type(response->value) == FL_VALUE_TYPE_LIST);
ASSERT_TRUE(fl_value_get_length(response->value) == 1);
g_autoptr(FlValue) expected = fl_value_new_bool(false);
EXPECT_TRUE(
fl_value_equal(fl_value_get_list_value(response->value, 0), expected));
}
} // namespace test
} // namespace url_launcher_plugin

View File

@@ -0,0 +1,109 @@
// 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 "messages.g.h"
#include "url_launcher_plugin_private.h"
struct _FlUrlLauncherPlugin {
GObject parent_instance;
FlPluginRegistrar* registrar;
};
G_DEFINE_TYPE(FlUrlLauncherPlugin, fl_url_launcher_plugin, g_object_get_type())
// 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;
}
FulUrlLauncherApiCanLaunchUrlResponse* handle_can_launch_url(
const gchar* url, gpointer user_data) {
FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(user_data);
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);
}
}
return ful_url_launcher_api_can_launch_url_response_new(is_launchable);
}
// Called when a URL should launch.
static FulUrlLauncherApiLaunchUrlResponse* handle_launch_url(
const gchar* url, gpointer user_data) {
FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(user_data);
FlView* view = fl_plugin_registrar_get_view(self->registrar);
g_autoptr(GError) error = nullptr;
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) {
return ful_url_launcher_api_launch_url_response_new(error->message);
}
return ful_url_launcher_api_launch_url_response_new(nullptr);
}
static void fl_url_launcher_plugin_dispose(GObject* object) {
FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(object);
ful_url_launcher_api_clear_method_handlers(
fl_plugin_registrar_get_messenger(self->registrar), nullptr);
g_clear_object(&self->registrar);
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));
static FulUrlLauncherApiVTable api_vtable = {
.can_launch_url = handle_can_launch_url,
.launch_url = handle_launch_url,
};
ful_url_launcher_api_set_method_handlers(
fl_plugin_registrar_get_messenger(registrar), nullptr, &api_vtable,
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);
}

View File

@@ -0,0 +1,12 @@
// 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 <flutter_linux/flutter_linux.h>
#include "include/url_launcher_linux/url_launcher_plugin.h"
#include "messages.g.h"
// Called to check if a URL can be launched.
FulUrlLauncherApiCanLaunchUrlResponse* handle_can_launch_url(
const gchar* url, gpointer user_data);

View File

@@ -0,0 +1,3 @@
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.

View File

@@ -0,0 +1,21 @@
// 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.
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(PigeonOptions(
dartOut: 'lib/src/messages.g.dart',
gobjectHeaderOut: 'linux/messages.g.h',
gobjectSourceOut: 'linux/messages.g.cc',
gobjectOptions: GObjectOptions(module: 'Ful'),
copyrightHeader: 'pigeons/copyright.txt',
))
@HostApi()
abstract class UrlLauncherApi {
/// Returns true if the URL can definitely be launched.
bool canLaunchUrl(String url);
/// Opens the URL externally, returning an error string on failure.
String? launchUrl(String url);
}

View File

@@ -0,0 +1,34 @@
name: url_launcher_linux
description: Linux implementation of the url_launcher plugin.
repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_linux
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22
version: 3.2.1
environment:
sdk: ^3.3.0
flutter: ">=3.19.0"
flutter:
plugin:
implements: url_launcher
platforms:
linux:
pluginClass: UrlLauncherPlugin
dartPluginClass: UrlLauncherLinux
dependencies:
flutter:
sdk: flutter
url_launcher_platform_interface: ^2.2.0
dev_dependencies:
flutter_test:
sdk: flutter
pigeon: ^22.6.1
test: ^1.16.3
topics:
- links
- os-integration
- url-launcher
- urls

View File

@@ -0,0 +1,175 @@
// 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.
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:url_launcher_linux/src/messages.g.dart';
import 'package:url_launcher_linux/url_launcher_linux.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
void main() {
group('UrlLauncherLinux', () {
test('registers instance', () {
UrlLauncherLinux.registerWith();
expect(UrlLauncherPlatform.instance, isA<UrlLauncherLinux>());
});
test('canLaunch passes true', () async {
final _FakeUrlLauncherApi api = _FakeUrlLauncherApi();
final UrlLauncherLinux launcher = UrlLauncherLinux(api: api);
final bool canLaunch = await launcher.canLaunch('http://example.com/');
expect(canLaunch, true);
});
test('canLaunch passes false', () async {
final _FakeUrlLauncherApi api = _FakeUrlLauncherApi(canLaunch: false);
final UrlLauncherLinux launcher = UrlLauncherLinux(api: api);
final bool canLaunch = await launcher.canLaunch('http://example.com/');
expect(canLaunch, false);
});
test('launch', () async {
final _FakeUrlLauncherApi api = _FakeUrlLauncherApi();
final UrlLauncherLinux launcher = UrlLauncherLinux(api: api);
const String url = 'http://example.com/';
final bool launched = await launcher.launch(
url,
useSafariVC: true,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: const <String, String>{},
);
expect(launched, true);
expect(api.argument, url);
});
test('launch should throw if platform returns an error', () async {
final _FakeUrlLauncherApi api = _FakeUrlLauncherApi(error: 'An error');
final UrlLauncherLinux launcher = UrlLauncherLinux(api: api);
await expectLater(
launcher.launch(
'http://example.com/',
useSafariVC: true,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: const <String, String>{},
),
throwsA(isA<PlatformException>()
.having((PlatformException e) => e.code, 'code', 'Launch Error')
.having((PlatformException e) => e.message, 'message',
contains('Failed to launch URL: An error'))));
});
group('launchUrl', () {
test('passes URL', () async {
final _FakeUrlLauncherApi api = _FakeUrlLauncherApi();
final UrlLauncherLinux launcher = UrlLauncherLinux(api: api);
const String url = 'http://example.com/';
final bool launched =
await launcher.launchUrl(url, const LaunchOptions());
expect(launched, true);
expect(api.argument, url);
});
test('throws if platform returns an error', () async {
final _FakeUrlLauncherApi api = _FakeUrlLauncherApi(error: 'An error');
final UrlLauncherLinux launcher = UrlLauncherLinux(api: api);
await expectLater(
launcher.launchUrl('http://example.com/', const LaunchOptions()),
throwsA(isA<PlatformException>()
.having((PlatformException e) => e.code, 'code', 'Launch Error')
.having((PlatformException e) => e.message, 'message',
contains('Failed to launch URL: An error'))));
});
});
group('supportsMode', () {
test('returns true for platformDefault', () async {
final UrlLauncherLinux launcher = UrlLauncherLinux();
expect(await launcher.supportsMode(PreferredLaunchMode.platformDefault),
true);
});
test('returns true for external application', () async {
final UrlLauncherLinux launcher = UrlLauncherLinux();
expect(
await launcher
.supportsMode(PreferredLaunchMode.externalApplication),
true);
});
test('returns false for other modes', () async {
final UrlLauncherLinux launcher = UrlLauncherLinux();
expect(
await launcher.supportsMode(
PreferredLaunchMode.externalNonBrowserApplication),
false);
expect(
await launcher.supportsMode(PreferredLaunchMode.inAppBrowserView),
false);
expect(await launcher.supportsMode(PreferredLaunchMode.inAppWebView),
false);
});
});
test('supportsCloseForMode returns false', () async {
final UrlLauncherLinux launcher = UrlLauncherLinux();
expect(
await launcher
.supportsCloseForMode(PreferredLaunchMode.platformDefault),
false);
expect(
await launcher
.supportsCloseForMode(PreferredLaunchMode.externalApplication),
false);
});
});
}
class _FakeUrlLauncherApi implements UrlLauncherApi {
_FakeUrlLauncherApi({this.canLaunch = true, this.error});
/// The value to return from canLaunch.
final bool canLaunch;
/// The error to return from launchUrl, if any.
final String? error;
/// The argument that was passed to an API call.
String? argument;
@override
Future<bool> canLaunchUrl(String url) async {
argument = url;
return canLaunch;
}
@override
Future<String?> launchUrl(String url) async {
argument = url;
return error;
}
@override
// ignore: non_constant_identifier_names
BinaryMessenger? get pigeonVar_binaryMessenger => null;
@override
// ignore: non_constant_identifier_names
String get pigeonVar_messageChannelSuffix => '';
}