diff --git a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs index 05f3074..68b4331 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices.NearShare/NearShareSender.cs @@ -4,6 +4,7 @@ using ShortDev.Microsoft.ConnectedDevices.NearShare.Apps; using ShortDev.Microsoft.ConnectedDevices.NearShare.Messages; using ShortDev.Microsoft.ConnectedDevices.Serialization; +using ShortDev.Microsoft.ConnectedDevices.Transports; using System.Buffers; using System.Diagnostics; @@ -13,9 +14,11 @@ public sealed class NearShareSender(ConnectedDevicesPlatform platform) { public ConnectedDevicesPlatform Platform { get; } = platform; + public event EventHandler? TransportUpgraded; + async Task PrepareTransferInternalAsync(EndpointInfo endpoint, CancellationToken cancellationToken) { - var session = await Platform.ConnectAsync(endpoint); + var session = await Platform.ConnectAsync(endpoint, options: new() { TransportUpgraded = TransportUpgraded }); Guid operationId = Guid.NewGuid(); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/CdpLog.cs b/lib/ShortDev.Microsoft.ConnectedDevices/CdpLog.cs index 0a54c88..e43139e 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/CdpLog.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/CdpLog.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using ShortDev.Microsoft.ConnectedDevices.Messages.Connection; +using ShortDev.Microsoft.ConnectedDevices.Messages.Connection.TransportUpgrade; using ShortDev.Microsoft.ConnectedDevices.Messages.Control; using ShortDev.Microsoft.ConnectedDevices.Transports; @@ -71,4 +72,10 @@ internal static partial class CdpLog [LoggerMessage(EventId = 304, Level = LogLevel.Warning, Message = "Upgrade failed")] public static partial void UpgradeFailed(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 305, Level = LogLevel.Debug, Message = "Sending upgrade request {UpgradeId} to {UpgradeTypes}")] + public static partial void SendingUpgradeRequest(this ILogger logger, Guid upgradeId, IEnumerable upgradeTypes); + + [LoggerMessage(EventId = 306, Level = LogLevel.Debug, Message = "Upgrade response {UpgradeId} to {Endpoints}")] + public static partial void UpgradeResponse(this ILogger logger, Guid upgradeId, IEnumerable endpoints); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/CdpSession.cs b/lib/ShortDev.Microsoft.ConnectedDevices/CdpSession.cs index 6c689fd..9413002 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/CdpSession.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/CdpSession.cs @@ -72,7 +72,7 @@ internal static CdpSession GetOrCreate(ConnectedDevicesPlatform platform, Endpoi ), out _); } - internal static async Task ConnectClientAsync(ConnectedDevicesPlatform platform, CdpSocket socket) + internal static async Task ConnectClientAsync(ConnectedDevicesPlatform platform, CdpSocket socket, ConnectOptions? options = null) { var session = _sessionRegistry.Create(localSessionId => new( platform, @@ -81,6 +81,10 @@ internal static async Task ConnectClientAsync(ConnectedDevicesPlatfo ), out _); var connectHandler = (ClientConnectHandler)session._connectHandler; + + if (options is not null) + connectHandler.UpgradeHandler.Upgraded += options.TransportUpgraded; + await connectHandler.ConnectAsync(socket); return session; diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/ConnectOptions.cs b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectOptions.cs new file mode 100644 index 0000000..3b2998c --- /dev/null +++ b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectOptions.cs @@ -0,0 +1,8 @@ +using ShortDev.Microsoft.ConnectedDevices.Transports; + +namespace ShortDev.Microsoft.ConnectedDevices; + +public record ConnectOptions +{ + public EventHandler? TransportUpgraded { get; init; } +} diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.cs b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.cs index 64adb3f..8610ef1 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/ConnectedDevicesPlatform.cs @@ -140,10 +140,10 @@ await Task.WhenAll(_transportMap.Values } } - public async Task ConnectAsync([NotNull] EndpointInfo endpoint) + public async Task ConnectAsync([NotNull] EndpointInfo endpoint, ConnectOptions? options = null) { var socket = await CreateSocketAsync(endpoint).ConfigureAwait(false); - return await CdpSession.ConnectClientAsync(this, socket).ConfigureAwait(false); + return await CdpSession.ConnectClientAsync(this, socket, options).ConfigureAwait(false); } internal async Task CreateSocketAsync(EndpointInfo endpoint) diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs index f836283..44bbcd5 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/EndpointMetadata.cs @@ -15,7 +15,7 @@ public static EndpointMetadata Parse(ref EndianReader reader) return new(type, data.ToArray()); } - public static EndpointMetadata[] ParseArray(ref EndianReader reader) + public static IReadOnlyList ParseArray(ref EndianReader reader) { var arrayLength = reader.ReadUInt16(); var endpoints = new EndpointMetadata[arrayLength]; @@ -31,9 +31,9 @@ public void Write(EndianWriter writer) writer.Write(Data); } - public static void WriteArray(EndianWriter writer, EndpointMetadata[] endpoints) + public static void WriteArray(EndianWriter writer, IReadOnlyList endpoints) { - writer.Write((ushort)endpoints.Length); + writer.Write((ushort)endpoints.Count); foreach (var endpoint in endpoints) endpoint.Write(writer); } diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs index 1f2c0ec..f1d11ec 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeRequest.cs @@ -9,7 +9,7 @@ public sealed class UpgradeRequest : ICdpPayload /// A random GUID identifying this upgrade process across transports. /// public required Guid UpgradeId { get; init; } - public required EndpointMetadata[] Endpoints { get; init; } + public required IReadOnlyList Endpoints { get; init; } public static UpgradeRequest Parse(ref EndianReader reader) => new() diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs index 0e3cde8..8f04d51 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/Connection/TransportUpgrade/UpgradeResponse.cs @@ -10,8 +10,8 @@ public sealed class UpgradeResponse : ICdpPayload /// /// A length-prefixed list of endpoint structures (see following) that are provided by each transport on the host device. /// - public required EndpointInfo[] Endpoints { get; init; } - public required EndpointMetadata[] MetaData { get; init; } + public required IReadOnlyList Endpoints { get; init; } + public required IReadOnlyList MetaData { get; init; } public static UpgradeResponse Parse(ref EndianReader reader) { @@ -37,7 +37,7 @@ public static UpgradeResponse Parse(ref EndianReader reader) public void Write(EndianWriter writer) { - writer.Write((ushort)Endpoints.Length); + writer.Write((ushort)Endpoints.Count); foreach (var endpoint in Endpoints) { writer.WriteWithLength(endpoint.Address); diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpSerializable.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpSerializable.cs index de30d98..af68317 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpSerializable.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Messages/ICdpSerializable.cs @@ -16,8 +16,8 @@ public long CalcSize() public interface ICdpArraySerializable where T : ICdpArraySerializable { - static abstract T[] ParseArray(ref EndianReader reader); - static abstract void WriteArray(EndianWriter writer, T[] array); + static abstract IReadOnlyList ParseArray(ref EndianReader reader); + static abstract void WriteArray(EndianWriter writer, IReadOnlyList array); } public interface ICdpWriteable diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs index 7835f73..ee79628 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/ClientUpgradeHandler.cs @@ -1,4 +1,5 @@ -using ShortDev.Microsoft.ConnectedDevices.Messages; +using Microsoft.Extensions.Logging; +using ShortDev.Microsoft.ConnectedDevices.Messages; using ShortDev.Microsoft.ConnectedDevices.Messages.Connection; using ShortDev.Microsoft.ConnectedDevices.Messages.Connection.TransportUpgrade; using ShortDev.Microsoft.ConnectedDevices.Transports; @@ -8,6 +9,8 @@ namespace ShortDev.Microsoft.ConnectedDevices.Session.Upgrade; internal sealed class ClientUpgradeHandler(CdpSession session, EndpointInfo initialEndpoint) : UpgradeHandler(session, initialEndpoint) { + private readonly ILogger _logger = session.Platform.CreateLogger(); + protected override bool TryHandleConnectInternal(CdpSocket socket, ConnectionHeader connectionHeader, ref EndianReader reader) { if (!IsSocketAllowed(socket)) @@ -34,6 +37,8 @@ protected override bool TryHandleConnectInternal(CdpSocket socket, ConnectionHea return false; } + static readonly IReadOnlyList UpgradeEndpoints = [EndpointMetadata.Tcp]; + UpgradeInstance? _currentUpgrade; public async ValueTask RequestUpgradeAsync(CdpSocket oldSocket) { @@ -43,7 +48,8 @@ public async ValueTask RequestUpgradeAsync(CdpSocket oldSocket) _currentUpgrade = new(); try { - SendUpgradeRequest(oldSocket, _currentUpgrade.Id); + _logger.SendingUpgradeRequest(_currentUpgrade.Id, UpgradeEndpoints); + SendUpgradeRequest(oldSocket, _currentUpgrade.Id, UpgradeEndpoints); return await _currentUpgrade.Promise.Task; } finally @@ -51,7 +57,7 @@ public async ValueTask RequestUpgradeAsync(CdpSocket oldSocket) _currentUpgrade = null; } - void SendUpgradeRequest(CdpSocket socket, Guid upgradeId) + void SendUpgradeRequest(CdpSocket socket, Guid upgradeId, IReadOnlyList endpoints) { CommonHeader header = new() { @@ -68,10 +74,7 @@ void SendUpgradeRequest(CdpSocket socket, Guid upgradeId) new UpgradeRequest() { UpgradeId = upgradeId, - Endpoints = - [ - EndpointMetadata.Tcp - ] + Endpoints = endpoints }.Write(writer); _session.SendMessage(socket, header, writer); @@ -84,6 +87,8 @@ void HandleUpgradeResponse(CdpSocket oldSocket, ref EndianReader reader) return; var msg = UpgradeResponse.Parse(ref reader); + _logger.UpgradeResponse(_currentUpgrade.Id, msg.Endpoints); + FindNewEndpoint(); async void FindNewEndpoint() diff --git a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs index 80b912d..0436100 100644 --- a/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs +++ b/lib/ShortDev.Microsoft.ConnectedDevices/Session/Upgrade/UpgradeHandler.cs @@ -13,7 +13,18 @@ internal abstract class UpgradeHandler(CdpSession session, EndpointInfo initialE public bool IsSocketAllowed(CdpSocket socket) => _allowedAddresses.Contains(socket.Endpoint.Address); - public EndpointInfo RemoteEndpoint { get; protected set; } = initialEndpoint; + public event EventHandler? Upgraded; + + EndpointInfo _remoteEndpoint = initialEndpoint; + public EndpointInfo RemoteEndpoint + { + get => _remoteEndpoint; + protected set + { + _remoteEndpoint = value; + Upgraded?.Invoke(this, value.TransportType); + } + } public bool IsUpgradeSupported => (/* ToDo: header131Value & */ _session.ClientCapabilities & _session.HostCapabilities & PeerCapabilities.UpgradeSupport) != 0; diff --git a/src/SendActivity.cs b/src/SendActivity.cs index 471cae9..b8a9e93 100644 --- a/src/SendActivity.cs +++ b/src/SendActivity.cs @@ -30,7 +30,7 @@ namespace NearShare.Droid; [Activity(Label = "@string/app_name", Exported = true, Theme = "@style/AppTheme.TranslucentOverlay", ConfigurationChanges = UIHelper.ConfigChangesFlags)] public sealed class SendActivity : AppCompatActivity { - NearShareSender NearShareSender = null!; + NearShareSender _nearShareSender = null!; BottomSheetDialog _dialog = null!; RecyclerView DeviceDiscoveryListView = null!; @@ -78,13 +78,7 @@ protected override void OnCreate(Bundle? savedInstanceState) view.FindViewById(Resource.Id.deviceTypeImageView)!.SetImageResource( device.Type.IsMobile() ? Resource.Drawable.ic_fluent_phone_24_regular : Resource.Drawable.ic_fluent_desktop_24_regular ); - view.FindViewById(Resource.Id.transportTypeImageView)!.SetImageResource(device.Endpoint.TransportType switch - { - CdpTransportType.Tcp => Resource.Drawable.ic_fluent_wifi_1_20_regular, - CdpTransportType.Rfcomm => Resource.Drawable.ic_fluent_bluetooth_20_regular, - CdpTransportType.WifiDirect => Resource.Drawable.ic_fluent_live_20_regular, - _ => Resource.Drawable.ic_fluent_question_circle_20_regular - }); + view.FindViewById(Resource.Id.transportTypeImageView)!.SetImageResource(GetTransportIcon(device.Endpoint.TransportType)); view.FindViewById(Resource.Id.deviceNameTextView)!.Text = device.Name; view.Click += (s, e) => SendData(device); } @@ -137,7 +131,7 @@ void InitializePlatform() _cdp.DeviceDiscovered += Platform_DeviceDiscovered; _cdp.Discover(_discoverCancellationTokenSource.Token); - NearShareSender = new NearShareSender(_cdp); + _nearShareSender = new NearShareSender(_cdp); } readonly ObservableCollection RemoteSystems = []; @@ -190,13 +184,10 @@ private async void SendData(CdpDevice remoteSystem) sendingDataLayout.FindViewById(Resource.Id.deviceTypeImageView)!.SetImageResource( remoteSystem.Type.IsMobile() ? Resource.Drawable.ic_fluent_phone_24_regular : Resource.Drawable.ic_fluent_desktop_24_regular ); - sendingDataLayout.FindViewById(Resource.Id.transportTypeImageView)!.SetImageResource(remoteSystem.Endpoint.TransportType switch - { - CdpTransportType.Tcp => Resource.Drawable.ic_fluent_wifi_1_20_regular, - CdpTransportType.Rfcomm => Resource.Drawable.ic_fluent_bluetooth_20_regular, - CdpTransportType.WifiDirect => Resource.Drawable.ic_fluent_live_20_regular, - _ => Resource.Drawable.ic_fluent_question_circle_20_regular - }); + + var transportTypeImage = sendingDataLayout.FindViewById(Resource.Id.transportTypeImageView)!; + transportTypeImage.SetImageResource(GetTransportIcon(remoteSystem.Endpoint.TransportType)); + _nearShareSender.TransportUpgraded += OnTransportUpgrade; var deviceNameTextView = sendingDataLayout.FindViewById(Resource.Id.deviceNameTextView)!; var progressIndicator = sendingDataLayout.FindViewById(Resource.Id.sendProgressIndicator)!; @@ -220,7 +211,7 @@ private async void SendData(CdpDevice remoteSystem) if (files != null) { progress = new(); - transferPromise = NearShareSender.SendFilesAsync( + transferPromise = _nearShareSender.SendFilesAsync( remoteSystem, files, progress, @@ -229,7 +220,7 @@ private async void SendData(CdpDevice remoteSystem) } else if (uri != null) { - transferPromise = NearShareSender.SendUriAsync( + transferPromise = _nearShareSender.SendUriAsync( remoteSystem, uri ); @@ -308,6 +299,15 @@ private async void SendData(CdpDevice remoteSystem) progressIndicator.Indeterminate = false; progressIndicator.Progress = progressIndicator.Max; + + _nearShareSender.TransportUpgraded -= OnTransportUpgrade; + } + + void OnTransportUpgrade(object? sender, CdpTransportType transportType) + { + RunOnUiThread(() => + transportTypeImage.SetImageResource(GetTransportIcon(transportType)) + ); } } @@ -386,6 +386,17 @@ public override void Finish() _cdp?.Dispose(); } + static int GetTransportIcon(CdpTransportType transportType) + { + return transportType switch + { + CdpTransportType.Tcp => Resource.Drawable.ic_fluent_wifi_1_20_regular, + CdpTransportType.Rfcomm => Resource.Drawable.ic_fluent_bluetooth_20_regular, + CdpTransportType.WifiDirect => Resource.Drawable.ic_fluent_live_20_regular, + _ => Resource.Drawable.ic_fluent_question_circle_20_regular + }; + } + sealed class FinishActivityBottomSheetCallback(Activity activity) : BottomSheetBehavior.BottomSheetCallback { public override void OnSlide(View bottomSheet, float newState) { }