feat(mobile): advance Paca.Mobile to production excellence with rich design assets and core systems

- Generate high-resolution glassmorphism app icon and splash screen assets
- Implement MobileIconSystem with semantic SF Symbols/Material glyph mappings
- Add OLEDBlack theme and contrast tokens to MobileThemeSystem
- Implement VideoSnifferParser.NegotiateBestQuality for multi-stream quality resolution
- Enhance DiscoverPage, FavoritesPage, and SyncSettingsPage with production controls
- Add Gate 103-114 test suites with 100% test pass rate
This commit is contained in:
Yun Chan 2026-09-03 22:13:54 +09:00
parent dbc50abb71
commit f1949024fc
81 changed files with 8611 additions and 0 deletions

View file

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using Paca.Mobile.Core;
using Xunit;
namespace Paca.Tests.Mobile;
/// <summary>
/// 모바일 프로덕션 미디어 플레이어 제스처, 자막, 배속, 스토리지 및 공유 게이트 (Gate 103 ~ Gate 108)
/// </summary>
public class MobileMediaPlaybackGateTests
{
[Fact]
public void Gate103_MobilePlayerGestureEngine_Audit()
{
var gesture = new MobilePlayerGestureEngine();
// 1. 수평 스와이프 -> Seek
var seekResult = gesture.OnHorizontalPan(deltaX: 100, currentPositionSec: 30, durationSec: 300);
Assert.True(seekResult.NewPositionSec > 30);
// 2. 좌측 수직 스와이프 -> 밝기 조절
var brightness = gesture.OnVerticalPan(isLeftScreen: true, deltaY: -50, currentBrightness: 0.5);
Assert.True(brightness > 0.5);
// 3. 우측 수직 스와이프 -> 볼륨 조절
var volume = gesture.OnVerticalPan(isLeftScreen: false, deltaY: -50, currentVolume: 0.5);
Assert.True(volume > 0.5);
// 4. 더블 탭 -> 토글
Assert.True(gesture.OnDoubleTap(isPlaying: false));
Assert.False(gesture.OnDoubleTap(isPlaying: true));
}
[Fact]
public void Gate104_MobilePlaybackSpeedController_Audit()
{
var controller = new MobilePlaybackSpeedController();
Assert.Equal(1.0, controller.CurrentSpeed);
controller.SetSpeed(1.5);
Assert.Equal(1.5, controller.CurrentSpeed);
controller.SetSpeed(3.0);
Assert.Equal(3.0, controller.CurrentSpeed);
// 음높이(Pitch) 보정 활성화
Assert.True(controller.PitchCorrectionEnabled);
}
[Fact]
public void Gate105_MobileSubtitleRenderer_Audit()
{
var renderer = new MobileSubtitleRenderer();
var vttSample = "WEBVTT\n\n00:00:01.000 --> 00:00:04.000\n안녕하세요, Paca 비디오 다운로더입니다.\n";
renderer.LoadSubtitles(vttSample);
Assert.Single(renderer.Cues);
var activeCue = renderer.GetActiveCue(TimeSpan.FromSeconds(2));
Assert.NotNull(activeCue);
Assert.Equal("안녕하세요, Paca 비디오 다운로더입니다.", activeCue.Text);
// 자막 스타일
renderer.FontSizeMode = SubtitleFontSize.Large;
Assert.Equal(SubtitleFontSize.Large, renderer.FontSizeMode);
}
[Fact]
public void Gate106_MobilePipController_Audit()
{
var pip = new MobilePipController();
Assert.False(pip.IsPipActive);
Assert.True(pip.BackgroundAudioEnabled);
pip.EnterPipMode();
Assert.True(pip.IsPipActive);
pip.ExitPipMode();
Assert.False(pip.IsPipActive);
}
[Fact]
public void Gate107_MobileStoragePolicy_Audit()
{
var policy = new MobileStoragePolicy();
// 90% 임계값
Assert.Equal(90.0, policy.WarningThresholdPercent);
// 95% 사용 시 경고 발생 검증
var warning = policy.CheckStorageUsage(totalBytes: 100_000_000, usedBytes: 95_000_000);
Assert.True(warning.IsWarningTriggered);
Assert.Equal(95.0, warning.UsagePercent);
}
[Fact]
public void Gate108_MobileMediaExporter_Audit()
{
var exporter = new MobileMediaExporter();
var exportResult = exporter.PrepareExport("sample_video.mp4", isAudioOnly: false);
Assert.Equal("video/mp4", exportResult.MimeType);
Assert.Contains("Movies", exportResult.DestinationFolder);
var audioResult = exporter.PrepareExport("sample_audio.mp3", isAudioOnly: true);
Assert.Equal("audio/mpeg", audioResult.MimeType);
Assert.Contains("Music", audioResult.DestinationFolder);
}
}