using System; using System.Windows.Forms; using Microsoft.Win32; namespace TimerApp { public enum MonitorState { Idle, Working, Resting } public sealed class ActivityMonitor : IDisposable { private CancellationTokenSource? _cts; private readonly object _lock = new object(); private TimeSpan _accumulatedWorkTime; private int _restElapsedSeconds; private bool _isPaused; private bool _disposed; // 状态检测缓存 private int _checkTickCounter; private const int CheckIntervalTicks = 3; // 每3秒检查一次空闲状态 private long _cachedIdleMs; // 配置 (默认值) 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 bool IsPaused { get { lock (_lock) return _isPaused; } private set { lock (_lock) _isPaused = value; } } // 事件 public event EventHandler? WorkProgressChanged; // 剩余工作时间 public event EventHandler? RestProgressChanged; // 剩余休息时间 public event EventHandler? RestStarted; public event EventHandler? RestEnded; public event EventHandler? StateChanged; public ActivityMonitor() { } public void Start() { lock (_lock) { StopInternal(); _cts = new CancellationTokenSource(); var token = _cts.Token; // 启动时立即触发一次检测 _checkTickCounter = CheckIntervalTicks; ResetWork(); Task.Run(() => MonitorLoop(token), token); } } public void Stop() { lock (_lock) { StopInternal(); } } private void StopInternal() { if (_cts != null) { _cts.Cancel(); _cts.Dispose(); _cts = null; } } private void ResetWork() { // Must be called within lock _accumulatedWorkTime = TimeSpan.Zero; ChangeState(MonitorState.Idle); } private void ChangeState(MonitorState newState) { // Must be called within lock if (CurrentState != newState) { CurrentState = newState; StateChanged?.Invoke(this, EventArgs.Empty); } } private async Task MonitorLoop(CancellationToken token) { while (!token.IsCancellationRequested) { try { await Task.Delay(1000, token); OnTick(); } catch (OperationCanceledException) { break; } catch (Exception ex) { Logger.LogError("Error in ActivityMonitor MonitorLoop", ex); } } } private void OnTick() { // 在锁外执行耗时的系统检测 long idleMs = 0; bool shouldUpdate = false; lock (_lock) { // 如果处于暂停状态,不处理计时逻辑 if (_isPaused) return; // 检查是否需要更新状态 _checkTickCounter++; if (_checkTickCounter >= CheckIntervalTicks) { _checkTickCounter = 0; // 需要更新,但在锁外进行 shouldUpdate = true; } // 如果不需要更新,直接使用缓存值 if (!shouldUpdate) { idleMs = _cachedIdleMs; } } // 在锁外执行实际的检测 if (shouldUpdate) { idleMs = NativeMethods.GetIdleTime(); // 更新缓存 lock (_lock) { _cachedIdleMs = idleMs; } } lock (_lock) { // 再次检查暂停状态 if (_isPaused) return; // 使用(可能是新更新的)缓存值 idleMs = _cachedIdleMs; 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 isUserInactive = idleTime > IdleThreshold; if (isUserInactive) { // 用户确实离开了 -> 进入空闲状态 if (CurrentState == MonitorState.Working) { ChangeState(MonitorState.Idle); } // 如果在 Idle 状态,且空闲时间非常长(比如超过了休息时间), // 重置工作计时器 if (idleTime > RestDuration) { _accumulatedWorkTime = TimeSpan.Zero; } } else { // 用户在活动 if (CurrentState == MonitorState.Idle) { // 从空闲变为工作 ChangeState(MonitorState.Working); } // 正常工作:累加时间 _accumulatedWorkTime += TimeSpan.FromSeconds(1); // 检查是否达到工作时长 TimeSpan remainingWork = WorkDuration - _accumulatedWorkTime; if (remainingWork <= TimeSpan.Zero) { // 触发休息 _restElapsedSeconds = 0; // 重置休息计数器 ChangeState(MonitorState.Resting); RestStarted?.Invoke(this, EventArgs.Empty); } else { WorkProgressChanged?.Invoke(this, remainingWork); } } } } } public void RefreshStatus() { lock (_lock) { 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() { lock (_lock) { _accumulatedWorkTime = TimeSpan.Zero; _isPaused = false; // Force state to Working since user manually restarted ChangeState(MonitorState.Working); // Immediately refresh UI to show full duration RefreshStatus(); } } public void Pause() { lock (_lock) { if (!_isPaused && CurrentState != MonitorState.Idle) { _isPaused = true; StateChanged?.Invoke(this, EventArgs.Empty); } } } public void Resume() { lock (_lock) { if (_isPaused) { _isPaused = false; StateChanged?.Invoke(this, EventArgs.Empty); RefreshStatus(); } } } public void Dispose() { if (_disposed) return; _disposed = true; // SystemEvents.PowerModeChanged -= OnPowerModeChanged; Stop(); GC.SuppressFinalize(this); } } }