Copying pictures with ADB
I’ve been copying pictures from my Android phone to my Mac using Android File Transfer. It worked great when my pictures were stored in the phone’s internal storage. However, now that I’m storing my pictures on an external SD card, it has become unreliable. More and more frequently, I’ve been getting:
despite having my screen unlocked. Unplugging and reconnecting the USB cable repeatedly sometimes works eventually, but lately it’s been getting less and less likely to work. I wish software would just work correctly, instead of being so buggy and crappy. But that’s a gripe for another post sometime.
Anyway, I decided to use the Android Debug Bridge (adb) command-line tool to copy my photos. I wrote the following Haskell program to determine which photos I don’t yet have on my computer, and then copy them. It works great!
#!/usr/bin/env runhaskell
import Data.List
import System.Directory
import System.IO
import System.Process
remoteDir = "/storage/extSdCard/DCIM/Camera"
localDir = "/Users/ppelleti/Pictures/Android"
getRemoteFiles :: String -> IO [String]
getRemoteFiles remoteDir = do
  files <- readProcess "adb" ["shell", "ls", remoteDir] ""
  return $ lines $ filter (/= '\r') files
getLocalFiles :: String -> IO [String]
getLocalFiles localDir = do
  files <- getDirectoryContents localDir
  return $ filter shouldKeep files
  where shouldKeep ('.':_ ) = False
        shouldKeep _ = True
copyOneFile :: String -> String -> IO ()
copyOneFile remoteFile localFile =
  cmd ["adb", "pull", "-a", remoteFile, localFile]
cmd :: [String] -> IO ()
cmd (exe:args) = do
  putStrLn $ unwords $ exe : map show args
  callProcess exe args
copyFiles :: String -> String -> [String] -> IO ()
copyFiles rDir lDir files =
  mapM_ cfile files
  where cfile file = copyOneFile (rDir ++ "/" ++ file) (lDir ++ "/" ++ file)
copyNewFiles :: String -> String -> IO ()
copyNewFiles rDir lDir = do
  rFiles <- getRemoteFiles rDir
  lFiles <- getLocalFiles lDir
  let files = rFiles \\ lFiles
  copyFiles rDir lDir files
main = copyNewFiles remoteDir localDirAlso available as a gist.
