#!/bin/bash # Pull files from Android device via ADB # Author: Naomi Carrigan & Hikari 💕 set -euo pipefail # Color codes for pretty output RED='\033[0;31m' GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Function to display usage usage() { echo -e "${BLUE}Usage: $0 [android_source] [local_destination]${NC}" echo -e "${YELLOW}If no arguments provided, interactive mode will start${NC}" echo "" echo "Common Android paths:" echo " /sdcard/Download/ - Downloads folder" echo " /sdcard/DCIM/ - Camera folder" echo " /sdcard/Pictures/ - Pictures folder" echo " /sdcard/WhatsApp/ - WhatsApp media" echo " /sdcard/Screenshots/ - Screenshots" echo "" echo "Examples:" echo " $0 /sdcard/DCIM/Camera/ ~/Pictures/phone-backup/" echo " $0 /sdcard/Download/document.pdf ~/Downloads/" echo " $0" exit 1 } # Function to check if ADB is installed and device is connected check_adb() { if ! command -v adb &> /dev/null; then echo -e "${RED}❌ Error: ADB is not installed or not in PATH${NC}" echo "Please install Android Debug Bridge (ADB) first" exit 1 fi # Check if device is connected if ! adb devices | grep -q "device$"; then echo -e "${RED}❌ Error: No Android device connected${NC}" echo "Please connect your device and enable USB debugging" echo "" echo "Current devices:" adb devices exit 1 fi } # Function to browse Android filesystem browse_android_fs() { local current_path="${1:-/sdcard/}" while true; do echo -e "${CYAN}📱 Current path: $current_path${NC}" echo "" # List contents echo -e "${YELLOW}Contents:${NC}" local items=$(adb shell "ls -la '$current_path' 2>/dev/null" | tail -n +2 | awk '{print $NF}' | grep -v "^\.$" | grep -v "^\.\.$") local i=1 local -a entries=() # Add parent directory option if not at root if [[ "$current_path" != "/" ]]; then echo "0) .. (Go up)" entries[0]=".." fi # List items while IFS= read -r item; do if [[ -n "$item" ]]; then # Check if directory if adb shell "test -d '$current_path/$item' 2>/dev/null && echo 'dir'" | grep -q "dir"; then echo "$i) $item/" else echo "$i) $item" fi entries[$i]="$item" ((i++)) fi done <<< "$items" echo "" echo "s) Select this path" echo "q) Quit" echo "" read -p "Enter choice: " choice case $choice in s|S) echo "$current_path" return 0 ;; q|Q) return 1 ;; 0) if [[ "$current_path" != "/" ]]; then current_path=$(dirname "$current_path") fi ;; [0-9]*) if [[ -n "${entries[$choice]:-}" ]]; then local selected="${entries[$choice]}" local new_path="$current_path/$selected" # Clean up path new_path=$(echo "$new_path" | sed 's|//|/|g') # Check if it's a directory if adb shell "test -d '$new_path' 2>/dev/null && echo 'dir'" | grep -q "dir"; then current_path="$new_path" else # It's a file, return it echo "$new_path" return 0 fi else echo -e "${RED}Invalid choice${NC}" fi ;; *) echo -e "${RED}Invalid choice${NC}" ;; esac echo "" done } # Interactive mode interactive_mode() { echo -e "${BLUE}📲 ADB Pull - Interactive Mode${NC}" echo "" # Choose method echo -e "${YELLOW}How would you like to select the source?${NC}" echo "1) Browse Android filesystem" echo "2) Enter path directly" echo "3) Quick access to common folders" echo "" read -p "Select method (1-3): " method case $method in 1) # Browse mode if source_path=$(browse_android_fs); then echo -e "${GREEN}Selected: $source_path${NC}" else echo -e "${RED}❌ Cancelled${NC}" exit 1 fi ;; 2) # Direct path entry read -p "Enter Android source path: " -e source_path ;; 3) # Quick access echo "" echo -e "${YELLOW}Common locations:${NC}" echo "1) /sdcard/DCIM/Camera/" echo "2) /sdcard/Pictures/" echo "3) /sdcard/Download/" echo "4) /sdcard/WhatsApp/Media/" echo "5) /sdcard/Screenshots/" echo "6) /sdcard/Documents/" echo "7) /sdcard/Music/" echo "" read -p "Select location (1-7): " quick_choice case $quick_choice in 1) source_path="/sdcard/DCIM/Camera/" ;; 2) source_path="/sdcard/Pictures/" ;; 3) source_path="/sdcard/Download/" ;; 4) source_path="/sdcard/WhatsApp/Media/" ;; 5) source_path="/sdcard/Screenshots/" ;; 6) source_path="/sdcard/Documents/" ;; 7) source_path="/sdcard/Music/" ;; *) echo -e "${RED}❌ Invalid choice${NC}" exit 1 ;; esac ;; *) echo -e "${RED}❌ Invalid choice${NC}" exit 1 ;; esac # Get destination echo "" read -p "Enter local destination path (default: current directory): " -e dest_path dest_path="${dest_path:-.}" dest_path="${dest_path/#\~/$HOME}" # Pull the file/directory pull_from_android "$source_path" "$dest_path" } # Function to pull files from Android pull_from_android() { local source="$1" local dest="$2" # Validate source exists if ! adb shell "test -e '$source' 2>/dev/null && echo 'exists'" | grep -q "exists"; then echo -e "${RED}❌ Error: Source '$source' does not exist on device${NC}" exit 1 fi # Create destination directory if needed if [[ ! -d "$dest" ]]; then mkdir -p "$dest" fi echo -e "${BLUE}📥 Pulling from Android device...${NC}" echo "Source: $source" echo "Destination: $dest" echo "" # Check if source is directory if adb shell "test -d '$source' 2>/dev/null && echo 'dir'" | grep -q "dir"; then echo -e "${YELLOW}Pulling directory...${NC}" # Count files for progress local file_count=$(adb shell "find '$source' -type f 2>/dev/null | wc -l" | tr -d '\r\n') echo "Found $file_count files to pull" echo "" if adb pull "$source" "$dest"; then echo -e "${GREEN}✅ Directory pulled successfully!${NC}" # Show summary local pulled_dir="$dest/$(basename "$source")" if [[ -d "$pulled_dir" ]]; then echo "" echo -e "${BLUE}📊 Summary:${NC}" echo "Location: $pulled_dir" echo "Files: $(find "$pulled_dir" -type f | wc -l)" echo "Total size: $(du -sh "$pulled_dir" | cut -f1)" fi else echo -e "${RED}❌ Failed to pull directory${NC}" exit 1 fi else # Single file if adb pull "$source" "$dest"; then echo -e "${GREEN}✅ File pulled successfully!${NC}" # Show file info local filename=$(basename "$source") local full_path="$dest/$filename" echo "" echo -e "${BLUE}📄 File info:${NC}" ls -lh "$full_path" else echo -e "${RED}❌ Failed to pull file${NC}" exit 1 fi fi } # Main script main() { check_adb if [[ $# -eq 0 ]]; then # No arguments, run interactive mode interactive_mode elif [[ $# -eq 1 ]]; then if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then usage else echo -e "${RED}❌ Error: Missing destination path${NC}" usage fi elif [[ $# -eq 2 ]]; then # Arguments provided dest_path="${2/#\~/$HOME}" pull_from_android "$1" "$dest_path" else echo -e "${RED}❌ Error: Too many arguments${NC}" usage fi } main "$@"