196 lines
3.8 KiB
Go
196 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
adb "github.com/zach-klippenstein/goadb"
|
|
goadb "github.com/zach-klippenstein/goadb"
|
|
"gitlab.com/tslocum/cbind"
|
|
"gitlab.com/tslocum/cview"
|
|
)
|
|
|
|
const defaultRemotePath = "/storage/emulated/0"
|
|
|
|
type appConfig struct {
|
|
Input map[string][]string // Keybinds
|
|
}
|
|
|
|
var config = &appConfig{}
|
|
|
|
var (
|
|
showHidden bool
|
|
|
|
localPath string
|
|
remotePath string
|
|
|
|
connectAddress string
|
|
|
|
localEntries []*adb.DirEntry
|
|
localEntriesShown []*adb.DirEntry
|
|
remoteEntries []*adb.DirEntry
|
|
remoteEntriesShown []*adb.DirEntry
|
|
|
|
localLock sync.Mutex
|
|
remoteLock sync.Mutex
|
|
|
|
bridge *adbConn
|
|
|
|
done = make(chan bool)
|
|
)
|
|
|
|
const (
|
|
actionPreviousField = "previous-field"
|
|
actionNextField = "next-field"
|
|
actionExit = "exit"
|
|
)
|
|
|
|
var actionHandlers = map[string]func(*tcell.EventKey) *tcell.EventKey{
|
|
actionPreviousField: previousField,
|
|
actionNextField: nextField,
|
|
actionExit: exit,
|
|
}
|
|
|
|
func setKeyBinds() error {
|
|
if len(config.Input) == 0 {
|
|
setDefaultKeyBinds()
|
|
}
|
|
|
|
for a, keys := range config.Input {
|
|
a = strings.ToLower(a)
|
|
handler := actionHandlers[a]
|
|
if handler == nil {
|
|
return fmt.Errorf("failed to set keybind for %s: unknown action", a)
|
|
}
|
|
|
|
for _, k := range keys {
|
|
mod, key, ch, err := cbind.Decode(k)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set keybind %s for %s: %s", k, a, err)
|
|
}
|
|
|
|
if key == tcell.KeyRune {
|
|
inputConfig.SetRune(mod, ch, handler)
|
|
} else {
|
|
inputConfig.SetKey(mod, key, handler)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func setDefaultKeyBinds() {
|
|
config.Input = map[string][]string{
|
|
actionPreviousField: {"Backtab"},
|
|
actionNextField: {"Tab"},
|
|
actionExit: {"Alt+q"},
|
|
}
|
|
}
|
|
|
|
func setDeviceFunc(device *goadb.DeviceInfo) func(index int, option *cview.DropDownOption) {
|
|
return func(index int, option *cview.DropDownOption) {
|
|
go func() {
|
|
localLock.Lock()
|
|
remoteLock.Lock()
|
|
|
|
err := bridge.setDevice(device)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
remoteLock.Unlock()
|
|
localLock.Unlock()
|
|
|
|
browseRemote(remotePath)
|
|
}()
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
log.SetFlags(0)
|
|
log.SetPrefix("")
|
|
|
|
flag.StringVar(&localPath, "local", "", "local starting directory")
|
|
flag.StringVar(&remotePath, "remote", "", "remote starting directory")
|
|
flag.BoolVar(&showHidden, "hidden", false, "show hidden files and folders")
|
|
flag.StringVar(&connectAddress, "connect", "", "connect to device over network")
|
|
flag.Parse()
|
|
|
|
err := run()
|
|
if err != nil {
|
|
log.Fatalf("failed to start adbfm: %s", err)
|
|
}
|
|
}
|
|
|
|
func run() error {
|
|
if localPath == "" {
|
|
homeDir, err := os.UserHomeDir()
|
|
if err == nil && homeDir != "" {
|
|
localPath = homeDir
|
|
}
|
|
}
|
|
if localPath == "" {
|
|
return errors.New("local path must be specified with --local")
|
|
}
|
|
|
|
if remotePath == "" {
|
|
remotePath = defaultRemotePath
|
|
}
|
|
|
|
var err error
|
|
bridge, err = connectBridge("", 0, "")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect to ADB server: %s", err)
|
|
}
|
|
|
|
if connectAddress != "" {
|
|
if !strings.ContainsRune(connectAddress, ':') {
|
|
connectAddress += ":5555"
|
|
}
|
|
|
|
host, p, err := net.SplitHostPort(connectAddress)
|
|
if err != nil {
|
|
log.Fatalf("invalid device address %s: %s", connectAddress, err)
|
|
}
|
|
|
|
port, err := strconv.Atoi(p)
|
|
if err != nil {
|
|
log.Fatalf("invalid device address %s: %s", connectAddress, err)
|
|
}
|
|
|
|
err = bridge.connectDevice(host, port)
|
|
if err != nil {
|
|
log.Fatalf("failed to connect to device %s: %s", connectAddress, err)
|
|
}
|
|
}
|
|
|
|
err = setKeyBinds()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set keybinds: %s", err)
|
|
}
|
|
|
|
initTUI()
|
|
|
|
go browseLocal(localPath)
|
|
|
|
go func() {
|
|
if err := app.Run(); err != nil {
|
|
log.Fatalf("failed to run application: %s", err)
|
|
}
|
|
|
|
done <- true
|
|
}()
|
|
|
|
<-done
|
|
return nil
|
|
}
|