Skip to content

Commit

Permalink
Merge pull request #3861 from Ginger-Automation/Enhancement/DevTimeTr…
Browse files Browse the repository at this point in the history
…ackerWhenIdle

dev time tracker when user is idle for more than 5 mins
  • Loading branch information
Maheshkale447 authored Aug 14, 2024
2 parents 14d5b1c + 682439e commit d69534f
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 83 deletions.
205 changes: 205 additions & 0 deletions Ginger/Ginger/DevTimeTrackerIdle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
#region License
/*
Copyright © 2014-2024 European Support Limited
Licensed under the Apache License, Version 2.0 (the "License")
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion

using Amdocs.Ginger.Repository;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Threading;
using amdocs.ginger.GingerCoreNET;
using Amdocs.Ginger.Common;

namespace Ginger
{
/// <summary>
/// The DevTimeTrackerIdle class tracks user activity within a WPF application and triggers events when the user is idle for a specified duration.
/// It monitors various input events (mouse movement, keyboard input, mouse click) and raises events to pause and resume
/// time-based activities when the user becomes idle or active, respectively.
/// </summary>
public class DevTimeTrackerIdle
{
private DispatcherTimer idleTimer;
private DateTime lastActivity;
private bool isPaused;


/// <summary>
/// Initializes a new instance of the DevTimeTrackerIdle class.
/// The DevTimeTrackerIdle starts tracking user activity and will raise PauseTime and ResumeTime events as appropriate.
/// </summary>

public DevTimeTrackerIdle()
{
idleTimer = new DispatcherTimer();
idleTimer.Interval = TimeSpan.FromMinutes(1); // Check every minute
idleTimer.Tick += IdleTimer_Tick;
idleTimer.Start();

lastActivity = DateTime.Now;
isPaused = false;
}

/// <summary>
/// Attaches event handlers to monitor user activity on the specified UIElement.
/// The IdleMonitor will track mouse movement, keyboard input, mouse clicks.
/// </summary>
/// <param name="element">The UIElement to monitor for activity.</param>
public void AttachActivityHandlers(UIElement element)
{
element.MouseMove += OnActivity;
element.KeyDown += OnActivity;
element.MouseDown += OnActivity;
}

/// <summary>
/// Detaches the event handlers previously attached to monitor user activity on the specified UIElement.
/// This method should be called to clean up resources when the monitoring is no longer needed.
/// </summary>
/// <param name="element">The UIElement to stop monitoring for activity.</param>
public void DetachActivityHandlers(UIElement element)
{
element.MouseMove -= OnActivity;
element.KeyDown -= OnActivity;
element.MouseDown -= OnActivity;
}

/// <summary>
/// Handles the DispatcherTimer tick event to check for user inactivity.
/// If the user has been idle for longer than the specified duration (5 minutes), the OnPauseTime event is raised.
/// </summary>
private void IdleTimer_Tick(object sender, EventArgs e)
{
if (DateTime.Now - lastActivity > TimeSpan.FromMinutes(5) && !isPaused)
{
PauseDevelopmentTimeTracker();
}
}

/// <summary>
/// Handles user activity events by resetting the last activity time.
/// If the user was previously idle and becomes active again, the OnResumeTime event is raised.
/// </summary>
private void OnActivity(object sender, EventArgs e)
{
if (isPaused)
{
ResumeDevelopmentTimeTracker();
}

lastActivity = DateTime.Now;

}

private List<RepositoryItemBase> _itemsWithPausedDevelopmentTimeTracker = [];

/// <summary>
/// Pauses the development time tracker for modified files in the solution.
/// </summary>
public void PauseDevelopmentTimeTracker()
{
if (WorkSpace.Instance == null ||
WorkSpace.Instance.SolutionRepository == null ||
WorkSpace.Instance.SolutionRepository.ModifiedFiles == null)
{
return;
}

try
{
_itemsWithPausedDevelopmentTimeTracker.Clear();
List<RepositoryItemBase> modifiedFiles = new(WorkSpace.Instance.SolutionRepository.ModifiedFiles);

foreach (RepositoryItemBase modifiedFile in modifiedFiles)
{
if (modifiedFile is GingerCore.BusinessFlow bf && bf.IsTimerRunning())
{
bf.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(bf);

foreach (GingerCore.Activity bfActivity in bf.Activities)
{
if (bfActivity.IsTimerRunning())
{
bfActivity.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(bfActivity);
}
}
}
else if (modifiedFile is GingerCore.Activity activity && activity.IsTimerRunning())
{
activity.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(activity);
}
else if (modifiedFile is Amdocs.Ginger.Repository.ApplicationPOMModel applicationPOMModel && applicationPOMModel.IsTimerRunning())
{
applicationPOMModel.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(applicationPOMModel);
}
}

//Setting flag to true
if (_itemsWithPausedDevelopmentTimeTracker.Count > 0)
{
isPaused = true;
}
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.DEBUG, "error while pausing development tracker", ex);
}
}

/// <summary>
/// Resumes the development time tracker for paused items.
/// </summary>
public void ResumeDevelopmentTimeTracker()
{
try
{
List<RepositoryItemBase> items = new(_itemsWithPausedDevelopmentTimeTracker);
_itemsWithPausedDevelopmentTimeTracker.Clear();

foreach (RepositoryItemBase item in items)
{
if (item is GingerCore.BusinessFlow bf)
{
bf.StartTimer();
}
else if (item is GingerCore.Activity activity)
{
activity.StartTimer();
}
else if (item is Amdocs.Ginger.Repository.ApplicationPOMModel applicationPOMModel)
{
applicationPOMModel.StartTimer();
}
}

//Setting flag to false
if (_itemsWithPausedDevelopmentTimeTracker.Count == 0)
{
isPaused = false;
}
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.DEBUG, "error while resuming development tracker", ex);
}
}
}
}
99 changes: 16 additions & 83 deletions Ginger/Ginger/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public partial class MainWindow : Window
public enum eSolutionTabType { None, BusinessFlows, Run, Configurations, Resources };
public eSolutionTabType SelectedSolutionTab;

private readonly DevTimeTrackerIdle devTimeTrackerIdle; //for Pause/resume the development time

private bool mAskUserIfToClose = true;

ObservableList<HelpLayoutArgs> mHelpLayoutList = new ObservableList<HelpLayoutArgs>();
Expand All @@ -84,8 +86,13 @@ public enum eSolutionTabType { None, BusinessFlows, Run, Configurations, Resourc
public MainWindow()
{
InitializeComponent();

devTimeTrackerIdle = new();
devTimeTrackerIdle.AttachActivityHandlers(this);

StateChanged -= MainWindow_StateChanged;
StateChanged += MainWindow_StateChanged;

mRestartApplication = false;
lblAppVersion.Content = "Version " + Amdocs.Ginger.Common.GeneralLib.ApplicationInfo.ApplicationUIversion;
xVersionAndNewsIcon.Visibility = Visibility.Collapsed;
Expand All @@ -95,102 +102,26 @@ public MainWindow()
DriverWindowHandler.Init();

GingerCore.General.DoEvents();

}

private void MainWindow_StateChanged(object? sender, EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
PauseDevelopmentTimeTracker();
devTimeTrackerIdle.PauseDevelopmentTimeTracker();
}
else
{
ResumeDevelopmentTimeTracker();
devTimeTrackerIdle.ResumeDevelopmentTimeTracker();
}
}

private List<RepositoryItemBase> _itemsWithPausedDevelopmentTimeTracker = [];

/// <summary>
/// Pauses the development time tracker for modified files in the solution.
/// </summary>
private void PauseDevelopmentTimeTracker()
private void DetachEventHandlers()
{
if (WorkSpace.Instance == null ||
WorkSpace.Instance.SolutionRepository == null ||
WorkSpace.Instance.SolutionRepository.ModifiedFiles == null)
{
return;
}

try
if (devTimeTrackerIdle != null)
{
_itemsWithPausedDevelopmentTimeTracker.Clear();
List<RepositoryItemBase> modifiedFiles = new(WorkSpace.Instance.SolutionRepository.ModifiedFiles);

foreach (RepositoryItemBase modifiedFile in modifiedFiles)
{
if (modifiedFile is BusinessFlow bf && bf.IsTimerRunning())
{
bf.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(bf);

foreach (GingerCore.Activity bfActivity in bf.Activities)
{
if (bfActivity.IsTimerRunning())
{
bfActivity.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(bfActivity);
}
}
}
else if (modifiedFile is GingerCore.Activity activity && activity.IsTimerRunning())
{
activity.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(activity);
}
else if (modifiedFile is ApplicationPOMModel applicationPOMModel && applicationPOMModel.IsTimerRunning())
{
applicationPOMModel.StopTimer();
_itemsWithPausedDevelopmentTimeTracker.Add(applicationPOMModel);
}
}
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.DEBUG, "error while pausing development tracker", ex);
}
}

/// <summary>
/// Resumes the development time tracker for paused items.
/// </summary>
private void ResumeDevelopmentTimeTracker()
{
try
{
List<RepositoryItemBase> items = new(_itemsWithPausedDevelopmentTimeTracker);
_itemsWithPausedDevelopmentTimeTracker.Clear();

foreach (RepositoryItemBase item in items)
{
if (item is BusinessFlow bf)
{
bf.StartTimer();
}
else if (item is GingerCore.Activity activity)
{
activity.StartTimer();
}
else if (item is ApplicationPOMModel applicationPOMModel)
{
applicationPOMModel.StartTimer();
}
}
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.DEBUG, "error while resuming development tracker", ex);
devTimeTrackerIdle.DetachActivityHandlers(this);
}
}

Expand All @@ -204,7 +135,7 @@ private void TelemetryEventHandler(object sender, Telemetry.TelemetryEventArgs e


}

private void XVersionAndNewsIcon_MouseDown(object sender, MouseButtonEventArgs e)
{
xVersionAndNewsIcon.Visibility = Visibility.Collapsed;
Expand Down Expand Up @@ -632,6 +563,8 @@ private void MainWindow_Closing(object sender, System.ComponentModel.CancelEvent

if (userSelection == eUserMsgSelection.Yes)
{
DetachEventHandlers();

AppCleanUp();
}
else
Expand Down

0 comments on commit d69534f

Please sign in to comment.