feat: browser extensions native messaging host (#890)

This commit is contained in:
Levi authored and GitHub committed 2025-01-31 17:57:34 +08:00
1 parent a372b67edb
commit 520a72415c
31 files changed
+611 -140

No files matched your search

+4 -1
View File
@@ -67,4 +67,7 @@ windows/flutter/generated_plugins.cmake
# Hive database files
database.hive
database.lock
database.lock
assets/host/host
assets/host/host.exe
+1 -1
View File
@@ -44,7 +44,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.gopeed"
applicationId "com.gopeed.gopeed"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 21
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
package="com.gopeed.gopeed">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
package="com.gopeed.gopeed">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
@@ -1,4 +1,4 @@
package com.gopeed
package com.gopeed.gopeed
import androidx.annotation.NonNull
import com.gopeed.libgopeed.Libgopeed
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
package="com.gopeed.gopeed">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
View File
Whitespace-only changes.
@@ -452,7 +452,7 @@
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.ShareExtension;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
@@ -492,7 +492,7 @@
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.ShareExtension;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -530,7 +530,7 @@
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.ShareExtension;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -605,7 +605,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -736,7 +736,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -761,7 +761,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
+1 -1
View File
@@ -4,7 +4,7 @@
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.gopeed</string>
<string>group.com.gopeed.gopeed</string>
</array>
</dict>
</plist>
@@ -4,7 +4,7 @@
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.gopeed</string>
<string>group.com.gopeed.gopeed</string>
</array>
</dict>
</plist>
@@ -337,7 +337,8 @@ class AppController extends GetxController with WindowListener, TrayListener {
if (uri.path == "/create") {
final params = uri.queryParameters["params"];
if (params?.isNotEmpty == true) {
final paramsJson = String.fromCharCodes(base64Decode(params!));
final paramsJson =
String.fromCharCodes(base64Decode(base64.normalize(params!)));
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
arguments: RedirectArgs(Routes.CREATE,
arguments:
@@ -16,7 +16,6 @@ import '../../../../api/model/extension.dart';
import '../../../../api/model/install_extension.dart';
import '../../../../api/model/switch_extension.dart';
import '../../../../api/model/update_extension_settings.dart';
import '../../../../util/mac_secure_util.dart';
import '../../../../util/message.dart';
import '../../../../util/util.dart';
import '../../../views/icon_button_loading.dart';
@@ -96,7 +95,6 @@ class ExtensionView extends GetView<ExtensionController> {
var dir =
await FilePicker.platform.getDirectoryPath();
if (dir != null) {
MacSecureUtil.saveBookmark(dir);
try {
await installExtension(
InstallExtension(devMode: true, url: dir));
@@ -9,7 +9,6 @@ import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:toggle_switch/toggle_switch.dart';
import '../../util/mac_secure_util.dart';
import '../../util/message.dart';
import '../../util/util.dart';
@@ -42,7 +41,6 @@ class _DirectorySelectorState extends State<DirectorySelector> {
var dir = await FilePicker.platform.getDirectoryPath();
if (dir != null) {
widget.controller.text = dir;
MacSecureUtil.saveBookmark(dir);
}
});
}
+26 -11
View File
@@ -10,9 +10,9 @@ import 'app/modules/app/views/app_view.dart';
import 'core/libgopeed_boot.dart';
import 'database/database.dart';
import 'i18n/message.dart';
import 'util/browser_extension_host/browser_extension_host.dart';
import 'util/locale_manager.dart';
import 'util/log_util.dart';
import 'util/mac_secure_util.dart';
import 'util/package_info.dart';
import 'util/scheme_register/scheme_register.dart';
import 'util/util.dart';
@@ -82,21 +82,36 @@ Future<void> init(Args args) async {
logger.e("libgopeed init fail", e);
}
try {
registerUrlScheme("gopeed");
if (controller.downloaderConfig.value.extra.defaultBtClient) {
registerDefaultTorrentClient();
}
} catch (e) {
logger.e("register scheme fail", e);
}
try {
await controller.loadDownloaderConfig();
MacSecureUtil.loadBookmark();
} catch (e) {
logger.e("load config fail", e);
}
() async {
try {
registerUrlScheme("gopeed");
if (controller.downloaderConfig.value.extra.defaultBtClient) {
registerDefaultTorrentClient();
}
} catch (e) {
logger.e("register scheme fail", e);
}
try {
await installHost();
} catch (e) {
logger.e("browser extension host binary install fail", e);
}
for (final browser in Browser.values) {
try {
await installManifest(browser);
} catch (e) {
logger.e(
"browser [${browser.name}] extension host integration fail", e);
}
}
}();
}
Future<void> onStart() async {
@@ -0,0 +1,18 @@
import 'browser_extension_host_stub.dart'
if (dart.library.io) 'entry/browser_extension_host_native.dart';
enum Browser { chrome, edge, firefox }
/// Install host binary for browser extension
Future<void> installHost() => doInstallHost();
/// Check if specified browser is installed
Future<bool> checkBrowserInstalled(Browser browser) =>
doCheckBrowserInstalled(browser);
/// Check if browser extension manifest is properly installed
Future<bool> checkManifestInstalled(Browser browser) =>
doCheckManifestInstalled(browser);
/// Install browser extension manifest
Future<void> installManifest(Browser browser) => doInstallManifest(browser);
@@ -0,0 +1,8 @@
import 'browser_extension_host.dart';
Future<void> doInstallHost() => throw UnimplementedError();
Future<bool> doCheckBrowserInstalled(Browser browser) =>
throw UnimplementedError();
Future<bool> doCheckManifestInstalled(Browser browser) =>
throw UnimplementedError();
Future<void> doInstallManifest(Browser browser) => throw UnimplementedError();
@@ -0,0 +1,313 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:win32_registry/win32_registry.dart';
import '../../win32.dart';
import '../browser_extension_host.dart';
final _hostExecName = 'host${Platform.isWindows ? '.exe' : ''}';
const _hostName = 'com.gopeed.gopeed';
const _chromeExtensionId = 'mijpgljlfcapndmchhjffkpckknofcnd';
const _edgeExtensionId = 'dkajnckekendchdleoaenoophcobooce';
const _firefoxExtensionId = '{c5d69a8f-2ed0-46a7-afa4-b3a00dc58088}';
const _debugExtensionIds = [
'gjddllnejledbfaeondocjpejpamclkk',
'goaohdfiokcjapgonhofgljfccoccief'
];
// Windows NativeMessagingHosts registry constants
const _chromeNativeHostsKey = r'Software\Google\Chrome\NativeMessagingHosts';
const _edgeNativeHostsKey = r'Software\Microsoft\Edge\NativeMessagingHosts';
const _firefoxNativeHostsKey = r'Software\Mozilla\NativeMessagingHosts';
/// Install host binary for browser extension
Future<void> doInstallHost() async {
final hostPath =
path.join((await getApplicationSupportDirectory()).path, _hostExecName);
Future<List<int>> getHostAssetData() async {
final hostAsset = await rootBundle.load('assets/host/$_hostExecName');
return hostAsset.buffer
.asUint8List(hostAsset.offsetInBytes, hostAsset.lengthInBytes);
}
// Check if host binary is not installed
if (!await File(hostPath).exists()) {
final hostData = await getHostAssetData();
final file = File(hostPath);
await file.writeAsBytes(hostData);
// Add execute permission
if (!Platform.isWindows) {
await Process.run('chmod', ['+x', hostPath]);
}
return;
}
// Check if host binary is outdated
final hostAssetData = await getHostAssetData();
final hostFileData = await File(hostPath).readAsBytes();
if (hostAssetData.length != hostFileData.length) {
await File(hostPath).writeAsBytes(hostAssetData);
return;
}
}
/// Check if specified browser is installed
Future<bool> doCheckBrowserInstalled(Browser browser) async {
if (Platform.isWindows) {
switch (browser) {
case Browser.chrome:
return await _checkWindowsRegistry(
r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe') ||
await _checkWindowsRegistry(
r'SOFTWARE\WOW6432Node\Google\Chrome') ||
await _checkWindowsExecutable(browser);
case Browser.edge:
return await _checkWindowsRegistry(
r'SOFTWARE\Microsoft\Edge\BLBeacon') ||
await _checkWindowsRegistry(
r'SOFTWARE\WOW6432Node\Microsoft\Edge\BLBeacon') ||
await _checkWindowsExecutable(browser);
case Browser.firefox:
return await _checkWindowsRegistry(r'SOFTWARE\Mozilla\Firefox') ||
await _checkWindowsRegistry(
r'SOFTWARE\WOW6432Node\Mozilla\Firefox') ||
await _checkWindowsExecutable(browser);
}
} else {
return await _checkUnixExecutable(browser);
}
}
/// Check if browser extension manifest is properly installed
Future<bool> doCheckManifestInstalled(Browser browser) async {
if (await checkBrowserInstalled(browser) == false) return false;
final manifestPath = _getManifestPath(browser);
if (manifestPath == null) return false;
if (Platform.isWindows) {
final regKey = _getWindowsRegistryKey(browser);
if (!checkRegistry('$regKey\\$_hostName', '', manifestPath)) {
return false;
}
}
if (!await File(manifestPath).exists()) {
return false;
}
final existingContent = await File(manifestPath).readAsString();
final expectedContent = await _getManifestContent();
return existingContent == expectedContent;
}
/// Install browser extension manifest
Future<void> doInstallManifest(Browser browser) async {
if (await checkBrowserInstalled(browser) == false) return;
if (await checkManifestInstalled(browser)) return;
final manifestPath = _getManifestPath(browser)!;
final manifestContent = await _getManifestContent();
final manifestDir = path.dirname(manifestPath);
await Directory(manifestDir).create(recursive: true);
await File(manifestPath).writeAsString(manifestContent);
if (Platform.isWindows) {
final regKey = _getWindowsRegistryKey(browser);
upsertRegistry('$regKey\\$_hostName', '', manifestPath);
}
}
Future<bool> _checkWindowsExecutable(Browser browser) async {
final paths = _getWindowsExecutablePaths(browser);
for (var execPath in paths) {
if (await File(execPath).exists()) {
return true;
}
}
return false;
}
List<String> _getWindowsExecutablePaths(Browser browser) {
final programFiles = Platform.environment['PROGRAMFILES'];
final programFilesX86 = Platform.environment['PROGRAMFILES(X86)'];
final localAppData = Platform.environment['LOCALAPPDATA'];
switch (browser) {
case Browser.chrome:
return [
if (programFiles != null)
path.join(
programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe'),
if (programFilesX86 != null)
path.join(
programFilesX86, 'Google', 'Chrome', 'Application', 'chrome.exe'),
if (localAppData != null)
path.join(
localAppData, 'Google', 'Chrome', 'Application', 'chrome.exe'),
];
case Browser.edge:
return [
if (programFiles != null)
path.join(
programFiles, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
if (programFilesX86 != null)
path.join(programFilesX86, 'Microsoft', 'Edge', 'Application',
'msedge.exe'),
if (localAppData != null)
path.join(
localAppData, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
];
case Browser.firefox:
return [
if (programFiles != null)
path.join(programFiles, 'Mozilla Firefox', 'firefox.exe'),
if (programFilesX86 != null)
path.join(programFilesX86, 'Mozilla Firefox', 'firefox.exe'),
];
}
}
Future<bool> _checkUnixExecutable(Browser browser) async {
final paths = _getUnixExecutablePaths(browser);
for (var execPath in paths) {
execPath = execPath.replaceAll('~', Platform.environment['HOME'] ?? '');
if (await Directory(execPath).exists() || await File(execPath).exists()) {
return true;
}
}
return false;
}
List<String> _getUnixExecutablePaths(Browser browser) {
if (Platform.isMacOS) {
switch (browser) {
case Browser.chrome:
return [
'/Applications/Google Chrome.app',
'~/Applications/Google Chrome.app',
'/Users/${Platform.environment['USER']}/Applications/Google Chrome.app'
];
case Browser.edge:
return [
'/Applications/Microsoft Edge.app',
'~/Applications/Microsoft Edge.app',
'/Users/${Platform.environment['USER']}/Applications/Microsoft Edge.app'
];
case Browser.firefox:
return [
'/Applications/Firefox.app',
'~/Applications/Firefox.app',
'/Users/${Platform.environment['USER']}/Applications/Firefox.app'
];
}
} else {
switch (browser) {
case Browser.chrome:
return [
'/usr/bin/google-chrome',
'/usr/bin/google-chrome-stable',
'/usr/bin/chrome',
'/snap/bin/google-chrome',
'/opt/google/chrome/google-chrome'
];
case Browser.edge:
return [
'/usr/bin/microsoft-edge',
'/usr/bin/microsoft-edge-stable',
'/snap/bin/microsoft-edge',
'/opt/microsoft/msedge/msedge'
];
case Browser.firefox:
return [
'/usr/bin/firefox',
'/snap/bin/firefox',
'/usr/lib/firefox/firefox',
'/opt/firefox/firefox'
];
}
}
}
String? _getManifestPath(Browser browser) {
if (Platform.isWindows) {
final execPath = Platform.resolvedExecutable;
final execDir = path.dirname(execPath);
return path.join(execDir, '$_hostName.json');
}
final home =
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
if (home == null) return null;
if (Platform.isMacOS) {
switch (browser) {
case Browser.chrome:
return path.join(home, 'Library', 'Application Support', 'Google',
'Chrome', 'NativeMessagingHosts', '$_hostName.json');
case Browser.edge:
return path.join(home, 'Library', 'Application Support',
'Microsoft Edge', 'NativeMessagingHosts', '$_hostName.json');
case Browser.firefox:
return path.join(home, 'Library', 'Application Support', 'Mozilla',
'NativeMessagingHosts', '$_hostName.json');
}
} else if (Platform.isLinux) {
switch (browser) {
case Browser.chrome:
return path.join(home, '.config', 'google-chrome',
'NativeMessagingHosts', '$_hostName.json');
case Browser.edge:
return path.join(home, '.config', 'microsoft-edge',
'NativeMessagingHosts', '$_hostName.json');
case Browser.firefox:
return path.join(
home, '.mozilla', 'native-messaging-hosts', '$_hostName.json');
}
}
return null;
}
Future<bool> _checkWindowsRegistry(String keyPath) async {
try {
final key = Registry.openPath(RegistryHive.localMachine, path: keyPath);
key.close();
return true;
} catch (e) {
return false;
}
}
Future<String> _getManifestContent() async {
final hostPath =
path.join((await getApplicationSupportDirectory()).path, _hostExecName);
final manifest = {
'name': _hostName,
'description': 'Gopeed browser extension host',
'path': hostPath,
'type': 'stdio',
'allowed_origins': [
'chrome-extension://$_chromeExtensionId/',
'chrome-extension://$_edgeExtensionId/',
..._debugExtensionIds.map((id) => 'chrome-extension://$id/'),
],
'allowed_extensions': [_firefoxExtensionId],
};
return const JsonEncoder.withIndent(' ').convert(manifest);
}
String _getWindowsRegistryKey(Browser browser) {
switch (browser) {
case Browser.chrome:
return _chromeNativeHostsKey;
case Browser.edge:
return _edgeNativeHostsKey;
case Browser.firefox:
return _firefoxNativeHostsKey;
}
}
-30
View File
@@ -1,30 +0,0 @@
import 'dart:io';
import 'package:macos_secure_bookmarks/macos_secure_bookmarks.dart';
import '../database/database.dart';
import 'util.dart';
class MacSecureUtil {
static Future<void> saveBookmark(String dir) async {
if (!Util.isMacos()) {
return;
}
final secureBookmarks = SecureBookmarks();
final bookmark = await secureBookmarks.bookmark(Directory(dir));
Database.instance.saveBookmark(MapEntry(dir, bookmark));
}
static Future<void> loadBookmark() async {
if (!Util.isMacos()) {
return;
}
final secureBookmarks = SecureBookmarks();
final bookmark = Database.instance.getBookmark() ?? {};
bookmark.forEach((_, v) async {
final resolvedFile =
await secureBookmarks.resolveBookmark(v, isDirectory: true);
await secureBookmarks.startAccessingSecurityScopedResource(resolvedFile);
});
}
}
@@ -3,18 +3,19 @@ import 'dart:io';
import 'package:win32_registry/win32_registry.dart';
import '../../util.dart';
import '../../win32.dart';
doRegisterUrlScheme(String scheme) {
if (Util.isWindows()) {
final schemeKey = 'Software\\Classes\\$scheme';
final appPath = Platform.resolvedExecutable;
_upsertRegistry(
upsertRegistry(
schemeKey,
'URL Protocol',
'',
);
_upsertRegistry(
upsertRegistry(
'$schemeKey\\shell\\open\\command',
'',
'"$appPath" "%1"',
@@ -43,22 +44,22 @@ doRegisterDefaultTorrentClient() {
final appPath = Platform.resolvedExecutable;
final iconPath =
'${File(appPath).parent.path}\\data\\flutter_assets\\assets\\tray_icon\\icon.ico';
_upsertRegistry(
upsertRegistry(
_torrentRegKey,
'',
_torrentRegValue,
);
_upsertRegistry(
upsertRegistry(
_torrentAppRegKey,
'',
'Torrent file',
);
_upsertRegistry(
upsertRegistry(
'$_torrentAppRegKey\\DefaultIcon',
'',
iconPath,
);
_upsertRegistry(
upsertRegistry(
'$_torrentAppRegKey\\shell\\open\\command',
'',
'"$appPath" "file:///%1"',
@@ -74,20 +75,3 @@ doUnregisterDefaultTorrentClient() {
Registry.currentUser.deleteKey(_torrentAppRegKey, recursive: true);
}
}
// Add Windows registry key and value if not exists
_upsertRegistry(String keyPath, String valueName, String value) {
RegistryKey regKey;
try {
regKey = Registry.openPath(RegistryHive.currentUser,
path: keyPath, desiredAccessRights: AccessRights.allAccess);
} catch (e) {
regKey = Registry.currentUser.createKey(keyPath);
}
if (regKey.getValueAsString(valueName) != value) {
regKey
.createValue(RegistryValue(valueName, RegistryValueType.string, value));
}
regKey.close();
}
+32
View File
@@ -0,0 +1,32 @@
import 'package:win32_registry/win32_registry.dart';
/// Check registry key
/// If the key does not exist or the value is different, return false
checkRegistry(String keyPath, String valueName, String value) {
RegistryKey regKey;
try {
regKey = Registry.openPath(RegistryHive.currentUser, path: keyPath);
return regKey.getValueAsString(valueName) == value;
} catch (e) {
return false;
}
}
/// Upsert registry key
/// If the key does not exist, create it
/// If the value does not exist or is different, update it
upsertRegistry(String keyPath, String valueName, String value) {
RegistryKey regKey;
try {
regKey = Registry.openPath(RegistryHive.currentUser,
path: keyPath, desiredAccessRights: AccessRights.allAccess);
} catch (e) {
regKey = Registry.currentUser.createKey(keyPath);
}
if (regKey.getValueAsString(valueName) != value) {
regKey
.createValue(RegistryValue(valueName, RegistryValueType.string, value));
}
regKey.close();
}
-6
View File
@@ -6,8 +6,6 @@ PODS:
- device_info_plus (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- macos_secure_bookmarks (0.0.3):
- FlutterMacOS
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_foundation (0.0.1):
@@ -32,7 +30,6 @@ DEPENDENCIES:
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- macos_secure_bookmarks (from `Flutter/ephemeral/.symlinks/plugins/macos_secure_bookmarks/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
@@ -51,8 +48,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
FlutterMacOS:
:path: Flutter/ephemeral
macos_secure_bookmarks:
:path: Flutter/ephemeral/.symlinks/plugins/macos_secure_bookmarks/macos
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
@@ -75,7 +70,6 @@ SPEC CHECKSUMS:
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
device_info_plus: 74e614483d05c89290d30a4c8feae15d555f7427
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
macos_secure_bookmarks: cff041c4b377fa00941bb901aaec4d27fac5edf2
package_info_plus: f5790acc797bf17c3e959e9d6cf162cc68ff7523
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
@@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@@ -31,7 +31,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@@ -54,7 +54,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@@ -71,7 +71,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@@ -8,7 +8,7 @@
PRODUCT_NAME = Gopeed
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved.
@@ -2,19 +2,9 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
@@ -2,17 +2,7 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
-16
View File
@@ -658,22 +658,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
logging_appenders:
dependency: transitive
description:
name: logging_appenders
sha256: e329e7472f99416d0edaaf6451fe6c02dec91d34535bd252e284a0b94ab23d79
url: "https://pub.dev"
source: hosted
version: "1.3.1"
macos_secure_bookmarks:
dependency: "direct main"
description:
name: macos_secure_bookmarks
sha256: c3163875f8fd530a1654a7cf8aa426e6e208d28bf05724a1d4960e4b7d2ca1c6
url: "https://pub.dev"
source: hosted
version: "0.2.1"
macros:
dependency: transitive
description:
+2 -1
View File
@@ -42,7 +42,6 @@ dependencies:
dio: ^5.2.1
path_provider: ^2.1.4
file_picker: ^8.1.2
macos_secure_bookmarks: ^0.2.1
rounded_loading_button_plus: ^3.0.1
url_launcher: ^6.3.1
logger: ^1.4.0
@@ -108,6 +107,8 @@ flutter:
assets:
- assets/tray_icon/
- assets/extension/
# Browser extension native host binary, see https://github.com/GopeedLab/gopeed/tree/main/cmd/host
- assets/host/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware