mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98c003ae77 | ||
|
|
d26611ccf7 |
@@ -11,12 +11,6 @@ namespace Snap.Hutao.Test.IncomingFeature;
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public class GameRegistryContentTest
|
public class GameRegistryContentTest
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions RegistryContentSerializerOptions = new()
|
|
||||||
{
|
|
||||||
WriteIndented = true,
|
|
||||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
|
||||||
};
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
public void GetRegistryContent()
|
public void GetRegistryContent()
|
||||||
@@ -39,29 +33,28 @@ public class GameRegistryContentTest
|
|||||||
data[valueName] = gameKey.GetValueKind(valueName) switch
|
data[valueName] = gameKey.GetValueKind(valueName) switch
|
||||||
{
|
{
|
||||||
RegistryValueKind.DWord => (int)gameKey.GetValue(valueName)!,
|
RegistryValueKind.DWord => (int)gameKey.GetValue(valueName)!,
|
||||||
RegistryValueKind.Binary => GetStringOrObject((byte[])gameKey.GetValue(valueName)!),
|
RegistryValueKind.Binary => GetString((byte[])gameKey.GetValue(valueName)!),
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonSerializerOptions options = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||||
|
};
|
||||||
|
|
||||||
Console.WriteLine($"Subkey: {subkey}");
|
Console.WriteLine($"Subkey: {subkey}");
|
||||||
Console.WriteLine(JsonSerializer.Serialize(data, RegistryContentSerializerOptions));
|
Console.WriteLine(JsonSerializer.Serialize(data, options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe object GetStringOrObject(byte[] bytes)
|
private static unsafe string GetString(byte[] bytes)
|
||||||
{
|
{
|
||||||
fixed (byte* pByte = bytes)
|
fixed (byte* pByte = bytes)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> span = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pByte);
|
ReadOnlySpan<byte> span = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pByte);
|
||||||
string temp = Encoding.UTF8.GetString(span);
|
return Encoding.UTF8.GetString(span);
|
||||||
|
|
||||||
if (temp.AsSpan()[0] is '{' or '[')
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<JsonElement>(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
using Snap.Hutao.Core.Caching;
|
using Snap.Hutao.Core.Caching;
|
||||||
|
using Snap.Hutao.Model.Metadata.Converter;
|
||||||
|
using Snap.Hutao.Web;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Image;
|
namespace Snap.Hutao.Control.Image;
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Threading;
|
|
||||||
|
|
||||||
internal delegate bool SpinWaitPredicate<T>(ref readonly T state);
|
|
||||||
|
|
||||||
internal static class SpinWaitPolyfill
|
|
||||||
{
|
|
||||||
public static unsafe void SpinUntil<T>(ref T state, delegate*<ref readonly T, bool> condition)
|
|
||||||
{
|
|
||||||
SpinWait spinner = default;
|
|
||||||
while (!condition(ref state))
|
|
||||||
{
|
|
||||||
spinner.SpinOnce();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,45 +8,123 @@ namespace Snap.Hutao.Model.Entity;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed partial class SettingEntry
|
internal sealed partial class SettingEntry
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏路径
|
||||||
|
/// </summary>
|
||||||
public const string GamePath = "GamePath";
|
public const string GamePath = "GamePath";
|
||||||
|
|
||||||
public const string GamePathEntries = "GamePathEntries";
|
public const string GamePathEntries = "GamePathEntries";
|
||||||
public const string Culture = "Culture";
|
|
||||||
|
|
||||||
public const string SystemBackdropType = "SystemBackdropType";
|
|
||||||
|
|
||||||
public const string AnnouncementRegion = "AnnouncementRegion";
|
|
||||||
|
|
||||||
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
|
|
||||||
|
|
||||||
public const string GeetestCustomCompositeUrl = "GeetestCustomCompositeUrl";
|
|
||||||
|
|
||||||
public const string DailyNoteRefreshSeconds = "DailyNote.RefreshSeconds";
|
|
||||||
public const string DailyNoteReminderNotify = "DailyNote.ReminderNotify";
|
|
||||||
public const string DailyNoteSilentWhenPlayingGame = "DailyNote.SilentWhenPlayingGame";
|
|
||||||
public const string DailyNoteWebhookUrl = "DailyNote.WebhookUrl";
|
|
||||||
|
|
||||||
public const string IsAdvancedLaunchOptionsEnabled = "IsAdvancedLaunchOptionsEnabled";
|
|
||||||
|
|
||||||
public const string LaunchIsLaunchOptionsEnabled = "Launch.IsLaunchOptionsEnabled";
|
|
||||||
public const string LaunchIsExclusive = "Launch.IsExclusive";
|
|
||||||
public const string LaunchIsFullScreen = "Launch.IsFullScreen";
|
|
||||||
public const string LaunchIsBorderless = "Launch.IsBorderless";
|
|
||||||
public const string LaunchScreenWidth = "Launch.ScreenWidth";
|
|
||||||
public const string LaunchIsScreenWidthEnabled = "Launch.IsScreenWidthEnabled";
|
|
||||||
public const string LaunchScreenHeight = "Launch.ScreenHeight";
|
|
||||||
public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled";
|
|
||||||
public const string LaunchUnlockFps = "Launch.UnlockFps";
|
|
||||||
public const string LaunchTargetFps = "Launch.TargetFps";
|
|
||||||
public const string LaunchMonitor = "Launch.Monitor";
|
|
||||||
public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled";
|
|
||||||
public const string LaunchIsUseCloudThirdPartyMobile = "Launch.IsUseCloudThirdPartyMobile";
|
|
||||||
public const string LaunchIsWindowsHDREnabled = "Launch.IsWindowsHDREnabled";
|
|
||||||
public const string LaunchUseStarwardPlayTimeStatistics = "Launch.UseStarwardPlayTimeStatistics";
|
|
||||||
public const string LaunchSetDiscordActivityWhenPlaying = "Launch.SetDiscordActivityWhenPlaying";
|
|
||||||
|
|
||||||
[Obsolete("不再支持多开")]
|
|
||||||
public const string MultipleInstances = "Launch.MultipleInstances";
|
|
||||||
|
|
||||||
[Obsolete("不再使用 PowerShell")]
|
[Obsolete("不再使用 PowerShell")]
|
||||||
public const string PowerShellPath = "PowerShellPath";
|
public const string PowerShellPath = "PowerShellPath";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 空的历史记录卡池是否可见
|
||||||
|
/// </summary>
|
||||||
|
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 窗口背景类型
|
||||||
|
/// </summary>
|
||||||
|
public const string SystemBackdropType = "SystemBackdropType";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启用高级功能
|
||||||
|
/// </summary>
|
||||||
|
public const string IsAdvancedLaunchOptionsEnabled = "IsAdvancedLaunchOptionsEnabled";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺刷新时间
|
||||||
|
/// </summary>
|
||||||
|
public const string DailyNoteRefreshSeconds = "DailyNote.RefreshSeconds";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺提醒式通知
|
||||||
|
/// </summary>
|
||||||
|
public const string DailyNoteReminderNotify = "DailyNote.ReminderNotify";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺免打扰模式
|
||||||
|
/// </summary>
|
||||||
|
public const string DailyNoteSilentWhenPlayingGame = "DailyNote.SilentWhenPlayingGame";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺 WebhookUrl
|
||||||
|
/// </summary>
|
||||||
|
public const string DailyNoteWebhookUrl = "DailyNote.WebhookUrl";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 总开关
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchIsLaunchOptionsEnabled = "Launch.IsLaunchOptionsEnabled";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 独占全屏
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchIsExclusive = "Launch.IsExclusive";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 全屏
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchIsFullScreen = "Launch.IsFullScreen";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 无边框
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchIsBorderless = "Launch.IsBorderless";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 宽度
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchScreenWidth = "Launch.ScreenWidth";
|
||||||
|
|
||||||
|
public const string LaunchIsScreenWidthEnabled = "Launch.IsScreenWidthEnabled";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 高度
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchScreenHeight = "Launch.ScreenHeight";
|
||||||
|
|
||||||
|
public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 解锁帧率
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchUnlockFps = "Launch.UnlockFps";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 目标帧率
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchTargetFps = "Launch.TargetFps";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 显示器编号
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchMonitor = "Launch.Monitor";
|
||||||
|
|
||||||
|
public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled";
|
||||||
|
|
||||||
|
public const string LaunchIsUseCloudThirdPartyMobile = "Launch.IsUseCloudThirdPartyMobile";
|
||||||
|
|
||||||
|
public const string LaunchUseStarwardPlayTimeStatistics = "Launch.UseStarwardPlayTimeStatistics";
|
||||||
|
|
||||||
|
public const string LaunchSetDiscordActivityWhenPlaying = "Launch.SetDiscordActivityWhenPlaying";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动游戏 多倍启动
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("不再支持多开")]
|
||||||
|
public const string MultipleInstances = "Launch.MultipleInstances";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 语言
|
||||||
|
/// </summary>
|
||||||
|
public const string Culture = "Culture";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义极验接口
|
||||||
|
/// </summary>
|
||||||
|
public const string GeetestCustomCompositeUrl = "GeetestCustomCompositeUrl";
|
||||||
|
|
||||||
|
public const string AnnouncementRegion = "AnnouncementRegion";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2192,12 +2192,6 @@
|
|||||||
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
<data name="ViewPageLaunchGameUnlockFpsOn" xml:space="preserve">
|
||||||
<value>启用</value>
|
<value>启用</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameWindowsHDRDescription" xml:space="preserve">
|
|
||||||
<value>充分利用支持高动态范围的显示器获得更亮、更生动、更精细的画面</value>
|
|
||||||
</data>
|
|
||||||
<data name="ViewPageLaunchGameWindowsHDRHeader" xml:space="preserve">
|
|
||||||
<value>Windows HDR</value>
|
|
||||||
</data>
|
|
||||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||||
<value>请输入你的 HoYoLab Uid</value>
|
<value>请输入你的 HoYoLab Uid</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2753,9 +2747,6 @@
|
|||||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||||
<value>{0} 小时后结束</value>
|
<value>{0} 小时后结束</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebBridgeShareCopyToClipboardFailed" xml:space="preserve">
|
|
||||||
<value>打开剪贴板失败</value>
|
|
||||||
</data>
|
|
||||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||||
<value>已复制到剪贴板</value>
|
<value>已复制到剪贴板</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Discord.GameSDK;
|
||||||
using Snap.Discord.GameSDK.ABI;
|
using Snap.Discord.GameSDK.ABI;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.Unicode;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Discord;
|
namespace Snap.Hutao.Service.Discord;
|
||||||
|
|
||||||
@@ -18,94 +18,75 @@ internal static class DiscordController
|
|||||||
private static readonly CancellationTokenSource StopTokenSource = new();
|
private static readonly CancellationTokenSource StopTokenSource = new();
|
||||||
private static readonly object SyncRoot = new();
|
private static readonly object SyncRoot = new();
|
||||||
|
|
||||||
private static long currentClientId;
|
private static Snap.Discord.GameSDK.Discord? discordManager;
|
||||||
private static unsafe IDiscordCore* discordCorePtr;
|
|
||||||
private static bool isInitialized;
|
private static bool isInitialized;
|
||||||
|
|
||||||
public static async ValueTask<DiscordResult> SetDefaultActivityAsync(DateTimeOffset startTime)
|
public static async ValueTask<Result> SetDefaultActivityAsync(DateTimeOffset startTime)
|
||||||
{
|
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
|
||||||
return SetDefaultActivity(startTime);
|
|
||||||
|
|
||||||
static unsafe DiscordResult SetDefaultActivity(in DateTimeOffset startTime)
|
|
||||||
{
|
{
|
||||||
ResetManagerOrIgnore(HutaoAppId);
|
ResetManagerOrIgnore(HutaoAppId);
|
||||||
|
|
||||||
if (discordCorePtr is null)
|
if (discordManager is null)
|
||||||
{
|
{
|
||||||
return DiscordResult.Ok;
|
return Result.Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDiscordActivityManager* activityManagerPtr = discordCorePtr->get_activity_manager(discordCorePtr);
|
ActivityManager activityManager = discordManager.GetActivityManager();
|
||||||
|
|
||||||
DiscordActivity activity = default;
|
Activity activity = default;
|
||||||
activity.timestamps.start = startTime.ToUnixTimeSeconds();
|
activity.Timestamps.Start = startTime.ToUnixTimeSeconds();
|
||||||
SetString(activity.assets.large_image, 128, "icon"u8);
|
activity.Assets.LargeImage = "icon";
|
||||||
SetString(activity.assets.large_text, 128, SH.AppName);
|
activity.Assets.LargeText = SH.AppName;
|
||||||
|
|
||||||
return new DiscordUpdateActivityAsyncAction(activityManagerPtr).WaitUpdateActivity(activity);
|
return await activityManager.UpdateActivityAsync(activity).ConfigureAwait(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async ValueTask<DiscordResult> SetPlayingYuanShenAsync()
|
public static async ValueTask<Result> SetPlayingYuanShenAsync()
|
||||||
{
|
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
|
||||||
return SetPlayingYuanShen();
|
|
||||||
|
|
||||||
static unsafe DiscordResult SetPlayingYuanShen()
|
|
||||||
{
|
{
|
||||||
ResetManagerOrIgnore(YuanshenId);
|
ResetManagerOrIgnore(YuanshenId);
|
||||||
|
|
||||||
if (discordCorePtr is null)
|
if (discordManager is null)
|
||||||
{
|
{
|
||||||
return DiscordResult.Ok;
|
return Result.Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDiscordActivityManager* activityManagerPtr = discordCorePtr->get_activity_manager(discordCorePtr);
|
ActivityManager activityManager = discordManager.GetActivityManager();
|
||||||
|
|
||||||
DiscordActivity activity = default;
|
Activity activity = default;
|
||||||
SetString(activity.state, 128, SH.FormatServiceDiscordGameLaunchedBy(SH.AppName));
|
activity.State = SH.FormatServiceDiscordGameLaunchedBy(SH.AppName);
|
||||||
SetString(activity.details, 128, SH.ServiceDiscordGameActivityDetails);
|
activity.Details = SH.ServiceDiscordGameActivityDetails;
|
||||||
activity.timestamps.start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
activity.Timestamps.Start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
SetString(activity.assets.large_image, 128, "icon"u8);
|
activity.Assets.LargeImage = "icon";
|
||||||
SetString(activity.assets.large_text, 128, "原神"u8);
|
activity.Assets.LargeText = "原神";
|
||||||
SetString(activity.assets.small_image, 128, "app"u8);
|
activity.Assets.SmallImage = "app";
|
||||||
SetString(activity.assets.small_text, 128, SH.AppName);
|
activity.Assets.SmallText = SH.AppName;
|
||||||
|
|
||||||
return new DiscordUpdateActivityAsyncAction(activityManagerPtr).WaitUpdateActivity(activity);
|
return await activityManager.UpdateActivityAsync(activity).ConfigureAwait(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async ValueTask<DiscordResult> SetPlayingGenshinImpactAsync()
|
public static async ValueTask<Result> SetPlayingGenshinImpactAsync()
|
||||||
{
|
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
|
||||||
return SetPlayingGenshinImpact();
|
|
||||||
|
|
||||||
static unsafe DiscordResult SetPlayingGenshinImpact()
|
|
||||||
{
|
{
|
||||||
ResetManagerOrIgnore(GenshinImpactId);
|
ResetManagerOrIgnore(GenshinImpactId);
|
||||||
|
|
||||||
if (discordCorePtr is null)
|
if (discordManager is null)
|
||||||
{
|
{
|
||||||
return DiscordResult.Ok;
|
return Result.Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDiscordActivityManager* activityManagerPtr = discordCorePtr->get_activity_manager(discordCorePtr);
|
ActivityManager activityManager = discordManager.GetActivityManager();
|
||||||
|
|
||||||
DiscordActivity activity = default;
|
Activity activity = default;
|
||||||
SetString(activity.state, 128, SH.FormatServiceDiscordGameLaunchedBy(SH.AppName));
|
activity.State = SH.FormatServiceDiscordGameLaunchedBy(SH.AppName);
|
||||||
SetString(activity.details, 128, SH.ServiceDiscordGameActivityDetails);
|
activity.Details = SH.ServiceDiscordGameActivityDetails;
|
||||||
activity.timestamps.start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
activity.Timestamps.Start = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
SetString(activity.assets.large_image, 128, "icon"u8);
|
activity.Assets.LargeImage = "icon";
|
||||||
SetString(activity.assets.large_text, 128, "Genshin Impact"u8);
|
activity.Assets.LargeText = "Genshin Impact";
|
||||||
SetString(activity.assets.small_image, 128, "app"u8);
|
activity.Assets.SmallImage = "app";
|
||||||
SetString(activity.assets.small_text, 128, SH.AppName);
|
activity.Assets.SmallText = SH.AppName;
|
||||||
|
|
||||||
return new DiscordUpdateActivityAsyncAction(activityManagerPtr).WaitUpdateActivity(activity);
|
return await activityManager.UpdateActivityAsync(activity).ConfigureAwait(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void Stop()
|
public static void Stop()
|
||||||
{
|
{
|
||||||
if (!isInitialized)
|
if (!isInitialized)
|
||||||
{
|
{
|
||||||
@@ -117,7 +98,7 @@ internal static class DiscordController
|
|||||||
StopTokenSource.Cancel();
|
StopTokenSource.Cancel();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
discordCorePtr = default;
|
discordManager?.Dispose();
|
||||||
}
|
}
|
||||||
catch (SEHException)
|
catch (SEHException)
|
||||||
{
|
{
|
||||||
@@ -127,30 +108,23 @@ internal static class DiscordController
|
|||||||
|
|
||||||
private static unsafe void ResetManagerOrIgnore(long clientId)
|
private static unsafe void ResetManagerOrIgnore(long clientId)
|
||||||
{
|
{
|
||||||
if (currentClientId == clientId)
|
if (discordManager?.ClientId == clientId)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually requires a discord client to be running on Windows platform.
|
// Actually requires a discord client to be running on Windows platform.
|
||||||
// If not, the following creation code will throw.
|
// If not, the following creation code will throw.
|
||||||
if (System.Diagnostics.Process.GetProcessesByName("Discord").Length <= 0)
|
if (System.Diagnostics.Process.GetProcessesByName("Discord").Length == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
DiscordCreateParams @params = default;
|
discordManager?.Dispose();
|
||||||
Methods.DiscordCreateParamsSetDefault(&@params);
|
discordManager = new(clientId, CreateFlags.NoRequireDiscord);
|
||||||
@params.client_id = clientId;
|
discordManager.SetLogHook(Snap.Discord.GameSDK.LogLevel.Debug, SetLogHookHandler.Create(&DebugWriteDiscordMessage));
|
||||||
@params.flags = (uint)DiscordCreateFlags.Default;
|
|
||||||
IDiscordCore* ptr = default;
|
|
||||||
Methods.DiscordCreate(3, &@params, &ptr);
|
|
||||||
|
|
||||||
currentClientId = clientId;
|
|
||||||
discordCorePtr = ptr;
|
|
||||||
discordCorePtr->set_log_hook(discordCorePtr, DiscordLogLevel.Debug, default, &DebugWriteDiscordMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInitialized)
|
if (isInitialized)
|
||||||
@@ -161,10 +135,10 @@ internal static class DiscordController
|
|||||||
DiscordRunCallbacksAsync(StopTokenSource.Token).SafeForget();
|
DiscordRunCallbacksAsync(StopTokenSource.Token).SafeForget();
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
|
|
||||||
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
|
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
|
||||||
static unsafe void DebugWriteDiscordMessage(void* state, DiscordLogLevel logLevel, sbyte* ptr)
|
static unsafe void DebugWriteDiscordMessage(Snap.Discord.GameSDK.LogLevel logLevel, byte* ptr)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> utf8 = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)ptr);
|
ReadOnlySpan<byte> utf8 = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(ptr);
|
||||||
string message = System.Text.Encoding.UTF8.GetString(utf8);
|
string message = System.Text.Encoding.UTF8.GetString(utf8);
|
||||||
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[{logLevel}]:{message}");
|
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[{logLevel}]:{message}");
|
||||||
}
|
}
|
||||||
@@ -172,7 +146,7 @@ internal static class DiscordController
|
|||||||
|
|
||||||
private static async ValueTask DiscordRunCallbacksAsync(CancellationToken cancellationToken)
|
private static async ValueTask DiscordRunCallbacksAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
using (PeriodicTimer timer = new(TimeSpan.FromMilliseconds(500)))
|
using (PeriodicTimer timer = new(TimeSpan.FromMilliseconds(1000)))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -187,10 +161,15 @@ internal static class DiscordController
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DiscordResult result = DiscordCoreRunRunCallbacks();
|
discordManager?.RunCallbacks();
|
||||||
if (result is not DiscordResult.Ok)
|
}
|
||||||
|
catch (ResultException ex)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:{result:D} {result}");
|
// If result is Ok
|
||||||
|
// Maybe the connection is reset.
|
||||||
|
if (ex.Result is not Result.Ok)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK ERROR]:{ex.Result:D} {ex.Result}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SEHException ex)
|
catch (SEHException ex)
|
||||||
@@ -206,70 +185,5 @@ internal static class DiscordController
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe DiscordResult DiscordCoreRunRunCallbacks()
|
|
||||||
{
|
|
||||||
if (discordCorePtr is not null)
|
|
||||||
{
|
|
||||||
return discordCorePtr->run_callbacks(discordCorePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DiscordResult.Ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static unsafe void SetString(sbyte* reference, int length, string source)
|
|
||||||
{
|
|
||||||
Span<sbyte> sbytes = new(reference, length);
|
|
||||||
sbytes.Clear();
|
|
||||||
Utf8.FromUtf16(source.AsSpan(), MemoryMarshal.Cast<sbyte, byte>(sbytes), out _, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static unsafe void SetString(sbyte* reference, int length, in ReadOnlySpan<byte> source)
|
|
||||||
{
|
|
||||||
Span<sbyte> sbytes = new(reference, length);
|
|
||||||
sbytes.Clear();
|
|
||||||
source.CopyTo(MemoryMarshal.Cast<sbyte, byte>(sbytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct DiscordAsyncAction
|
|
||||||
{
|
|
||||||
public DiscordResult Result;
|
|
||||||
public bool IsCompleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe struct DiscordUpdateActivityAsyncAction
|
|
||||||
{
|
|
||||||
private readonly IDiscordActivityManager* activityManagerPtr;
|
|
||||||
private DiscordAsyncAction discordAsyncAction;
|
|
||||||
|
|
||||||
public DiscordUpdateActivityAsyncAction(IDiscordActivityManager* activityManagerPtr)
|
|
||||||
{
|
|
||||||
this.activityManagerPtr = activityManagerPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscordResult WaitUpdateActivity(DiscordActivity activity)
|
|
||||||
{
|
|
||||||
fixed (DiscordAsyncAction* actionPtr = &discordAsyncAction)
|
|
||||||
{
|
|
||||||
activityManagerPtr->update_activity(activityManagerPtr, &activity, actionPtr, &HandleResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpinWaitPolyfill.SpinUntil(ref discordAsyncAction, &CheckActionCompleted);
|
|
||||||
return discordAsyncAction.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
|
|
||||||
private static void HandleResult(void* state, DiscordResult result)
|
|
||||||
{
|
|
||||||
DiscordAsyncAction* action = (DiscordAsyncAction*)state;
|
|
||||||
action->Result = result;
|
|
||||||
action->IsCompleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CheckActionCompleted(ref readonly DiscordAsyncAction state)
|
|
||||||
{
|
|
||||||
return state.IsCompleted;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,8 +20,6 @@ internal static class RegistryInterop
|
|||||||
private const string SdkChineseValueName = "MIHOYOSDK_ADL_PROD_CN_h3123967166";
|
private const string SdkChineseValueName = "MIHOYOSDK_ADL_PROD_CN_h3123967166";
|
||||||
private const string SdkOverseaValueName = "MIHOYOSDK_ADL_PROD_OVERSEA_h1158948810";
|
private const string SdkOverseaValueName = "MIHOYOSDK_ADL_PROD_OVERSEA_h1158948810";
|
||||||
|
|
||||||
private const string WindowsHDROnValueName = "WINDOWS_HDR_ON_h3132281285";
|
|
||||||
|
|
||||||
public static bool Set(GameAccount? account)
|
public static bool Set(GameAccount? account)
|
||||||
{
|
{
|
||||||
if (account is not null)
|
if (account is not null)
|
||||||
@@ -58,12 +56,6 @@ internal static class RegistryInterop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetWindowsHDR(bool isOversea)
|
|
||||||
{
|
|
||||||
string keyName = isOversea ? OverseaKeyName : ChineseKeyName;
|
|
||||||
Registry.SetValue(keyName, WindowsHDROnValueName, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (string KeyName, string ValueName) GetKeyValueName(SchemeType scheme)
|
private static (string KeyName, string ValueName) GetKeyValueName(SchemeType scheme)
|
||||||
{
|
{
|
||||||
return scheme switch
|
return scheme switch
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ internal sealed class LaunchOptions : DbStoreOptions
|
|||||||
private NameValue<int>? monitor;
|
private NameValue<int>? monitor;
|
||||||
private bool? isMonitorEnabled;
|
private bool? isMonitorEnabled;
|
||||||
private bool? isUseCloudThirdPartyMobile;
|
private bool? isUseCloudThirdPartyMobile;
|
||||||
private bool? isWindowsHDREnabled;
|
|
||||||
private AspectRatio? selectedAspectRatio;
|
private AspectRatio? selectedAspectRatio;
|
||||||
private bool? useStarwardPlayTimeStatistics;
|
private bool? useStarwardPlayTimeStatistics;
|
||||||
private bool? setDiscordActivityWhenPlaying;
|
private bool? setDiscordActivityWhenPlaying;
|
||||||
@@ -199,12 +198,6 @@ internal sealed class LaunchOptions : DbStoreOptions
|
|||||||
set => SetOption(ref isUseCloudThirdPartyMobile, SettingEntry.LaunchIsUseCloudThirdPartyMobile, value);
|
set => SetOption(ref isUseCloudThirdPartyMobile, SettingEntry.LaunchIsUseCloudThirdPartyMobile, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsWindowsHDREnabled
|
|
||||||
{
|
|
||||||
get => GetOption(ref isWindowsHDREnabled, SettingEntry.LaunchIsWindowsHDREnabled, false);
|
|
||||||
set => SetOption(ref isWindowsHDREnabled, SettingEntry.LaunchIsWindowsHDREnabled, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<AspectRatio> AspectRatios { get; } =
|
public List<AspectRatio> AspectRatios { get; } =
|
||||||
[
|
[
|
||||||
new(2560, 1440),
|
new(2560, 1440),
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Factory.Progress;
|
using Snap.Hutao.Factory.Progress;
|
||||||
using Snap.Hutao.Service.Discord;
|
using Snap.Hutao.Service.Discord;
|
||||||
using Snap.Hutao.Service.Game.Account;
|
|
||||||
using Snap.Hutao.Service.Game.Scheme;
|
using Snap.Hutao.Service.Game.Scheme;
|
||||||
using Snap.Hutao.Service.Game.Unlocker;
|
using Snap.Hutao.Service.Game.Unlocker;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -62,11 +61,6 @@ internal sealed partial class GameProcessService : IGameProcessService
|
|||||||
|
|
||||||
bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileName);
|
bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileName);
|
||||||
|
|
||||||
if (launchOptions.IsWindowsHDREnabled)
|
|
||||||
{
|
|
||||||
RegistryInterop.SetWindowsHDR(isOversea);
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.Report(new(LaunchPhase.ProcessInitializing, SH.ServiceGameLaunchPhaseProcessInitializing));
|
progress.Report(new(LaunchPhase.ProcessInitializing, SH.ServiceGameLaunchPhaseProcessInitializing));
|
||||||
using (System.Diagnostics.Process game = InitializeGameProcess(gamePath))
|
using (System.Diagnostics.Process game = InitializeGameProcess(gamePath))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -311,7 +311,7 @@
|
|||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231115000" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231115000" />
|
||||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||||
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
<PackageReference Include="Snap.Discord.GameSDK" Version="1.5.0" />
|
||||||
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.9.0">
|
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.9.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Snap.Hutao.Web.Response;
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
using Windows.Foundation;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Dialog;
|
namespace Snap.Hutao.View.Dialog;
|
||||||
|
|
||||||
|
|||||||
@@ -199,12 +199,6 @@
|
|||||||
ItemsSource="{Binding GameAccountsView}"
|
ItemsSource="{Binding GameAccountsView}"
|
||||||
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}"/>
|
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}"/>
|
||||||
</Border>
|
</Border>
|
||||||
<cwc:SettingsCard
|
|
||||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameWindowsHDRDescription}"
|
|
||||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameWindowsHDRHeader}"
|
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
|
||||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsWindowsHDREnabled, Mode=TwoWay}"/>
|
|
||||||
</cwc:SettingsCard>
|
|
||||||
|
|
||||||
<!-- 进程 -->
|
<!-- 进程 -->
|
||||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameProcessHeader}"/>
|
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameProcessHeader}"/>
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
using Microsoft.Web.WebView2.Core;
|
using Microsoft.Web.WebView2.Core;
|
||||||
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||||
using Snap.Hutao.Core.IO.DataTransfer;
|
|
||||||
using Snap.Hutao.Service.Metadata;
|
using Snap.Hutao.Service.Metadata;
|
||||||
using Snap.Hutao.Service.Notification;
|
|
||||||
using Snap.Hutao.Service.User;
|
using Snap.Hutao.Service.User;
|
||||||
using Snap.Hutao.ViewModel.User;
|
using Snap.Hutao.ViewModel.User;
|
||||||
using Snap.Hutao.Web.Bridge.Model;
|
using Snap.Hutao.Web.Bridge.Model;
|
||||||
@@ -14,12 +12,8 @@ using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
|||||||
using Snap.Hutao.Web.Hoyolab.DataSigning;
|
using Snap.Hutao.Web.Hoyolab.DataSigning;
|
||||||
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
|
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.IO;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using Windows.Graphics.Imaging;
|
|
||||||
using Windows.Storage.Streams;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Bridge;
|
namespace Snap.Hutao.Web.Bridge;
|
||||||
|
|
||||||
@@ -30,7 +24,7 @@ namespace Snap.Hutao.Web.Bridge;
|
|||||||
[SuppressMessage("", "CA1001")]
|
[SuppressMessage("", "CA1001")]
|
||||||
internal class MiHoYoJSBridge
|
internal class MiHoYoJSBridge
|
||||||
{
|
{
|
||||||
private const string InitializeJsInterfaceScript = """
|
private const string InitializeJsInterfaceScript2 = """
|
||||||
window.MiHoYoJSInterface = {
|
window.MiHoYoJSInterface = {
|
||||||
postMessage: function(arg) { chrome.webview.postMessage(arg) },
|
postMessage: function(arg) { chrome.webview.postMessage(arg) },
|
||||||
closePage: function() { this.postMessage('{"method":"closePage"}') },
|
closePage: function() { this.postMessage('{"method":"closePage"}') },
|
||||||
@@ -45,6 +39,7 @@ internal class MiHoYoJSBridge
|
|||||||
|
|
||||||
private const string ConvertMouseToTouchScript = """
|
private const string ConvertMouseToTouchScript = """
|
||||||
function mouseListener (e, event) {
|
function mouseListener (e, event) {
|
||||||
|
console.log(event);
|
||||||
let touch = new Touch({
|
let touch = new Touch({
|
||||||
identifier: Date.now(),
|
identifier: Date.now(),
|
||||||
target: e.target,
|
target: e.target,
|
||||||
@@ -62,6 +57,7 @@ internal class MiHoYoJSBridge
|
|||||||
targetTouches: [touch],
|
targetTouches: [touch],
|
||||||
changedTouches: [touch],
|
changedTouches: [touch],
|
||||||
});
|
});
|
||||||
|
console.log(touchEvent);
|
||||||
e.target.dispatchEvent(touchEvent);
|
e.target.dispatchEvent(touchEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,9 +86,6 @@ internal class MiHoYoJSBridge
|
|||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
private readonly ILogger<MiHoYoJSBridge> logger;
|
private readonly ILogger<MiHoYoJSBridge> logger;
|
||||||
private readonly IInfoBarService infoBarService;
|
|
||||||
private readonly IClipboardProvider clipboardProvider;
|
|
||||||
private readonly HttpClient httpClient;
|
|
||||||
|
|
||||||
private readonly TypedEventHandler<CoreWebView2, CoreWebView2WebMessageReceivedEventArgs> webMessageReceivedEventHandler;
|
private readonly TypedEventHandler<CoreWebView2, CoreWebView2WebMessageReceivedEventArgs> webMessageReceivedEventHandler;
|
||||||
private readonly TypedEventHandler<CoreWebView2, CoreWebView2DOMContentLoadedEventArgs> domContentLoadedEventHandler;
|
private readonly TypedEventHandler<CoreWebView2, CoreWebView2DOMContentLoadedEventArgs> domContentLoadedEventHandler;
|
||||||
@@ -109,9 +102,6 @@ internal class MiHoYoJSBridge
|
|||||||
|
|
||||||
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||||
logger = serviceProvider.GetRequiredService<ILogger<MiHoYoJSBridge>>();
|
logger = serviceProvider.GetRequiredService<ILogger<MiHoYoJSBridge>>();
|
||||||
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
|
|
||||||
clipboardProvider = serviceProvider.GetRequiredService<IClipboardProvider>();
|
|
||||||
httpClient = serviceProvider.GetRequiredService<HttpClient>();
|
|
||||||
|
|
||||||
webMessageReceivedEventHandler = OnWebMessageReceived;
|
webMessageReceivedEventHandler = OnWebMessageReceived;
|
||||||
domContentLoadedEventHandler = OnDOMContentLoaded;
|
domContentLoadedEventHandler = OnDOMContentLoaded;
|
||||||
@@ -374,51 +364,8 @@ internal class MiHoYoJSBridge
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async ValueTask<IJsBridgeResult?> Share(JsParam<SharePayload> param)
|
protected virtual IJsBridgeResult? Share(JsParam<SharePayload> param)
|
||||||
{
|
{
|
||||||
if (param.Payload.Type is "image")
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
await ShareImageBase64CoreAsync(base64).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new JsResult<Dictionary<string, string>>()
|
return new JsResult<Dictionary<string, string>>()
|
||||||
{
|
{
|
||||||
Data = new()
|
Data = new()
|
||||||
@@ -428,30 +375,6 @@ internal class MiHoYoJSBridge
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual ValueTask<IJsBridgeResult?> ShowAlertDialogAsync(JsParam param)
|
protected virtual ValueTask<IJsBridgeResult?> ShowAlertDialogAsync(JsParam param)
|
||||||
{
|
{
|
||||||
return ValueTask.FromException<IJsBridgeResult?>(new NotSupportedException());
|
return ValueTask.FromException<IJsBridgeResult?>(new NotSupportedException());
|
||||||
@@ -582,7 +505,7 @@ internal class MiHoYoJSBridge
|
|||||||
"hideLoading" => null,
|
"hideLoading" => null,
|
||||||
"login" => null,
|
"login" => null,
|
||||||
"pushPage" => await PushPageAsync(param).ConfigureAwait(false),
|
"pushPage" => await PushPageAsync(param).ConfigureAwait(false),
|
||||||
"share" => await Share(param).ConfigureAwait(false),
|
"share" => Share(param),
|
||||||
"showLoading" => null,
|
"showLoading" => null,
|
||||||
_ => LogUnhandledMessage("Unhandled Message Type: {Method}", param.Method),
|
_ => LogUnhandledMessage("Unhandled Message Type: {Method}", param.Method),
|
||||||
};
|
};
|
||||||
@@ -607,7 +530,7 @@ internal class MiHoYoJSBridge
|
|||||||
ReadOnlySpan<char> uriHostSpan = uriHost.AsSpan();
|
ReadOnlySpan<char> uriHostSpan = uriHost.AsSpan();
|
||||||
if (uriHostSpan.EndsWith("mihoyo.com") || uriHostSpan.EndsWith("hoyolab.com"))
|
if (uriHostSpan.EndsWith("mihoyo.com") || uriHostSpan.EndsWith("hoyolab.com"))
|
||||||
{
|
{
|
||||||
coreWebView2.ExecuteScriptAsync(InitializeJsInterfaceScript).AsTask().SafeForget(logger);
|
coreWebView2.ExecuteScriptAsync(InitializeJsInterfaceScript2).AsTask().SafeForget(logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,23 +3,8 @@
|
|||||||
|
|
||||||
namespace Snap.Hutao.Web.Bridge.Model;
|
namespace Snap.Hutao.Web.Bridge.Model;
|
||||||
|
|
||||||
[SuppressMessage("", "SA1124")]
|
|
||||||
internal sealed class ShareContent
|
internal sealed class ShareContent
|
||||||
{
|
{
|
||||||
#region Screenshot
|
|
||||||
|
|
||||||
[JsonPropertyName("preview")]
|
[JsonPropertyName("preview")]
|
||||||
public bool Preview { get; set; }
|
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
|
|
||||||
}
|
}
|
||||||
@@ -3,4 +3,6 @@
|
|||||||
|
|
||||||
namespace Snap.Hutao.Web.Request.Builder.Abstraction;
|
namespace Snap.Hutao.Web.Request.Builder.Abstraction;
|
||||||
|
|
||||||
internal interface IBuilder;
|
internal interface IBuilder
|
||||||
|
{
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user