From d43f2e76c4d93282fac1863166f6c36c265c91a5 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Wed, 3 Jan 2024 14:26:21 +0800 Subject: [PATCH] code style --- .../Web/Bridge/BridgeShareContext.cs | 41 +++++ .../Web/Bridge/BridgeShareImplmentation.cs | 108 +++++++++++++ .../Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs | 148 ++---------------- .../Web/Bridge/Model/ShareContent.cs | 8 - 4 files changed, 161 insertions(+), 144 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareContext.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareImplmentation.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareContext.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareContext.cs new file mode 100644 index 00000000..7a7ea616 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareContext.cs @@ -0,0 +1,41 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.Web.WebView2.Core; +using Snap.Hutao.Core.IO.DataTransfer; +using Snap.Hutao.Service.Notification; +using System.Net.Http; + +namespace Snap.Hutao.Web.Bridge; + +internal sealed class BridgeShareContext +{ + private readonly CoreWebView2 coreWebView2; + private readonly ITaskContext taskContext; + private readonly HttpClient httpClient; + private readonly IInfoBarService infoBarService; + private readonly IClipboardProvider clipboardProvider; + private readonly JsonSerializerOptions jsonSerializerOptions; + + public BridgeShareContext(CoreWebView2 coreWebView2, ITaskContext taskContext, HttpClient httpClient, IInfoBarService infoBarService, IClipboardProvider clipboardProvider, JsonSerializerOptions jsonSerializerOptions) + { + this.httpClient = httpClient; + this.taskContext = taskContext; + this.infoBarService = infoBarService; + this.clipboardProvider = clipboardProvider; + this.coreWebView2 = coreWebView2; + this.jsonSerializerOptions = jsonSerializerOptions; + } + + public CoreWebView2 CoreWebView2 { get => coreWebView2; } + + public ITaskContext TaskContext { get => taskContext; } + + public HttpClient HttpClient { get => httpClient; } + + public IInfoBarService InfoBarService { get => infoBarService; } + + public IClipboardProvider ClipboardProvider { get => clipboardProvider; } + + public JsonSerializerOptions JsonSerializerOptions { get => jsonSerializerOptions; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareImplmentation.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareImplmentation.cs new file mode 100644 index 00000000..f3aeae0c --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareImplmentation.cs @@ -0,0 +1,108 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Service.Notification; +using Snap.Hutao.Web.Bridge.Model; +using System.IO; +using Windows.Graphics.Imaging; +using Windows.Storage.Streams; + +namespace Snap.Hutao.Web.Bridge; + +internal sealed partial class BridgeShareImplmentation +{ + public static async ValueTask ShareAsync(JsParam param, BridgeShareContext context) + { + SharePayload payload = param.Payload; + switch (payload.Type) + { + case "image": + { + ShareContent content = payload.Content; + if (content.ImageUrl is { Length: > 0 } imageUrl) + { + await ShareFromImageUrlAsync(context, imageUrl).ConfigureAwait(false); + } + else if (content.ImageBase64 is { } imageBase64) + { + await ShareFromImageBase64Async(context, imageBase64).ConfigureAwait(false); + } + + break; + } + + case "screenshot": + { + await context.TaskContext.SwitchToMainThreadAsync(); + + // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot + string jsonParameters = """{ "format": "png", "captureBeyondViewport": true }"""; + string resultJson = await context.CoreWebView2.CallDevToolsProtocolMethodAsync("Page.captureScreenshot", jsonParameters); + + CaptureScreenshotResult? result = JsonSerializer.Deserialize(resultJson, context.JsonSerializerOptions); + ArgumentNullException.ThrowIfNull(result); + + await ShareFromRawPixelDataAsync(context, result.Data).ConfigureAwait(false); + break; + } + } + + return new JsResult>() + { + Data = new() + { + ["type"] = param.Payload.Type, + }, + }; + } + + private static async ValueTask ShareFromImageUrlAsync(BridgeShareContext context, string imageUrl) + { + using (Stream stream = await context.HttpClient.GetStreamAsync(imageUrl).ConfigureAwait(false)) + { + await ShareCoreAsync(context, stream, static (stream, web) => web.CopyToAsync(stream.AsStreamForWrite())).ConfigureAwait(false); + } + } + + private static ValueTask ShareFromImageBase64Async(BridgeShareContext context, string base64ImageData) + { + return ShareFromRawPixelDataAsync(context, Convert.FromBase64String(base64ImageData)); + } + + private static ValueTask ShareFromRawPixelDataAsync(BridgeShareContext context, byte[] rawPixelData) + { + return ShareCoreAsync(context, rawPixelData, static (stream, bytes) => stream.AsStreamForWrite().WriteAsync(bytes).AsTask()); + } + + private static async ValueTask ShareCoreAsync(BridgeShareContext context, TData data, Func asyncWriteData) + { + using (InMemoryRandomAccessStream rawPixelDataStream = new()) + { + await asyncWriteData(rawPixelDataStream, data).ConfigureAwait(false); + BitmapDecoder decoder = await BitmapDecoder.CreateAsync(rawPixelDataStream); + + using (InMemoryRandomAccessStream stream = new()) + { + BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream); + encoder.SetSoftwareBitmap(await decoder.GetSoftwareBitmapAsync()); + await encoder.FlushAsync(); + + await context.TaskContext.SwitchToMainThreadAsync(); + if (context.ClipboardProvider.SetBitmap(stream)) + { + context.InfoBarService.Success(SH.WebBridgeShareCopyToClipboardSuccess); + } + else + { + context.InfoBarService.Error(SH.WebBridgeShareCopyToClipboardFailed); + } + } + } + } + + private sealed class CaptureScreenshotResult + { + [JsonPropertyName("data")] + public byte[] Data { get; set; } = default!; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs index c900adf6..21307c6b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs @@ -84,15 +84,12 @@ internal class MiHoYoJSBridge """; private readonly SemaphoreSlim webMessageSemaphore = new(1); - private readonly Guid interfaceId = Guid.NewGuid(); + private readonly Guid bridgeId = Guid.NewGuid(); private readonly UserAndUid userAndUid; private readonly IServiceProvider serviceProvider; private readonly ITaskContext taskContext; private readonly ILogger logger; - private readonly IInfoBarService infoBarService; - private readonly IClipboardProvider clipboardProvider; - private readonly HttpClient httpClient; private readonly TypedEventHandler webMessageReceivedEventHandler; private readonly TypedEventHandler domContentLoadedEventHandler; @@ -109,9 +106,6 @@ internal class MiHoYoJSBridge taskContext = serviceProvider.GetRequiredService(); logger = serviceProvider.GetRequiredService>(); - infoBarService = serviceProvider.GetRequiredService(); - clipboardProvider = serviceProvider.GetRequiredService(); - httpClient = serviceProvider.GetRequiredService(); webMessageReceivedEventHandler = OnWebMessageReceived; domContentLoadedEventHandler = OnDOMContentLoaded; @@ -132,11 +126,6 @@ internal class MiHoYoJSBridge coreWebView2 = default!; } - /// - /// 关闭 - /// - /// 参数 - /// 响应 protected virtual async ValueTask ClosePageAsync(JsParam param) { await taskContext.SwitchToMainThreadAsync(); @@ -152,21 +141,11 @@ internal class MiHoYoJSBridge return null; } - /// - /// 调整分享设置 - /// - /// 参数 - /// 响应 protected virtual IJsBridgeResult? ConfigureShare(JsParam param) { return null; } - /// - /// 获取ActionTicket - /// - /// 参数 - /// 响应 protected virtual async ValueTask GetActionTicketAsync(JsParam jsParam) { return await serviceProvider @@ -175,11 +154,6 @@ internal class MiHoYoJSBridge .ConfigureAwait(false); } - /// - /// 异步获取账户信息 - /// - /// 参数 - /// 响应 protected virtual JsResult> GetCookieInfo(JsParam param) { ArgumentNullException.ThrowIfNull(userAndUid.User.LToken); @@ -195,11 +169,6 @@ internal class MiHoYoJSBridge }; } - /// - /// 获取CookieToken - /// - /// 参数 - /// 响应 protected virtual async ValueTask>> GetCookieTokenAsync(JsParam param) { IUserService userService = serviceProvider.GetRequiredService(); @@ -215,11 +184,6 @@ internal class MiHoYoJSBridge return new() { Data = new() { [Cookie.COOKIE_TOKEN] = userAndUid.User.CookieToken[Cookie.COOKIE_TOKEN] } }; } - /// - /// 获取当前语言和时区 - /// - /// param - /// 语言与时区 protected virtual JsResult> GetCurrentLocale(JsParam param) { MetadataOptions metadataOptions = serviceProvider.GetRequiredService(); @@ -234,11 +198,6 @@ internal class MiHoYoJSBridge }; } - /// - /// 获取1代动态密钥 - /// - /// 参数 - /// 响应 protected virtual JsResult> GetDynamicSecrectV1(JsParam param) { DataSignOptions options = DataSignOptions.CreateForGeneration1(SaltType.LK2, true); @@ -251,11 +210,6 @@ internal class MiHoYoJSBridge }; } - /// - /// 获取2代动态密钥 - /// - /// 参数 - /// 响应 protected virtual JsResult> GetDynamicSecrectV2(JsParam param) { DataSignOptions options = DataSignOptions.CreateForGeneration2(SaltType.X4, false, param.Payload.Body, param.Payload.GetQueryParam()); @@ -268,11 +222,6 @@ internal class MiHoYoJSBridge }; } - /// - /// 获取Http请求头 - /// - /// 参数 - /// Http请求头 protected virtual JsResult> GetHttpRequestHeader(JsParam param) { Dictionary headers = new() @@ -306,21 +255,11 @@ internal class MiHoYoJSBridge { } - /// - /// 获取状态栏高度 - /// - /// 参数 - /// 结果 protected virtual JsResult> GetStatusBarHeight(JsParam param) { return new() { Data = new() { ["statusBarHeight"] = 0 } }; } - /// - /// 获取用户基本信息 - /// - /// 参数 - /// 响应 protected virtual async ValueTask>> GetUserInfoAsync(JsParam param) { Response response = await serviceProvider @@ -374,81 +313,18 @@ internal class MiHoYoJSBridge return null; } - protected virtual async ValueTask Share(JsParam param) + protected virtual async ValueTask ShareAsync(JsParam param) { - if (param.Payload.Type is "image") + using (IServiceScope scope = serviceProvider.CreateScope()) { - if (param.Payload.Content.ImageUrl is { } imageUrl) - { - using (Stream stream = await httpClient.GetStreamAsync(imageUrl).ConfigureAwait(false)) - { - using (InMemoryRandomAccessStream origStream = new()) - { - await stream.CopyToAsync(origStream.AsStreamForWrite()).ConfigureAwait(false); - using (InMemoryRandomAccessStream imageStream = new()) - { - BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, imageStream); - BitmapDecoder decoder = await BitmapDecoder.CreateAsync(origStream); - encoder.SetSoftwareBitmap(await decoder.GetSoftwareBitmapAsync()); - await encoder.FlushAsync(); - await taskContext.SwitchToMainThreadAsync(); - if (clipboardProvider.SetBitmap(imageStream)) - { - infoBarService.Success(SH.WebBridgeShareCopyToClipboardSuccess); - } - else - { - infoBarService.Error(SH.WebBridgeShareCopyToClipboardFailed); - } - } - } - } - } - else if (param.Payload.Content.ImageBase64 is { } imageBase64) - { - await ShareImageBase64CoreAsync(imageBase64).ConfigureAwait(false); - } - } - else if (param.Payload.Type is "screenshot") - { - await taskContext.SwitchToMainThreadAsync(); - string base64Json = await coreWebView2.CallDevToolsProtocolMethodAsync("Page.captureScreenshot", """{"format":"png","captureBeyondViewport":true}"""); - string? base64 = JsonDocument.Parse(base64Json).RootElement.GetProperty("data").GetString(); - ArgumentNullException.ThrowIfNull(base64); + JsonSerializerOptions jsonSerializerOptions = scope.ServiceProvider.GetRequiredService(); + HttpClient httpClient = scope.ServiceProvider.GetRequiredService(); + IClipboardProvider clipboardProvider = scope.ServiceProvider.GetRequiredService(); + IInfoBarService infoBarService = scope.ServiceProvider.GetRequiredService(); - await ShareImageBase64CoreAsync(base64).ConfigureAwait(false); - } + BridgeShareContext context = new(coreWebView2, taskContext, httpClient, infoBarService, clipboardProvider, jsonSerializerOptions); - return new JsResult>() - { - Data = new() - { - ["type"] = param.Payload.Type, - }, - }; - } - - private async ValueTask ShareImageBase64CoreAsync(string base64) - { - using (MemoryStream imageStream = new()) - { - await imageStream.WriteAsync(Convert.FromBase64String(base64)).ConfigureAwait(false); - using (InMemoryRandomAccessStream stream = new()) - { - BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream); - BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream.AsRandomAccessStream()); - encoder.SetSoftwareBitmap(await decoder.GetSoftwareBitmapAsync()); - await encoder.FlushAsync(); - await taskContext.SwitchToMainThreadAsync(); - if (clipboardProvider.SetBitmap(stream)) - { - infoBarService.Success(SH.WebBridgeShareCopyToClipboardSuccess); - } - else - { - infoBarService.Error(SH.WebBridgeShareCopyToClipboardFailed); - } - } + return await BridgeShareImplmentation.ShareAsync(param, context).ConfigureAwait(false); } } @@ -519,7 +395,7 @@ internal class MiHoYoJSBridge .Append(')') .ToString(); - logger?.LogInformation("[{Id}][ExecuteScript: {callback}]\n{payload}", interfaceId, callback, payload); + logger?.LogInformation("[{Id}][ExecuteScript: {callback}]\n{payload}", bridgeId, callback, payload); await taskContext.SwitchToMainThreadAsync(); if (coreWebView2 is null || coreWebView2.IsDisposed()) @@ -533,7 +409,7 @@ internal class MiHoYoJSBridge private async void OnWebMessageReceived(CoreWebView2 webView2, CoreWebView2WebMessageReceivedEventArgs args) { string message = args.TryGetWebMessageAsString(); - logger.LogInformation("[{Id}][OnRawMessage]\n{message}", interfaceId, message); + logger.LogInformation("[{Id}][OnRawMessage]\n{message}", bridgeId, message); JsParam? param = JsonSerializer.Deserialize(message); ArgumentNullException.ThrowIfNull(param); @@ -582,7 +458,7 @@ internal class MiHoYoJSBridge "hideLoading" => null, "login" => null, "pushPage" => await PushPageAsync(param).ConfigureAwait(false), - "share" => await Share(param).ConfigureAwait(false), + "share" => await ShareAsync(param).ConfigureAwait(false), "showLoading" => null, _ => LogUnhandledMessage("Unhandled Message Type: {Method}", param.Method), }; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/ShareContent.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/ShareContent.cs index 26437013..efc607f9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/ShareContent.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/ShareContent.cs @@ -6,20 +6,12 @@ namespace Snap.Hutao.Web.Bridge.Model; [SuppressMessage("", "SA1124")] internal sealed class ShareContent { - #region Screenshot - [JsonPropertyName("preview")] public bool Preview { get; set; } - #endregion - - #region Image - [JsonPropertyName("image_url")] public string? ImageUrl { get; set; } [JsonPropertyName("image_base64")] public string? ImageBase64 { get; set; } - - #endregion }