Add local and remote folder browsing
This commit is contained in:
parent
f61003972c
commit
dc78a21119
|
@ -4,6 +4,8 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"gitlab.com/tslocum/cbind"
|
||||
|
@ -23,13 +25,13 @@ var (
|
|||
)
|
||||
|
||||
func initTUI() error {
|
||||
devices, err := adb.ListDevices()
|
||||
devices, err := bridge.ListDevices()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to list devices: %s", err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
log.Fatal("connect to a device via ADB before launching")
|
||||
log.Fatal("failed to start adbfm: please connect to a device via ADB before launching")
|
||||
}
|
||||
|
||||
/*cview.Styles.TitleColor = tcell.ColorDefault
|
||||
|
@ -42,7 +44,7 @@ func initTUI() error {
|
|||
|
||||
devicesDropDown = cview.NewDropDown().SetLabel(" Device: ").SetLabelColor(tcell.ColorDefault)
|
||||
for _, device := range devices {
|
||||
devicesDropDown.AddOption(device.Serial, setDeviceFunc(adb, device))
|
||||
devicesDropDown.AddOption(device.Serial, setDeviceFunc(device))
|
||||
}
|
||||
|
||||
localBuffer = cview.NewList().ShowSecondaryText(false).SetScrollBarVisibility(cview.ScrollBarAlways)
|
||||
|
@ -84,7 +86,11 @@ func focusUpdated() {
|
|||
}
|
||||
|
||||
func browseLocal(p string) {
|
||||
var entries []string
|
||||
localLock.Lock()
|
||||
defer localLock.Unlock()
|
||||
|
||||
localEntries = nil
|
||||
|
||||
var skippedFirst bool
|
||||
err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
|
@ -95,9 +101,10 @@ func browseLocal(p string) {
|
|||
}
|
||||
|
||||
// TODO Store entries with more info
|
||||
entries = append(entries, info.Name())
|
||||
localEntries = append(localEntries, info)
|
||||
|
||||
if info.IsDir() {
|
||||
// Do not search recursively
|
||||
if info.IsDir() && info.Name() != ".." {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
|
@ -106,25 +113,72 @@ func browseLocal(p string) {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
localBuffer.SetTitle(" " + p + " ")
|
||||
sort.Slice(localEntries, func(i, j int) bool {
|
||||
if localEntries[i].Name() == ".." {
|
||||
return true
|
||||
} else if localEntries[i].IsDir() != localEntries[j].IsDir() {
|
||||
return localEntries[i].IsDir()
|
||||
}
|
||||
|
||||
localBuffer.Clear()
|
||||
for _, entry := range entries {
|
||||
localBuffer.AddItem(entry, "", 0, nil)
|
||||
}
|
||||
return strings.ToLower(localEntries[i].Name()) < strings.ToLower(localEntries[j].Name())
|
||||
})
|
||||
|
||||
app.QueueUpdateDraw(func() {
|
||||
localLock.Lock()
|
||||
defer localLock.Unlock()
|
||||
|
||||
localBuffer.SetTitle(" " + p + " ")
|
||||
localBuffer.Clear()
|
||||
|
||||
for _, entry := range localEntries {
|
||||
if len(entry.Name()) > 0 && entry.Name()[0] == '.' && entry.Name() != ".." && !showHidden {
|
||||
continue
|
||||
}
|
||||
|
||||
label := entry.Name()
|
||||
if entry.IsDir() {
|
||||
label += "/"
|
||||
}
|
||||
|
||||
localBuffer.AddItem(label, "", 0, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func browseRemote(p string) {
|
||||
entries, err := adb.DirectoryEntries(p)
|
||||
remoteLock.Lock()
|
||||
defer remoteLock.Unlock()
|
||||
|
||||
app.QueueUpdateDraw(func() {
|
||||
remoteBuffer.SetTitle(" " + remotePath + "... ")
|
||||
})
|
||||
|
||||
var err error
|
||||
remoteEntries, err = bridge.DirectoryEntries(p)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to list files: %s", err)
|
||||
}
|
||||
|
||||
remotePath = p
|
||||
|
||||
go app.QueueUpdateDraw(func() {
|
||||
remoteBuffer.Clear()
|
||||
remoteLock.Lock()
|
||||
defer remoteLock.Unlock()
|
||||
|
||||
remoteBuffer.SetTitle(" " + p + " ")
|
||||
for _, entry := range entries {
|
||||
remoteBuffer.AddItem(entry.Name, "", 0, nil)
|
||||
remoteBuffer.Clear()
|
||||
|
||||
for _, entry := range remoteEntries {
|
||||
if len(entry.Name) > 0 && entry.Name[0] == '.' && entry.Name != ".." && !showHidden {
|
||||
continue
|
||||
}
|
||||
|
||||
label := entry.Name
|
||||
if entry.Mode&os.ModeDir != 0 {
|
||||
label += "/"
|
||||
}
|
||||
|
||||
remoteBuffer.AddItem(label, "", 0, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,13 +1,54 @@
|
|||
package main
|
||||
|
||||
import "github.com/gdamore/tcell"
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
func selectItem(ev *tcell.EventKey) *tcell.EventKey {
|
||||
if currentFocus == 0 {
|
||||
return ev
|
||||
// Select device
|
||||
} else if currentFocus == 1 {
|
||||
localLock.Lock()
|
||||
defer localLock.Unlock()
|
||||
|
||||
if localBuffer.GetItemCount() > 0 {
|
||||
currentItem := localBuffer.GetCurrentItem()
|
||||
if currentItem >= 0 && currentItem < len(localEntries) {
|
||||
newPath := localEntries[currentItem].Name()
|
||||
if localEntries[currentItem].IsDir() {
|
||||
go browseLocal(path.Join(localPath, newPath))
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Fatal(remoteEntries[currentItem])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else { // == 2
|
||||
remoteLock.Lock()
|
||||
defer remoteLock.Unlock()
|
||||
|
||||
if remoteBuffer.GetItemCount() > 0 {
|
||||
currentItem := remoteBuffer.GetCurrentItem()
|
||||
if currentItem >= 0 && currentItem < len(remoteEntries) {
|
||||
newPath := remoteEntries[currentItem].Name
|
||||
if remoteEntries[currentItem].Mode&os.ModeDir != 0 {
|
||||
go browseRemote(path.Join(remotePath, newPath))
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Fatal(remoteEntries[currentItem])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return ev
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
adb "github.com/zach-klippenstein/goadb"
|
||||
goadb "github.com/zach-klippenstein/goadb"
|
||||
"gitlab.com/tslocum/adbfm/pkg/android"
|
||||
"gitlab.com/tslocum/cbind"
|
||||
|
@ -22,10 +25,18 @@ type appConfig struct {
|
|||
var config = &appConfig{}
|
||||
|
||||
var (
|
||||
showHidden bool
|
||||
|
||||
localPath string
|
||||
remotePath string
|
||||
|
||||
adb *android.ADB
|
||||
localEntries []os.FileInfo
|
||||
remoteEntries []*adb.DirEntry
|
||||
|
||||
localLock sync.Mutex
|
||||
remoteLock sync.Mutex
|
||||
|
||||
bridge *android.Bridge
|
||||
|
||||
done = make(chan bool)
|
||||
)
|
||||
|
@ -82,22 +93,41 @@ func setDefaultKeyBinds() {
|
|||
}
|
||||
}
|
||||
|
||||
func setDeviceFunc(adb *android.ADB, device *goadb.DeviceInfo) func() {
|
||||
func setDeviceFunc(device *goadb.DeviceInfo) func() {
|
||||
return func() {
|
||||
err := adb.SetDevice(device)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
go func() {
|
||||
localLock.Lock()
|
||||
remoteLock.Lock()
|
||||
|
||||
browseRemote(remotePath)
|
||||
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.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 != "" {
|
||||
|
@ -105,7 +135,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
if localPath == "" {
|
||||
log.Fatalf("failed to start adbfm: local path must be specified with --local")
|
||||
return errors.New("ocal path must be specified with --local")
|
||||
}
|
||||
|
||||
if remotePath == "" {
|
||||
|
@ -113,22 +143,22 @@ func main() {
|
|||
}
|
||||
|
||||
var err error
|
||||
adb, err = android.NewADB("", 0, "")
|
||||
bridge, err = android.NewBridge("", 0, "")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to connect to ADB server: %s", err)
|
||||
return fmt.Errorf("failed to connect to ADB server: %s", err)
|
||||
}
|
||||
|
||||
err = setKeyBinds()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to set keybinds: %s", err)
|
||||
return fmt.Errorf("failed to set keybinds: %s", err)
|
||||
}
|
||||
|
||||
err = initTUI()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize user interface: %s", err)
|
||||
return fmt.Errorf("failed to initialize user interface: %s", err)
|
||||
}
|
||||
|
||||
browseLocal(localPath)
|
||||
go browseLocal(localPath)
|
||||
|
||||
go func() {
|
||||
if err := app.Run(); err != nil {
|
||||
|
@ -139,4 +169,5 @@ func main() {
|
|||
}()
|
||||
|
||||
<-done
|
||||
return nil
|
||||
}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -7,6 +7,6 @@ require (
|
|||
github.com/stretchr/testify v1.4.0 // indirect
|
||||
github.com/zach-klippenstein/goadb v0.0.0-20170530005145-029cc6bee481
|
||||
gitlab.com/tslocum/cbind v0.1.1
|
||||
gitlab.com/tslocum/cview v1.4.4-0.20200224160604-74844d6d3f06
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
|
||||
gitlab.com/tslocum/cview v1.4.5-0.20200403150121-d2f2938ea070
|
||||
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b // indirect
|
||||
)
|
||||
|
|
14
go.sum
14
go.sum
|
@ -11,6 +11,8 @@ github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
|
|||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
|
@ -22,15 +24,15 @@ github.com/zach-klippenstein/goadb v0.0.0-20170530005145-029cc6bee481 h1:yVrbGOZ
|
|||
github.com/zach-klippenstein/goadb v0.0.0-20170530005145-029cc6bee481/go.mod h1:Drd+klC4FSDx0vKNEQDsSpWX5so04NA7l0vzHqkH8AQ=
|
||||
gitlab.com/tslocum/cbind v0.1.1 h1:JXXtxMWHgWLvoF+QkrvcNvOQ59juy7OE1RhT7hZfdt0=
|
||||
gitlab.com/tslocum/cbind v0.1.1/go.mod h1:rX7vkl0pUSg/yy427MmD1FZAf99S7WwpUlxF/qTpPqk=
|
||||
gitlab.com/tslocum/cview v1.4.4-0.20200224160604-74844d6d3f06 h1:in6osHxntontXHPyNg46ry0rbKDdrZQf4OoNVJLXaQs=
|
||||
gitlab.com/tslocum/cview v1.4.4-0.20200224160604-74844d6d3f06/go.mod h1:+bEf1cg6IoWvL16YHJAKwGGpQf5s/nxXAA7YJr+WOHE=
|
||||
gitlab.com/tslocum/cview v1.4.5-0.20200403150121-d2f2938ea070 h1:JjfUFMVbUbB5R2ozidsmicpIZcRC7h6p6c019WIJODQ=
|
||||
gitlab.com/tslocum/cview v1.4.5-0.20200403150121-d2f2938ea070/go.mod h1:3J8gp3ocr/DDjkyDKcstfZvMGs3BBYZHpjnlpOPzASQ=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b h1:h03Ur1RlPrGTjua4koYdpGl8W0eYo8p1uI9w7RPlkdk=
|
||||
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
|
|
@ -4,6 +4,9 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
adb "github.com/zach-klippenstein/goadb"
|
||||
|
@ -13,16 +16,16 @@ import (
|
|||
const DefaultPort = 5037
|
||||
|
||||
// ADB represents a connection with an ADB server.
|
||||
type ADB struct {
|
||||
type Bridge struct {
|
||||
bridge *adb.Adb
|
||||
device *adb.Device
|
||||
|
||||
*sync.Mutex
|
||||
}
|
||||
|
||||
// NewADB establishes a connection with an ADB server. When host and port are
|
||||
// unspecified they are replaced with localhost and the default port.
|
||||
func NewADB(host string, port int, pathToADB string) (*ADB, error) {
|
||||
// NewBridge establishes a connection with an ADB server. When host and port
|
||||
// are unspecified they are replaced with localhost and the default port.
|
||||
func NewBridge(host string, port int, pathToADB string) (*Bridge, error) {
|
||||
bridge, err := adb.NewWithConfig(adb.ServerConfig{
|
||||
PathToAdb: pathToADB,
|
||||
Host: host,
|
||||
|
@ -39,11 +42,11 @@ func NewADB(host string, port int, pathToADB string) (*ADB, error) {
|
|||
return nil, fmt.Errorf("failed to get server version: %s", err)
|
||||
}
|
||||
|
||||
a := ADB{bridge: bridge, Mutex: new(sync.Mutex)}
|
||||
a := Bridge{bridge: bridge, Mutex: new(sync.Mutex)}
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func (a *ADB) ListDevices() (devices []*adb.DeviceInfo, err error) {
|
||||
func (a *Bridge) ListDevices() (devices []*adb.DeviceInfo, err error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
|
@ -59,14 +62,14 @@ func (a *ADB) ListDevices() (devices []*adb.DeviceInfo, err error) {
|
|||
return devices, nil
|
||||
}
|
||||
|
||||
func (a *ADB) SetDevice(deviceInfo *adb.DeviceInfo) (err error) {
|
||||
func (a *Bridge) SetDevice(deviceInfo *adb.DeviceInfo) (err error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
return a.setDevice(deviceInfo)
|
||||
}
|
||||
|
||||
func (a *ADB) setDevice(deviceInfo *adb.DeviceInfo) (err error) {
|
||||
func (a *Bridge) setDevice(deviceInfo *adb.DeviceInfo) (err error) {
|
||||
device := a.bridge.Device(adb.DeviceWithSerial(deviceInfo.Serial))
|
||||
if device == nil {
|
||||
return fmt.Errorf("failed to get device with serial %s", deviceInfo.Serial)
|
||||
|
@ -76,7 +79,7 @@ func (a *ADB) setDevice(deviceInfo *adb.DeviceInfo) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *ADB) DirectoryEntries(path string) (entries []*adb.DirEntry, err error) {
|
||||
func (a *Bridge) DirectoryEntries(path string) (entries []*adb.DirEntry, err error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
|
@ -90,11 +93,26 @@ func (a *ADB) DirectoryEntries(path string) (entries []*adb.DirEntry, err error)
|
|||
}
|
||||
|
||||
for listEntries.Next() {
|
||||
entries = append(entries, listEntries.Entry())
|
||||
entry := listEntries.Entry()
|
||||
if entry.Name == "." {
|
||||
continue
|
||||
}
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
if listEntries.Err() != nil {
|
||||
return nil, fmt.Errorf("failed to list files: %s", err)
|
||||
}
|
||||
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
if entries[i].Name == ".." {
|
||||
return true
|
||||
} else if (entries[i].Mode&os.ModeDir != 0) != (entries[j].Mode&os.ModeDir != 0) {
|
||||
return entries[i].Mode&os.ModeDir != 0
|
||||
}
|
||||
|
||||
return strings.ToLower(entries[i].Name) < strings.ToLower(entries[j].Name)
|
||||
})
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue