/* * Copyright (c) 2022 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ using Aurora; using Aurora.Api; using Aurora.Api.Control; using Aurora.View; using AuroraUIComponent.Util.WindowAPI; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; namespace AuroraUIComponent.Sequence { public class ScreenShotSequenceInit { /// /// 마지막으로 스크린샷 저장했던 폴더 열기 /// public ICommand CMD_OpenFolder { get; } /// /// - 기본적으로 Aurora ViewManager의 메인/서브 메뉴 리스트를 이동하면서 캡처 /// - Param으로 Dictionary<(string, string), List> 타입의 메뉴 리스트가 들어오면 /// 1번째 (string, string) : Aurora main/sub 메뉴 /// 2번째 List : 위의 main/sub 메뉴로 이동한 상태에서 ScreenShotSequence.SYS_REQ_MENU_CHANGE_EXT 이벤트의 param으로 해당 string을 담아 Publish하면 해당 뷰에서 내부 메뉴를 체인지한다는 약속. /// Sequence 동작 시 메인/서브/추가메뉴 조합으로 모두 traverse하면서 스크린샷 진행 /// public ICommand CMD_ScreenShotAll { get; } /// /// 현재 열려 있는 뷰 상태로 Single 스크린샷 /// public ICommand CMD_ScreenShotSingle { get; } private IAuroraEnvironment _AuroraEnv; private string _SeqId; public PermissionLevel AllowedPermissionLevel = PermissionLevel.NONE; public ScreenShotSequenceInit(IAuroraEnvironment aurora, string seqId) { _AuroraEnv = aurora; _SeqId = seqId; CMD_OpenFolder = new DelegateCommand(DoOpenLastFolder); CMD_ScreenShotAll = new DelegateCommand(DoStartScreenShotAll); CMD_ScreenShotSingle = new DelegateCommand(DoScreenShotSingle); } private void DoOpenLastFolder(object obj) { var applyPermission = ApplicationExt.UsePermission; if (applyPermission && ApplicationExt.PermissionLevel > (ushort)AllowedPermissionLevel) return; StartScreenShot(ScreenShotSequence.eReqType.REQ_OPEN_FOLDER); } private void DoScreenShotSingle(object obj) { var applyPermission = ApplicationExt.UsePermission; if (applyPermission && ApplicationExt.PermissionLevel > (ushort)AllowedPermissionLevel) return; //if (!MessageUtil.ShowYesNoMessageDialog("ScreenShot", $"Do you want to start ScreenShot Sequence?")) return; StartScreenShot(ScreenShotSequence.eReqType.REQ_SCREENSHOT_SINGLE); } private List MenuList; private void DoStartScreenShotAll(object obj) { var applyPermission = ApplicationExt.UsePermission; if (applyPermission && ApplicationExt.PermissionLevel > (ushort)AllowedPermissionLevel) return; //if (!MessageUtil.ShowYesNoMessageDialog("ScreenShot", $"Do you want to start ScreenShot Sequence?")) return; if (MenuList == null) { MenuList = new List(); foreach (var item in _AuroraEnv.ViewManager.GetMenuList()) { if (obj != null && obj is Dictionary<(string, string), List> subViewList) { if (subViewList.Any(x => x.Key.Item1 == item.mainName && x.Key.Item2 == item.subName) && subViewList.First(x => x.Key.Item1 == item.mainName && x.Key.Item2 == item.subName).Value.Count() > 0) { foreach (var tab in subViewList.First(x => x.Key.Item1 == item.mainName && x.Key.Item2 == item.subName).Value) { MenuList.Add(new ScreenShotSequence.ScreenShotWorkItem(item.mainName, item.subName, tab, true)); } } else { MenuList.Add(new ScreenShotSequence.ScreenShotWorkItem(item.mainName, item.subName, "", true)); } } else { MenuList.Add(new ScreenShotSequence.ScreenShotWorkItem(item.mainName, item.subName, "", true)); } } } if (MenuList.Count > 0) { string prevMain = MenuList[0].MainMenu; string prevSub = MenuList[0].SubMenu; MenuList[0].IsFirstMain = true; MenuList[0].IsFirstSub = true; for (int i = 1; i < MenuList.Count; i++) { if (prevMain != MenuList[i].MainMenu) { MenuList[i].IsFirstMain = true; MenuList[i].IsFirstSub = true; prevMain = MenuList[i].MainMenu; prevSub = MenuList[i].SubMenu; } else if (prevSub != MenuList[i].SubMenu) { MenuList[i].IsFirstSub = true; prevSub = MenuList[i].SubMenu; } } } var _title = Application.Current.FindStrToken("ScreenShot"); var result = MessageDialog.Show( Application.Current.MainWindow, Application.Current.FindStrToken("ScreenShot"), new UCScreenShotSelect() { DataContext = new UCScreenShotSelectViewModel(MenuList) }, MessageDialog.Type.AffirmativeAndNegative ); if (result != MessageDialog.Result.Affirmative || !MenuList.Any(x => x.IsSelected)) return; StartScreenShot(MenuList); } private void StartScreenShot(object param) { switch (_AuroraEnv.SequenceEntry[_SeqId].RunState) { case SequenceRunState.Idle: case SequenceRunState.Finish: _AuroraEnv.SequenceEntry[_SeqId].Start(param); break; case SequenceRunState.Paused: // N/A (현재 중지시키는 UX 없음) _AuroraEnv.SequenceEntry[_SeqId].Resume(); break; case SequenceRunState.Running: case SequenceRunState.StopHandling: case SequenceRunState.EmergencyStopHandling: case SequenceRunState.ForceErrorHandling: MessageUtil.ShowErrorToast($"ScreenShot Sequence is in state {_AuroraEnv.SequenceEntry[_SeqId].RunState.ToString()}. Please Try Again Later"); break; } } } public class ScreenShotSequence { public const string SYS_REQ_MENU_CHANGE_EXT = AuroraSysEvent.SYS_REQ_MENU_CHANGE + "_EXT"; public static Dictionary<(string, string), List> MenuList = new Dictionary<(string, string), List>(); static readonly Logger Log = new Logger("MainSequence"); const string SEQ_Start = "Start"; // 딱 한번 Sequence 진입 시에만 실행될 Node. 필요한 Initialize 처리 const string SEQ_CheckNext = "CheckNext"; const string SEQ_MoveMenu = "MoveMenu"; const string SEQ_ScreenShot = "ScreenShot"; const string SEQ_End = "End"; public SequenceBuilder Builder() { SequenceBuilder sb = new SequenceBuilder(1000, true); // 무한, 시퀀스 Stop시 최대 대기시간. //sb.AddAction(SEQ_FAIL_ERROR_HANDLING, HandleErrorAlarm); sb.AddAction(SEQ_Start, NodeStart); sb.AddDecision(SEQ_CheckNext, NodeCheckNext); sb.AddAction(SEQ_MoveMenu, NodeMoveMenu); sb.AddAction(SEQ_ScreenShot, NodeScreenShot); sb.AddAction(SEQ_End, NodeEnd); sb.Flows(sb.Init > sb[SEQ_Start] > sb[SEQ_CheckNext]); sb.Flows(sb[SEQ_CheckNext] > sb[SEQ_MoveMenu] > sb[SEQ_ScreenShot]); sb.Flows(sb[SEQ_CheckNext] > sb[SEQ_ScreenShot] > sb[SEQ_CheckNext]); sb.Flows(sb[SEQ_CheckNext] > sb[SEQ_End]); sb.Flows(sb[SEQ_End] > sb.Final); //// Forced Error //sb.Flows(sb[SEQ_Start] > sb.ForceError); //sb.Flows(sb[SEQ_CheckNext] > sb.ForceError); //sb.Flows(sb[SEQ_MoveMenu] > sb.ForceError); //sb.Flows(sb[SEQ_ScreenShot] > sb.ForceError); sb.OnStopped(OnStopped); sb.OnForceError(OnForceError); #if SEQ_DRAW sb.Draw($"{GetType().Name}.pdf"); #endif return sb; } public enum eReqType { NONE, REQ_SCREENSHOT_MULTI, REQ_SCREENSHOT_SINGLE, REQ_OPEN_FOLDER }; private string ManualSaveFolder = "ScreenShot_Manual"; private string SaveFolder = ""; public string GetLastSaveFolder() => SaveFolder; public class ScreenShotWorkItem : NotifyPropertyChangedBase { private string _MainMenu; public string MainMenu { get => _MainMenu; set => SetProperty(ref _MainMenu, value); } private string _SubMenu; public string SubMenu { get => _SubMenu; set => SetProperty(ref _SubMenu, value); } private string _ExtMenu; public string ExtMenu { get => _ExtMenu; set => SetProperty(ref _ExtMenu, value); } private bool _IsSelected; public bool IsSelected { get => _IsSelected; set => SetProperty(ref _IsSelected, value); } private bool _IsFirstMain; public bool IsFirstMain { get => _IsFirstMain; set => SetProperty(ref _IsFirstMain, value); } private bool _IsFirstSub; public bool IsFirstSub { get => _IsFirstSub; set => SetProperty(ref _IsFirstSub, value); } public ScreenShotWorkItem(string mainName, string subName, string tabName = null, bool isSelected = true) { MainMenu = mainName; SubMenu = subName; ExtMenu = tabName; IsSelected = isSelected; } } private IList WorkList; private int ScreenShotCount = 0; private eReqType _Mode = eReqType.NONE; private SequenceActionResult NodeStart(SequenceContext context, string id, SequenceActionResult param) { ScreenShotCount = 0; if (param.Param is eReqType reqType) { if (reqType == eReqType.REQ_OPEN_FOLDER) { _Mode = reqType; Log.I($"Open Last ScreenShot Folder : {SaveFolder}"); return SequenceAction.Ok(); } else /* if (reqType == eReqType.REQ_SCREENSHOT_SINGLE) */ { WorkList = null; SaveFolder = ManualSaveFolder; if (!string.IsNullOrEmpty(ApplicationExt.DataPath)) { SaveFolder = Path.Combine(ApplicationExt.DataPath, "ScreenShot", SaveFolder); } _Mode = reqType; Log.I($"ScreenShot Start : Single"); } } else if (param.Param is IList menuList) { WorkList = menuList; SaveFolder = $"ScreenShot_{DateTime.Now.ToString("yyMMdd-HHmmss")}"; if (!string.IsNullOrEmpty(ApplicationExt.DataPath)) { SaveFolder = Path.Combine(ApplicationExt.DataPath, "ScreenShot", SaveFolder); } _Mode = eReqType.REQ_SCREENSHOT_MULTI; Log.I($"ScreenShot Start : Total[{WorkList.Count(x => x.IsSelected)}] at [{SaveFolder}]"); } else { // Not Expected return new SequenceActionResult(-1, "Invalid Request"); } if (string.IsNullOrEmpty(SaveFolder)) { return new SequenceActionResult(-1, "Invalid Folder"); } if (!Directory.Exists(SaveFolder)) { Directory.CreateDirectory(SaveFolder); } if (!Directory.Exists(SaveFolder)) { return new SequenceActionResult(-1, "Something Wrong :: Cannot Create Directory"); } return SequenceAction.Ok(); } private string NodeCheckNext(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { switch (_Mode) { case eReqType.REQ_SCREENSHOT_SINGLE: if (ScreenShotCount > 0) { return SEQ_End; } else { return SEQ_ScreenShot; } case eReqType.REQ_SCREENSHOT_MULTI: if (WorkList.Count > 0 && WorkList.Count(x => x.IsSelected) > ScreenShotCount) { // Move Menu return SEQ_MoveMenu; } else { return SEQ_End; } case eReqType.REQ_OPEN_FOLDER: default: return SEQ_End; } } public string CheckValidFilename(string testName) { Regex containsABadCharacter = new Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars())) + "]"); if (containsABadCharacter.IsMatch(testName)) { testName = containsABadCharacter.Replace(testName, "_"); }; // other checks for UNC, drive-path format, etc return testName; } private SequenceActionResult NodeMoveMenu(SequenceContext context, string id, SequenceActionResult param) { Log.FuncV(); if (_Mode != eReqType.REQ_SCREENSHOT_MULTI) { return new SequenceActionResult(0, string.Empty); } if (WorkList == null || WorkList.Count(x => x.IsSelected) <= ScreenShotCount) { return new SequenceActionResult(0, string.Empty); } var curItem = WorkList.Where(x => x.IsSelected).ElementAt(ScreenShotCount); // [Coverity] CID# 12646 if (curItem == null) { return new SequenceActionResult(0, string.Empty); } Application.Current.AuroraEnv().ViewManager.ChangeMenu(curItem.MainMenu, curItem.SubMenu); Thread.Sleep(1000); string fileName; if (!string.IsNullOrEmpty(curItem.ExtMenu)) { context.EventPublisher.Publish(new EventMessage("ScreenShot", SYS_REQ_MENU_CHANGE_EXT, curItem.ExtMenu)); if (!curItem.IsFirstSub) { Thread.Sleep(1000); } fileName = $"Screen_{curItem.MainMenu}_{curItem.SubMenu}_{curItem.ExtMenu}.png"; } else { fileName = $"Screen_{curItem.MainMenu}_{curItem.SubMenu}.png"; } fileName = CheckValidFilename(fileName); return new SequenceActionResult(0, fileName); } private SequenceActionResult NodeScreenShot(SequenceContext context, string id, SequenceActionResult param) { string fileName = null; if (_Mode == eReqType.REQ_SCREENSHOT_SINGLE) { fileName = $"{SaveFolder}\\Manual_{DateTime.Now.ToString("yyMMdd-HHmmss")}.png"; Log.FuncV($"Process Single ScreehShot"); } else if (_Mode == eReqType.REQ_SCREENSHOT_MULTI) { if (param == null || string.IsNullOrEmpty(param.Param as string)) { // Unknown Error fileName = $"{SaveFolder}\\Unknown_{DateTime.Now.ToString("yyMMdd-HHmmss")}.png"; } else { fileName = $"{SaveFolder}\\{param.Param.ToString()}"; } Log.FuncV($"Process {ScreenShotCount} / {WorkList.Count(x => x.IsSelected)} : {fileName}"); } if (!string.IsNullOrEmpty(fileName)) { Application.Current.Dispatcher.Invoke(() => { var bounds = Application.Current.MainWindow.RenderSize; RenderTargetBitmap rtb = new RenderTargetBitmap((int)bounds.Width, (int)bounds.Height, 96, 96, PixelFormats.Pbgra32); rtb.Render(Application.Current.MainWindow); PngBitmapEncoder png = new PngBitmapEncoder(); png.Frames.Add(BitmapFrame.Create(rtb)); using (Stream stm = File.Create(fileName)) { png.Save(stm); } if (_Mode == eReqType.REQ_SCREENSHOT_SINGLE) { // 클립보드에 이미지 복사 //Clipboard.SetImage(BitmapSource.Create(rtb.PixelWidth, rtb.PixelHeight, 96, 96, PixelFormats.Pbgra32, null, rtb.BackBuffer, rtb.BackBufferStride)); Clipboard.SetImage(rtb); } }); } ScreenShotCount++; return SequenceAction.Ok(); } private SequenceActionResult NodeEnd(SequenceContext context, string id, SequenceActionResult param) { Log.FuncV($"Open Folder :: {SaveFolder}"); if (!string.IsNullOrEmpty(SaveFolder) && Directory.Exists(SaveFolder)) { //Process.Start("explorer.exe", SaveFolder); WindowUtil.OpenOrActivateFolder(SaveFolder); } else { return new SequenceActionResult(-1, "Invalid Folder"); } return SequenceAction.Ok(); } private void OnStopped(SequenceContext obj) { // MessageUtil.ShowSuccessToast(); } private void OnForceError(SequenceContext arg1, string arg2) { MessageUtil.ShowErrorToast("Sequence Failed"); } } }