feat: support windows share sheet (#2555)

This commit is contained in:
Chenx Dust
2025-07-10 04:32:58 +08:00
committed by GitHub
parent b6db22f1b5
commit ae2780638d
99 changed files with 215 additions and 1 deletions
@@ -1,4 +1,4 @@
import 'dart:convert' show utf8;
import 'dart:convert' show jsonDecode, utf8;
import 'dart:io';
import 'dart:typed_data';
@@ -13,6 +13,7 @@ import 'package:localsend_app/util/send_ignore.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:refena_flutter/refena_flutter.dart';
import 'package:share_handler/share_handler.dart';
import 'package:uuid/uuid.dart';
final _logger = Logger('SelectedSendingFiles');
@@ -287,7 +288,27 @@ class LoadSelectionFromArgsAction extends AsyncReduxActionWithResult<SelectedSen
@override
Future<(List<CrossFile>, bool)> reduce() async {
bool filesAdded = false;
bool nextShare = false;
for (final arg in args) {
if (arg == '--share') {
nextShare = true;
continue;
}
if (nextShare) {
nextShare = false;
final json = jsonDecode(arg);
final SharedMedia payload = SharedMedia.decode(json);
final message = payload.content;
if (message != null && message.trim().isNotEmpty) {
dispatch(AddMessageAction(message: message));
}
await dispatchAsync(AddFilesAction(
files: payload.attachments?.where((a) => a != null).cast<SharedAttachment>() ?? <SharedAttachment>[],
converter: CrossFileConverters.convertSharedAttachment,
));
filesAdded = true;
continue;
}
if (arg.startsWith('-')) {
continue;
}
+2
View File
@@ -15,3 +15,5 @@ x86/
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
localsend_msix_helper.msix
+7
View File
@@ -74,6 +74,13 @@ set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES
"${BINARY_NAME}.exe.manifest"
"localsend_msix_helper.msix"
"install_msix_helper.ps1"
DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
+1
View File
@@ -0,0 +1 @@
Add-AppxPackage .\localsend_msix_helper.msix -ExternalLocation $(Get-Location)
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="0.0.0.0" name="localsendapp" />
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
publisher="CN=Tien Do Nam, O=Tien Do Nam, S=Sachsen, C=DE"
packageName="com.flutter.localsendapp"
applicationId="localsendapp"
/>
</assembly>
+1
View File
@@ -11,6 +11,7 @@ add_executable(${BINARY_NAME} WIN32
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"winrt_ext.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
+10
View File
@@ -4,6 +4,7 @@
#include "flutter_window.h"
#include "utils.h"
#include "winrt_ext.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) {
@@ -22,6 +23,15 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
std::vector<std::string> command_line_arguments =
GetCommandLineArguments();
if (IsRunningWithIdentity()) {
winrt::hstring share_arg = GetSharedMedia();
if (!share_arg.empty()) {
printf("share: %ls\n", share_arg.c_str());
command_line_arguments.push_back("--share");
command_line_arguments.push_back(Utf8FromUtf16(share_arg.c_str()));
}
}
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project);
+69
View File
@@ -0,0 +1,69 @@
#include "winrt_ext.h"
#include <windows.h>
#include <appmodel.h>
#include <winrt/windows.foundation.h>
#include <winrt/windows.foundation.collections.h>
#include <winrt/windows.storage.h>
#include <winrt/windows.storage.streams.h>
#include <winrt/windows.applicationmodel.activation.h>
#include <winrt/windows.applicationmodel.datatransfer.h>
#include <winrt/windows.applicationmodel.datatransfer.sharetarget.h>
#include <winrt/windows.data.json.h>
using winrt::Windows::ApplicationModel::AppInstance;
using winrt::Windows::ApplicationModel::Activation::ActivationKind;
using winrt::Windows::ApplicationModel::Activation::ShareTargetActivatedEventArgs;
using winrt::Windows::ApplicationModel::DataTransfer::DataPackageView;
using winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats;
using winrt::Windows::Data::Json::JsonArray;
using winrt::Windows::Data::Json::JsonObject;
using winrt::Windows::Data::Json::JsonValue;
enum class SharedAttachmentType {
IMAGE,
VIDEO,
AUDIO,
FILE,
};
bool IsRunningWithIdentity() {
constexpr SIZE_T kPackageNameMaxLength = 1024;
UINT32 length = kPackageNameMaxLength;
wchar_t packageName[kPackageNameMaxLength];
LONG result = GetCurrentPackageFullName(&length, packageName);
return (result == ERROR_SUCCESS);
}
winrt::hstring GetSharedMedia() {
auto args = AppInstance::GetActivatedEventArgs();
if (args == nullptr)
return winrt::hstring();
if (args.Kind() != ActivationKind::ShareTarget)
return winrt::hstring();
auto share_target_args = args.as<ShareTargetActivatedEventArgs>();
auto op = share_target_args.ShareOperation();
auto data = op.Data();
JsonObject json;
if (data.Contains(StandardDataFormats::Text())) {
auto text = data.GetTextAsync().get();
json.SetNamedValue(L"content", JsonValue::CreateStringValue(text));
}
if (data.Contains(StandardDataFormats::Uri())) {
auto uri = data.GetUriAsync().get();
json.SetNamedValue(L"content", JsonValue::CreateStringValue(uri.ToString()));
}
if (data.Contains(StandardDataFormats::StorageItems())) {
JsonArray attachments;
auto storage_items = data.GetStorageItemsAsync().get();
for (const auto& item : storage_items) {
JsonObject attachment;
attachment.SetNamedValue(L"type", JsonValue::CreateNumberValue(double(SharedAttachmentType::FILE)));
attachment.SetNamedValue(L"path", JsonValue::CreateStringValue(item.Path()));
attachments.Append(attachment);
}
json.SetNamedValue(L"attachments", attachments);
}
return json.Stringify();
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef RUNNER_WINRT_EXT_H_
#define RUNNER_WINRT_EXT_H_
#include <winrt/base.h>
bool IsRunningWithIdentity();
winrt::hstring GetSharedMedia();
#endif // RUNNER_WINRT_EXT_H_
+75
View File
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
xmlns:uap6="http://schemas.microsoft.com/appx/manifest/uap/windows10/6"
xmlns:uap7="http://schemas.microsoft.com/appx/manifest/uap/windows10/7"
xmlns:uap8="http://schemas.microsoft.com/appx/manifest/uap/windows10/8"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop2="http://schemas.microsoft.com/appx/manifest/desktop/windows10/2"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:rescap3="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/3"
xmlns:rescap6="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/6"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:com2="http://schemas.microsoft.com/appx/manifest/com/windows10/2"
xmlns:com3="http://schemas.microsoft.com/appx/manifest/com/windows10/3"
IgnorableNamespaces="uap3 desktop">
<Identity Name="11157TienDoNam.LocalSend" Version="1.14.0.0"
Publisher="CN=Tien Do Nam, O=Tien Do Nam, S=Sachsen, C=DE" ProcessorArchitecture="x64" />
<Properties>
<DisplayName>LocalSend MSIX Helper</DisplayName>
<PublisherDisplayName>Tien Do Nam</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
<Description>An open source cross-platform alternative to AirDrop</Description>
<uap10:AllowExternalContent>true</uap10:AllowExternalContent>
</Properties>
<Resources>
<Resource Language="en" /><Resource Language="ar" /><Resource Language="bn" /><Resource Language="cs" /><Resource Language="da" /><Resource Language="de" /><Resource Language="el" /><Resource Language="es-ES" /><Resource Language="eu" /><Resource Language="fa" /><Resource Language="fr" /><Resource Language="he" /><Resource Language="hu" /><Resource Language="in" /><Resource Language="it" /><Resource Language="ja" /><Resource Language="ko" /><Resource Language="ne" /><Resource Language="nl" /><Resource Language="pl" /><Resource Language="pt-BR" /><Resource Language="ru" /><Resource Language="sv" /><Resource Language="th" /><Resource Language="tr" /><Resource Language="uk" /><Resource Language="vi" /><Resource Language="zh-CN" /><Resource Language="zh-HK" /><Resource Language="zh-TW" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22621.2506" />
</Dependencies>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
<Applications>
<Application Id="localsendapp" Executable="localsend_app.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements AppListEntry="none" BackgroundColor="transparent"
DisplayName="LocalSend" Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png" Description="An open source cross-platform alternative to AirDrop">
<uap:DefaultTile ShortName="LocalSend" Square310x310Logo="Images\LargeTile.png"
Square71x71Logo="Images\SmallTile.png" Wide310x150Logo="Images\Wide310x150Logo.png">
<uap:ShowNameOnTiles>
<uap:ShowOn Tile="square150x150Logo"/>
<uap:ShowOn Tile="square310x310Logo"/>
<uap:ShowOn Tile="wide310x150Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Images\SplashScreen.png"/>
<uap:LockScreen BadgeLogo="Images\BadgeLogo.png" Notification="badge"/>
</uap:VisualElements>
<Extensions>
<desktop:Extension Category="windows.startupTask" Executable="localsend_app.exe" EntryPoint="Windows.FullTrustApplication" uap10:Parameters="autostart">
<desktop:StartupTask TaskId="localsend" Enabled="false" DisplayName="LocalSend"/>
</desktop:Extension>
<uap:Extension Category="windows.shareTarget">
<uap:ShareTarget Description="Send to Localsend">
<uap:SupportedFileTypes>
<uap:SupportsAnyFileType />
</uap:SupportedFileTypes>
<uap:DataFormat>Text</uap:DataFormat>
<uap:DataFormat>Uri</uap:DataFormat>
<uap:DataFormat>StorageItems</uap:DataFormat>
</uap:ShareTarget>
</uap:Extension>
</Extensions>
</Application>
</Applications>
</Package>
Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6
View File
@@ -8,6 +8,7 @@
#define MyAppPublisher "Tien Do Nam"
#define MyAppURL "https://localsend.org"
#define MyAppExeName "localsend_app.exe"
#define MyAppMsixHelper "localsend_msix_helper.msix"
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
@@ -70,8 +71,10 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
[Files]
Source: "D:\inno\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\inno\{#MyAppExeName}.manifest"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\inno\*.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\inno\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "D:\inno\{#MyAppMsixHelper}"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
@@ -80,4 +83,7 @@ Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: de
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -Command Add-AppxPackage .\localsend_msix_helper.msix -ExternalLocation $(Get-Location)"; WorkingDir: {app}; Flags: nowait postinstall
[UninstallRun]
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -Command Remove-AppxPackage $(Get-AppxPackage com.flutter.localsendapp)"; WorkingDir: {app}; Flags: nowait
+4
View File
@@ -0,0 +1,4 @@
# Using Visual Studio 2022 Developer PowerShell
# or using e.g. "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\makeappx.exe"
MakeAppx.exe pack /o /d .\msix /nv /p .\app\windows\localsend_msix_helper.msix