-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from prashantgupta24/dev1
adding activity type to heartbeat
- Loading branch information
Showing
12 changed files
with
374 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
example-start: | ||
go run example/tracker.go | ||
go run example/example.go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package service | ||
|
||
import ( | ||
"log" | ||
"time" | ||
|
||
"github.com/go-vgo/robotgo" | ||
"github.com/prashantgupta24/activity-tracker/pkg/activity" | ||
) | ||
|
||
type MouseClickHandler struct { | ||
tickerCh chan struct{} | ||
} | ||
|
||
func (m *MouseClickHandler) Start(activityCh chan *activity.Type) { | ||
m.tickerCh = make(chan struct{}) | ||
registrationFree := make(chan struct{}) | ||
|
||
go func() { | ||
go addMouseClickRegistration(activityCh, registrationFree) //run once before first check | ||
for range m.tickerCh { | ||
log.Printf("mouse clicker checked at : %v\n", time.Now()) | ||
select { | ||
case _, ok := <-registrationFree: | ||
if ok { | ||
//log.Printf("registration free for mouse click \n") | ||
go addMouseClickRegistration(activityCh, registrationFree) | ||
} else { | ||
//log.Printf("error : channel closed \n") | ||
return | ||
} | ||
default: | ||
//log.Printf("registration is busy for mouse click handler, do nothing\n") | ||
} | ||
} | ||
log.Printf("stopping click handler") | ||
return | ||
}() | ||
} | ||
|
||
func (m *MouseClickHandler) Trigger() { | ||
//doing it the non-blocking sender way | ||
select { | ||
case m.tickerCh <- struct{}{}: | ||
default: | ||
//service is blocked, handle it somehow? | ||
} | ||
} | ||
func (m *MouseClickHandler) Close() { | ||
close(m.tickerCh) | ||
} | ||
|
||
func addMouseClickRegistration(activityCh chan *activity.Type, registrationFree chan struct{}) { | ||
log.Printf("adding reg \n") | ||
mleft := robotgo.AddEvent("mleft") | ||
if mleft { | ||
//log.Printf("mleft clicked \n") | ||
activityCh <- &activity.Type{ | ||
ActivityType: activity.MOUSE_LEFT_CLICK, | ||
} | ||
registrationFree <- struct{}{} | ||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package service | ||
|
||
import ( | ||
"log" | ||
"time" | ||
|
||
"github.com/prashantgupta24/activity-tracker/internal/pkg/mouse" | ||
"github.com/prashantgupta24/activity-tracker/pkg/activity" | ||
) | ||
|
||
type MouseCursorHandler struct { | ||
tickerCh chan struct{} | ||
} | ||
|
||
type cursorInfo struct { | ||
didCursorMove bool | ||
currentMousePos *mouse.Position | ||
} | ||
|
||
func (m *MouseCursorHandler) Start(activityCh chan *activity.Type) { | ||
|
||
m.tickerCh = make(chan struct{}) | ||
|
||
go func() { | ||
lastMousePos := mouse.GetPosition() | ||
for range m.tickerCh { | ||
log.Printf("mouse cursor checked at : %v\n", time.Now()) | ||
commCh := make(chan *cursorInfo) | ||
go checkCursorChange(commCh, lastMousePos) | ||
select { | ||
case cursorInfo := <-commCh: | ||
if cursorInfo.didCursorMove { | ||
activityCh <- &activity.Type{ | ||
ActivityType: activity.MOUSE_CURSOR_MOVEMENT, | ||
} | ||
lastMousePos = cursorInfo.currentMousePos | ||
} | ||
case <-time.After(timeout * time.Millisecond): | ||
//timeout, do nothing | ||
log.Printf("timeout happened after %vms while checking mouse cursor handler", timeout) | ||
} | ||
} | ||
log.Printf("stopping cursor handler") | ||
return | ||
}() | ||
} | ||
|
||
func (m *MouseCursorHandler) Trigger() { | ||
//doing it the non-blocking sender way | ||
select { | ||
case m.tickerCh <- struct{}{}: | ||
default: | ||
//service is blocked, handle it somehow? | ||
} | ||
} | ||
func (m *MouseCursorHandler) Close() { | ||
close(m.tickerCh) | ||
} | ||
|
||
func checkCursorChange(commCh chan *cursorInfo, lastMousePos *mouse.Position) { | ||
currentMousePos := mouse.GetPosition() | ||
//log.Printf("current mouse position: %v\n", currentMousePos) | ||
//log.Printf("last mouse position: %v\n", lastMousePos) | ||
if currentMousePos.MouseX == lastMousePos.MouseX && | ||
currentMousePos.MouseY == lastMousePos.MouseY { | ||
commCh <- &cursorInfo{ | ||
didCursorMove: false, | ||
currentMousePos: nil, | ||
} | ||
} else { | ||
commCh <- &cursorInfo{ | ||
didCursorMove: true, | ||
currentMousePos: currentMousePos, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package service | ||
|
||
import ( | ||
"log" | ||
"time" | ||
|
||
"github.com/go-vgo/robotgo" | ||
"github.com/prashantgupta24/activity-tracker/pkg/activity" | ||
) | ||
|
||
type ScreenChangeHandler struct { | ||
tickerCh chan struct{} | ||
} | ||
|
||
type screenInfo struct { | ||
didScreenChange bool | ||
currentPixelColor string | ||
} | ||
|
||
func (s *ScreenChangeHandler) Start(activityCh chan *activity.Type) { | ||
|
||
s.tickerCh = make(chan struct{}) | ||
|
||
go func() { | ||
screenSizeX, screenSizeY := robotgo.GetScreenSize() | ||
pixelPointX := int(screenSizeX / 2) | ||
pixelPointY := int(screenSizeY / 2) | ||
lastPixelColor := robotgo.GetPixelColor(pixelPointX, pixelPointY) | ||
for range s.tickerCh { | ||
log.Printf("screen change checked at : %v\n", time.Now()) | ||
commCh := make(chan *screenInfo) | ||
go checkScreenChange(commCh, lastPixelColor, pixelPointX, pixelPointY) | ||
select { | ||
case screenInfo := <-commCh: | ||
if screenInfo.didScreenChange { | ||
activityCh <- &activity.Type{ | ||
ActivityType: activity.SCREEN_CHANGE, | ||
} | ||
lastPixelColor = screenInfo.currentPixelColor | ||
} | ||
case <-time.After(timeout * time.Millisecond): | ||
//timeout, do nothing | ||
log.Printf("timeout happened after %vms while checking screen change handler", timeout) | ||
} | ||
} | ||
log.Printf("stopping screen change handler") | ||
return | ||
}() | ||
} | ||
|
||
func (s *ScreenChangeHandler) Trigger() { | ||
//doing it the non-blocking sender way | ||
select { | ||
case s.tickerCh <- struct{}{}: | ||
default: | ||
//service is blocked, handle it somehow? | ||
} | ||
} | ||
func (s *ScreenChangeHandler) Close() { | ||
close(s.tickerCh) | ||
} | ||
|
||
func checkScreenChange(commCh chan *screenInfo, lastPixelColor string, pixelPointX, pixelPointY int) { | ||
currentPixelColor := robotgo.GetPixelColor(pixelPointX, pixelPointY) | ||
// log.Printf("current pixel color: %v\n", currentPixelColor) | ||
// log.Printf("last pixel color: %v\n", lastPixelColor) | ||
//robotgo.MoveMouse(pixelPointX, pixelPointY) | ||
if lastPixelColor != currentPixelColor { | ||
commCh <- &screenInfo{ | ||
didScreenChange: true, | ||
currentPixelColor: currentPixelColor, | ||
} | ||
} else { //comment this section out to test timeout logic | ||
commCh <- &screenInfo{ | ||
didScreenChange: false, | ||
currentPixelColor: "", | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package service | ||
|
||
import "github.com/prashantgupta24/activity-tracker/pkg/activity" | ||
|
||
const ( | ||
timeout = 100 //ms | ||
) | ||
|
||
type Instance interface { | ||
Start(chan *activity.Type) | ||
Trigger() | ||
Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package activity | ||
|
||
type activityType string | ||
|
||
const ( | ||
MOUSE_CURSOR_MOVEMENT activityType = "cursor-move" | ||
MOUSE_LEFT_CLICK activityType = "left-mouse-click" | ||
SCREEN_CHANGE activityType = "screen-change" | ||
) | ||
|
||
type Type struct { | ||
ActivityType activityType | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package tracker | ||
|
||
import ( | ||
"log" | ||
"time" | ||
|
||
"github.com/prashantgupta24/activity-tracker/internal/pkg/service" | ||
"github.com/prashantgupta24/activity-tracker/pkg/activity" | ||
) | ||
|
||
const ( | ||
preHeartbeatTime = time.Millisecond * 100 | ||
) | ||
|
||
func (tracker *Instance) Start() (heartbeatCh chan *Heartbeat, quit chan struct{}) { | ||
|
||
//register service handlers | ||
tracker.registerHandlers(&service.MouseClickHandler{}, &service.MouseCursorHandler{}, | ||
&service.ScreenChangeHandler{}) | ||
|
||
//returned channels | ||
heartbeatCh = make(chan *Heartbeat, 1) | ||
quit = make(chan struct{}) | ||
|
||
go func(tracker *Instance) { | ||
timeToCheck := tracker.TimeToCheck | ||
//tickers | ||
tickerHeartbeat := time.NewTicker(time.Second * timeToCheck) | ||
tickerWorker := time.NewTicker(time.Second*timeToCheck - preHeartbeatTime) | ||
|
||
activities := makeActivityMap() | ||
|
||
for { | ||
select { | ||
case <-tickerWorker.C: | ||
log.Printf("tracker worker working at %v\n", time.Now()) | ||
//time to trigger all registered services | ||
for service := range tracker.services { | ||
service.Trigger() | ||
} | ||
case <-tickerHeartbeat.C: | ||
log.Printf("tracker heartbeat checking at %v\n", time.Now()) | ||
var heartbeat *Heartbeat | ||
if len(activities) == 0 { | ||
//log.Printf("no activity detected in the last %v seconds ...\n", int(timeToCheck)) | ||
heartbeat = &Heartbeat{ | ||
IsActivity: false, | ||
Activity: nil, | ||
Time: time.Now(), | ||
} | ||
} else { | ||
//log.Printf("activity detected in the last %v seconds ...\n", int(timeToCheck)) | ||
heartbeat = &Heartbeat{ | ||
IsActivity: true, | ||
Activity: activities, | ||
Time: time.Now(), | ||
} | ||
|
||
} | ||
heartbeatCh <- heartbeat | ||
activities = makeActivityMap() //reset the activities map | ||
case activity := <-tracker.activityCh: | ||
activities[activity] = time.Now() | ||
//log.Printf("activity received: %#v\n", activity) | ||
case <-quit: | ||
log.Printf("stopping activity tracker\n") | ||
//close all services for a clean exit | ||
for service := range tracker.services { | ||
service.Close() | ||
} | ||
return | ||
} | ||
} | ||
}(tracker) | ||
|
||
return heartbeatCh, quit | ||
} | ||
|
||
func makeActivityMap() map[*activity.Type]time.Time { | ||
activityMap := make(map[*activity.Type]time.Time) | ||
return activityMap | ||
} | ||
|
||
func (tracker *Instance) registerHandlers(services ...service.Instance) { | ||
|
||
tracker.services = make(map[service.Instance]bool) | ||
tracker.activityCh = make(chan *activity.Type, len(services)) // number based on types of activities being tracked | ||
|
||
for _, service := range services { | ||
service.Start(tracker.activityCh) | ||
if _, ok := tracker.services[service]; !ok { //duplicate registration prevention | ||
tracker.services[service] = true | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.