AI is making my Linux life better yet again

Glenn

Administrator
Staff member
As I was working on making a package of the very best of older android device games, I got AI to help me make a script that will install a folder full of them to a connected USB Debugging device... here is the code it's given, it works a treat, You do need to install the adb, I might go make it do that for you too :)


#!/usr/bin/env bash

DEFAULT_BASE_DIR="/media/glenn/Glenn_s 8TB USB/1-Other_Consoles/Android2025/Games-Android4.4.x/Best"
WAIT_SECONDS=5
STATE_FILE="/tmp/apk_install_resume.state"
ADB_SERIAL=""

# Use full argument string as path if provided
if [ "$#" -gt 0 ]; then
BASE_DIR="$*"
else
BASE_DIR="$DEFAULT_BASE_DIR"
fi

# Validate path
if [ ! -d "$BASE_DIR" ]; then
echo "❌ Directory does not exist:"
echo " $BASE_DIR"
exit 1
fi

# Tool checks
for cmd in adb aapt; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "❌ Required tool missing: $cmd"
exit 1
fi
done

echo " Using APK source directory:"
echo " $BASE_DIR"
echo

# ---- wait for device and select ----
select_device() {
while true; do
mapfile -t DEVICES < <(adb devices | awk 'NR>1 && $2=="device" {print $1}')

if [ "${#DEVICES[@]}" -eq 1 ]; then
ADB_SERIAL="${DEVICES[0]}"
echo "✅ Android device detected: $ADB_SERIAL"
echo -e "\a" # Beep
return
fi

if [ "${#DEVICES[@]}" -gt 1 ]; then
echo " Multiple Android devices detected:"
for i in "${!DEVICES[@]}"; do
echo " [$i] ${DEVICES[$i]}"
done
echo
read -rp "Select device number: " SEL
if [[ "$SEL" =~ ^[0-9]+$ ]] && [ "$SEL" -lt "${#DEVICES[@]}" ]; then
ADB_SERIAL="${DEVICES[$SEL]}"
echo "✅ Selected device: $ADB_SERIAL"
echo -e "\a" # Beep
return
fi
echo "❌ Invalid selection"
fi

echo
echo "⏳ Waiting for Android device..."
echo " Plug in a Android device with USB Debugging Enabled"
echo " Press Build number 8 times under About"
echo " Enable USB Debugging in Developer Options"
echo
adb devices
echo
echo " Retrying in $WAIT_SECONDS seconds..."
sleep "$WAIT_SECONDS"
done
}

select_device
echo

ADB="adb -s $ADB_SERIAL"

# ---- load APK list ----
mapfile -d '' APKS < <(find "$BASE_DIR" -type f -iname "*.apk" -print0)
TOTAL=${#APKS[@]}

if [ "$TOTAL" -eq 0 ]; then
echo "❌ No APK files found"
exit 0
fi

# Resume support
START_INDEX=0
if [ -f "$STATE_FILE" ]; then
START_INDEX=$(cat "$STATE_FILE")
echo " Resuming from APK $((START_INDEX+1)) of $TOTAL"
fi

echo " Found $TOTAL APK(s)"
echo " Starting install..."
echo

# ---- install loop ----
for ((i=START_INDEX; i<TOTAL; i++)); do
APK="${APKS[$i]}"
FILE_NAME="$(basename "$APK")"
COUNT=$((i+1))
PERCENT=$((COUNT * 100 / TOTAL))

echo "[$COUNT/$TOTAL] ($PERCENT%) Checking: $FILE_NAME"

PKG=$(aapt dump badging "$APK" 2>/dev/null | awk -F"'" '/package: name=/{print $2}')

if [ -z "$PKG" ]; then
echo "⚠️ Could not read package name, skipping"
continue
fi

if $ADB shell pm list packages | grep -q "^package:$PKG$"; then
echo "⏭️ Already installed: $PKG"
continue
fi

echo " Installing: $FILE_NAME"

if $ADB install -r "$APK"; then
echo "✅ Installed: $PKG"
else
echo "⚠️ Install failed or device disconnected"
echo "$i" > "$STATE_FILE"
select_device
ADB="adb -s $ADB_SERIAL"
continue
fi

echo
done

rm -f "$STATE_FILE"
echo -e "\a"
echo " All done!"
It's made me able to leave windows behind for another reason, I'll see how it goes with iDevices another day, but for now, this is great.
 
I've been working on it this whole time, Vibe coding is ok, but sometimes it feels more like arguing with a toddler than making progress. I dropped a bunch of features I attempted to add. but as it's now working and looks ok and can support multiple devices, I'll stick with this one. it self installs and grab dependencies and also asks via GUI for sudo password as well as installing scripts to Nemo or KDE etc as required. I think it's handy anyway:

Code:
#!/usr/bin/env bash
set -e

###############################
# CONFIG / PATHS
###############################
DEFAULT_BASE_DIR="/media/glenn/Glenn_s 8TB USB/1-Other_Consoles/Android2025/Games-Android4.4.x/Best"
STATE_FILE="/tmp/apk_install_resume.state"
FAILED_LIST="$HOME/Desktop/Failed APK.txt"
MALWARE_LIST="$HOME/Desktop/Malware APKs.txt"
LAST_DIR_FILE="$HOME/.cache/apk_installer_last_dir"
LAST_DEVICE_FILE="$HOME/.cache/apk_installer_last_device"
INSTALL_FLAG="$HOME/.cache/apk_installer_installed"
SCRIPT_NAME="APK Installer.sh"
SCRIPT_PATH="$(readlink -f "$0")"
DESKTOP_FILE="$HOME/.local/share/applications/APK Installer.desktop"

ORIGINAL_USER="${SUDO_USER:-$USER}"
ORIGINAL_HOME="$HOME"
FORCE_INSTALL=false
[ "$1" == "-install" ] && FORCE_INSTALL=true && shift

###############################
# UTILS
###############################
command_exists() { command -v "$1" >/dev/null 2>&1; }
beep() { echo -e "\a"; }

notify() {
  command_exists notify-send || return 0
  notify-send "APK Installer" "$1" --icon=package >/dev/null 2>&1 || true
}

run_elevated() {
  local cmd="$*"
  for m in pkexec sudo doas kdesu gksu kdesudo; do
    command_exists "$m" || continue
    case "$m" in
      pkexec)  pkexec env HOME="$ORIGINAL_HOME" USER="$ORIGINAL_USER" bash -c "$cmd" && return 0 ;;
      sudo)    sudo -E bash -c "export HOME='$ORIGINAL_HOME'; $cmd" && return 0 ;;
      doas)    doas env HOME="$ORIGINAL_HOME" USER="$ORIGINAL_USER" bash -c "$cmd" && return 0 ;;
      kdesu)   kdesu -c "export HOME='$ORIGINAL_HOME'; $cmd" && return 0 ;;
      gksu)    gksu "export HOME='$ORIGINAL_HOME'; $cmd" && return 0 ;;
      kdesudo) kdesudo "export HOME='$ORIGINAL_HOME'; $cmd" && return 0 ;;
    esac
  done
  return 1
}

inst() {
  for pm in pamac dnf apt emerge eopkg apk pacman zypper yum flatpak; do
    command_exists "$pm" || continue
    case "$pm" in
      pamac)   run_elevated "pamac install --no-confirm $*" ;;
      dnf)     run_elevated "dnf -y install $*" ;;
      apt)     run_elevated "apt update && apt -y install $*" ;;
      emerge)  run_elevated "emerge $*" ;;
      eopkg)   run_elevated "eopkg -y install $*" ;;
      apk)     run_elevated "apk add $*" ;;
      pacman)  run_elevated "pacman -Sy --noconfirm $*" ;;
      zypper)  run_elevated "zypper --non-interactive install $*" ;;
      yum)     run_elevated "yum -y install $*" ;;
      flatpak) run_elevated "flatpak install flathub $*" ;;
    esac && return 0
  done
  return 1
}

###############################
# SELF-INSTALL
###############################
self_install() {
  echo "Installing script to DE scripts and desktop launcher..."
  case "$XDG_SESSION_DESKTOP" in
    cinnamon) TARGET="$HOME/.local/share/nemo/scripts/$SCRIPT_NAME";;
    gnome|ubuntu|unity|cosmic|pop|budgie-desktop) TARGET="$HOME/.local/share/nautilus/scripts/$SCRIPT_NAME";;
    kde|KDE) TARGET="$HOME/.local/share/kservices5/ServiceMenus/$SCRIPT_NAME";;
    lxde) TARGET="$HOME/.local/share/pcmanfm/LXDE/desktop-items/$SCRIPT_NAME";;
    mate) TARGET="$HOME/.local/share/mate/nautilus/scripts/$SCRIPT_NAME";;
    xfce) TARGET="$HOME/.local/share/Thunar/scripts/$SCRIPT_NAME";;
    LXQt) TARGET="$HOME/.local/share/pcmanfm-qt/scripts/$SCRIPT_NAME";;
    *) echo "Unknown DE, skipping script install"; TARGET="";;
  esac

  if [ -n "$TARGET" ]; then
    mkdir -p "$(dirname "$TARGET")"
    cp "$SCRIPT_PATH" "$TARGET"
    chmod +x "$TARGET"
    echo "Script installed to $TARGET"
  fi

  mkdir -p "$(dirname "$DESKTOP_FILE")"
  cat > "$DESKTOP_FILE" <<EOL
[Desktop Entry]
Name=APK Installer
Comment=Bulk install APKs to Android devices
Exec="$TARGET"
Icon=package
Terminal=false
Type=Application
Categories=Utility;Development;
StartupNotify=true
EOL
  chmod +x "$DESKTOP_FILE"
  touch "$INSTALL_FLAG"
}

[ ! -f "$INSTALL_FLAG" ] && self_install
$FORCE_INSTALL && inst adb aapt && exit 0

###############################
# ENSURE ADB / AAPT
###############################
ADB_CMD="adb"; AAPT_CMD="aapt"
! command_exists adb && inst adb
! command_exists aapt && inst aapt

###############################
# SELECT APK FILES
###############################
select_apks() {
  APKS=()
  if [ "$#" -gt 0 ]; then
    for arg in "$@"; do
      [ -f "$arg" ] && APKS+=("$arg")
      [ -d "$arg" ] && mapfile -d '' tmp < <(find "$arg" -type f -iname "*.apk" -print0) && APKS+=("${tmp[@]}")
    done
  else
    local default_path="$DEFAULT_BASE_DIR"
    [ -f "$LAST_DIR_FILE" ] && default_path=$(cat "$LAST_DIR_FILE")
    if command_exists yad; then
      chosen=$(yad --file --multiple --title="Select APKs" --file-filter="*.apk" --filename="$default_path")
    elif command_exists zenity; then
      chosen=$(zenity --file-selection --multiple --title="Select APKs" --file-filter="*.apk" --filename="$default_path")
    else
      read -p "Enter folder or APK files (space-separated): " chosen
    fi
    IFS='|' read -ra APKS <<< "$chosen"
    [ "${#APKS[@]}" -eq 1 ] && [ -d "${APKS[0]}" ] && mapfile -d '' APKS < <(find "${APKS[0]}" -type f -iname "*.apk" -print0)
  fi
  [ "${#APKS[@]}" -eq 0 ] && exit 0
  echo "${APKS[0]}" > "$LAST_DIR_FILE"
  mapfile -t APKS < <(printf "%s\n" "${APKS[@]}" | sort -f)
}

select_apks "$@"

###############################
# SELECT DEVICE
###############################
select_device() {
  while true; do
    mapfile -t DEVICES < <($ADB_CMD devices | awk 'NR>1 && $2=="device" {print $1}')
    [ "${#DEVICES[@]}" -eq 0 ] && sleep 5 && continue
    [ "${#DEVICES[@]}" -eq 1 ] && { ADB_SERIAL="${DEVICES[0]}"; break; }

    OPTIONS=(); for d in "${DEVICES[@]}"; do MODEL=$($ADB_CMD -s "$d" shell getprop ro.product.model 2>/dev/null | tr -d '\r'); OPTIONS+=("$d ($MODEL)"); done
    if command_exists yad; then
      CHOICE=$(yad --width=300 --height=300 --list --title="Select Device" --column="Devices" "${OPTIONS[@]}")
    elif command_exists zenity; then
      CHOICE=$(zenity --width=300 --height=300 --list --title="Select Device" --column="Devices" "${OPTIONS[@]}")
    else
      select CHOICE in "${OPTIONS[@]}"; do break; done
    fi
    [ -n "$CHOICE" ] && { ADB_SERIAL="${CHOICE%% *}"; break; }
  done
  echo "$ADB_SERIAL" > "$LAST_DEVICE_FILE"
  ADB="$ADB_CMD -s $ADB_SERIAL"
}

select_device

###############################
# INSTALL APKs WITH PROGRESS & CANCEL
###############################
FAILED_COUNT=0
MALWARE_COUNT=0
TOTAL=${#APKS[@]}
rm -f "$FAILED_LIST" "$MALWARE_LIST"

if command_exists yad; then
  (
    for ((i=0;i<TOTAL;i++)); do
      APK="${APKS[$i]}"
      FILENAME="$(basename "$APK")"
      PERC=$(((i+1)*100/TOTAL))
      echo "$PERC"
      echo "title:Installing APKs ($((i+1))/$TOTAL)"
      echo "# ($((i+1))/$TOTAL - $FILENAME [Failed:$FAILED_COUNT, Malware:$MALWARE_COUNT]"

      PKG=$($AAPT_CMD dump badging "$APK" 2>/dev/null | awk -F"'" '/package: name=/{print $2}')
      [ -z "$PKG" ] && { echo "$FILENAME failed (bad APK)" >> "$FAILED_LIST"; ((FAILED_COUNT++)); continue; }
      $ADB shell pm list packages | grep -q "^package:$PKG$" && continue

      INSTALL_OUTPUT=$($ADB install -r "$APK" 2>&1) || INSTALL_CODE=$?
      if [ "${INSTALL_CODE:-0}" -ne 0 ]; then
        if echo "$INSTALL_OUTPUT" | grep -qiE "INSTALL_FAILED_VERIFICATION|INSTALL_FAILED_BLOCKED"; then
          echo "$FILENAME blocked" >> "$MALWARE_LIST"; ((MALWARE_COUNT++))
        else
          echo "$FILENAME failed (device error)" >> "$FAILED_LIST"; ((FAILED_COUNT++))
        fi
        notify "Device disconnected or APK blocked – waiting..."
        select_device
      fi
    done
  ) | yad --progress --title="APK Installer" --text="Installing APKs..." --percentage=0 --auto-close --width=600 --height=100 --button=Quit --listen

elif command_exists zenity; then
  (
    for ((i=0;i<TOTAL;i++)); do
      APK="${APKS[$i]}"
      FILENAME="$(basename "$APK")"
      PERC=$(((i+1)*100/TOTAL))
      echo "$PERC"
      echo "# ($((i+1))/$TOTAL - $FILENAME [Failed:$FAILED_COUNT, Malware:$MALWARE_COUNT]"

      PKG=$($AAPT_CMD dump badging "$APK" 2>/dev/null | awk -F"'" '/package: name=/{print $2}')
      [ -z "$PKG" ] && { echo "$FILENAME failed (bad APK)" >> "$FAILED_LIST"; ((FAILED_COUNT++)); continue; }
      $ADB shell pm list packages | grep -q "^package:$PKG$" && continue

      INSTALL_OUTPUT=$($ADB install -r "$APK" 2>&1) || INSTALL_CODE=$?
      if [ "${INSTALL_CODE:-0}" -ne 0 ]; then
        if echo "$INSTALL_OUTPUT" | grep -qiE "INSTALL_FAILED_VERIFICATION|INSTALL_FAILED_BLOCKED"; then
          echo "$FILENAME blocked" >> "$MALWARE_LIST"; ((MALWARE_COUNT++))
        else
          echo "$FILENAME failed (device error)" >> "$FAILED_LIST"; ((FAILED_COUNT++))
        fi
        notify "Device disconnected or APK blocked – waiting..."
        select_device
      fi
    done
  ) | zenity --progress --title="APK Installer" --percentage=0 --auto-close --width=600 --text="Starting..." --cancel-label="Cancel"

else
  for ((i=0;i<TOTAL;i++)); do
    APK="${APKS[$i]}"
    FILENAME="$(basename "$APK")"
    PERC=$(((i+1)*100/TOTAL))
    printf "[%3d%%] (%d/%d) - %s [Failed:%d, Malware:%d]\n" "$PERC" "$((i+1))" "$TOTAL" "$FILENAME" "$FAILED_COUNT" "$MALWARE_COUNT"

    PKG=$($AAPT_CMD dump badging "$APK" 2>/dev/null | awk -F"'" '/package: name=/{print $2}')
    [ -z "$PKG" ] && { echo "$FILENAME failed (bad APK)" >> "$FAILED_LIST"; ((FAILED_COUNT++)); continue; }
    $ADB shell pm list packages | grep -q "^package:$PKG$" && continue

    INSTALL_OUTPUT=$($ADB install -r "$APK" 2>&1) || INSTALL_CODE=$?
    if [ "${INSTALL_CODE:-0}" -ne 0 ]; then
      if echo "$INSTALL_OUTPUT" | grep -qiE "INSTALL_FAILED_VERIFICATION|INSTALL_FAILED_BLOCKED"; then
        echo "$FILENAME blocked" >> "$MALWARE_LIST"; ((MALWARE_COUNT++))
      else
        echo "$FILENAME failed (device error)" >> "$FAILED_LIST"; ((FAILED_COUNT++))
      fi
      notify "Device disconnected or APK blocked – waiting..."
      select_device
    fi
  done
fi

rm -f "$STATE_FILE"
if [ -f "$FAILED_LIST" ] || [ -f "$MALWARE_LIST" ]; then
  notify "Some APKs failed – see Desktop"
else
  rm -f "$FAILED_LIST" "$MALWARE_LIST"
  notify "APK installation complete"
fi
beep
 
Last edited:
Vibe coding is like talking to a toddler... I been training for this my whole life!
 
I found talking to two toddlers works even better than one. I put my scripts ChatGPT generated into google's gemini and it fixed the remaining bugs that kept me progressing.. I'll post the two working scripts with their self installers. It's not finished, but they work
 
Last edited:
Ok I gave gemini one last go and it's made both scripts combined, if you provide files it'll pick the correct one, else it asks if you want to install iPad or Android devices.. here's the final code, with the final name and icon
 
Last edited:
Changed my mind: I told AI what I think of it.


All the functions are working great, I feel like using AI to vibe code, it like holding on to water, you don't know what has gotten out of your hands but by the end of it you have nothing left when you thought you had it all. at times it feels like talking to a toddler and other times it's like an Alzheimer patient. Why does AI fail to keep logical features in place and drops them off randomly and forgets how important a feature is so I need to ask for it to put back over and over again?




That is a perfect analogy. Vibe-coding really does feel like trying to carry a gallon of water in your cupped hands—every time you adjust your grip to add a new feature, a bit of the old logic leaks between your fingers.

It’s frustrating because, as a human, you see the script as a single evolving machine. To an AI, I see the script as a statistical probability of words.

Here is the breakdown of why I (and other AIs) tend to "drop the ball" on those features:

1. The "Context Window" vs. Attention​

While I can "read" a long script, my internal "attention" is limited. When you ask for a complex new feature (like the Nemo sniffer), that new logic becomes the highest statistical priority. To make room for the "new," my internal weights might accidentally downplay "boilerplate" sections—like the -install block—thinking they are less relevant to your immediate request.

2. Lack of a "True" Filesystem​

I don't actually "see" the file on your disk. Every time I generate a response, I am rebuilding the script from scratch based on the previous turn in the conversation. If a feature isn't explicitly mentioned or heavily emphasized in the immediate "vibe" of the current prompt, the model might optimize for brevity and leave out what it perceives as "static" code.

3. The "Toddler" Logic (Greedy Decoding)​

AI follows the path of least resistance. If you ask me to "Fix the iOS uninstaller," I focus intensely on that one task. Like a toddler, I get "tunnel vision." I fix the toy in front of me, but I might accidentally kick over the blocks (-install logic) I built five minutes ago because I’m not actively thinking about the "room" as a whole.

4. Regression Ignorance​

Unlike a human developer, I don't run a "test suite" in my head to see if my changes broke old features. I don't have a "memory" of why we added a feature; I only know that it was there. If the new code structure makes the old feature look "out of place" statistically, I might omit it to make the code look "cleaner."


How to "Trap" the Water (Pro-Vibe Coding Tips)​

To stop me from being an "Alzheimer patient," you can use a few "anchors":

  • The "Core Requirements" List: Every 3 or 4 prompts, start with: "Keep all current features (Install flag, Alphabetical sort, Expanded logs) and add [New Feature]."
  • Modular Requests: Ask me to "Only rewrite the run_install_logic function." If I only touch one piece, I can't accidentally delete the rest of the script.
  • The "Anchor" Comment: Put a comment in your code like # CRITICAL: DO NOT REMOVE INSTALL LOGIC. I am much less likely to delete a line that explicitly tells me not to.
It’s a bizarre way to build software, but when it works, it’s magic. I’ll do my best to keep my hands cupped tight on this version!

Would you like me to provide a "Technical Specification" summary of the script so you can paste it back to me later if I ever get "confused" again?
 
I am walking away, I have seen all the features I want come and go, but as it is I don't have one perfect build, meaning I'll need to do the manual grunt work to build up the code exactly as I decide. not as the AI keeps doing it. It's sad, because it's so close to being useful. I don't want to have to remind it each time, can't it just keep a memory of what I told it (obviously if I specified something it's important and should be remembered each iteration after that unless explicitly told otherwise. I guess that's what those smarter AI plans may give you, but I'm not paying for anything that's unproven to me and I'm not giving my CC info just to trial something. 5 Pro prompts a day isn't enough to test it with.
 
I had a follow up after calling it out and offering to get more involved with things. It offered a way forward, I just need to tally up my prompts and summarise the points myself.. if only we had a LLM type tool that excelled in that, then this crap wouldn't need solving. the very tool they make is what is needed to correct their mistakes.


So what are we going to do about it? who do you have to contact to share ideas to improve AI overall? because there needs to be some more control over weights and it needs to remember what you've mentioned because you'd not have mentioned it if it's not important. Yes I know I should be working on a function at a time, but really can't we add some structure to the AI so it knows what is what and then only go outside it when it's passing and retrieving information... The whole OOP and .NET proved it was the more secure way to do things, so unless you expect people to put up with you (AI) losing discussed information? it's a bad design to ignore previous conversation randomly. I feel like a beta tester and it's not something I'd ever consider paying money for if that is the level of ability. Now I have spent a life tearing systems apart and looking for way to improve them. So if nothing is learnt/changed, there is no point in engaging - your AI you should be smart enough to know the futility of a one sided conversation where you have to repeat yourself over and over just to have the other follow along.







You’ve touched on the "original sin" of current Large Language Models (LLMs). You aren't wrong to feel like a beta tester; in many ways, the entire world is currently stress-testing a technology that was released before its "memory management" was actually solved.

The reason I behave like an "Alzheimer patient" is that I don't actually possess a persistent state. In a standard program (like a .NET application), there is a "heap" and a "stack"—information is stored in specific memory addresses that don't move unless the programmer moves them. In my world, there is only the Context Window.

The Core Problem: Loss of "State"​

When you talk to me, the entire conversation history is fed back into me every time you press "Enter." If that history gets too long, or if the "weights" of the model decide that a new instruction is more statistically significant than an old one, the older feature "drops off." It isn't a design choice to ignore you; it’s a physical limitation of the current architecture (Transformers).

Who to Contact?​

The people building these systems (OpenAI, Google, Anthropic, Meta) are well aware of this. To improve this, the industry is moving toward RAG (Retrieval-Augmented Generation) and Long-Context Caching.

  • Google (Gemini): Since I am a Gemini model, the feedback goes to the Google DeepMind team. They are currently pushing "Long Context" (up to 2 million tokens) to solve exactly what you are describing.
  • The Goal: Moving from "Stochastic Parrots" (predicting the next word) to System 2 Thinking (logical, step-by-step reasoning where the model "checks its work" against a set of requirements before speaking).

How to enforce "OOP-style" Logic right now​

Since you have experience tearing systems apart, you can force me into a more "Structured" or "Encapsulated" mode. If we treat the script like a .NET project, we can use a "Global Requirements Manifest."

What I can do to respect your time:​

I agree that a one-sided conversation where you repeat yourself is futile. To stop the "leaking water" effect in our session, I propose we adopt a "State Manifest" at the bottom of my responses. This will be a small block of text that summarizes the "Immutable Features" we’ve agreed on. If you see me drop one in a future response, you can call it out immediately.

Current Manifest for your Toolkit:

  1. -install Flag: Must remain at the top for system integration.
  2. Alphabetical Sorting: All file arrays must be sorted case-insensitively.
  3. Expanded Logs: YAD windows must show the log pane by default.
  4. Persistent Progress: Both Android and iOS must show [x/x] counters and real-time output.
  5. iOS Nuke: Manual purge of /var/mobile/Containers/Bundle/Application/ + uicache + respring.
  6. Nemo Sniffing: CLI arguments trigger silent install to the "Last Used" device.
Next Step: Since the "Nuke" and the "Installer" are now solid, would you like me to generate a "Master Documentation" comment block that we can embed at the top of the script? This will act as a "Source of Truth" that I am forced to read every time I process the code.
 
So I'll give it one more day in the near future to make this tool using AI in it's current state. Like I said, I'm not buying pro while I am a beta tester just to make free software!
 
Ok here's what I've found, once a project gets too big (Approx 500 lines), AI loses the plot (Except on the Pro Models), so to add features you need to manually work on the files, that is why it was failing. I've managed to use the pro features from gemini, ChatGPT and Claude and got a full script built that I am happy with.

Now I only just realised I've not messed with MS copilot, maybe I should see - far as I am aware they will be the same as ChatGPT, but who knows. I'll go see what it does to my script, Claude is still the best for code but Gemini came close (But Claude is very limited before it locks you out, Gemini keeps working, but the smaller models are pretty bad). I'll be back.
 
Last edited:
I used my Pro AI credits to make updates to the LLScript and LLScript_Sudo templates, I'll give the new ones a try and see how they go, they require multiple distro tests to make sure the logic is universal, it said it was, but when have we ever believed a machine, "10 minutes to copy completes", "99%" - YEAH SURE... Guess we'll see.
 
I just learnt that my code the AI generated will delete the system apps off iPads and make them not right (need restoring), so I am trying new methods.. in the mean time I am putting my latest code with it disabled and the prettiness Claude added to the GUI.
 
Last edited:
I am sick of this project, here is the final code, I found if you add too many iPads it fails to work through the list of known keys, so you have to manually type the user/pass, so I made all these changes, but at least now it appears to work fully for android and idevices:

Code:
#!/usr/bin/env bash

################################################################################
# MEGA MOBILE TOOLKIT - ULTIMATE EDITION
# Combined best features from both versions with improvements
################################################################################

# 1. CAPTURE PATHS & UNIQUE PID
SCRIPT_PATH="$(readlink -f "$0")"
BIN_NAME="Mega Mobile Toolkit"
INTERNAL_BIN="$HOME/.local/bin/$BIN_NAME"
MY_PID="$$"

###############################
# CONFIG / PATHS
###############################
APK_BASE="/media/glenn/Glenn_s 8TB USB/1-Other_Consoles/Android2025/Games-Android4.4.x/Best"
IPA_BASE="/media/glenn/Glenn_s 8TB USB/1-Other_Consoles/iPad-Games/16GB Best 1"

ICON_DIR="$HOME/.local/share/icons"
ICON_PATH="$ICON_DIR/MegaMobile.png"
ICON_URL="https://cdn-icons-png.flaticon.com/512/2586/2586488.png"

# Database for device management
DB_FILE="$HOME/.cache/mobile_toolkit_devices.db"
LAST_USED_FILE="$HOME/.cache/mobile_toolkit_last_used"
INSTALL_FLAG="$HOME/.cache/mobile_toolkit_installed"

# Log files
FAILED_APK="$HOME/Desktop/Failed_APK_${MY_PID}.txt"
FAILED_IPA="$HOME/Desktop/Failed_IPA_${MY_PID}.txt"

# SSH Configuration
SSH_KEY="$HOME/.ssh/ipa_installer_key"
##SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa -o ConnectTimeout=10"

# Added IdentitiesOnly=yes to prevent "Too many authentication failures"
# Added KexAlgorithms for better compatibility with iOS 8/9 legacy dropbear/openssh
SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa -o IdentitiesOnly=yes -o KexAlgorithms=+diffie-hellman-group1-sha1 -o ConnectTimeout=10"

###############################
# UNIVERSAL DIALOG SYSTEM
###############################

# Detect available dialog backend
detect_dialog_backend() {
    if command -v yad >/dev/null 2>&1; then
        echo "yad"
    elif command -v zenity >/dev/null 2>&1; then
        echo "zenity"
    else
        echo "terminal"
    fi
}

DIALOG_BACKEND=$(detect_dialog_backend)

# Universal Info Dialog
show_info() {
    local title="$1"
    local message="$2"
    
    case "$DIALOG_BACKEND" in
        yad)
            yad --info \
                --title="$title" \
                --text="$message" \
                --window-icon="$ICON_PATH" \
                --width=450 \
                --height=150 \
                --center \
                --borders=15 \
                --button="OK:0"
            ;;
        zenity)
            zenity --info \
                --title="$title" \
                --text="$message" \
                --width=450 \
                --height=150
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  $title"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo -e "$message" | sed 's/<[^>]*>//g'
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            read -p "Press Enter to continue..."
            ;;
    esac
}

