• PSA: Script to copy files to/from Windows/Android adbcopy.bat

    From Maria Sophia@mariasophia@comprehension.com to alt.comp.os.windows-10,comp.mobile.android,alt.msdos.batch on Mon Jul 27 10:30:45 2026
    From Newsgroup: alt.msdos.batch

    I wrote this script to solve a problem that USB can't solve.
    adbcopy.bat
    It accidentally solved a few other common interoperability issues.
    Windows <--> Android (over WiFi or USB)

    My main problem was that my USB port is broken so I have to use Wi-Fi.
    So I originally wrote this script to copy Android:Windows over Wi-Fi.

    But then I realized some directories aren't indexed when photos are added.
    In that case, WhatsApp / PulseSMS / Signal / Messenger do NOT show it.
    a. Because the file is not indexed by MediaStore
    b. Or I put it in the wrong folder for those clients to find
    c. Or it's owned by the wrong UID
    d. Or it's not SAF-visible
    e. Or it's not tagged as "user-created

    USB drag-n-drop cannot fix this but this script runs a re-indexing.
    So the images are immediately available to all the users' media tools.

    Also, the script bypasses Android's SAF restrictions, so, for example,
    the script can push into folders that USB isn't able to push into, such as
    /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images

    The script is portable in that it can push files to any phone on the LAN.
    The phone can be two floors away yet you can pull & push files to/from it.

    Actual output:

    adbcopy.bat
    Targeting device: 192.168.1.4:5555
    ----------------
    Select Direction:
    [1] Android to Windows (Pull Images/Screenshots) [Default]
    [2] Windows to Android (Copy/Install APK)

    Enter choice [1 or 2]:
    1
    --- Running Android to Windows Pull ---

    What do you want to pull?
    [1] Camera photos (SD Card DCIM/Camera)
    [2] Screenshots
    [3] Camera photos (Internal Storage DCIM/Camera)
    [4] Pull directories one-by-one (user selected)
    Enter choice [1, 2, 3 or 4]:

    ... or ...
    Enter choice [1 or 2]:
    2

    --- Windows to Android Push Mode ---
    Select file input mode:
    [0] Browse for file (no full path needed)
    [1] Enter full path manually

    Enter choice [0 or 1]: 0
    Opening file browser... (you can then pick the file to copy)

    Here's the batch file latest version as of today...

    :: adbcopy.bat
    :: Uses adb on a desktop to copy all image files off my Android phone
    :: -------------------------------------------------------------------------
    :: v3p4 20260727 Added checks for typos when inputting Windows file paths
    :: v3p3 20260727 Added Windows browsing capability if full path is not known
    :: v3p2 20260726 Added choice of known Android destinations for convenience
    :: v3p1 20260725 Added Windows push to Android using a known full path
    :: Added broadcast registration so images can quickly be found
    :: v3p0 20260708 added dual-layer deletion (filesystem + mediastore)
    :: which prevents FUSE from repeatedly restoring ghost files
    :: and which handles private-app sandbox directories
    :: v2p9 20260707 fixed quoting and path issues caused by filenames with spaces
    :: v2p8 20260707 added skipping of empty directories before prompting user
    :: v2p7 20260702 changed to delete only files (not folders)
    :: v2p6 20260701 added auto-detect all DCIM subdirectories
    :: v2p5 20260701 expanded image file matching
    :: v2p4 20260701 added internal DCIM/Camera option
    :: v2p3 20260621 open destination folder when done
    :: v2p2 20260617 added screenshot directory detection
    :: v2p2 20260612 fixed quoting bug in DCIM ls command
    :: v2p1 20260607 added Windows-to-Android APK install mode
    :: v2p0 20260603 added Screenshot directory support
    :: v1p9 20260528 trim trailing spaces from destination path
    :: v1p8 20260528 replaced destructive path truncation with CR stripping
    :: v1p7 20260528 escaped redirection character in backtick loop
    :: v1p6 20260528 updated external SD card path handling
    :: v1p5 20260528 fixed null-redirection error and stabilized extraction
    :: v1p4 20260528 fixed "unexpected at this time" crash
    :: v1p3 20260528 fixed crash on empty variable comparison
    :: v1p2 20260528 fixed multi-device detection and nested quoting
    :: v1p1 20260528 added debug output for failure cases
    :: v1p0 20260528 initial version: copy images from Android by date
    :: -------------------------------------------------------------------------

    @echo off
    setlocal enabledelayedexpansion

    cd /d "%~dp0"
    set "ADB_EXE=c:\app\editor\android\scrcpy\adb.exe"

    :: --- DEVICE DETECTION ---
    for /f "skip=1 tokens=1" %%g in ('"%ADB_EXE%" devices') do (
    if not "%%g"=="" (
    if "!ADB_TARGET!"=="" set "ADB_TARGET=%%g"
    )
    )

    if "!ADB_TARGET!"=="" (
    echo [ERROR] No connected ADB devices found.
    pause
    exit /b
    )

    echo Targeting device: %ADB_TARGET%
    echo ----------------

    :: --- MAIN DIRECTION MENU --- default is [1] ---
    echo Select Direction:
    echo [1] Android to Windows (Pull Images/Screenshots) [Default]
    echo [2] Windows to Android (Copy/Install APK)
    echo.
    set "CHOICE=1"
    set /p "CHOICE=Enter choice [1 or 2]: "

    if "%CHOICE%"=="2" goto :windows_to_android
    if "%CHOICE%"=="1" goto :android_to_windows
    goto :android_to_windows

    :windows_to_android
    echo.
    echo --- Windows to Android Push Mode ---

    :: begin file selection
    echo Select file input mode:
    echo [0] Browse for file (no full path needed)
    echo [1] Enter full path manually
    echo.
    set /p "FILE_MODE=Enter choice [0 or 1]: "

    if "%FILE_MODE%"=="0" (
    echo Opening file browser...
    for /f "delims=" %%F in ('powershell -command "Add-Type -AssemblyName System.Windows.Forms; $f=New-Object System.Windows.Forms.OpenFileDialog; $f.Filter='Images|*.jpg;*.jpeg;*.png;*.gif'; if($f.ShowDialog() -eq 'OK'){Write-Output $f.FileName}"') do set "SRC_FILE=%%F"
    ) else (
    :ask_path
    set /p "SRC_FILE=Enter full path of file to push: "
    if "%SRC_FILE%"=="" (
    echo You must enter a full path or choose option 0.
    goto ask_path
    )
    )

    if not exist "%SRC_FILE%" (
    echo [ERROR] File not found: %SRC_FILE%
    pause
    exit /b
    )

    :: end file selection

    :: Extract just the filename (e.g., pizzamyheart.jpg)
    for %%F in ("%SRC_FILE%") do set "SRC_NAME=%%~nxF"

    echo.
    echo Select Android destination:
    echo [1] /storage/emulated/0/DCIM/Camera
    echo [2] /storage/emulated/0/DCIM/Screenshots
    echo [3] /storage/emulated/0/Pictures
    echo [4] /storage/emulated/0/Pictures/Screenshots
    echo [5] /storage/emulated/0/Download
    echo [6] /storage/emulated/0/Snapseed
    echo [7] /storage/emulated/0/Samsung
    echo [8] /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images
    echo.
    set /p "DEST_CHOICE=Enter choice [1-8]: "

    if "%DEST_CHOICE%"=="1" set "DEST_DIR=/storage/emulated/0/DCIM/Camera"
    if "%DEST_CHOICE%"=="2" set "DEST_DIR=/storage/emulated/0/DCIM/Screenshots"
    if "%DEST_CHOICE%"=="3" set "DEST_DIR=/storage/emulated/0/Pictures"
    if "%DEST_CHOICE%"=="4" set "DEST_DIR=/storage/emulated/0/Pictures/Screenshots"
    if "%DEST_CHOICE%"=="5" set "DEST_DIR=/storage/emulated/0/Download"
    if "%DEST_CHOICE%"=="6" set "DEST_DIR=/storage/emulated/0/Snapseed"
    if "%DEST_CHOICE%"=="7" set "DEST_DIR=/storage/emulated/0/Samsung"
    if "%DEST_CHOICE%"=="8" set "DEST_DIR=/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images"

    echo.
    echo Pushing "%SRC_FILE%" to "%DEST_DIR%"
    "%ADB_EXE%" -s %ADB_TARGET% push "%SRC_FILE%" "%DEST_DIR%"

    echo Forcing media scan...
    "%ADB_EXE%" -s %ADB_TARGET% shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://%DEST_DIR%/%SRC_NAME%

    echo Done.
    pause
    exit /b

    :: ROUTINE: ANDROID TO WINDOWS
    :android_to_windows
    echo.
    echo --- Running Android to Windows Pull ---

    echo.
    echo What do you want to pull?
    echo [1] Camera photos (SD Card DCIM/Camera)
    echo [2] Screenshots
    echo [3] Camera photos (Internal Storage DCIM/Camera)
    echo [4] Pull directories one-by-one (user selected)
    set "PULL_CHOICE=1"
    set /p "PULL_CHOICE=Enter choice [1, 2, 3 or 4]: "

    :: Only ask for date if pulling dated files
    if "%PULL_CHOICE%"=="4" goto :skip_date

    :: --- DATE DETECTION ---
    for /f "tokens=2 delims==" %%i in ('wmic os get localdatetime /value') do set "dt=%%i"
    set "DETECTED_DATE=%dt:~0,8%"

    echo Detected Date: %DETECTED_DATE%
    set /p "USER_DATE=Press Enter to confirm, or type a different date (YYYYMMDD): "
    if "%USER_DATE%"=="" (
    set "TARGET_DATE=%DETECTED_DATE%"
    ) else (
    set "TARGET_DATE=%USER_DATE%"
    )

    :skip_date

    echo.
    if "%PULL_CHOICE%"=="4" (
    rem Interactive, pulling everything iV no date-based subfolder
    set "DEST_DIR=H:\003_camera\20260701"

    ) else (
    rem Date-based pulls still go into YYYYMMDD subfolder
    set "DEST_DIR=H:\004_upload\home\pool\assay\%TARGET_DATE%"
    )

    set /p "USER_DIR=Enter destination directory [Default: %DEST_DIR%]: "
    if not "%USER_DIR%"=="" set "DEST_DIR=%USER_DIR%"

    :: Remove quotes
    set "DEST_DIR=%DEST_DIR:"=%"

    :: Trim trailing spaces
    :trim
    if "!DEST_DIR:~-1!"==" " (
    set "DEST_DIR=!DEST_DIR:~0,-1!"
    goto trim
    )

    if not exist "%DEST_DIR%" (
    echo Creating directory: %DEST_DIR%
    mkdir "%DEST_DIR%"
    )

    if "%PULL_CHOICE%"=="1" goto :pull_camera
    if "%PULL_CHOICE%"=="2" goto :pull_screenshots
    if "%PULL_CHOICE%"=="3" goto :pull_camera_internal
    if "%PULL_CHOICE%"=="4" goto :pull_interactive_dirs

    goto :pull_camera

    if "%PULL_CHOICE%"=="1" goto :pull_camera
    if "%PULL_CHOICE%"=="2" goto :pull_screenshots
    if "%PULL_CHOICE%"=="3" goto :pull_camera_internal
    if "%PULL_CHOICE%"=="4" goto :pull_interactive_dirs

    goto :pull_camera

    :: PULL CAMERA (DCIM)
    :pull_camera
    echo.
    echo Pulling CAMERA images matching *%TARGET_DATE%*.jpg ...
    echo Sending files to: "%DEST_DIR%"
    echo ----------------

    for /f "usebackq delims=" %%i in (`
    %ADB_EXE% -s !ADB_TARGET! shell ls /storage/0CA4-352D/DCIM/Camera/*%TARGET_DATE%*.jpg
    `) do (
    set "FILE_PATH=%%i"
    for /f "delims=" %%r in ("!FILE_PATH!") do set "FILE_PATH=%%r"
    if not "!FILE_PATH!"=="" (
    echo Pulling: !FILE_PATH!
    "%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
    )
    )

    goto :complete

    :: PULL SCREENSHOTS (handle both directories explicitly)
    :pull_screenshots
    echo.
    echo Pulling SCREENSHOTS matching *%TARGET_DATE%*.jpg ...
    echo Sending files to: "%DEST_DIR%"
    echo ----------------

    :: Directory A Android 11 legacy
    for /f "usebackq delims=" %%i in (`
    %ADB_EXE% -s !ADB_TARGET! shell ls /sdcard/DCIM/Screenshots/*%TARGET_DATE%*.jpg
    `) do (
    set "FILE_PATH=%%i"
    if not "!FILE_PATH!"=="" (
    echo Pulling (legacy): !FILE_PATH!
    "%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
    )
    )

    :: Directory B Android 13 modern
    for /f "usebackq delims=" %%i in (`
    %ADB_EXE% -s !ADB_TARGET! shell ls /storage/emulated/0/DCIM/Screenshots/*%TARGET_DATE%*.jpg
    `) do (
    set "FILE_PATH=%%i"
    if not "!FILE_PATH!"=="" (
    echo Pulling (modern): !FILE_PATH!
    "%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
    )
    )

    goto :complete

    :: PULL CAMERA (INTERNAL STORAGE DCIM)
    :pull_camera_internal
    echo.
    echo Pulling CAMERA images from INTERNAL STORAGE matching *%TARGET_DATE%*.jpg ...
    echo Sending files to: "%DEST_DIR%"
    echo ----------------

    for /f "usebackq delims=" %%i in (`
    %ADB_EXE% -s !ADB_TARGET! shell ls /storage/emulated/0/DCIM/Camera/*%TARGET_DATE%*.jpg
    `) do (
    set "FILE_PATH=%%i"
    for /f "delims=" %%r in ("!FILE_PATH!") do set "FILE_PATH=%%r"
    if not "!FILE_PATH!"=="" (
    echo Pulling: !FILE_PATH!
    "%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
    )
    )
    goto :complete

    :: FINALIZE
    :complete
    start "" "%DEST_DIR%"
    echo Process complete
    pause
    exit /b

    :pull_interactive_dirs
    echo.
    echo --- Interactive Directory Pull Mode ---
    echo Destination: "%DEST_DIR%"

    echo.

    :: --- Detect ALL folders under /storage/emulated/0/DCIM ---
    echo Detecting DCIM folders...

    rem Correct quoting: do NOT quote the ls command
    "%ADB_EXE%" -s %ADB_TARGET% shell ls -d /storage/emulated/0/DCIM/*/ > "%TEMP%\dcimdirs.txt" 2>&1

    echo.
    echo --- Interactive Directory Pull Mode (adb uses the FUSE layer) ---
    echo Destination: "%DEST_DIR%"
    echo.

    :: First pull all DCIM subfolders dynamically
    for /f "usebackq delims=" %%D in ("%TEMP%\dcimdirs.txt") do (
    echo Found DCIM folder: %%D
    call :pull_dir "%%D"
    )

    :: Then pull the other known directories
    call :pull_dir "/storage/emulated/0/Pictures"
    call :pull_dir "/storage/emulated/0/Download"
    call :pull_dir "/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images"
    call :pull_dir "/storage/emulated/0/Android/media/net.psyberia.offlinemaps/OfflineMaps Exports"
    call :pull_dir "/storage/emulated/0/ReadEra/Covers"
    call :pull_dir "/storage/emulated/0/OziExplorer/System Data"
    call :pull_dir "/storage/0CA4-352D/DCIM/Camera"
    call :pull_dir "/sdcard/DCIM/Screenshots"

    goto :eod

    :pull_dir
    set "DIR=%~1"

    echo.
    echo Directory: %DIR%

    echo Listing files in %DIR% ...
    %ADB_EXE% -s %ADB_TARGET% shell ls "%DIR%" > "%TEMP%\filelist_raw.txt"

    :: Filter out directories so as to keep only real files
    > "%TEMP%\filelist.txt" (
    for /f "usebackq delims=" %%Z in ("%TEMP%\filelist_raw.txt") do (
    %ADB_EXE% -s %ADB_TARGET% shell "[ -f \"%DIR%/%%Z\" ]" && echo %%Z
    )
    )

    :: Check if any real files exist
    set "HASFILES="
    for /f "usebackq delims=" %%Z in ("%TEMP%\filelist.txt") do set "HASFILES=1"

    if not defined HASFILES (
    echo No files found in %DIR%. Skipping.
    goto :eod
    )

    :: Print the actual file list (Option A)
    echo Files found:
    type "%TEMP%\filelist.txt"
    echo.

    :: Ask user only if real files exist
    set /p "ANS=Pull this directory? [y/N]: "
    if /i not "%ANS%"=="y" goto :eod

    echo Checking directory: %DIR%

    :: Count files
    set "COUNT=0"
    for /f "usebackq delims=" %%C in ("%TEMP%\filelist.txt") do set /a COUNT+=1
    echo Found %COUNT% files in %DIR%.

    echo Pulling files...
    for /f "usebackq delims=" %%F in ("%TEMP%\filelist.txt") do (
    echo Pulling: %DIR%/%%F
    %ADB_EXE% -s %ADB_TARGET% pull "%DIR%/%%F" "%DEST_DIR%"
    )

    :: delete the file and the MediaStore entry and handle space differences
    :: Take into account many mediastore naming syntax and access intricacies
    set /p "DEL=Delete original files from phone (and MediaStore)? [y/N]: "
    if /i "%DEL%"=="y" (
    echo Deleting originals and MediaStore entries...

    for /f "usebackq delims=" %%F in ("%TEMP%\filelist.txt") do (

    rem --- Delete file (handles spaces correctly) ---
    echo Deleting file: %DIR%/%%F
    "%ADB_EXE%" -s %ADB_TARGET% shell rm "\"%DIR%/%%F\""

    rem --- Delete MediaStore entry using full filename (with spaces) ---
    echo Deleting MediaStore entry for: %%F
    "%ADB_EXE%" -s %ADB_TARGET% shell content delete --uri content://media/external/images/media --where "\"_data LIKE '%/%%F'\""
    )
    )

    :eod
    exit /b

    REM end of adbcopy.bat
    --
    On Usenet, some of us share what we have learned so others do not struggle.
    --- Synchronet 3.22a-Linux NewsLink 1.2