using System; using System.Windows.Forms; namespace TimerApp { public enum MonitorState { Idle, Working, Resting } public class ActivityMonitor { private System.Windows.Forms.Timer _timer; private DateTime _lastWorkStartTime; private TimeSpan _accumulatedWorkTime; private DateTime _restStartTime; private int _restElapsedSeconds; // 休息已过秒数(用于避免时间计算导致的跳变) // 配置 (默认值) public TimeSpan WorkDuration { get; set; } = TimeSpan.FromMinutes(20); public TimeSpan RestDuration { get; set; } = TimeSpan.FromMinutes(1); public TimeSpan IdleThreshold { get; set; } = TimeSpan.FromSeconds(30); public MonitorState CurrentState { get; private set; } = MonitorState.Idle; // 事件 public event EventHandler WorkProgressChanged; // 剩余工作时间 public event EventHandler RestProgressChanged; // 剩余休息时间 public event EventHandler RestStarted; public event EventHandler RestEnded; public event EventHandler StateChanged; public ActivityMonitor() { _timer = new System.Windows.Forms.Timer(); _timer.Interval = 1000; // 1秒检查一次 _timer.Tick += Timer_Tick; } public void Start() { _timer.Start(); ResetWork(); } public void Stop() { _timer.Stop(); } private void ResetWork() { _accumulatedWorkTime = TimeSpan.Zero; _lastWorkStartTime = DateTime.Now; ChangeState(MonitorState.Idle); } private void ChangeState(MonitorState newState) { if (CurrentState != newState) { CurrentState = newState; StateChanged?.Invoke(this, EventArgs.Empty); } } private void Timer_Tick(object sender, EventArgs e) { long idleMs = NativeMethods.GetIdleTime(); TimeSpan idleTime = TimeSpan.FromMilliseconds(idleMs); if (CurrentState == MonitorState.Resting) { // 休息模式逻辑 // 使用计数器而不是时间差,避免秒数跳变 _restElapsedSeconds++; int totalRestSeconds = (int)RestDuration.TotalSeconds; int remainingSeconds = totalRestSeconds - _restElapsedSeconds; if (remainingSeconds <= 0) { // 休息结束 RestEnded?.Invoke(this, EventArgs.Empty); ResetWork(); // 重新开始工作周期 } else { TimeSpan remainingRest = TimeSpan.FromSeconds(remainingSeconds); RestProgressChanged?.Invoke(this, remainingRest); } } else { // 检测是否有视频/媒体正在播放 bool isMediaPlaying = NativeMethods.IsMediaPlaying(); // 工作/空闲模式逻辑 if (idleTime > IdleThreshold || isMediaPlaying) { // 用户离开了或正在播放视频 if (CurrentState == MonitorState.Working) { // 如果正在工作,但离开了,暂停工作计时? // 简单起见,如果离开时间过长,可以视为一种“休息”,或者只是暂停累积 // 这里我们简单处理:如果空闲时间超过阈值,状态变为空闲 ChangeState(MonitorState.Idle); } // 如果在 Idle 状态,且空闲时间非常长(比如超过了休息时间), // 是否应该重置工作计时器? // 假设用户去开会了1小时,回来应该重新计算20分钟。 if (idleTime > RestDuration) { _accumulatedWorkTime = TimeSpan.Zero; } // 如果正在播放视频,不累加工作时间,但保持当前状态 if (isMediaPlaying && CurrentState == MonitorState.Working) { // 保持当前剩余时间不变(不累加,也不减少) TimeSpan remainingWork = WorkDuration - _accumulatedWorkTime; WorkProgressChanged?.Invoke(this, remainingWork); } } else { // 用户在活动且没有播放视频 if (CurrentState == MonitorState.Idle) { // 从空闲变为工作 ChangeState(MonitorState.Working); _lastWorkStartTime = DateTime.Now; } // 累加工作时间 // 简单的累加逻辑:这一秒是工作的 _accumulatedWorkTime += TimeSpan.FromSeconds(1); // 检查是否达到工作时长 TimeSpan remainingWork = WorkDuration - _accumulatedWorkTime; if (remainingWork <= TimeSpan.Zero) { // 触发休息 _restStartTime = DateTime.Now; _restElapsedSeconds = 0; // 重置休息计数器 ChangeState(MonitorState.Resting); RestStarted?.Invoke(this, EventArgs.Empty); } else { WorkProgressChanged?.Invoke(this, remainingWork); } } } } // 用于强制重置或测试 public void ForceRest() { _restStartTime = DateTime.Now; _restElapsedSeconds = 0; // 重置休息计数器 ChangeState(MonitorState.Resting); RestStarted?.Invoke(this, EventArgs.Empty); } public void RefreshStatus() { if (CurrentState == MonitorState.Working) { TimeSpan remaining = WorkDuration - _accumulatedWorkTime; WorkProgressChanged?.Invoke(this, remaining); } else if (CurrentState == MonitorState.Resting) { // 使用计数器计算剩余时间,保持一致性 int totalRestSeconds = (int)RestDuration.TotalSeconds; int remainingSeconds = totalRestSeconds - _restElapsedSeconds; if (remainingSeconds < 0) remainingSeconds = 0; TimeSpan remaining = TimeSpan.FromSeconds(remainingSeconds); RestProgressChanged?.Invoke(this, remaining); } } public void Restart() { _accumulatedWorkTime = TimeSpan.Zero; _lastWorkStartTime = DateTime.Now; // Ensure timer is running if (!_timer.Enabled) _timer.Start(); // Force state to Working since user manually restarted ChangeState(MonitorState.Working); // Immediately refresh UI to show full duration RefreshStatus(); } } }