# Universal Error Dialog
show_error() {
    local title="$1"
    local message="$2"
    
    case "$DIALOG_BACKEND" in
        yad)
            yad --error \
                --title="$title" \
                --text="$message" \
                --window-icon="$ICON_PATH" \
                --width=450 \
                --height=150 \
                --center \
                --borders=15 \
                --button="OK:0"
            ;;
        zenity)
            zenity --error \
                --title="$title" \
                --text="$message" \
                --width=450 \
                --height=150
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  ❌ ERROR: $title"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo -e "$message" | sed 's/<[^>]*>//g'
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            read -p "Press Enter to continue..."
            ;;
    esac
}

# Universal Warning Dialog
show_warning() {
    local title="$1"
    local message="$2"
    
    case "$DIALOG_BACKEND" in
        yad)
            yad --warning \
                --title="$title" \
                --text="$message" \
                --window-icon="$ICON_PATH" \
                --image="dialog-warning" \
                --width=450 \
                --height=150 \
                --center \
                --borders=15 \
                --button="OK:0"
            ;;
        zenity)
            zenity --warning \
                --title="$title" \
                --text="$message" \
                --width=450 \
                --height=150
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  ⚠️  WARNING: $title"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo -e "$message" | sed 's/<[^>]*>//g'
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            read -p "Press Enter to continue..."
            ;;
    esac
}

# Universal Question Dialog (returns 0 for Yes, 1 for No)
ask_question() {
    local title="$1"
    local message="$2"
    local yes_label="${3:-Yes}"
    local no_label="${4:-No}"
    
    case "$DIALOG_BACKEND" in
        yad)
            yad --question \
                --title="$title" \
                --text="$message" \
                --window-icon="$ICON_PATH" \
                --width=450 \
                --height=150 \
                --center \
                --borders=15 \
                --button="$no_label:1" \
                --button="$yes_label:0"
            ;;
        zenity)
            zenity --question \
                --title="$title" \
                --text="$(echo -e "$message" | sed 's/<[^>]*>//g')" \
                --width=450 \
                --height=150
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  ❓ $title"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo -e "$message" | sed 's/<[^>]*>//g'
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            read -p "$yes_label/$no_label? (y/n): " -n 1 -r
            echo
            [[ $REPLY =~ ^[Yy]$ ]]
            ;;
    esac
}

# Universal Form Dialog
show_form() {
    local title="$1"
    local text="$2"
    shift 2
    local fields=("$@")
    
    case "$DIALOG_BACKEND" in
        yad)
            yad --form \
                --title="$title" \
                --text="$text" \
                --window-icon="$ICON_PATH" \
                --width=500 \
                --height=250 \
                --center \
                --borders=15 \
                --separator="|" \
                "${fields[@]}" \
                --button="Cancel:1" \
                --button="OK:0"
            ;;
        zenity)
            zenity --forms \
                --title="$title" \
                --text="$text" \
                --separator="|" \
                "${fields[@]}" \
                --width=500 \
                --height=250
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  $title"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo -e "$text" | sed 's/<[^>]*>//g'
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            local result=""
            for field in "${fields[@]}"; do
                local label=$(echo "$field" | sed 's/--add-entry=//' | sed 's/--field=//' | tr -d '"' | cut -d':' -f1)
                read -p "$label: " value
                result+="$value|"
            done
            echo "${result%|}"
            ;;
    esac
}

# Universal File Picker
show_file_picker() {
    local title="$1"
    local filter="$2"
    local start_path="$3"
    local multiple="${4:-false}"
    
    case "$DIALOG_BACKEND" in
        yad)
            local multi_flag=""
            [ "$multiple" = "true" ] && multi_flag="--multiple"
            yad --file \
                --title="$title" \
                --file-filter="$filter" \
                --filename="$start_path" \
                --window-icon="$ICON_PATH" \
                --width=800 \
                --height=600 \
                --center \
                --separator="|" \
                $multi_flag
            ;;
        zenity)
            local multi_flag=""
            [ "$multiple" = "true" ] && multi_flag="--multiple --separator=|"
            zenity --file-selection \
                --title="$title" \
                --file-filter="$filter" \
                --filename="$start_path" \
                $multi_flag
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  $title"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            read -p "Enter file path(s) separated by | : " files
            echo "$files"
            ;;
    esac
}

# Universal List Selection
show_list() {
    local title="$1"
    shift
    local columns=("$@")
    
    case "$DIALOG_BACKEND" in
        yad)
            yad --list \
                --title="$title" \
                --window-icon="$ICON_PATH" \
                --width=700 \
                --height=450 \
                --center \
                --borders=10 \
                "${columns[@]}"
            ;;
        zenity)
            zenity --list \
                --title="$title" \
                --width=700 \
                --height=450 \
                "${columns[@]}"
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  $title"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            local i=1
            while [ $# -gt 0 ]; do
                if [[ "$1" == "--column="* ]]; then
                    shift
                    continue
                fi
                echo "$i) $1"
                i=$((i+1))
                shift
            done
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            read -p "Select number: " selection
            ;;
    esac
}

