2019-09-05 00:26:26 +08:00
|
|
|
#include "pch.h"
|
|
|
|
#include "trace.h"
|
2019-12-17 16:21:46 +08:00
|
|
|
#include "lib/ZoneSet.h"
|
|
|
|
#include "lib/Settings.h"
|
2020-07-22 16:39:13 +08:00
|
|
|
#include "lib/FancyZonesData.h"
|
|
|
|
#include "lib/FancyZonesDataTypes.h"
|
2019-09-05 00:26:26 +08:00
|
|
|
|
2020-08-11 19:51:06 +08:00
|
|
|
// Telemetry strings should not be localized.
|
|
|
|
#define LoggingProviderKey "Microsoft.PowerToys"
|
|
|
|
|
|
|
|
#define EventEnableFancyZonesKey "FancyZones_EnableFancyZones"
|
|
|
|
#define EventKeyDownKey "FancyZones_OnKeyDown"
|
|
|
|
#define EventZoneSettingsChangedKey "FancyZones_ZoneSettingsChanged"
|
|
|
|
#define EventEditorLaunchKey "FancyZones_EditorLaunch"
|
|
|
|
#define EventSettingsChangedKey "FancyZones_SettingsChanged"
|
|
|
|
#define EventDesktopChangedKey "FancyZones_VirtualDesktopChanged"
|
|
|
|
#define EventZoneWindowKeyUpKey "FancyZones_ZoneWindowKeyUp"
|
|
|
|
#define EventMoveSizeEndKey "FancyZones_MoveSizeEnd"
|
|
|
|
#define EventCycleActiveZoneSetKey "FancyZones_CycleActiveZoneSet"
|
|
|
|
|
|
|
|
#define EventEnabledKey "Enabled"
|
|
|
|
#define PressedKeyCodeKey "Hotkey"
|
|
|
|
#define PressedWindowKey "WindowsKey"
|
|
|
|
#define PressedControlKey "ControlKey"
|
|
|
|
#define MoveSizeActionKey "InMoveSize"
|
|
|
|
#define AppsInHistoryCountKey "AppsInHistoryCount"
|
|
|
|
#define CustomZoneSetCountKey "CustomZoneSetCount"
|
|
|
|
#define NumberOfZonesForEachCustomZoneSetKey "NumberOfZonesForEachCustomZoneSet"
|
|
|
|
#define ActiveZoneSetsCountKey "ActiveZoneSetsCount"
|
|
|
|
#define ActiveZoneSetsListKey "ActiveZoneSetsList"
|
|
|
|
#define EditorLaunchValueKey "Value"
|
|
|
|
#define ShiftDragKey "ShiftDrag"
|
|
|
|
#define MouseSwitchKey "MouseSwitch"
|
|
|
|
#define MoveWindowsOnDisplayChangeKey "MoveWindowsOnDisplayChange"
|
|
|
|
#define FlashZonesOnZoneSetChangeKey "FlashZonesOnZoneSetChange"
|
|
|
|
#define MoveWindowsOnZoneSetChangeKey "MoveWindowsOnZoneSetChange"
|
|
|
|
#define OverrideSnapHotKeysKey "OverrideSnapHotKeys"
|
|
|
|
#define MoveWindowAcrossMonitorsKey "MoveWindowAcrossMonitors"
|
2020-08-21 18:53:03 +08:00
|
|
|
#define MoveWindowsBasedOnPositionKey "MoveWindowsBasedOnPosition"
|
2020-08-11 19:51:06 +08:00
|
|
|
#define MoveWindowsToLastZoneOnAppOpeningKey "MoveWindowsToLastZoneOnAppOpening"
|
|
|
|
#define OpenWindowOnActiveMonitorKey "OpenWindowOnActiveMonitor"
|
|
|
|
#define RestoreSizeKey "RestoreSize"
|
|
|
|
#define UseCursorPosOnEditorStartupKey "UseCursorPosOnEditorStartup"
|
|
|
|
#define ShowZonesOnAllMonitorsKey "ShowZonesOnAllMonitors"
|
|
|
|
#define SpanZonesAcrossMonitorsKey "SpanZonesAcrossMonitors"
|
|
|
|
#define MakeDraggedWindowTransparentKey "MakeDraggedWindowTransparent"
|
|
|
|
#define ZoneColorKey "ZoneColor"
|
|
|
|
#define ZoneBorderColorKey "ZoneBorderColor"
|
|
|
|
#define ZoneHighlightColorKey "ZoneHighlightColor"
|
|
|
|
#define ZoneHighlightOpacityKey "ZoneHighlightOpacity"
|
|
|
|
#define HotkeyKey "Hotkey"
|
|
|
|
#define ExcludedAppsCountKey "ExcludedAppsCount"
|
|
|
|
#define KeyboardValueKey "KeyboardValue"
|
|
|
|
#define ActiveSetKey "ActiveSet"
|
|
|
|
#define NumberOfZonesKey "NumberOfZones"
|
|
|
|
#define NumberOfWindowsKey "NumberOfWindows"
|
|
|
|
#define InputModeKey "InputMode"
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
TRACELOGGING_DEFINE_PROVIDER(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
LoggingProviderKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
|
|
|
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
|
|
|
TraceLoggingOptionProjectTelemetry());
|
|
|
|
|
|
|
|
struct ZoneSetInfo
|
|
|
|
{
|
|
|
|
size_t NumberOfZones = 0;
|
|
|
|
size_t NumberOfWindows = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
ZoneSetInfo GetZoneSetInfo(_In_opt_ winrt::com_ptr<IZoneSet> set) noexcept
|
|
|
|
{
|
|
|
|
ZoneSetInfo info;
|
|
|
|
if (set)
|
|
|
|
{
|
|
|
|
auto zones = set->GetZones();
|
|
|
|
info.NumberOfZones = zones.size();
|
2020-05-26 22:01:12 +08:00
|
|
|
info.NumberOfWindows = 0;
|
|
|
|
for (int i = 0; i < static_cast<int>(zones.size()); i++)
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
2020-05-26 22:01:12 +08:00
|
|
|
if (!set->IsZoneEmpty(i))
|
|
|
|
{
|
|
|
|
info.NumberOfWindows++;
|
|
|
|
}
|
|
|
|
}
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Trace::RegisterProvider() noexcept
|
|
|
|
{
|
|
|
|
TraceLoggingRegister(g_hProvider);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Trace::UnregisterProvider() noexcept
|
|
|
|
{
|
|
|
|
TraceLoggingUnregister(g_hProvider);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Trace::FancyZones::EnableFancyZones(bool enabled) noexcept
|
|
|
|
{
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventEnableFancyZonesKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingBoolean(enabled, EventEnabledKey));
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
2019-09-11 18:38:20 +08:00
|
|
|
void Trace::FancyZones::OnKeyDown(DWORD vkCode, bool win, bool control, bool inMoveSize) noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventKeyDownKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingValue(vkCode, PressedKeyCodeKey),
|
|
|
|
TraceLoggingBoolean(win, PressedWindowKey),
|
|
|
|
TraceLoggingBoolean(control, PressedControlKey),
|
|
|
|
TraceLoggingBoolean(inMoveSize, MoveSizeActionKey));
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
2020-02-18 00:40:02 +08:00
|
|
|
void Trace::FancyZones::DataChanged() noexcept
|
|
|
|
{
|
2020-07-22 16:39:13 +08:00
|
|
|
const FancyZonesData& data = FancyZonesDataInstance();
|
2020-02-18 00:40:02 +08:00
|
|
|
int appsHistorySize = static_cast<int>(data.GetAppZoneHistoryMap().size());
|
|
|
|
const auto& customZones = data.GetCustomZoneSetsMap();
|
|
|
|
const auto& devices = data.GetDeviceInfoMap();
|
|
|
|
|
|
|
|
std::unique_ptr<INT32[]> customZonesArray(new (std::nothrow) INT32[customZones.size()]);
|
|
|
|
if (!customZonesArray)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-22 16:39:13 +08:00
|
|
|
auto getCustomZoneCount = [&data](const std::variant<FancyZonesDataTypes::CanvasLayoutInfo, FancyZonesDataTypes::GridLayoutInfo>& layoutInfo) -> int {
|
|
|
|
if (std::holds_alternative<FancyZonesDataTypes::GridLayoutInfo>(layoutInfo))
|
2020-02-18 00:40:02 +08:00
|
|
|
{
|
2020-07-22 16:39:13 +08:00
|
|
|
const auto& info = std::get<FancyZonesDataTypes::GridLayoutInfo>(layoutInfo);
|
2020-02-18 00:40:02 +08:00
|
|
|
return (info.rows() * info.columns());
|
|
|
|
}
|
2020-07-22 16:39:13 +08:00
|
|
|
else if (std::holds_alternative<FancyZonesDataTypes::CanvasLayoutInfo>(layoutInfo))
|
2020-02-18 00:40:02 +08:00
|
|
|
{
|
2020-07-22 16:39:13 +08:00
|
|
|
const auto& info = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(layoutInfo);
|
2020-02-18 00:40:02 +08:00
|
|
|
return static_cast<int>(info.zones.size());
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
//NumberOfZonesForEachCustomZoneSet
|
|
|
|
int i = 0;
|
|
|
|
for (const auto& [id, customZoneSetData] : customZones)
|
|
|
|
{
|
|
|
|
customZonesArray.get()[i] = getCustomZoneCount(customZoneSetData.info);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//ActiveZoneSetsList
|
|
|
|
std::wstring activeZoneSetInfo;
|
|
|
|
for (const auto& [id, device] : devices)
|
|
|
|
{
|
2020-07-22 16:39:13 +08:00
|
|
|
const FancyZonesDataTypes::ZoneSetLayoutType type = device.activeZoneSet.type;
|
2020-02-18 00:40:02 +08:00
|
|
|
if (!activeZoneSetInfo.empty())
|
|
|
|
{
|
|
|
|
activeZoneSetInfo += L"; ";
|
|
|
|
}
|
2020-07-22 16:39:13 +08:00
|
|
|
activeZoneSetInfo += L"type: " + FancyZonesDataTypes::TypeToString(type);
|
2020-02-18 00:40:02 +08:00
|
|
|
|
|
|
|
int zoneCount = -1;
|
2020-07-22 16:39:13 +08:00
|
|
|
if (type == FancyZonesDataTypes::ZoneSetLayoutType::Custom)
|
2020-02-18 00:40:02 +08:00
|
|
|
{
|
|
|
|
const auto& activeCustomZone = customZones.find(device.activeZoneSet.uuid);
|
|
|
|
if (activeCustomZone != customZones.end())
|
|
|
|
{
|
|
|
|
zoneCount = getCustomZoneCount(activeCustomZone->second.info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
zoneCount = device.zoneCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zoneCount != -1)
|
|
|
|
{
|
|
|
|
activeZoneSetInfo += L", zone count: " + std::to_wstring(zoneCount);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
activeZoneSetInfo += L", custom zone data was deleted";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventZoneSettingsChangedKey,
|
2020-02-18 00:40:02 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingInt32(appsHistorySize, AppsInHistoryCountKey),
|
|
|
|
TraceLoggingInt32(static_cast<int>(customZones.size()), CustomZoneSetCountKey),
|
|
|
|
TraceLoggingInt32Array(customZonesArray.get(), static_cast<int>(customZones.size()), NumberOfZonesForEachCustomZoneSetKey),
|
|
|
|
TraceLoggingInt32(static_cast<int>(devices.size()), ActiveZoneSetsCountKey),
|
|
|
|
TraceLoggingWideString(activeZoneSetInfo.c_str(), ActiveZoneSetsListKey));
|
2020-02-18 00:40:02 +08:00
|
|
|
}
|
|
|
|
|
2020-03-26 18:54:12 +08:00
|
|
|
void Trace::FancyZones::EditorLaunched(int value) noexcept
|
|
|
|
{
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventEditorLaunchKey,
|
2020-03-26 18:54:12 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingInt32(value, EditorLaunchValueKey));
|
2020-03-26 18:54:12 +08:00
|
|
|
}
|
|
|
|
|
2020-09-10 05:27:40 +08:00
|
|
|
// Log if an error occurs in FZ
|
|
|
|
void Trace::FancyZones::Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept
|
|
|
|
{
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
|
|
|
"FancyZones_Error",
|
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
|
|
|
TraceLoggingValue(methodName.c_str(), "MethodName"),
|
|
|
|
TraceLoggingValue(errorCode, "ErrorCode"),
|
|
|
|
TraceLoggingValue(errorMessage.c_str(), "ErrorMessage"));
|
|
|
|
}
|
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
void Trace::SettingsChanged(const Settings& settings) noexcept
|
|
|
|
{
|
2020-02-12 18:08:11 +08:00
|
|
|
const auto& editorHotkey = settings.editorHotkey;
|
2020-08-21 18:53:03 +08:00
|
|
|
std::wstring hotkeyStr = L"alt:" + std::to_wstring(editorHotkey.alt_pressed())
|
|
|
|
+ L", ctrl:" + std::to_wstring(editorHotkey.ctrl_pressed())
|
|
|
|
+ L", shift:" + std::to_wstring(editorHotkey.shift_pressed())
|
|
|
|
+ L", win:" + std::to_wstring(editorHotkey.win_pressed())
|
|
|
|
+ L", code:" + std::to_wstring(editorHotkey.get_code())
|
2020-02-12 18:08:11 +08:00
|
|
|
+ L", keyFromCode:" + editorHotkey.get_key();
|
2020-08-21 18:53:03 +08:00
|
|
|
|
2019-09-05 00:26:26 +08:00
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventSettingsChangedKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingBoolean(settings.shiftDrag, ShiftDragKey),
|
|
|
|
TraceLoggingBoolean(settings.mouseSwitch, MouseSwitchKey),
|
|
|
|
TraceLoggingBoolean(settings.displayChange_moveWindows, MoveWindowsOnDisplayChangeKey),
|
|
|
|
TraceLoggingBoolean(settings.zoneSetChange_flashZones, FlashZonesOnZoneSetChangeKey),
|
|
|
|
TraceLoggingBoolean(settings.zoneSetChange_moveWindows, MoveWindowsOnZoneSetChangeKey),
|
|
|
|
TraceLoggingBoolean(settings.overrideSnapHotkeys, OverrideSnapHotKeysKey),
|
|
|
|
TraceLoggingBoolean(settings.moveWindowAcrossMonitors, MoveWindowAcrossMonitorsKey),
|
2020-08-21 18:53:03 +08:00
|
|
|
TraceLoggingBoolean(settings.moveWindowsBasedOnPosition, MoveWindowsBasedOnPositionKey),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingBoolean(settings.appLastZone_moveWindows, MoveWindowsToLastZoneOnAppOpeningKey),
|
|
|
|
TraceLoggingBoolean(settings.openWindowOnActiveMonitor, OpenWindowOnActiveMonitorKey),
|
|
|
|
TraceLoggingBoolean(settings.restoreSize, RestoreSizeKey),
|
|
|
|
TraceLoggingBoolean(settings.use_cursorpos_editor_startupscreen, UseCursorPosOnEditorStartupKey),
|
|
|
|
TraceLoggingBoolean(settings.showZonesOnAllMonitors, ShowZonesOnAllMonitorsKey),
|
|
|
|
TraceLoggingBoolean(settings.spanZonesAcrossMonitors, SpanZonesAcrossMonitorsKey),
|
|
|
|
TraceLoggingBoolean(settings.makeDraggedWindowTransparent, MakeDraggedWindowTransparentKey),
|
|
|
|
TraceLoggingWideString(settings.zoneColor.c_str(), ZoneColorKey),
|
|
|
|
TraceLoggingWideString(settings.zoneBorderColor.c_str(), ZoneBorderColorKey),
|
|
|
|
TraceLoggingWideString(settings.zoneHighlightColor.c_str(), ZoneHighlightColorKey),
|
|
|
|
TraceLoggingInt32(settings.zoneHighlightOpacity, ZoneHighlightOpacityKey),
|
|
|
|
TraceLoggingWideString(hotkeyStr.c_str(), HotkeyKey),
|
|
|
|
TraceLoggingInt32(static_cast<int>(settings.excludedAppsArray.size()), ExcludedAppsCountKey));
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Trace::VirtualDesktopChanged() noexcept
|
|
|
|
{
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventDesktopChangedKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
}
|
|
|
|
|
2019-11-19 07:29:42 +08:00
|
|
|
void Trace::ZoneWindow::KeyUp(WPARAM wParam) noexcept
|
2019-09-05 00:26:26 +08:00
|
|
|
{
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventZoneWindowKeyUpKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingValue(wParam, KeyboardValueKey));
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Trace::ZoneWindow::MoveSizeEnd(_In_opt_ winrt::com_ptr<IZoneSet> activeSet) noexcept
|
|
|
|
{
|
|
|
|
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventMoveSizeEndKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), ActiveSetKey),
|
|
|
|
TraceLoggingValue(zoneInfo.NumberOfZones, NumberOfZonesKey),
|
|
|
|
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Trace::ZoneWindow::CycleActiveZoneSet(_In_opt_ winrt::com_ptr<IZoneSet> activeSet, InputMode mode) noexcept
|
|
|
|
{
|
|
|
|
auto const zoneInfo = GetZoneSetInfo(activeSet);
|
|
|
|
TraceLoggingWrite(
|
|
|
|
g_hProvider,
|
2020-08-11 19:51:06 +08:00
|
|
|
EventCycleActiveZoneSetKey,
|
2019-09-05 00:26:26 +08:00
|
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
2020-08-11 19:51:06 +08:00
|
|
|
TraceLoggingValue(reinterpret_cast<void*>(activeSet.get()), ActiveSetKey),
|
|
|
|
TraceLoggingValue(zoneInfo.NumberOfZones, NumberOfZonesKey),
|
|
|
|
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey),
|
|
|
|
TraceLoggingValue(static_cast<int>(mode), InputModeKey));
|
2019-09-05 00:26:26 +08:00
|
|
|
}
|