Files
adb/examples/tapRecorder/main.go

202 lines
4.6 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"os/signal"
"syscall"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/taigrr/adb"
)
var (
command string
file string
chosen adb.Serial
titleStyle = lipgloss.NewStyle().MarginLeft(2)
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4)
)
func main() {
flag.StringVar(&command, "command", "rec", "rec or play")
flag.StringVar(&file, "file", "taps.json", "Name of the file to save taps to or to play from")
flag.Parse()
if command != "play" && command != "rec" {
flag.PrintDefaults()
os.Exit(1)
}
sigChan := make(chan os.Signal)
ctx, cancel := context.WithCancel(context.Background())
go func() {
<-sigChan
cancel()
}()
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
devs, err := adb.Devices(ctx)
if err != nil {
fmt.Printf("Error enumerating devices: %v\n", err)
return
}
devNames := []adb.Serial{}
for _, dev := range devs {
devNames = append(devNames, dev.SerialNo)
}
selected := chooseDev(devNames)
for _, dev := range devs {
if dev.SerialNo != selected {
continue
}
if !dev.IsAuthorized {
fmt.Printf("Dev `%s` is not authorized, authorize it to continue.\n", dev.SerialNo)
continue
}
switch command {
case "rec":
fmt.Println("Recording taps now. Hit ctrl+c to stop.")
t, err := dev.CaptureSequence(ctx)
if err != nil {
fmt.Printf("Error capturing sequence: %v\n", err)
return
}
b, _ := json.Marshal(t)
f, err := os.Create(file)
if err != nil {
fmt.Printf("Error creating tap file %s: %v", file, err)
return
}
defer f.Close()
f.Write(b)
case "play":
fmt.Println("Replaying taps now. Hit ctrl+c to stop.")
f, err := os.Open(file)
if err != nil {
fmt.Printf("Error opening tap file %s: %v", file, err)
return
}
defer f.Close()
var b bytes.Buffer
b.ReadFrom(f)
t, err := adb.TapSequenceFromJSON(b.Bytes())
if err != nil {
fmt.Printf("Error parsing tap file %s: %v", file, err)
return
}
dev.ReplayTapSequence(ctx, t)
}
}
}
func NewModel(devs []adb.Serial) Model {
var m Model
items := []list.Item{}
for _, d := range devs {
items = append(items, DevEntry(d))
}
m.List = list.New(items, itemDelegate{}, 0, len(devs)+15)
return m
}
func chooseDev(devs []adb.Serial) adb.Serial {
if len(devs) == 0 {
return ""
}
if len(devs) == 1 {
return devs[0]
}
m := NewModel(devs)
m.List.Title = "Which device?"
m.List.SetShowStatusBar(false)
m.List.SetFilteringEnabled(false)
m.List.Styles.Title = titleStyle
m.List.Styles.PaginationStyle = paginationStyle
m.List.Styles.HelpStyle = helpStyle
if err := tea.NewProgram(m).Start(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
return adb.Serial(chosen)
}
type Model struct {
List list.Model
quitting bool
Choice DevEntry
}
type DevEntry adb.Serial
func (d DevEntry) FilterValue() string {
return ""
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.List.SetWidth(msg.Width)
return m, nil
case tea.KeyMsg:
switch keypress := msg.String(); keypress {
case "ctrl+c":
m.quitting = true
return m, tea.Quit
case "enter":
i, ok := m.List.SelectedItem().(DevEntry)
if ok {
chosen = adb.Serial(i)
}
return m, tea.Quit
}
}
var cmd tea.Cmd
m.List, cmd = m.List.Update(msg)
return m, cmd
}
func (m Model) View() string {
if chosen != "" {
return quitTextStyle.Render("Chosen device: " + string(chosen))
}
return "\n" + m.List.View()
}
type itemDelegate struct{}
func (d itemDelegate) Height() int { return 1 }
func (d itemDelegate) Spacing() int { return 0 }
func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
i, ok := listItem.(DevEntry)
if !ok {
return
}
str := fmt.Sprintf("%d. %s", index+1, i)
fn := itemStyle.Render
if index == m.Index() {
fn = func(s string) string {
return selectedItemStyle.Render("> " + s)
}
}
fmt.Fprint(w, fn(str))
}