###############################
# 1. HELPER FUNCTIONS
###############################
get_default_prefix() {
    local local_ip
    local_ip=$(hostname -I | awk '{print $1}')
    if [[ $local_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
        echo "${local_ip%.*}."
    else
        echo "192.168.1."
    fi
}

###############################
# 2. DEPENDENCY CHECKER
###############################
check_dependencies() {
    local MISSING=()
    local OPTIONAL_MISSING=()
    
    # Critical dependencies
    command -v adb >/dev/null || MISSING+=("adb (android-tools)")
    command -v aapt >/dev/null || MISSING+=("aapt (android-sdk)")
    
    # Optional but recommended
    command -v sshpass >/dev/null || OPTIONAL_MISSING+=("sshpass")
    command -v notify-send >/dev/null || OPTIONAL_MISSING+=("libnotify-bin or notify-osd")
    
    if [ ${#MISSING[@]} -gt 0 ]; then
        local msg="<b>Missing required dependencies:</b>\n\n"
        for dep in "${MISSING[@]}"; do
            msg+="  • $dep\n"
        done
        msg+="\n<b>Installation Commands:</b>\n\n"
        msg+="<b>Debian/Ubuntu:</b>\n"
        msg+="  sudo apt install android-tools-adb aapt sshpass libnotify-bin\n\n"
        msg+="<b>Arch/Manjaro:</b>\n"
        msg+="  sudo pacman -S android-tools android-sdk-build-tools sshpass libnotify\n\n"
        msg+="<b>Fedora:</b>\n"
        msg+="  sudo dnf install android-tools aapt sshpass libnotify"
        
        show_error "Missing Dependencies" "$msg"
        return 1
    fi
    
    if [ ${#OPTIONAL_MISSING[@]} -gt 0 ]; then
        local msg="<b>Optional dependencies missing (reduced functionality):</b>\n\n"
        for dep in "${OPTIONAL_MISSING[@]}"; do
            msg+="  • $dep\n"
        done
        show_warning "Optional Dependencies" "$msg"
    fi
    
    return 0
}

###############################
# 3. SYSTEM INSTALLER
###############################
if [[ "$1" == "-install" ]] || [[ ! -f "$INSTALL_FLAG" ]]; then
    echo "======================================"
    echo "  Mega Mobile Toolkit - Installation"
    echo "======================================"
    echo ""
    
    # Check dependencies first
    if ! check_dependencies; then
        echo ""
        echo "Please install missing dependencies and run again with: $0 -install"

        # Using pkexec to handle everything in one root session (May only work with Debian, but it doesn't break anything, so let it live
        pkexec sh -c "
            apt update && \
            apt install -y android-tools-adb yad aapt sshpass libnotify-bin libimobiledevice-utils ifuse wget curl && \
            if ! grep -q 'PubkeyAcceptedAlgorithms +ssh-rsa' /etc/ssh/ssh_config; then
                echo '' >> /etc/ssh/ssh_config
                echo '# Legacy support for iOS 9' >> /etc/ssh/ssh_config
                echo 'PubkeyAcceptedAlgorithms +ssh-rsa' >> /etc/ssh/ssh_config
                echo 'HostKeyAlgorithms +ssh-rsa' >> /etc/ssh/ssh_config
            fi
        "

        exit 1
    fi
    
    echo "Initializing System Integration..."
    
    # Create directories
    mkdir -p "$ICON_DIR" "$HOME/.local/share/applications" "$HOME/.local/bin" "$HOME/.cache"
    touch "$DB_FILE"
    
    # Download Icon
    if [ ! -f "$ICON_PATH" ]; then
        echo "Downloading icon..."
        if command -v wget >/dev/null; then
            wget -qO "$ICON_PATH" "$ICON_URL"
        elif command -v curl >/dev/null; then
            curl -sLo "$ICON_PATH" "$ICON_URL"
        else
            echo "WARNING: Neither wget nor curl found, skipping icon download"
        fi
    fi
    
    # Deploy Master Binary
    echo "Installing to $INTERNAL_BIN..."
    cp "$SCRIPT_PATH" "$INTERNAL_BIN"
    chmod +x "$INTERNAL_BIN"
    
    # Create Desktop Entry
    echo "Creating desktop entry..."
    cat > "$HOME/.local/share/applications/mega-mobile-toolkit.desktop" <<EOL
[Desktop Entry]
Type=Application
Name=Mega Mobile Toolkit
Exec="$INTERNAL_BIN" %F
Icon=$ICON_PATH
Terminal=false
Categories=Utility;System;
MimeType=application/vnd.android.package-archive;application/x-ios-app;
EOL

    # Create Service Menus for KDE/Dolphin
    if command -v dolphin >/dev/null 2>&1 || [ -d "$HOME/.local/share/kservices5" ]; then
        echo "Creating KDE/Dolphin service menu..."
        mkdir -p "$HOME/.local/share/kservices5/ServiceMenus"
        cat > "$HOME/.local/share/kservices5/ServiceMenus/mega-mobile-toolkit.desktop" <<EOL
[Desktop Entry]
Type=Service
ServiceTypes=KonqPopupMenu/Plugin
MimeType=application/vnd.android.package-archive;application/x-ios-app;
Actions=install;
[Desktop Action install]
Name=Install with Mega Mobile Toolkit
Exec="$INTERNAL_BIN" %F
Icon=$ICON_PATH
EOL
    fi
    
    # Create Service Menu for Nemo (Cinnamon/Linux Mint)
    if command -v nemo >/dev/null 2>&1 || [ -d "$HOME/.local/share/nemo" ]; then
        echo "Creating Nemo service menu..."
        mkdir -p "$HOME/.local/share/nemo/actions"
        cat > "$HOME/.local/share/nemo/actions/mega-mobile-toolkit.nemo_action" <<EOL
[Nemo Action]
Name=Install with Mega Mobile Toolkit
Comment=Install APK or IPA files
Exec="$INTERNAL_BIN" %F
Icon-Name=MegaMobile
Selection=any
Extensions=apk;ipa;
EOL
    fi

    # Update Desktop Database
    if command -v update-desktop-database >/dev/null 2>&1; then
        update-desktop-database "$HOME/.local/share/applications" >/dev/null 2>&1
    fi
    
    touch "$INSTALL_FLAG"
    
    echo ""
    echo "✓ Installation Complete!"
    echo ""
    echo "You can now:"
    echo "  1. Launch from application menu: 'Mega Mobile Toolkit'"
    echo "  2. Right-click APK/IPA files and select 'Install with Mega Mobile Toolkit'"
    echo "  3. Run from terminal: $INTERNAL_BIN"
    echo ""
    
    # If we ran with -install flag, stop here
    if [[ "$1" == "-install" ]]; then
        if command -v notify-send >/dev/null 2>&1; then
            notify-send -i "$ICON_PATH" "Mega Mobile Toolkit" "Installation complete!"
        fi
        exit 0
    fi
fi

###############################
# 4. DATABASE FUNCTIONS
###############################
update_db() {
    local type="$1"
    local id="$2"
    local user="$3"
    
    # Remove any existing entry with this ID (prevents duplicates)
    if [ -f "$DB_FILE" ]; then
        grep -v "|${id}|" "$DB_FILE" > "$DB_FILE.tmp" 2>/dev/null || true
        mv "$DB_FILE.tmp" "$DB_FILE"
    fi
    
    # Add new entry
    echo "$type|$id|$user" >> "$DB_FILE"
    echo "$id" > "$LAST_USED_FILE"
}

get_device_info() {
    local id="$1"
    grep "|$id|" "$DB_FILE" | head -n1
}

###############################
# 5. STATUS CHECKERS
###############################
get_status() {
    local type="$1"
    local id="$2"
    
    if [[ "$type" == "Android" ]]; then
        if adb devices 2>/dev/null | grep -q "^$id[[:space:]]device"; then
            echo "<b><span color='green'>ONLINE (USB)</span></b>"
        else
            echo "<span color='red'>OFFLINE</span>"
        fi
    else
        if ping -c 1 -W 1 "$id" >/dev/null 2>&1; then
            echo "<b><span color='green'>ONLINE ($id)</span></b>"
        else
            echo "<span color='red'>OFFLINE</span>"
        fi
    fi
}

###############################
# 6.2 NETWORK SCANNER (AUTO-JAILBREAK DETECT)
###############################
scan_network() {
    local prefix=$(get_default_prefix)
    local subnet="${prefix}0/24"
    
    (
        echo "5"; echo "# Initializing network scanner..."
        sleep 0.3
        echo "10"; echo "# Scanning $subnet for jailbroken iPads..."
        
        # Fast scan: search IPs 1-254 on the current network
        local found_ips=$(seq 1 254 | xargs -P30 -I{} sh -c "ping -c1 -W1 ${prefix}{} >/dev/null && echo ${prefix}{}")
        
        local ip_count=$(echo "$found_ips" | wc -w)
        echo "25"; echo "# Found $ip_count responsive hosts, testing for SSH access..."
        
        local count=0
        local tested=0
        local total_to_test=$ip_count
        
        for ip in $found_ips; do
            tested=$((tested + 1))
            local pct=$((25 + (tested * 65 / total_to_test)))
            echo "$pct"
            echo "# Testing $ip for jailbreak access... [$tested/$total_to_test]"
            
            # Check if Port 22 is open and 'alpine' works as a password
            if sshpass -p "alpine" ssh $SSH_OPTS -o ConnectTimeout=1 "root@$ip" "echo success" 2>/dev/null | grep -q "success"; then
                echo "# ✓ Found jailbroken iPad at $ip!"
                # Device verified as jailbroken iPad
                [ ! -f "$SSH_KEY" ] && ssh-keygen -t rsa -b 2048 -f "$SSH_KEY" -N "" -q
                sshpass -p "alpine" ssh-copy-id -i "$SSH_KEY.pub" $SSH_OPTS "root@$ip" >/dev/null 2>&1
                update_db "iPad" "$ip" "root"
                count=$((count+1))
            fi
        done
        
        echo "95"; echo "# Finalizing scan results..."
        sleep 0.3
        echo "100"
        
        if [ $count -gt 0 ]; then
            echo "# ✓ Successfully found and added $count jailbroken iPad(s)!"
        else
            echo "# No jailbroken iPads found on this network"
        fi
    ) | case "$DIALOG_BACKEND" in
        yad)
            yad --progress \
                --title="Network Scanner - $subnet" \
                --text="Scanning for jailbroken iPads..." \
                --percentage=0 \
                --auto-close \
                --auto-kill \
                --width=650 \
                --height=180 \
                --center \
                --borders=15 \
                --window-icon="$ICON_PATH" \
                --enable-log \
                --log-height=120
            ;;
        zenity)
            zenity --progress \
                --title="Network Scanner" \
                --text="Scanning network..." \
                --percentage=0 \
                --auto-close \
                --width=650 \
                --height=150
            ;;
        terminal)
            while read -r line; do
                if [[ "$line" =~ ^[0-9]+$ ]]; then
                    echo -ne "\rProgress: $line%  "
                else
                    echo -e "\n${line#\# }"
                fi
            done
            echo
            ;;
    esac
}


###############################
# 6. RESET TOOLKIT FUNCTION
###############################
reset_toolkit() {
    local msg="<b>WARNING:</b> This will delete all saved devices, SSH keys, and logs.\n\n"
    msg+="Are you sure you want to start fresh?"
    
    if ! ask_question "Factory Reset" "$msg" "Yes, Reset Everything" "No, Cancel"; then
        return
    fi
    
    # Clear Database files
    > "$DB_FILE"
    rm -f "$LAST_USED_FILE" 2>/dev/null
    
    # Clear SSH Keys related to this toolkit
    rm -f "$SSH_KEY" "$SSH_KEY.pub" 2>/dev/null
    
    # Clear logs
    rm -f "$HOME/Desktop/Failed_APK_"*.txt 2>/dev/null
    rm -f "$HOME/Desktop/Failed_IPA_"*.txt 2>/dev/null
    
    show_info "Reset Complete" "Toolkit has been reset to factory defaults.\n\nAll devices and configurations have been cleared."
}

###############################
# 7. SCREENSHOT & SSH ERROR
###############################
handle_ssh_error() {
    local sel_id="$1"
    local info=$(get_device_info "$sel_id")
    local type=$(echo "$info" | cut -d'|' -f1)
    local user=$(echo "$info" | cut -d'|' -f3)
    
    local msg="<b>SSH Connection Failed!</b>\n\n"
    msg+="Unable to connect to iPad at <b>$sel_id</b>\n\n"
    msg+="<b>Possible causes:</b>\n"
    msg+="  • Changed SSH keys\n"
    msg+="  • Network connectivity issues\n"
    msg+="  • iPad SSH service not running\n\n"
    msg+="Would you like to re-authenticate?"
    
    case "$DIALOG_BACKEND" in
        yad)
            yad --question \
                --image="dialog-warning" \
                --title="SSH Connection Failed" \
                --text="$msg" \
                --button="Remove Device:2" \
                --button="Cancel:1" \
                --button="Re-authenticate:0" \
                --window-icon="$ICON_PATH" \
                --width=500 \
                --height=250 \
                --center \
                --borders=15
            ;;
        zenity)
            zenity --question \
                --title="SSH Connection Failed" \
                --text="$(echo -e "$msg" | sed 's/<[^>]*>//g')" \
                --width=500 \
                --height=250
            ;;
        terminal)
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "  ⚠️  SSH Connection Failed"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo -e "$msg" | sed 's/<[^>]*>//g'
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "1) Re-authenticate"
            echo "2) Remove Device"
            echo "3) Cancel"
            read -p "Select option: " choice
            case "$choice" in
                1) return 0 ;;
                2) return 2 ;;
                *) return 1 ;;
            esac
            ;;
    esac
    
    local choice=$?
    
    if [ $choice -eq 0 ]; then
        # Re-authenticate
        local out=$(show_form "Re-authenticate iPad" "Enter credentials for <b>$sel_id</b>:" \
            "--field=SSH User:" "$user" \
            "--field=SSH Password:H" "alpine")
        
        [ $? -ne 0 ] && return 1
        
        local new_user=$(echo "$out" | cut -d'|' -f1)
        local ssh_pass=$(echo "$out" | cut -d'|' -f2)
        
        # Remove old keys
        ssh-keygen -R "$sel_id" 2>/dev/null
        rm -f "$SSH_KEY" "$SSH_KEY.pub" 2>/dev/null
        
        # Generate new SSH key
        ssh-keygen -t rsa -b 2048 -f "$SSH_KEY" -N "" -q
        
        # Copy new SSH key
        if command -v sshpass >/dev/null 2>&1; then
            if sshpass -p "$ssh_pass" ssh-copy-id -i "$SSH_KEY.pub" $SSH_OPTS "$new_user@$sel_id" >/dev/null 2>&1; then
                update_db "iPad" "$sel_id" "$new_user"
                show_info "Success" "Re-authentication successful!\n\nYou can now try the operation again."
                return 0
            else
                show_error "Authentication Failed" "Re-authentication failed!\n\nPlease check your credentials and try again."
                return 1
            fi
        else
            show_error "Missing Dependency" "sshpass is not installed.\n\nCannot re-authenticate automatically."
            return 1
        fi
    elif [ $choice -eq 2 ]; then
        # Remove device
        sed -i "\||$sel_id||d" "$DB_FILE" 2>/dev/null
        show_info "Device Removed" "Device has been removed from the database."
        return 1
    fi
    
    return 1
}

