Files
adb/capture.go
2022-07-19 17:50:10 -07:00

184 lines
4.1 KiB
Go

package adb
import (
"context"
"log"
"regexp"
"strconv"
"strings"
"time"
)
// CaptureSequence allows you to record a series of taps and swipes on the screen to replay later
//
// This function is useful if you need to run an obscure shell command or if
// you require functionality not provided by the exposed functions here.
// Instead of using Shell, please consider submitting a PR with the functionality
// you require.
type SequenceSleep struct {
Duration time.Duration
}
func (s SequenceSleep) Play(d Device, ctx context.Context) error {
// TODO check if context is expired
time.Sleep(s.Duration)
return nil
}
func (s SequenceSleep) Length() time.Duration {
return s.Duration
}
type SequenceTap struct {
X int
Y int
}
func (s SequenceTap) Play(d Device, ctx context.Context) error {
return d.Tap(ctx, s.X, s.Y)
}
func (s SequenceTap) Length() time.Duration {
return 0
}
type SequenceSwipe struct {
X1 int
Y1 int
X2 int
Y2 int
Duration time.Duration
}
func (s SequenceSwipe) Play(d Device, ctx context.Context) error {
return d.Swipe(ctx, s.X1, s.Y1, s.X2, s.Y2, s.Duration)
}
func (s SequenceSwipe) Length() time.Duration {
return s.Duration
}
type Input interface {
Play(d Device, ctx context.Context) error
Length() time.Duration
}
type TapSequence struct {
Events []Input
}
// ShortenSleep allows you to shorten all the sleep times between tap and swipe events.
//
// Provide a scalar value to divide the sleeps by. Providing `2` will halve all
// sleep durations in the TapSequence. Swipe durations and tap durations are
// unaffected.
func (t TapSequence) ShortenSleep(scalar int) TapSequence {
seq := []Input{}
for _, s := range t.Events {
switch y := s.(type) {
case SequenceSleep:
y.Duration = y.Duration / time.Duration(scalar)
seq = append(seq, y)
default:
seq = append(seq, s)
}
}
t.Events = seq
return t
}
// GetLength returns the length of all Input events inside of a given TapSequence
//
// This function is useful for devermining how long a context timeout should
// last when calling ReplayTapSequence
func (t TapSequence) GetLength() time.Duration {
duration := time.Duration(0)
for _, x := range t.Events {
duration += x.Length()
}
return duration * 110 / 100
}
func (d Device) ReplayTapSequence(ctx context.Context, t TapSequence) error {
for _, e := range t.Events {
err := e.Play(d, ctx)
if err != nil {
return err
}
}
return nil
}
//CaptureSequence allows you to capture and replay screen taps and swipes.
//
// ctx, cancelFunc := context.WithCancel(context.TODO())
//
// go dev.CaptureSequence(ctx)
// time.Sleep(time.Second * 30)
// cancelFunc()
func (d Device) CaptureSequence(ctx context.Context) (t TapSequence, err error) {
// this command will never finish, and always returns error code 130 if successful
stdout, _, errCode, err := execute(ctx, []string{"shell", "getevent", "-tl"})
if errCode != 130 {
log.Printf("Expected error code 130, but got %d\n", errCode)
}
if stdout == "" {
return TapSequence{}, ErrStdoutEmpty
}
t.Events = parseGetEvent(stdout)
return TapSequence{}, nil
}
type Event struct {
TimeStamp time.Time
DevicePath string
Type string
Key string
Value string
}
func parseGetEvent(input string) (events []Input) {
lines := strings.Split(input, "\n")
lines = trimDeviceDescriptors(lines)
// Trim off the beginning with device descriptors
return
}
func parseInputToEvent(input []string) []Event {
var e []Event
r := regexp.MustCompile(`\[\W*(\d+\.\d+)]`)
for _, line := range input {
var l Event
timeStr := r.FindStringSubmatch(line)
if len(timeStr) != 2 {
continue
}
f, err := strconv.ParseFloat(timeStr[1], 32)
if err != nil {
continue
}
msec := int64(f * 1000)
l.TimeStamp = time.UnixMilli(msec)
e = append(e, l)
}
// fmt.Println(e[0].TimeStamp.Sub(e[len(e)-1].TimeStamp))
return e
}
func trimDeviceDescriptors(input []string) []string {
start := 0
for i, line := range input {
if strings.Contains(line, "DOWN") {
start = i
break
}
}
for i := range input {
input[i] = strings.TrimSpace(input[i])
}
return input[start:]
}