using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; using System.Windows; using System.Windows.Input; using System.Windows.Media.Imaging; using Tango.Core.Commands; using Tango.Core.DI; using Tango.Core.Threading; using Tango.FSE.Common; using Tango.FSE.Common.Notifications; using Tango.FSE.Common.RemoteActions; using Tango.FSE.Common.RemoteDesktop; using Tango.RemoteDesktop.Frames; using Tango.RemoteDesktop.Network; namespace Tango.FSE.PPCConsole.ViewModels { /// /// Represents the PPC remote dekstop view model. /// /// public class RemoteDesktopViewVM : FSEViewModel { private DateTime _recordingStartTime; private Timer _recordingTimer; private VideoRecordingHandler _recordingHandler; private SnackbarItem _recordingSnackbarItem; private System.Windows.Point _lastMouseLocation; #region Properties private BitmapSource _source; /// /// Gets or sets the remote desktop image source. /// public BitmapSource Source { get { return _source; } set { _source = value; RaisePropertyChangedAuto(); } } private bool _isRecording; public bool IsRecording { get { return _isRecording; } set { _isRecording = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private TimeSpan _recordingTime; public TimeSpan RecordingTime { get { return _recordingTime; } set { _recordingTime = value; RaisePropertyChangedAuto(); } } private bool _cursorVisible; public bool CursorVisible { get { return _cursorVisible; } set { _cursorVisible = value; RaisePropertyChangedAuto(); SetCursorVisibility(); } } private bool _touchMode; public bool TouchMode { get { return _touchMode; } set { _touchMode = value; RaisePropertyChangedAuto(); } } [TangoInject] public IRemoteActionsProvider RemoteActionsProvider { get; set; } #endregion #region Commands /// /// Starts a remote desktop session with the currently connected machine. /// public RelayCommand StartCommand { get; set; } /// /// Stops the current remote desktop session. /// public RelayCommand StopCommand { get; set; } /// /// Executes the task manager on the remote PPC. /// public RelayCommand OpenTaskManagerCommand { get; set; } /// /// Hides the remote PPC application and opens the windows shell. /// public RelayCommand HideAndOpenShellCommand { get; set; } /// /// Save current desktop frame to image /// public RelayCommand TakeSnapshotCommand { get; set; } /// /// Starts the video recording. /// public RelayCommand StartRecordingCommand { get; set; } /// /// Stops the video recording. /// public RelayCommand StopRecordingCommand { get; set; } /// /// Gets or sets the restart application command. /// public RelayCommand RestartApplicationCommand { get; set; } #endregion #region Constructors /// /// Initializes a new instance of the class. /// public RemoteDesktopViewVM() { _recordingTimer = new Timer(1000); _recordingTimer.Elapsed += _recordingTimer_Elapsed; _recordingTimer.Stop(); StartCommand = new RelayCommand(StartRemoteDesktop); StopCommand = new RelayCommand(StopRemoteDesktop); OpenTaskManagerCommand = new RelayCommand(OpenTaskManager); HideAndOpenShellCommand = new RelayCommand(HideAndOpenShell); TakeSnapshotCommand = new RelayCommand(TakeSnapshot); StartRecordingCommand = new RelayCommand(StartRecording); StopRecordingCommand = new RelayCommand(StopRecording, () => IsRecording); RestartApplicationCommand = new RelayCommand(RestartApplication); } #endregion #region Override Methods public override void OnApplicationStarted() { base.OnApplicationStarted(); RemoteDesktopProvider.FrameReceived += RemoteDesktopProvider_FrameReceived; RemoteDesktopProvider.SessionStopped += RemoteDesktopProvider_SessionStopped; MachineProvider.MachineConnected += MachineProvider_MachineConnected; } public override async Task OnApplicationLogout() { if (RemoteDesktopProvider.InSession) { if (await NotificationProvider.ShowWarningQuestion("The remote desktop session with the remote machine will be closed.\nAre you sure you want to log out?")) { await RemoteDesktopProvider.EndSession(); return true; } else { return false; } } else { return true; } } #endregion #region Event Handlers private void MachineProvider_MachineConnected(object sender, Common.Connection.MachineConnectedEventArgs e) { Source = null; } private void RemoteDesktopProvider_FrameReceived(object sender, DesktopFrameReceivedEventArgs e) { Source = e.Source; _cursorVisible = e.CursorVisible; RaisePropertyChanged(nameof(CursorVisible)); } private void RemoteDesktopProvider_SessionStopped(object sender, EventArgs e) { _cursorVisible = false; RaisePropertyChanged(nameof(CursorVisible)); } #endregion #region Start / Stop Session private async void StartRemoteDesktop() { try { await RemoteDesktopProvider.StartSession(); } catch (Exception ex) { await NotificationProvider.ShowError($"Error starting the remote desktop session.\n{ex.FlattenMessage()}"); } } private async void StopRemoteDesktop() { try { await RemoteDesktopProvider.EndSession(); } catch (Exception ex) { await NotificationProvider.ShowError($"Error stopping the remote desktop session.\n{ex.FlattenMessage()}"); } } #endregion #region Mouse & Keyboard Handlers From View public void OnMouseDown(MouseButton changedButton, System.Windows.Point point, System.Windows.Size size) { _lastMouseLocation = new System.Windows.Point(point.X, point.Y); if (!TouchMode) { RemoteDesktopProvider.MouseDown(changedButton, point, size); } else { RemoteDesktopProvider.TouchDown(point, size); } } public void OnMouseUp(MouseButton changedButton, System.Windows.Point point, System.Windows.Size size) { if (!TouchMode) { RemoteDesktopProvider.MouseUp(changedButton, point, size); } else { RemoteDesktopProvider.TouchUp(); } } public void OnMouseMove(System.Windows.Point point, System.Windows.Size size) { if (!TouchMode) { RemoteDesktopProvider.MouseMove(point, size); } else { var delta = new System.Windows.Point(point.X - _lastMouseLocation.X, point.Y - _lastMouseLocation.Y); RemoteDesktopProvider.TouchMove((int)delta.X, (int)delta.Y, size); } _lastMouseLocation = new System.Windows.Point(point.X, point.Y); } public void OnMouseDoubleClick(MouseButton changedButton, System.Windows.Point point, System.Windows.Size size) { RemoteDesktopProvider.MouseDoubleClick(changedButton, point, size); } public void OnMouseScroll(int delta, System.Windows.Point point, System.Windows.Size size) { RemoteDesktopProvider.MouseScroll(delta, point, size); } public void OnKeyboardDown(Key key, bool ctrlDown, bool shitDown, bool altDown) { RemoteDesktopProvider.KeyboardDown(key, ctrlDown, shitDown, altDown); } public void OnKeyboardUp(Key key, bool ctrlDown, bool shitDown, bool altDown) { RemoteDesktopProvider.KeyboardUp(key, ctrlDown, shitDown, altDown); } private void SetCursorVisibility() { RemoteDesktopProvider.SetRemoteCursorVisibility(CursorVisible); } #endregion #region Snapshot private async void TakeSnapshot() { var snapshot = Source.Clone(); await Task.Delay(500); //Delay for blink animation.. var result = await StorageProvider.SaveFile("Save Snapshot", "Jpeg Files|*.jpg", $"TangoApp_{DateTime.Now.ToFileName()}.jpg", ".jpg"); if (result.Confirmed) { snapshot.SaveJpegFile(result.SelectedItem, 100); NotificationProvider.PushSnackbarItem(MessageType.Success, "Remote Desktop Snapshot Saved", true, "Remote desktop snapshot saved successfully.\nTap to view the image file.", TimeSpan.FromSeconds(8), null, () => { StorageProvider.ShowInExplorer(result.SelectedItem); }); } } #endregion #region Remote Actions private void OpenTaskManager() { RemoteDesktopProvider.KeyboardDown(Key.Escape, true, true, false); RemoteDesktopProvider.KeyboardDown(Key.Escape, false, false, false); } private void HideAndOpenShell() { RemoteDesktopProvider.SendCommand(RemoteDesktopCommand.HideAndOpenShell); } private async void RestartApplication() { if (await NotificationProvider.ShowWarningQuestion("Are you sure you want to restart the remote application?", "RESTART")) { bool restarted = false; try { try { await RemoteDesktopProvider.EndSession(); } catch { } await Task.Factory.StartNew(RemoteActionsProvider.RestartApplication); restarted = true; } catch (Exception ex) { LogManager.Log(ex, "Error restarting the remote application."); } if (restarted) { try { await MachineProvider.DisconnectAndWaitForReconnection(TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(1), "The remote PPC is now restarting..."); if (MachineProvider.IsConnected) { StartRemoteDesktop(); } } catch { } } } } #endregion #region Video Recording private void _recordingTimer_Elapsed(object sender, ElapsedEventArgs e) { RecordingTime = DateTime.Now - _recordingStartTime; } private void StartRecording() { if (!IsRecording) { RecordingTime = TimeSpan.Zero; _recordingStartTime = DateTime.Now; _recordingTimer.Start(); _recordingHandler = RemoteDesktopProvider.CreateRecordingHandler(); _recordingHandler.Progress += _recordingHandler_Progress; IsRecording = true; } } private async void StopRecording() { if (IsRecording) { IsRecording = false; var result = await StorageProvider.SaveFile("Save Video Recording", "Mp4 Video Files|*.mp4", $"TangoApp_{DateTime.Now.ToFileName()}", ".mp4"); if (result.Confirmed) { _recordingHandler.Stop(); try { _recordingSnackbarItem = NotificationProvider.PushProgressSnackbar("Encoding Video File", "Encoding remote desktop recording to disk..."); await _recordingHandler.Save(result.SelectedItem); _recordingSnackbarItem.ProgressCompleted("Video encoding completed.\nTap to view the video file.", null, () => { StorageProvider.ShowInExplorer(result.SelectedItem); }); } catch (Exception ex) { LogManager.Log(ex, "Error saving remote desktop video recording."); _recordingSnackbarItem.ProgressFailed($"Video encoding failed.\n{ex.FlattenMessage()}", TimeSpan.FromSeconds(5)); await NotificationProvider.ShowError($"Error saving video recording.\n{ex.FlattenMessage()}"); } } else { _recordingHandler.Cancel(); } } } private void _recordingHandler_Progress(object sender, Core.TangoProgressChangedEventArgs e) { _recordingSnackbarItem.Message = $"Encoding remote desktop recording to disk ({(e.Progress.Value / e.Progress.Maximum * 100d).ToString("0")}%)..."; } #endregion } }