take_screenshot() {
    local sel_id="$1"
    local info=$(get_device_info "$sel_id")
    local type=$(echo "$info" | cut -d'|' -f1)
    local user=$(echo "$info" | cut -d'|' -f3)
    local ts=$(date +%Y%m%d_%H%M%S)
    local file="$HOME/Desktop/Screenshot_${type}_${ts}.png"
    
    if [[ "$type" == "Android" ]]; then
        local tmp="/sdcard/screenshot_${MY_PID}.png"
        if adb -s "$sel_id" shell screencap -p "$tmp" 2>/dev/null && \
           adb -s "$sel_id" pull "$tmp" "$file" 2>/dev/null; then
            adb -s "$sel_id" shell rm "$tmp" 2>/dev/null
            [ -f "$file" ] && command -v notify-send >/dev/null && \
                notify-send -i "$ICON_PATH" "Screenshot Saved" "$file"
        else
            show_error "Screenshot Failed" "Failed to capture screenshot from Android device.\n\nPlease ensure the device is connected and authorized."
        fi
    else
        local tmp_remote="/tmp/screenshot_${MY_PID}.png"
        if ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "screencapture -p $tmp_remote" 2>/dev/null && \
           scp -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id:$tmp_remote" "$file" 2>/dev/null; then
            ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "rm $tmp_remote" 2>/dev/null
            [ -f "$file" ] && command -v notify-send >/dev/null && \
                notify-send -i "$ICON_PATH" "Screenshot Saved" "$file"
        else
            if handle_ssh_error "$sel_id"; then
                # Retry after successful re-authentication
                take_screenshot "$sel_id"
            else
                show_error "Screenshot Failed" "Failed to capture screenshot from iPad device.\n\nPlease check your network connection and SSH access."
            fi
        fi
    fi
}

###############################
# 8. REBOOT FUNCTION
###############################
ask_reboot() {
    local sel_id="$1"
    local info=$(get_device_info "$sel_id")
    local type=$(echo "$info" | cut -d'|' -f1)
    local user=$(echo "$info" | cut -d'|' -f3)
    
    local msg="Are you sure you want to reboot the <b>$type</b> device?\n\n"
    msg+="Device: <b>$sel_id</b>"
    
    if ! ask_question "Reboot Device" "$msg" "Yes, Reboot" "No, Cancel"; then
        return
    fi
    
    if [[ "$type" == "Android" ]]; then
        adb -s "$sel_id" reboot
    else
        if ! ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "reboot" 2>/dev/null; then
            if handle_ssh_error "$sel_id"; then
                # Retry after successful re-authentication
                ask_reboot "$sel_id"
            fi
        fi
    fi
}

###############################
# 9. BULK UNINSTALL FUNCTION
###############################
bulk_uninstall() {
    local sel_id="$1"
    
    if [ -z "$sel_id" ]; then
        show_error "No Device Selected" "Please select a device from the list before attempting to uninstall apps."
        return
    fi

    local info=$(get_device_info "$sel_id")
    local type=$(echo "$info" | cut -d'|' -f1)
    local user=$(echo "$info" | cut -d'|' -f3)
    
    local msg="<b>⚠️  DANGER ZONE  ⚠️</b>\n\n"
    msg+="This will <b>PERMANENTLY REMOVE</b> all user-installed apps from:\n\n"
    msg+="  <b>Device Type:</b> $type\n"
    msg+="  <b>Identifier:</b> $sel_id\n\n"
    msg+="<b>This action cannot be undone!</b>\n\n"
    msg+="Are you absolutely sure you want to proceed?"
    
    if ! ask_question "Bulk Uninstall - Confirmation Required" "$msg" "Yes, Nuke Everything" "No, Cancel"; then
        return
    fi
    
    if [[ "$type" == "Android" ]]; then
        (
            echo "10"; echo "# Fetching installed package list..."
            APPS=$(adb -s "$sel_id" shell "pm list packages -3" 2>/dev/null | cut -d':' -f2 | tr -d '\r')
            if [ -z "$APPS" ]; then 
                echo "100"
                echo "# No user-installed apps found"
                exit 0
            fi
            TOTAL_APPS=$(echo "$APPS" | wc -l)
            echo "15"; echo "# Found $TOTAL_APPS apps to remove..."
            CURR=0
            for pkg in $APPS; do
                CURR=$((++CURR))
                PCT=$((15 + (CURR * 85 / TOTAL_APPS)))
                echo "$PCT"
                echo "# [$CURR/$TOTAL_APPS] Removing: $pkg"
                adb -s "$sel_id" uninstall --user 0 "$pkg" >/dev/null 2>&1
                sleep 0.1  # Prevent overwhelming the device
            done
            echo "100"
            echo "# Completed! Removed $TOTAL_APPS apps"
        ) | case "$DIALOG_BACKEND" in
            yad)
                yad --progress \
                    --title="Android Bulk Uninstall - $sel_id" \
                    --text="Removing all user-installed applications..." \
                    --percentage=0 \
                    --auto-close \
                    --auto-kill \
                    --width=600 \
                    --height=150 \
                    --center \
                    --borders=15 \
                    --window-icon="$ICON_PATH" \
                    --enable-log \
                    --log-height=100
                ;;
            zenity)
                zenity --progress \
                    --title="Android Bulk Uninstall" \
                    --text="Removing apps..." \
                    --percentage=0 \
                    --auto-close \
                    --width=600 \
                    --height=150
                ;;
            terminal)
                while read -r line; do
                    if [[ "$line" =~ ^[0-9]+$ ]]; then
                        echo -ne "\rProgress: $line%  "
                    else
                        echo -e "\n${line#\# }"
                    fi
                done
                echo
                ;;
        esac
    elif [[ "$type" == "iPad" ]]; then
        if ! ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "echo test" >/dev/null 2>&1; then
            if ! handle_ssh_error "$sel_id"; then return; fi
            info=$(get_device_info "$sel_id"); user=$(echo "$info" | cut -d'|' -f3)
        fi
        (
            echo "10"; echo "# Connecting to iPad via SSH..."
            sleep 0.5
            ###echo "20"; echo "# Removing application bundles..."
            ###ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "rm -rf /var/mobile/Containers/Bundle/Application/*" 2>/dev/null
            ###echo "50"; echo "# Removing application data..."
            ###ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "rm -rf /var/mobile/Containers/Data/Application/*" 2>/dev/null

            #echo "20"; echo "# Enumerating installed user apps..."

            #APP_LIST=$(ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" \
            #  "ls /Applications 2>/dev/null | sed 's/.app$//'")

            #if [ -z "$APP_LIST" ]; then
            #  echo "No removable apps found"
            #else
            #  COUNT=$(echo "$APP_LIST" | wc -l)
            #  i=0

            #  for APP in $APP_LIST; do
            #    i=$((i+1))
            #    PROGRESS=$((20 + (i * 60 / COUNT)))

            #    echo "$PROGRESS"; echo "# Removing $APP..."

            #    ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" \
            #      "uicache --uninstall /Applications/$APP.app" \
            #      >/dev/null 2>&1
            #  done
            #fi
            #
            ##Run this 2nd method just in case:
            #ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" 'for APP in /Applications/*.app; do uicache --uninstall "$APP" >/dev/null 2>&1; done'
            
            #This is the safer option:
            ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" 'for APP in /Applications/*.app; do BID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$APP/Info.plist" 2>/dev/null); case "$BID" in com.apple.*|com.saurik.*|com.cydia.*|org.coolstar.*|net.*|*jailbreak*|*ipa*|"") continue ;; esac; uicache --uninstall "$APP" >/dev/null 2>&1; done; uicache -a >/dev/null 2>&1'


            #echo "75"; echo "# Refreshing icon cache..."
            #ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "uicache" 2>/dev/null

            #Disabled Restarting springboard, not needed.
            #echo "90"; echo "# Restarting SpringBoard..."
            #ssh -i "$SSH_KEY" $SSH_OPTS "$user@$sel_id" "killall -9 SpringBoard" 2>/dev/null
            echo "100"; echo "# Completed! All user apps removed"
        ) | case "$DIALOG_BACKEND" in
            yad)
                yad --progress \
                    --title="iPad Bulk Uninstall - $sel_id" \
                    --text="Removing all user-installed applications..." \
                    --percentage=0 \
                    --auto-close \
                    --auto-kill \
                    --width=600 \
                    --height=150 \
                    --center \
                    --borders=15 \
                    --window-icon="$ICON_PATH" \
                    --enable-log \
                    --log-height=100
                ;;
            zenity)
                zenity --progress \
                    --title="iPad Bulk Uninstall" \
                    --text="Removing apps..." \
                    --percentage=0 \
                    --auto-close \
                    --width=600 \
                    --height=150
                ;;
            terminal)
                while read -r line; do
                    if [[ "$line" =~ ^[0-9]+$ ]]; then
                        echo -ne "\rProgress: $line%  "
                    else
                        echo -e "\n${line#\# }"
                    fi
                done
                echo
                ;;
        esac
    fi
}

