Add AI Team branding and configurable break counting
This commit is contained in:
@@ -35,6 +35,7 @@ Sub Process_Globals
|
||||
Private StatsFileName As String
|
||||
Private StateFileName As String
|
||||
Private AutoConfigFileName As String
|
||||
Private SettingsFileName As String
|
||||
Private MaxStoredDays As Int
|
||||
End Sub
|
||||
|
||||
@@ -52,8 +53,10 @@ Sub Globals
|
||||
Private BtnClearToday As Button
|
||||
Private BtnHelpOpen As Button
|
||||
Private BtnHelpClose As Button
|
||||
Private chkIncludePauses As CheckBox
|
||||
Private BtnBackground As Label
|
||||
Private lblAppName As Label
|
||||
Private lblBrandInfo As Label
|
||||
Private lblElapsedTime As Label
|
||||
Private lblAutoStatus As Label
|
||||
Private pnlClock As Panel
|
||||
@@ -84,6 +87,7 @@ Sub Globals
|
||||
Private AutoEndMinutes As Int
|
||||
Private AutoState As String
|
||||
Private LastActiveStateSaveAt As Long
|
||||
Private IncludePauseInTotal As Boolean
|
||||
End Sub
|
||||
|
||||
Sub Activity_Create(FirstTime As Boolean)
|
||||
@@ -101,7 +105,9 @@ Sub Activity_Create(FirstTime As Boolean)
|
||||
StatsFileName = "work_segments.txt"
|
||||
StateFileName = "active_state.txt"
|
||||
AutoConfigFileName = "auto_config.txt"
|
||||
SettingsFileName = "app_settings.txt"
|
||||
MaxStoredDays = 62
|
||||
IncludePauseInTotal = True
|
||||
|
||||
myClock.Initialize(pnlClock, 96)
|
||||
myClock.SetBaseDuration(12 * DateTime.TicksPerHour)
|
||||
@@ -111,6 +117,7 @@ Sub Activity_Create(FirstTime As Boolean)
|
||||
|
||||
StatsRows.Initialize
|
||||
LoadStatsRows
|
||||
LoadSettings
|
||||
HideLegacyButtons
|
||||
CreateStatsButton
|
||||
CreateStatsPage
|
||||
@@ -119,6 +126,7 @@ Sub Activity_Create(FirstTime As Boolean)
|
||||
CreateHelpPage
|
||||
CreateBackgroundButton
|
||||
CreateAppNameLabel
|
||||
CreateBrandInfoLabel
|
||||
CreateAutoStatusLabel
|
||||
LayoutMainButtons
|
||||
LoadAutoConfig
|
||||
@@ -219,6 +227,15 @@ Private Sub CreateAppNameLabel
|
||||
Activity.AddView(lblAppName, 0, 0, 10dip, 10dip)
|
||||
End Sub
|
||||
|
||||
Private Sub CreateBrandInfoLabel
|
||||
lblBrandInfo.Initialize("")
|
||||
lblBrandInfo.Text = "AI Team | aiteam.ai"
|
||||
lblBrandInfo.TextColor = Colors.RGB(90, 90, 90)
|
||||
lblBrandInfo.TextSize = 11
|
||||
lblBrandInfo.Gravity = Gravity.CENTER
|
||||
Activity.AddView(lblBrandInfo, 0, 0, 10dip, 10dip)
|
||||
End Sub
|
||||
|
||||
Private Sub LayoutMainButtons
|
||||
Dim margin As Int = 8dip
|
||||
Dim gap As Int = 8dip
|
||||
@@ -248,7 +265,9 @@ Private Sub LayoutMainButtons
|
||||
If lblAppName.IsInitialized Then
|
||||
Dim titleTop As Int = pnlClock.Top + pnlClock.Height + 4dip
|
||||
Dim titleHeight As Int = Max(28dip, row1Top - titleTop - 4dip)
|
||||
lblAppName.SetLayout(0, titleTop, Activity.Width, titleHeight)
|
||||
Dim appNameHeight As Int = Max(24dip, titleHeight * 0.58)
|
||||
lblAppName.SetLayout(0, titleTop, Activity.Width, appNameHeight)
|
||||
If lblBrandInfo.IsInitialized Then lblBrandInfo.SetLayout(0, titleTop + appNameHeight - 2dip, Activity.Width, Max(18dip, titleHeight - appNameHeight + 2dip))
|
||||
End If
|
||||
If lblElapsedTime.IsInitialized Then lblElapsedTime.SetLayout(0, labelTop, Activity.Width * 0.58, 28dip)
|
||||
If lblAutoStatus.IsInitialized Then lblAutoStatus.SetLayout(Activity.Width * 0.58, labelTop, Activity.Width * 0.42, 28dip)
|
||||
@@ -296,22 +315,29 @@ Private Sub CreateConfigPage
|
||||
txtCfgPauseEnd = AddConfigField(Localization.T("pause_end"), "13:00", y)
|
||||
y = y + 58dip
|
||||
txtCfgEnd = AddConfigField(Localization.T("work_end"), "17:00", y)
|
||||
y = y + 58dip
|
||||
|
||||
chkIncludePauses.Initialize("chkIncludePauses")
|
||||
chkIncludePauses.Text = Localization.T("include_pause_total")
|
||||
chkIncludePauses.TextSize = 15
|
||||
chkIncludePauses.Checked = IncludePauseInTotal
|
||||
pnlConfig.AddView(chkIncludePauses, 12dip, y, Activity.Width - 24dip, 48dip)
|
||||
|
||||
BtnConfigSave.Initialize("BtnConfigSave")
|
||||
BtnConfigSave.Text = Localization.T("set_config")
|
||||
pnlConfig.AddView(BtnConfigSave, 12dip, y + 70dip, (Activity.Width - 32dip) / 2, 48dip)
|
||||
pnlConfig.AddView(BtnConfigSave, 12dip, y + 56dip, (Activity.Width - 32dip) / 2, 48dip)
|
||||
|
||||
BtnConfigReset.Initialize("BtnConfigReset")
|
||||
BtnConfigReset.Text = Localization.T("reset_config")
|
||||
pnlConfig.AddView(BtnConfigReset, 20dip + ((Activity.Width - 32dip) / 2), y + 70dip, (Activity.Width - 32dip) / 2, 48dip)
|
||||
pnlConfig.AddView(BtnConfigReset, 20dip + ((Activity.Width - 32dip) / 2), y + 56dip, (Activity.Width - 32dip) / 2, 48dip)
|
||||
|
||||
BtnClearToday.Initialize("BtnClearToday")
|
||||
BtnClearToday.Text = Localization.T("clear_today")
|
||||
pnlConfig.AddView(BtnClearToday, 12dip, y + 126dip, Activity.Width - 24dip, 48dip)
|
||||
pnlConfig.AddView(BtnClearToday, 12dip, y + 112dip, Activity.Width - 24dip, 48dip)
|
||||
|
||||
BtnHelpOpen.Initialize("BtnHelpOpen")
|
||||
BtnHelpOpen.Text = "Help"
|
||||
pnlConfig.AddView(BtnHelpOpen, 12dip, y + 182dip, Activity.Width - 24dip, 48dip)
|
||||
BtnHelpOpen.Text = Localization.T("help")
|
||||
pnlConfig.AddView(BtnHelpOpen, 12dip, y + 168dip, Activity.Width - 24dip, 48dip)
|
||||
End Sub
|
||||
|
||||
Private Sub CreateHelpPage
|
||||
@@ -322,14 +348,14 @@ Private Sub CreateHelpPage
|
||||
|
||||
Dim title As Label
|
||||
title.Initialize("")
|
||||
title.Text = "Help"
|
||||
title.Text = Localization.T("help")
|
||||
title.TextSize = 22
|
||||
title.TextColor = Colors.Black
|
||||
title.Gravity = Gravity.CENTER_VERTICAL
|
||||
pnlHelp.AddView(title, 12dip, 8dip, Activity.Width - 110dip, 46dip)
|
||||
|
||||
BtnHelpClose.Initialize("BtnHelpClose")
|
||||
BtnHelpClose.Text = "Close"
|
||||
BtnHelpClose.Text = Localization.T("close")
|
||||
pnlHelp.AddView(BtnHelpClose, Activity.Width - 94dip, 8dip, 84dip, 46dip)
|
||||
|
||||
svHelp.Initialize(0)
|
||||
@@ -450,6 +476,12 @@ Sub BtnConfigReset_Click
|
||||
ResetAutomaticConfig
|
||||
End Sub
|
||||
|
||||
Sub chkIncludePauses_CheckedChange(Checked As Boolean)
|
||||
IncludePauseInTotal = Checked
|
||||
SaveSettings
|
||||
ApplyPauseCountingMode
|
||||
End Sub
|
||||
|
||||
Sub BtnClearToday_Click
|
||||
ClearDisplayedDay
|
||||
End Sub
|
||||
@@ -572,6 +604,7 @@ Private Sub CloseCurrentSegment(FinalTime As Long)
|
||||
If duration <= 0 Then Return
|
||||
If PauseActive Then
|
||||
RecordSegment(CurrentSegmentStart, duration, Colors.Yellow, True)
|
||||
If IncludePauseInTotal Then ProductiveElapsedMs = ProductiveElapsedMs + duration
|
||||
Else
|
||||
RecordSegment(CurrentSegmentStart, duration, GetCurrentWorkColor, False)
|
||||
ProductiveElapsedMs = ProductiveElapsedMs + duration
|
||||
@@ -599,6 +632,20 @@ Private Sub AddStatsSegment(DayStart As Long, DayKey As Long, SegmentStart As Lo
|
||||
End Sub
|
||||
|
||||
Private Sub ProcessActivePauseSegment(now As Long)
|
||||
If IncludePauseInTotal Then
|
||||
Dim remainingToLimit As Long = MaxProductiveMs - ProductiveElapsedMs
|
||||
If remainingToLimit <= 0 Then
|
||||
EndSession(CurrentSegmentStart, True)
|
||||
Log("Workday stopped automatically during pause: 9-hour limit reached.")
|
||||
Return
|
||||
End If
|
||||
Dim limitTime As Long = CurrentSegmentStart + remainingToLimit
|
||||
If limitTime <= GetNextMidnight(CurrentSegmentStart) And now >= limitTime Then
|
||||
EndSession(limitTime, True)
|
||||
Log("Workday stopped automatically during pause: 9-hour limit reached.")
|
||||
Return
|
||||
End If
|
||||
End If
|
||||
Dim midnight As Long = GetNextMidnight(CurrentSegmentStart)
|
||||
If now >= midnight Then
|
||||
EndSession(midnight, True)
|
||||
@@ -675,6 +722,7 @@ End Sub
|
||||
|
||||
Private Sub GetDisplayedProductiveElapsed(now As Long) As Long
|
||||
Dim total As Long = ProductiveElapsedMs
|
||||
If SessionActive And PauseActive And IncludePauseInTotal Then total = total + Max(0, now - CurrentSegmentStart)
|
||||
If SessionActive And PauseActive = False Then total = total + Max(0, now - CurrentSegmentStart)
|
||||
Return total
|
||||
End Sub
|
||||
@@ -685,6 +733,7 @@ Private Sub GetProductiveTotalForWorkDay(DayKey As Long) As Long
|
||||
If row.Get("dayKey") = DayKey Then
|
||||
Dim category As String = row.Get("category")
|
||||
If category = "lavoro" Or category = "straordinario" Then total = total + row.Get("duration")
|
||||
If IncludePauseInTotal And category = "pausa" Then total = total + row.Get("duration")
|
||||
End If
|
||||
Next
|
||||
Return total
|
||||
@@ -698,6 +747,7 @@ Private Sub LoadWorkDaySegmentsIntoClock(DayKey As Long)
|
||||
Dim duration As Long = row.Get("duration")
|
||||
If category = "pausa" Then
|
||||
myClock.AddSegment(duration, Colors.Yellow, True)
|
||||
If IncludePauseInTotal Then productiveCursor = productiveCursor + duration
|
||||
Else
|
||||
AddDisplayProductiveSegment(duration, productiveCursor)
|
||||
productiveCursor = productiveCursor + duration
|
||||
@@ -839,8 +889,7 @@ Private Sub RestoreActiveState
|
||||
CurrentWorkDayStart = lines.Get(0)
|
||||
CurrentWorkDayKey = CurrentWorkDayStart
|
||||
CurrentSegmentStart = lines.Get(1)
|
||||
ProductiveElapsedMs = lines.Get(2)
|
||||
ProductiveElapsedMs = Max(ProductiveElapsedMs, GetProductiveTotalForWorkDay(CurrentWorkDayKey))
|
||||
ProductiveElapsedMs = GetProductiveTotalForWorkDay(CurrentWorkDayKey)
|
||||
PauseActive = lines.Get(3)
|
||||
CurrentSegmentColor = lines.Get(4)
|
||||
|
||||
@@ -1023,7 +1072,9 @@ Private Sub SetAutomaticConfig
|
||||
Return
|
||||
End If
|
||||
Dim workMinutes As Int = (pauseStartMinutes - startMinutes) + (endMinutes - pauseEndMinutes)
|
||||
If workMinutes > 480 Then
|
||||
Dim countedMinutes As Int = workMinutes
|
||||
If IncludePauseInTotal Then countedMinutes = endMinutes - startMinutes
|
||||
If countedMinutes > 480 Then
|
||||
ToastMessageShow(Localization.T("work_exceed"), False)
|
||||
Return
|
||||
End If
|
||||
@@ -1059,6 +1110,13 @@ Private Sub SaveAutoConfig
|
||||
File.WriteList(File.DirInternal, AutoConfigFileName, lines)
|
||||
End Sub
|
||||
|
||||
Private Sub SaveSettings
|
||||
Dim lines As List
|
||||
lines.Initialize
|
||||
lines.Add(IncludePauseInTotal)
|
||||
File.WriteList(File.DirInternal, SettingsFileName, lines)
|
||||
End Sub
|
||||
|
||||
Private Sub LoadAutoConfig
|
||||
AutoModeEnabled = False
|
||||
AutoState = "stopped"
|
||||
@@ -1076,6 +1134,14 @@ Private Sub LoadAutoConfig
|
||||
txtCfgEnd.Text = MinutesToTime(AutoEndMinutes)
|
||||
End Sub
|
||||
|
||||
Private Sub LoadSettings
|
||||
IncludePauseInTotal = True
|
||||
If File.Exists(File.DirInternal, SettingsFileName) = False Then Return
|
||||
Dim lines As List = File.ReadList(File.DirInternal, SettingsFileName)
|
||||
If lines.Size = 0 Then Return
|
||||
IncludePauseInTotal = ParseBooleanValue(lines.Get(0), True)
|
||||
End Sub
|
||||
|
||||
Private Sub ParseTimeToMinutes(Value As String) As Int
|
||||
Dim parts() As String = Regex.Split(":", Value.Trim)
|
||||
If parts.Length <> 2 Then Return -1
|
||||
@@ -1192,6 +1258,57 @@ Private Sub ClearDisplayedDay
|
||||
ToastMessageShow(Localization.T("today_cleared"), False)
|
||||
End Sub
|
||||
|
||||
Private Sub ApplyPauseCountingMode
|
||||
If chkIncludePauses.IsInitialized And chkIncludePauses.Checked <> IncludePauseInTotal Then chkIncludePauses.Checked = IncludePauseInTotal
|
||||
Dim now As Long = DateTime.Now
|
||||
If SessionActive Then
|
||||
ProductiveElapsedMs = GetProductiveTotalForWorkDay(CurrentWorkDayKey)
|
||||
If PauseActive Then
|
||||
ProcessActivePauseSegment(now)
|
||||
If SessionActive = False Then
|
||||
UpdateElapsedLabel
|
||||
Return
|
||||
End If
|
||||
CurrentSegmentColor = Colors.Yellow
|
||||
myClock.ClearSegments
|
||||
LoadWorkDaySegmentsIntoClock(CurrentWorkDayKey)
|
||||
myClock.SetActiveSegment(Max(0, now - CurrentSegmentStart), Colors.Yellow, True)
|
||||
Else
|
||||
ProcessActiveWorkSegment(now)
|
||||
If SessionActive = False Then
|
||||
UpdateElapsedLabel
|
||||
Return
|
||||
End If
|
||||
CurrentSegmentColor = GetCurrentWorkColor
|
||||
myClock.ClearSegments
|
||||
LoadWorkDaySegmentsIntoClock(CurrentWorkDayKey)
|
||||
myClock.SetActiveSegment(Max(0, now - CurrentSegmentStart), CurrentSegmentColor, False)
|
||||
End If
|
||||
SaveActiveState
|
||||
Else
|
||||
If CurrentWorkDayKey > 0 Then
|
||||
ProductiveElapsedMs = GetProductiveTotalForWorkDay(CurrentWorkDayKey)
|
||||
CurrentSegmentColor = GetCurrentWorkColor
|
||||
myClock.ClearSegments
|
||||
LoadWorkDaySegmentsIntoClock(CurrentWorkDayKey)
|
||||
myClock.ClearActiveSegment
|
||||
Else
|
||||
RestoreLastClosedDayState
|
||||
End If
|
||||
End If
|
||||
UpdateElapsedLabel
|
||||
UpdateMainControlState
|
||||
End Sub
|
||||
|
||||
Private Sub ParseBooleanValue(Value As Object, DefaultValue As Boolean) As Boolean
|
||||
If Value = Null Then Return DefaultValue
|
||||
Dim s As String = Value
|
||||
s = s.Trim.ToLowerCase
|
||||
If s = "true" Then Return True
|
||||
If s = "false" Then Return False
|
||||
Return DefaultValue
|
||||
End Sub
|
||||
|
||||
Private Sub AddStatsRow(y As Int, DateText As String, WorkText As String, PauseText As String, OvertimeText As String, Header As Boolean)
|
||||
Dim rowHeight As Int = 30dip
|
||||
Dim col1 As Int = 104dip
|
||||
@@ -1220,6 +1337,10 @@ End Sub
|
||||
Private Sub BuildHelpText As String
|
||||
Return $"Overtime Guard
|
||||
|
||||
By AI Team
|
||||
aiteam.ai
|
||||
AI agents and software systems
|
||||
|
||||
Purpose
|
||||
Overtime Guard is a minimal workday timer. It helps you track work time, breaks, and overtime without projects, tasks, or extra screens.
|
||||
|
||||
@@ -1239,7 +1360,8 @@ Clock colors
|
||||
- Red: overtime after 8 productive hours.
|
||||
|
||||
How timing works
|
||||
- Breaks do not count toward the 8 working hours.
|
||||
- Breaks always stay visible in yellow.
|
||||
- In Config you can choose whether break time is included in the total 8-hour count.
|
||||
- Overtime starts only after 8 productive work hours.
|
||||
- Red overtime can continue for up to 1 extra hour.
|
||||
- At 9 total productive hours, recording stops automatically.
|
||||
@@ -1258,6 +1380,7 @@ Statistics
|
||||
|
||||
Automatic mode
|
||||
- In Config you can set work start, pause start, pause end, and work end.
|
||||
- The Include breaks in total option also affects automatic mode.
|
||||
- Set config enables automatic mode.
|
||||
- In automatic mode, Start and End on the main screen are disabled.
|
||||
- Reset config disables automatic mode and returns control to manual mode.
|
||||
|
||||
Reference in New Issue
Block a user