ADB file manager
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
4.4 KiB

package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"sort"
"strings"
"sync"
"time"
adb "github.com/zach-klippenstein/goadb"
)
// DefaultPort is the default port the ADB server listens on.
const DefaultPort = 5037
// adbConn represents a connection with an ADB server.
type adbConn struct {
bridge *adb.Adb
device *adb.Device
dir string
sync.Mutex
}
// connectBridge establishes a connection with an ADB server. When host and
// port are unspecified they are replaced with localhost and the default port.
func connectBridge(host string, port int, pathToADB string) (bridge *adbConn, err error) {
adbBridge, err := adb.NewWithConfig(adb.ServerConfig{
PathToAdb: pathToADB,
Host: host,
Port: port,
})
if err != nil {
log.Fatal(err)
}
adbBridge.StartServer() // Ignore error
_, err = adbBridge.ServerVersion()
if err != nil {
return nil, fmt.Errorf("failed to get server version: %s", err)
}
return &adbConn{bridge: adbBridge}, nil
}
func (a *adbConn) connectDevice(host string, port int) (err error) {
return a.bridge.Connect(host, port)
}
func (a *adbConn) listDevices() (devices []*adb.DeviceInfo, err error) {
a.Lock()
defer a.Unlock()
deviceInfoList, err := a.bridge.ListDevices()
if err != nil {
return nil, err
}
for _, deviceInfo := range deviceInfoList {
devices = append(devices, deviceInfo)
}
return devices, nil
}
func (a *adbConn) setDevice(deviceInfo *adb.DeviceInfo) (err error) {
a.Lock()
defer a.Unlock()
device := a.bridge.Device(adb.DeviceWithSerial(deviceInfo.Serial))
if device == nil {
return fmt.Errorf("failed to get device with serial %s", deviceInfo.Serial)
}
a.device = device
return nil
}
func (a *adbConn) upload(fileName string, data []byte, mtime time.Time) (err error) {
a.Lock()
defer a.Unlock()
writer, err := a.device.OpenWrite(path.Join(a.dir, fileName), 0655, mtime)
if err != nil {
return fmt.Errorf("failed to open remote file: %s", err)
}
defer writer.Close()
n, err := writer.Write(data)
if err != nil {
return err
} else if n != len(data) {
return errors.New("failed to write file")
}
return nil
}
func (a *adbConn) download(fileName string) (data []byte, err error) {
a.Lock()
defer a.Unlock()
reader, err := a.device.OpenRead(path.Join(a.dir, fileName))
if err != nil {
return nil, err
}
defer reader.Close()
data, err = ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
return data, nil
}
func (a *adbConn) directoryEntries(dir string) (entries []*adb.DirEntry, err error) {
a.Lock()
defer a.Unlock()
if a.device == nil {
return nil, errors.New("no device")
}
a.dir = dir
listEntries, err := a.device.ListDirEntries(dir)
if err != nil {
return nil, fmt.Errorf("failed to get dirents: %s", err)
}
for listEntries.Next() {
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
}
func (a *adbConn) copy(old string, new string) (err error) {
a.Lock()
defer a.Unlock()
if a.device == nil {
return errors.New("no device")
}
_, err = a.device.RunCommand("cp", "-R", old, new)
if err != nil {
return fmt.Errorf("failed to copy file: %s", err)
}
return nil
}
func (a *adbConn) move(old string, new string) (err error) {
a.Lock()
defer a.Unlock()
if a.device == nil {
return errors.New("no device")
}
_, err = a.device.RunCommand("mv", old, new)
if err != nil {
return fmt.Errorf("failed to move file: %s", err)
}
return nil
}
func (a *adbConn) delete(path string) (err error) {
a.Lock()
defer a.Unlock()
if a.device == nil {
return errors.New("no device")
}
_, err = a.device.RunCommand("rm", "-rf", path)
if err != nil {
return fmt.Errorf("failed to delete file or directory: %s", err)
}
return nil
}
func (a *adbConn) newDirectory(path string) (err error) {
a.Lock()
defer a.Unlock()
if a.device == nil {
return errors.New("no device")
}
_, err = a.device.RunCommand("mkdir", path)
if err != nil {
return fmt.Errorf("failed to create directory: %s", err)
}
return nil
}