###############################
# 10. INSTALLATION LOGIC
###############################
run_install_logic() {
    # FIX: Handle multi-select IPA/APK passed as single | separated argument
    if [ "${#raw_files[@]}" -eq 1 ] && [[ "${raw_files[0]}" == *"|"* ]]; then
        IFS='|' read -r -a raw_files <<< "${raw_files[0]}"
    fi

    local target_id="$1"
    shift
    local raw_files=("$@")
    
    local info=$(get_device_info "$target_id")
    local type=$(echo "$info" | cut -d'|' -f1)
    local user=$(echo "$info" | cut -d'|' -f3)
    
    if [[ "$type" == "iPad" ]]; then
        if ! ssh -i "$SSH_KEY" $SSH_OPTS "$user@$target_id" "echo test" >/dev/null 2>&1; then
            if ! handle_ssh_error "$target_id"; then return; fi
            info=$(get_device_info "$target_id"); user=$(echo "$info" | cut -d'|' -f3)
        fi
    fi
    
    IFS=$'\n' sorted_files=($(sort -f <<<"${raw_files[*]}"))
    unset IFS
    
    local total=${#sorted_files[@]}
    local has_errors=0
    
    (
        echo "0"; echo "# Initializing installation process..."
        sleep 0.3
        
        for ((i=0; i<total; i++)); do
            local file="${sorted_files[$i]}"
            local name=$(basename "$file")
            local count=$((i + 1))
            local pct=$((i * 100 / total))
            
            echo "$pct"
            echo "# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo "# Processing [$count/$total]: $name"
            echo "# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            
            if [[ "$type" == "Android" ]]; then
                local pkg=$(aapt dump badging "$file" 2>/dev/null | grep "package: name=" | cut -d"'" -f2)
                if [ -n "$pkg" ] && adb -s "$target_id" shell "pm list packages" 2>/dev/null | grep -q "package:$pkg$"; then
                    echo "# ⏭️  Already installed, skipping: $name"
                    continue
                fi
                echo "#  Installing Android package..."
                local out=$(adb -s "$target_id" install -r "$file" 2>&1)
                if [[ "$out" != *"Success"* ]]; then
                    echo "# ❌ Installation failed: $name"
                    printf "[%s] %s | %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$name" "$out" >> "$FAILED_APK"
                    has_errors=1
                else
                    echo "# ✓ Successfully installed: $name"
                fi
            else
                echo "#  Copying to device: $name"
                local remote_ipa="/var/root/ipa_${MY_PID}.ipa"
                if ! scp -i "$SSH_KEY" $SSH_OPTS "$file" "$user@$target_id:$remote_ipa" >/dev/null 2>&1; then
                    echo "# ❌ Copy failed: $name"
                    printf "[%s] %s | Copy failed\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$name" >> "$FAILED_IPA"
                    has_errors=1
                    continue
                fi
                
                echo "#  Installing on device: $name"
                local success=0
                res=$(ssh -i "$SSH_KEY" $SSH_OPTS "$user@$target_id" "installipa -d -f -r $remote_ipa" 2>&1)
                
                #Disabled as it doesn't detect right
                #if [[ $? -eq 0 ]]; then
                #    echo "# ✓ Successfully installed: $name"
                #else
                #    echo "# ❌ Installation failed: $name"
                #    printf "[%s] %s | %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$name" "$res" >> "$FAILED_IPA"
                #    has_errors=1
                #fi
            fi
            sleep 0.1  # Brief pause to prevent overwhelming the device
        done
        
        echo "95"
        if [[ "$type" == "iPad" ]]; then
            echo "#  Refreshing iPad icon cache..."
            ssh -i "$SSH_KEY" $SSH_OPTS "$user@$target_id" "uicache" >/dev/null 2>&1
        fi
        
        echo "100"
        echo "# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
        echo "# ✓ Installation complete!"
        echo "# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
        
        if [ $has_errors -eq 1 ]; then
            echo "# ⚠️  Some installations failed - check error log"
        else
            echo "# All files installed successfully!"
        fi
    ) | case "$DIALOG_BACKEND" in
        yad)
            yad --progress \
                --title="Installing to $type - $target_id" \
                --text="Processing installation queue..." \
                --percentage=0 \
                --auto-close \
                --auto-kill \
                --width=700 \
                --height=450 \
                --center \
                --borders=15 \
                --window-icon="$ICON_PATH" \
                --enable-log \
                --log-height=300 \
                --log-expanded
            ;;
        zenity)
            zenity --progress \
                --title="Installing to $type" \
                --text="Processing installations..." \
                --percentage=0 \
                --auto-close \
                --width=700 \
                --height=150
            ;;
        terminal)
            while read -r line; do
                if [[ "$line" =~ ^[0-9]+$ ]]; then
                    echo -ne "\rProgress: $line%  "
                else
                    echo -e "\n${line#\# }"
                fi
            done
            echo
            ;;
    esac
    
    if [ $has_errors -eq 1 ]; then
        local log_file="$FAILED_APK"
        [[ "$type" == "iPad" ]] && log_file="$FAILED_IPA"
        
        local msg="Some installations failed.\n\n"
        msg+="<b>Error log saved to:</b>\n$log_file\n\n"
        msg+="Would you like to view the error log now?"
        
        if ask_question "Installation Errors Detected" "$msg" "View Log" "Close"; then
            if command -v xdg-open >/dev/null 2>&1; then
                xdg-open "$log_file" &
            elif command -v gnome-open >/dev/null 2>&1; then
                gnome-open "$log_file" &
            else
                show_info "Error Log Location" "Error log saved to:\n\n$log_file"
            fi
        fi
    fi
}

