Skip to content

Commit 1607e1d

Browse files
committed
添加设备通信相关的消息和接口定义以及移动端基本实现
1 parent 67b3b53 commit 1607e1d

123 files changed

Lines changed: 8215 additions & 583 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Core/Core.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFrameworks>net10.0;net10.0-windows10.0.19041.0</TargetFrameworks>
44
</PropertyGroup>
@@ -63,6 +63,7 @@
6363
<ProjectReference Include="..\NodifyM.Avalonia\NodifyM.Avalonia\NodifyM.Avalonia.csproj" />
6464
<ProjectReference Include="..\PinyinM.NET\Pinyin.NET\Pinyin.NET.csproj"/>
6565
<ProjectReference Include="..\PluginCore\PluginCore.csproj"/>
66+
<ProjectReference Include="..\Kitopia.DeviceCommunication\Kitopia.DeviceCommunication.csproj"/>
6667
</ItemGroup>
6768

6869

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
using SharedApplication = Kitopia.DeviceCommunication.Application;
2+
using SharedMessages = Kitopia.DeviceCommunication.Messages;
3+
using SharedChat = Kitopia.DeviceCommunication.Messages.Chat;
4+
using SharedClipboard = Kitopia.DeviceCommunication.Messages.Clipboard;
5+
using CoreChat = Core.Services.DeviceCommunication.Messages.Chat;
6+
using CoreClipboard = Core.Services.DeviceCommunication.Messages.Clipboard;
7+
8+
namespace Core.Services.DeviceCommunication.Application;
9+
10+
public sealed class DesktopSharedMessageAppService : SharedApplication.IMessageAppService
11+
{
12+
private readonly Core.Services.DeviceCommunication.Application.IMessageAppService _coreService;
13+
14+
public DesktopSharedMessageAppService(Core.Services.DeviceCommunication.Application.IMessageAppService coreService)
15+
{
16+
_coreService = coreService;
17+
}
18+
19+
public ValueTask SendTextChatAsync(string deviceId, string text, CancellationToken cancellationToken = default)
20+
{
21+
return _coreService.SendTextChatAsync(deviceId, text, cancellationToken);
22+
}
23+
24+
public ValueTask SendFileChatAsync(string deviceId, SharedChat.FileChatMessage message, Stream stream, CancellationToken cancellationToken = default)
25+
{
26+
return _coreService.SendFileChatAsync(
27+
deviceId,
28+
new CoreChat.FileChatMessage(message.ConversationId, message.ChannelId, message.FileName, message.Length),
29+
stream,
30+
cancellationToken);
31+
}
32+
33+
public ValueTask SendImageChatAsync(string deviceId, SharedChat.ImageChatMessage message, Stream stream, CancellationToken cancellationToken = default)
34+
{
35+
return _coreService.SendImageChatAsync(
36+
deviceId,
37+
new CoreChat.ImageChatMessage(message.ConversationId, message.TransferId, message.SizeBytes, message.ContentType, message.IsDirect),
38+
stream,
39+
cancellationToken);
40+
}
41+
42+
public ValueTask AcceptFileAsync(string deviceId, Guid transferId, string savePath, CancellationToken cancellationToken = default)
43+
{
44+
return _coreService.AcceptFileAsync(deviceId, transferId, savePath, cancellationToken);
45+
}
46+
47+
public ValueTask RejectFileAsync(string deviceId, Guid transferId, string reason, CancellationToken cancellationToken = default)
48+
{
49+
return _coreService.RejectFileAsync(deviceId, transferId, reason, cancellationToken);
50+
}
51+
52+
public ValueTask CancelTransferAsync(string deviceId, Guid transferId, string reason, CancellationToken cancellationToken = default)
53+
{
54+
return _coreService.CancelTransferAsync(deviceId, transferId, reason, cancellationToken);
55+
}
56+
57+
public ValueTask SendClipboardTextAsync(string deviceId, SharedClipboard.TextClipboardMessage message, CancellationToken cancellationToken = default)
58+
{
59+
return _coreService.SendClipboardTextAsync(
60+
deviceId,
61+
new CoreClipboard.TextClipboardMessage(message.ConversationId, message.Text),
62+
cancellationToken);
63+
}
64+
65+
public async IAsyncEnumerable<SharedApplication.DeviceMessageEvent> ReceiveAsync([System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
66+
{
67+
await foreach (var messageEvent in _coreService.ReceiveAsync(cancellationToken))
68+
{
69+
yield return ConvertEvent(messageEvent);
70+
}
71+
}
72+
73+
public void UpdateDisplayContext(bool isMainWindowActive, bool isDeviceChatPageOpen, string? selectedConversationId)
74+
{
75+
_coreService.UpdateDisplayContext(isMainWindowActive, isDeviceChatPageOpen, selectedConversationId);
76+
}
77+
78+
public void RequestOpenConversation(string conversationId)
79+
{
80+
_coreService.RequestOpenConversation(conversationId);
81+
}
82+
83+
public string? GetRequestedConversationId()
84+
{
85+
return _coreService.GetRequestedConversationId();
86+
}
87+
88+
public void ClearRequestedConversationId()
89+
{
90+
_coreService.ClearRequestedConversationId();
91+
}
92+
93+
public SharedApplication.IncomingMessageDisplayMode ResolveIncomingDisplayMode(string conversationId)
94+
{
95+
return (SharedApplication.IncomingMessageDisplayMode)_coreService.ResolveIncomingDisplayMode(conversationId);
96+
}
97+
98+
public SharedApplication.IncomingMessageDisplayMode ResolveIncomingDisplayMode(
99+
bool isMainWindowActive,
100+
bool isDeviceChatPageOpen,
101+
string conversationId,
102+
string? selectedConversationId)
103+
{
104+
return (SharedApplication.IncomingMessageDisplayMode)_coreService.ResolveIncomingDisplayMode(
105+
isMainWindowActive,
106+
isDeviceChatPageOpen,
107+
conversationId,
108+
selectedConversationId);
109+
}
110+
111+
private static SharedApplication.DeviceMessageEvent ConvertEvent(Core.Services.DeviceCommunication.Application.DeviceMessageEvent messageEvent)
112+
{
113+
return messageEvent switch
114+
{
115+
Core.Services.DeviceCommunication.Application.ChatMessageReceivedEvent chatEvent => new SharedApplication.ChatMessageReceivedEvent(
116+
ConvertMessage(chatEvent.Message),
117+
chatEvent.PayloadBytes,
118+
chatEvent.ConversationId,
119+
chatEvent.TimestampUtc),
120+
Core.Services.DeviceCommunication.Application.FileTransferUpdatedEvent transferEvent => new SharedApplication.FileTransferUpdatedEvent(
121+
transferEvent.ConversationId,
122+
transferEvent.TransferId,
123+
(SharedApplication.FileTransferDirection)transferEvent.Direction,
124+
(SharedApplication.FileTransferStatus)transferEvent.Status,
125+
transferEvent.FileName,
126+
transferEvent.BytesTransferred,
127+
transferEvent.TotalBytes,
128+
transferEvent.Reason,
129+
transferEvent.TimestampUtc),
130+
_ => throw new NotSupportedException($"Unsupported device message event type: {messageEvent.GetType().FullName}")
131+
};
132+
}
133+
134+
private static SharedMessages.AppMessage ConvertMessage(Core.Services.DeviceCommunication.Messages.AppMessage message)
135+
{
136+
return message switch
137+
{
138+
CoreChat.TextChatMessage text => new SharedChat.TextChatMessage(text.ConversationId, text.Text),
139+
CoreChat.FileChatMessage file => new SharedChat.FileChatMessage(file.ConversationId, file.ChannelId, file.FileName, file.Length),
140+
CoreChat.ImageChatMessage image => new SharedChat.ImageChatMessage(image.ConversationId, image.TransferId, image.SizeBytes, image.ContentType, image.IsDirect),
141+
CoreChat.FileOfferChatMessage fileOffer => new SharedChat.FileOfferChatMessage(fileOffer.ConversationId, fileOffer.TransferId, fileOffer.FileName, fileOffer.SizeBytes, fileOffer.ContentType, fileOffer.Hash),
142+
CoreChat.FileOfferReceivedChatMessage offerReceived => new SharedChat.FileOfferReceivedChatMessage(offerReceived.ConversationId, offerReceived.TransferId),
143+
CoreChat.FileAcceptChatMessage accept => new SharedChat.FileAcceptChatMessage(accept.ConversationId, accept.TransferId),
144+
CoreChat.FileRejectChatMessage reject => new SharedChat.FileRejectChatMessage(reject.ConversationId, reject.TransferId, reject.Reason),
145+
CoreChat.FileCancelChatMessage cancel => new SharedChat.FileCancelChatMessage(cancel.ConversationId, cancel.TransferId, cancel.Reason),
146+
CoreChat.FileCompleteChatMessage complete => new SharedChat.FileCompleteChatMessage(complete.ConversationId, complete.TransferId),
147+
CoreClipboard.TextClipboardMessage clipboard => new SharedClipboard.TextClipboardMessage(clipboard.ConversationId, clipboard.Text),
148+
_ => throw new NotSupportedException($"Unsupported app message type: {message.GetType().FullName}")
149+
};
150+
}
151+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Core.Services.Config;
2+
using Kitopia.DeviceCommunication.Discovery;
3+
4+
namespace Core.Services.DeviceCommunication;
5+
6+
public sealed class DesktopDeviceCommunicationSettings : IDeviceCommunicationSettings
7+
{
8+
public string BroadcastName => ConfigManger.Config.deviceBroadcastName;
9+
10+
public string? GetCustomName(string publicKey)
11+
{
12+
return ConfigManger.Config.deviceCustomNames.TryGetValue(publicKey, out var customName)
13+
? customName
14+
: null;
15+
}
16+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Core.Services.Config;
2+
using Kitopia.DeviceCommunication.Identity;
3+
4+
namespace Core.Services.DeviceCommunication;
5+
6+
public sealed class DesktopDeviceIdentityStore : IDeviceIdentityStore
7+
{
8+
public bool TryGetIdentity(out DeviceIdentity identity)
9+
{
10+
var privateKey = ConfigManger.Config.devicePrivateKey?.Trim() ?? string.Empty;
11+
if (!DeviceDiscoverySignature.TryDerivePublicKey(privateKey, out var publicKey))
12+
{
13+
identity = default!;
14+
return false;
15+
}
16+
17+
identity = new DeviceIdentity(
18+
publicKey,
19+
privateKey,
20+
DeviceDiscoverySignature.ComputePublicKeyHash(publicKey));
21+
return true;
22+
}
23+
24+
public DeviceIdentity EnsureIdentity()
25+
{
26+
var changed = ConfigManger.Config.EnsureDeviceIdentity();
27+
if (changed)
28+
{
29+
ConfigManger.Save("KitopiaConfig");
30+
}
31+
32+
if (!TryGetIdentity(out var identity))
33+
{
34+
throw new InvalidOperationException("Device identity is not initialized.");
35+
}
36+
37+
return identity;
38+
}
39+
}

Core/Services/DeviceCommunication/DeviceCommunication.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,39 @@
55
// Date: 2026/04/11 10:04
66
// FileEffect:
77

8-
using Core.Services.Config;
98
using Core.Services.DeviceCommunication.Discovery;
10-
using Microsoft.Extensions.DependencyInjection;
9+
using Kitopia.DeviceCommunication.Identity;
1110
using PluginCore;
1211

1312
namespace Core.Services.DeviceCommunication;
1413

1514
public class DeviceCommunication : IDeviceCommunication
1615
{
16+
private readonly IDeviceIdentityStore _deviceIdentityStore;
17+
private readonly ILocalDataListener _localDataListener;
18+
private readonly IDeviceDiscoveryService _deviceDiscoveryService;
19+
20+
public DeviceCommunication(
21+
IDeviceIdentityStore deviceIdentityStore,
22+
ILocalDataListener localDataListener,
23+
IDeviceDiscoveryService deviceDiscoveryService)
24+
{
25+
_deviceIdentityStore = deviceIdentityStore;
26+
_localDataListener = localDataListener;
27+
_deviceDiscoveryService = deviceDiscoveryService;
28+
}
29+
1730
public async Task StartAsync(CancellationToken token = default)
1831
{
19-
if (ConfigManger.Config.EnsureDeviceIdentity())
20-
{
21-
ConfigManger.Save("KitopiaConfig");
22-
}
23-
24-
await ServiceManager.Services.GetService<ILocalDataListener>()!.StartListeningAsync(token).ConfigureAwait(false);
25-
await ServiceManager.Services.GetService<IDeviceDiscoveryService>()!.StartAsync(token).ConfigureAwait(false);
32+
_deviceIdentityStore.EnsureIdentity();
33+
34+
await _localDataListener.StartListeningAsync(token).ConfigureAwait(false);
35+
await _deviceDiscoveryService.StartAsync(token).ConfigureAwait(false);
2636
}
2737

2838
public async Task StopAsync()
2939
{
30-
await ServiceManager.Services.GetService<IDeviceDiscoveryService>()!.StopAsync().ConfigureAwait(false);
31-
await ServiceManager.Services.GetService<ILocalDataListener>()!.StopListeningAsync().ConfigureAwait(false);
40+
await _deviceDiscoveryService.StopAsync().ConfigureAwait(false);
41+
await _localDataListener.StopListeningAsync().ConfigureAwait(false);
3242
}
3343
}

0 commit comments

Comments
 (0)