Files
TimerApp/ActivityMonitor.cs
2026-01-17 12:48:01 +08:00

183 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
// 配置 (默认值)
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<TimeSpan> WorkProgressChanged; // 剩余工作时间
public event EventHandler<TimeSpan> 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)
{
// 休息模式逻辑
TimeSpan timeInRest = DateTime.Now - _restStartTime;
TimeSpan remainingRest = RestDuration - timeInRest;
if (remainingRest <= TimeSpan.Zero)
{
// 休息结束
RestEnded?.Invoke(this, EventArgs.Empty);
ResetWork(); // 重新开始工作周期
}
else
{
RestProgressChanged?.Invoke(this, remainingRest);
}
}
else
{
// 工作/空闲模式逻辑
if (idleTime > IdleThreshold)
{
// 用户离开了
if (CurrentState == MonitorState.Working)
{
// 如果正在工作,但离开了,暂停工作计时?
// 简单起见,如果离开时间过长,可以视为一种“休息”,或者只是暂停累积
// 这里我们简单处理:如果空闲时间超过阈值,状态变为空闲
ChangeState(MonitorState.Idle);
}
// 如果在 Idle 状态,且空闲时间非常长(比如超过了休息时间),
// 是否应该重置工作计时器?
// 假设用户去开会了1小时回来应该重新计算20分钟。
if (idleTime > RestDuration)
{
_accumulatedWorkTime = TimeSpan.Zero;
}
}
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;
ChangeState(MonitorState.Resting);
RestStarted?.Invoke(this, EventArgs.Empty);
}
else
{
WorkProgressChanged?.Invoke(this, remainingWork);
}
}
}
}
// 用于强制重置或测试
public void ForceRest()
{
_restStartTime = DateTime.Now;
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)
{
TimeSpan timeInRest = DateTime.Now - _restStartTime;
TimeSpan remaining = RestDuration - timeInRest;
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();
}
}
}