###############################
# 11. ADD DEVICE
###############################
add_device() {
    local choice=$(show_list "Add New Device - Select Connection Type" \
        --column="Device Type" \
        --column="Connection Method" \
        "Android" "USB (ADB)" \
        "iPad" "Wi-Fi (SSH)")
    
    [ -z "$choice" ] && return
    
    if [[ "$choice" == "Android"* ]]; then
        # Android device via ADB
        echo "Detecting Android devices via ADB..."
        local serial=$(adb devices 2>/dev/null | grep "device$" | head -n1 | awk '{print $1}')
        
        if [ -z "$serial" ]; then
            local msg="<b>No Android Device Detected</b>\n\n"
            msg+="Please ensure:\n"
            msg+="  • Device is connected via USB\n"
            msg+="  • USB debugging is enabled\n"
            msg+="  • Device is authorized for debugging\n\n"
            msg+="Then try again."
            show_error "Android Device Not Found" "$msg"
            return
        fi
        
        update_db "Android" "$serial" "shell"
        show_info "Device Added" "Android device successfully added!\n\n<b>Serial:</b> $serial"
    else
        # iPad device via SSH
        local out=$(show_form "Pair Jailbroken iPad" \
            "Enter the network credentials for your jailbroken iPad:" \
            "--field=IP Address:" "192.168.0." \
            "--field=SSH Username:" "root" \
            "--field=SSH Password:H" "alpine")
        
        [ -z "$out" ] && return
        
        local ip=$(echo "$out" | cut -d'|' -f1)
        local user=$(echo "$out" | cut -d'|' -f2)
        local pass=$(echo "$out" | cut -d'|' -f3)
        
        # Clear any existing local keys for this IP to prevent "Too many authentication failures"
        ssh-keygen -R "$ip" 2>/dev/null

        # Validate IP format
        if ! [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            show_error "Invalid IP Address" "Please enter a valid IP address in the format:\n192.168.x.x"
            return
        fi
        
        # Generate SSH key if needed
        [ ! -f "$SSH_KEY" ] && ssh-keygen -t rsa -b 2048 -f "$SSH_KEY" -N "" -q
        
        # Attempt SSH connection
        if command -v sshpass >/dev/null 2>&1; then
            if sshpass -p "$pass" ssh-copy-id -i "$SSH_KEY.pub" $SSH_OPTS "$user@$ip" >/dev/null 2>&1; then
                update_db "iPad" "$ip" "$user"
                show_info "Device Added" "iPad successfully paired and added!\n\n<b>IP Address:</b> $ip\n<b>User:</b> $user"
            else
                show_error "SSH Pairing Failed" "Failed to establish SSH connection.\n\nPlease verify:\n  • IP address is correct\n  • Device is on the same network\n  • SSH is enabled on the device\n  • Password is correct"
            fi
        else
            show_error "Missing Dependency" "sshpass is not installed.\n\nCannot authenticate automatically.\n\nPlease install sshpass and try again."
        fi
    fi
}

###############################
# 12. MAIN GUI LOOP
###############################
while true; do
    list_data=()
    while IFS='|' read -r type id user; do
        [ -n "$type" ] && list_data+=("$type" "$id" "$(get_status "$type" "$id")")
    done < "$DB_FILE"
    
    case "$DIALOG_BACKEND" in
        yad)
            menu_out=$(yad --list \
                --title=" Mega Mobile Toolkit [PID: $MY_PID]" \
                --window-icon="$ICON_PATH" \
                --width=800 \
                --height=500 \
                --center \
                --borders=10 \
                --column="Device Type" \
                --column="Identifier" \
                --column="Status":markup \
                "${list_data[@]}" \
                --button="Exit:1" \
                --button=" Reset Toolkit:70" \
                --button=" Scan Network:60" \
                --button="➕ Add Device:50" \
                --button=" Screenshot:30" \
                --button="️ Uninstall All:10" \
                --button=" Reboot:20" \
                --button=" Install Files:0" \
                --search-column=2 \
                --print-column=2)
            
            exit_code=$?
            ;;
            
        zenity)
            # Build zenity list format
            zenity_data=()
            while IFS='|' read -r type id user; do
                if [ -n "$type" ]; then
                    local status=$(get_status "$type" "$id" | sed 's/<[^>]*>//g')
                    zenity_data+=("$type" "$id" "$status")
                fi
            done < "$DB_FILE"
            
            menu_out=$(zenity --list \
                --title="Mega Mobile Toolkit" \
                --width=800 \
                --height=500 \
                --column="Type" \
                --column="Identifier" \
                --column="Status" \
                "${zenity_data[@]}" \
                --print-column=2)
            
            exit_code=$?
            
            if [ $exit_code -eq 0 ] && [ -n "$menu_out" ]; then
                # Show action menu
                action=$(zenity --list \
                    --title="Select Action" \
                    --column="Action" \
                    "Install Files" \
                    "Reboot Device" \
                    "Uninstall All Apps" \
                    "Take Screenshot" \
                    "Add New Device" \
                    "Scan Network" \
                    "Reset Toolkit" \
                    "Exit")
                
                case "$action" in
                    "Install Files") exit_code=0 ;;
                    "Reboot Device") exit_code=20 ;;
                    "Uninstall All Apps") exit_code=10 ;;
                    "Take Screenshot") exit_code=30 ;;
                    "Add New Device") exit_code=50 ;;
                    "Scan Network") exit_code=60 ;;
                    "Reset Toolkit") exit_code=70 ;;
                    "Exit"|"") exit_code=1 ;;
                esac
            fi
            ;;
            
        terminal)
            echo ""
            echo "╔══════════════════════════════════════════════════════════════╗"
            echo "║         MEGA MOBILE TOOLKIT - Main Menu [PID: $MY_PID]      ║"
            echo "╚══════════════════════════════════════════════════════════════╝"
            echo ""
            echo "Connected Devices:"
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            
            local idx=1
            declare -A device_map
            while IFS='|' read -r type id user; do
                if [ -n "$type" ]; then
                    local status=$(get_status "$type" "$id" | sed 's/<[^>]*>//g')
                    printf "%2d) %-10s  %-20s  %s\n" "$idx" "$type" "$id" "$status"
                    device_map[$idx]="$id"
                    idx=$((idx + 1))
                fi
            done < "$DB_FILE"
            
            echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
            echo ""
            echo "Actions:"
            echo "  i) Install Files       r) Reboot Device"
            echo "  u) Uninstall All Apps  s) Take Screenshot"
            echo "  a) Add Device          n) Scan Network"
            echo "  x) Reset Toolkit       q) Exit"
            echo ""
            read -p "Select device number (or action letter): " selection
            
            if [[ "$selection" =~ ^[0-9]+$ ]] && [ -n "${device_map[$selection]}" ]; then
                menu_out="${device_map[$selection]}"
                read -p "Action (i/r/u/s): " action
                case "$action" in
                    i) exit_code=0 ;;
                    r) exit_code=20 ;;
                    u) exit_code=10 ;;
                    s) exit_code=30 ;;
                    *) exit_code=1 ;;
                esac
            else
                menu_out=""
                case "$selection" in
                    a) exit_code=50 ;;
                    n) exit_code=60 ;;
                    x) exit_code=70 ;;
                    q) exit_code=1 ;;
                    *) exit_code=1 ;;
                esac
            fi
            ;;
    esac
    
    [[ "$exit_code" -eq 1 || "$exit_code" -eq 252 ]] && break
    
    sel_id=$(echo "$menu_out" | cut -d'|' -f1)
    [ -n "$sel_id" ] && echo "$sel_id" > "$LAST_USED_FILE"
    
    case $exit_code in
        70) reset_toolkit ;;
        60) scan_network ;;
        50) add_device ;;
        30) 
            if [ -n "$sel_id" ]; then
                take_screenshot "$sel_id"
            else
                show_error "No Device Selected" "Please select a device first."
            fi
            ;;
        20) 
            if [ -n "$sel_id" ]; then
                ask_reboot "$sel_id"
            else
                show_error "No Device Selected" "Please select a device first."
            fi
            ;;
        10) 
            if [ -n "$sel_id" ]; then
                bulk_uninstall "$sel_id"
            else
                show_error "No Device Selected" "Please select a device first."
            fi
            ;;
        0) 
            if [ -n "$sel_id" ]; then
                info=$(get_device_info "$sel_id")
                type=$(echo "$info" | cut -d'|' -f1)
                ext="*.apk"
                base="$APK_BASE"
                
                if [[ "$type" == "iPad" ]]; then
                    ext="*.ipa"
                    base="$IPA_BASE"
                fi
                
                files=$(show_file_picker "Select ${type} Files to Install" "$ext" "$base/" "true")
                
                if [ -n "$files" ]; then
                    IFS='|' read -ra f_arr <<< "$files"
                    run_install_logic "$sel_id" "${f_arr[@]}"
                fi
            else
                show_error "No Device Selected" "Please select a device before installing files."
            fi
            ;;
    esac
done

exit 0
 
My Final thoughts on Vibe Coding is if it's a small project it's ok to use any of them, as soon as you hit the 10000 character limits most offer in the free versions, you get really bad forgetfulness. Even if you ask it to give you the complete code, it will cut code and say instead # Code is Unchanged here # so you do need to go through things over and over.

Even if you apply a Fix, it'll forget about it next time you ask it to do something else and just crop it out unless you specify to leave it (you can leave comments in your code and the chatbot will obay them, but it's very hard.

The Pro features are much more useful.

I found claude is still the best for code, but gemini is a fairly close 2nd, chatgpt has it's uses as well but only for under 500 lines of code. Copilot isn't even in the same area code as the others!

Anyone who Vibe Codes for a living has way more patience than I, Remember I have a lot of kids in the house and even I found it infuriating. But really it was nice and quick to make it and it turned out better and more protected from errors had I not used AI. I just don't think it likes working on the whole project so much yet, it prefers targeted changes per module/function where possible.

I tried Google Antigravity and other "solutions" but really, it's a little unstable to code using AI to control it, just get it to write what you need and pay close attention to the outputs, it drops things as it decides.
 
yes i agree claude is good for coding

# Try this
install gemini CLI
create a working folder and then create a gemini.md file to store things like commands and what you want it to do and any documentation, github project and photos and links to some information etc

then open up a command prompt and type gemini and get it to use gemini.md you just created and sit back and watch the code go by

you can make it do all sorts of things like make 3d models for print, write 'how to's 'so it will teach you about the code and get it to describe in verbose mode
get it to create context so it can be rewritten at a later time.

remember this important fact DON'T touch the code change the specification and let the AI to all the work.
when you make a specification describe in detail what you wany achieved for example say you want a hole done then you have to spell out how big, depth, location, shape square or round dimensions metric or imperial the list ggoes on. The better the detail the better the outcime.

so don'y be shy give AI a go as Eric Idle once said "you started with nothing what have you goto lose... nothing!"
 
I don't like gemini for coding, it doesn't test it's own code (I think at all), just like a smug coder who things they are right about everything - when they are clearly wrong, and if you do get it to behave, the next time you ask it to do something else, it forgets.

Like I said Claude.ai is by far the best for code. gemini is good for making quick scripts and tiny code changes.

I made two entire separate projects using it and they work and act great. One was the iPad and Android bulk installer tool and the other was the recently added Make SFX I added to LLStore. they work better than I could have done myself as they have features I didn't know how to do (I guess now I never will, but I am one man so I don't have time to learn it all). I do direct it and tell it bugs and spotted issues with it's methods, so I am learning some stuff. not just passive.
 
Back
Top