Full Code of IGPenguin/mobile-toolkit for AI

master 491c778f676e cached
56 files
94.1 KB
29.9k tokens
1 requests
Download .txt
Repository: IGPenguin/mobile-toolkit
Branch: master
Commit: 491c778f676e
Files: 56
Total size: 94.1 KB

Directory structure:
gitextract_fh0eifjr/

├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── count_lines.yml
│       └── shellcheck.yml
├── .gitignore
├── LICENCE.md
├── README.md
├── android/
│   ├── aanimationspeed
│   ├── aappinfo
│   ├── abounds
│   ├── abuildproject
│   ├── acamera
│   ├── acheckdevice
│   ├── acontrol
│   ├── adarkmode
│   ├── aemulator
│   ├── aerase
│   ├── afontscale
│   ├── agoogleplay
│   ├── ainstall
│   ├── akill
│   ├── alaunch
│   ├── alog
│   ├── aoptions
│   ├── apaste
│   ├── apermissionreset
│   ├── apowerbutton
│   ├── areboot
│   ├── arecord
│   ├── ascreenshot
│   ├── aservices
│   ├── atalkback
│   ├── atestmonkey
│   ├── atestmonkeykill
│   ├── auninstall
│   ├── aurl
│   ├── awipe
│   └── awireless
├── changelog.txt
├── common_tools
├── data/
│   └── .gitignore
└── ios/
    ├── icheckdevice
    ├── iconsole
    ├── iinstall
    ├── ikill
    ├── ilang
    ├── ilaunch
    ├── ilog
    ├── ioptions
    ├── iquicktime
    ├── ireboot
    ├── irecord
    ├── iscreenshot
    ├── isimulator
    └── iuninstall

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CONTRIBUTING.md
================================================
## 💬 Contributing

If you consider contributing to this repository, please first discuss the desired changes via issue, email, or any other method

### 🕵️‍ Pull request requirements

1. Implement all functions thoroughly and remove any unused or non-working code
2. Format all terminal output in a similar way as the existing
3. Ensure every code change and usage variant is tested
4. Update the README.md and changelog.txt with details of changes to the commands

### ✏️ Right to make changes

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
### 🐞 Whats wrong
~Describe requested functionality or existing bug~
### 🌈 Desired state
~Describe desired code and functional changes~
### 📏 Challenges
~Hint any special or complicated tasks~
### 📂 Sources
~Recommend related code samples or documentation~


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## ⚠️ Progress checklist
- [ ] 🏗 **Features fully completed**
- [ ] 🔬 **Shellcheck issues resolved**
- [ ] 🔨 **All changes tested**
- [ ] 💬 **Terminal output satisfactory**
- [ ] 👀 **Diff examined thoroughly**
- [ ] 📝 **API changes included in `README.md`**
- [ ] 📣 **Major changes listed in `changelog.txt`**


================================================
FILE: .github/workflows/count_lines.yml
================================================
name: Count lines
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Count lines of text in this repository
        run: git ls-files *[^.png] | xargs wc -l | grep total | awk '{print $1}' > count.txt

      - uses: pCYSl5EDgo/cat@master
        id: count
        with:
          path: count.txt

      - run: echo $TEXT
        env:
          TEXT: ${{ steps.count.outputs.text }}


================================================
FILE: .github/workflows/shellcheck.yml
================================================
name: 'Shellcheck'
on: push
jobs:
  shellcheck:
    name: Shellcheck
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - name: Shellcheck
      uses: ludeeus/action-shellcheck@master
      env:
        SHELLCHECK_OPTS: -e SC1090 -e SC2207 -e SC2001 -e SC1091
        # SC1090 - non-constant source
        # SC2207 - no arrays like this ($())
        # SC2001 - replace sed with ${variable//search/replace}
        # SC1091 - common_tools was not specified as input (see shellcheck -x)


================================================
FILE: .gitignore
================================================
*DS_Store
ios/go-ios
ios/nohup.out
ios/selfidentity.plist


================================================
FILE: LICENCE.md
================================================
MIT License

Copyright (c) 2019 IntergalacticPenguin - Adam Svoboda

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
urnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
![Header](/media/header_v2.png?raw=true)
<div id='section-id-2'/>

## What is its purpose?
🛠 **Control Android & iOS devices:** Capture screen, manage apps, simulate input, examine system logs etc.<br>

⚡️ **Speed++** Are you an app developer or a tester? Boost your effectivity, discover new tools!


<div id='section-id-8'/>

## All features

📲 **Control Android and iOS devices** or Emulators/Simulators using terminal commands<br>

🛠 **Take screeshots, change device settings**, gather app & device information<br>

⚙️ **Manage mobile applications** - install, restart, wipe data and much more<br>

📋 **Handle multiple devices effortlessly** - select from list or target all connected devices<br>

⏳ **Save your precious time** - stop doing repetitive tasks manually<br>

🔄 **Automatic update** - get new features and fixes ASAP<br>

## Table of contents
🔩 Maybe you just want to skip to [Installation](#section-id-22)?<br>
<br>
<bold>[🤖 Android Commands](#section-id-52)</bold>
- [Capture screen](#section-id-54)
	- [📸 ascreenshot](#section-id-56)
	- [🎥 arecord](#section-id-60)
- [Control device](#section-id-66)
	- [✏️ apaste](#section-id-68)
	- [🌐 aurl](#section-id-78)
	- [🏴 adarkmode](#section-id-82)
	- [🔊 atalkback](#section-id-85)
	- [📐 abounds](#section-id-88)
	- [🚗 aanimationspeed](#section-id-92)
	- [🔠 afontscale](#section-id-96)
	- [🎹 acontrol](#section-id-100)
	- [📷 acamera](#section-id-104)
	- [⚡️ awireless](#section-id-107)
	- [👋 apowerbutton](#section-id-108)
- [Manage packages](#section-id-111)
	- [🚀 alaunch](#section-id-113)
	- [🕵️ aappinfo](#section-id-118)
	- [🔪 akill](#section-id-128)
	- [🧽 aerase](#section-id-132)
	- [🚚 ainstall](#section-id-136)
	- [🗑 auninstall](#section-id-140)
	- [🔥 awipe](#section-id-146)
	- [🐁 apermissionreset](#section-id-149)
	- [🛍 agoogleplay](#section-id-153)
	- [🏭 abuildproject](#section-id-157)
- [Manage device](#section-id-161)
	- [⚙️ aoptions](#section-id-163)
	- [📜 alog](#section-id-176)
	- [📋 acheckdevice](#section-id-180)
	- [😎 aservices](#section-id-192)
	- [♻ areboot](#section-id-196)
	- [📱 aemulator](#section-id-199)
	- [🐒 atestmonkey](#section-id-215)


<strong>[🍎 iOS Commands](#section-id-233)</strong><br>
- [Capture screen](#section-id-235)
	- [📸 iscreenshot](#section-id-237)
	- [🎥 irecord](#section-id-241)
	- [📹 iquicktime](#section-id-250)
- [Manage applications](#section-id-255)
	- [🚚 iinstall](#section-id-256)
	- [🗑 iuninstall](#section-id-260)
	- [🚀 ilaunch](#section-id-266)
	- [🔪 ikill](#section-id-271)
- [Manage device](#section-id-276)
	- [⚙️ ioptions](#section-id-278)
	- [💬 ilang](#section-id-281)
	- [📜 ilog](#section-id-285)
	- [📋 icheckdevice](#section-id-288)
	- [♻ ireboot](#section-id-292)
	- [📱 isimulator](#section-id-295)
	- [🖥 iconsole](#section-id-309)

💭 Do you want to share [Feedback or Contribute](#section-id-312)?

<div id='section-id-22'/>

# 💻 Installation
<details>
	  <summary>Click here to reveal step by step guide ↓</summary>

_Note: This tool targets macOS for compatibility, but most interactions should work on any Unix system._
<br>
1. **Open terminal**
2. **Clone this repository** `git clone https://github.com/IntergalacticPenguin/mobile-toolkit.git`
3. **Setup Android tools**
	* **[Download](https://developer.android.com/studio/ "Android Studio") and install Android Studio** and **Android command line tools** (using Android Studio SDK manager)
	* **Edit .zshrc** (or .bash_profile if you have bash shell) `open -e ~/.zshrc`
	  * **Insert this line at the end** `PATH=$PATH:/Users/dummyuser/Library/Android/sdk/platform-tools export PATH`
	  * **Don't forget to replace "dummyuser" with your account username**
	  * **Use full path to the "platform-tools" directory**
	* **[Allow USB debugging](https://developer.android.com/studio/debug/dev-options) on your device, connect it and authorize your computer** (click OK on the device screen)
4. **Setup iOS tools**
	* **Install latest Xcode and iOS command line tools** using [App Store](https://apps.apple.com/cz/app/xcode/id497799835?mt=12)
	* **Install [Homebrew](https://brew.sh/ "Homberew") package manager**
	* **Run Xcode, connect iOS device to USB and authorize your computer** (click "Trust" on the device screen)
	* **Run any script i.e. `iscreenshot`, installation of all required tools will be initiated automatically** ([jq](https://stedolan.github.io/jq/) and [go-ios](https://github.com/danielpaulus/go-ios "go-ios"))
5. **Add Mobile Toolkit to $PATH**, it is mandatory for iOS scripts and it will let you run scripts in any directory
	* **Edit .zshrc** (or .bash_profile if you have bash shell) `open -e ~/.zshrc`
	  * **Insert the following lines at the end** <br> `PATH=$PATH:/Users/dummyuser/mobile-toolkit/android` <br>
	`PATH=$PATH:/Users/dummyuser/mobile-toolkit/ios`
	  * **Don't forget to replace "dummyuser" with your account username**
	  * **Use full path to the "mobile-toolkit" directory** (where you cloned this repository)
	  * **Add** `export PATH` **to the end of the file**

</details>

<div id='section-id-52'/>

# 🤖 Android Commands

<div id='section-id-54'/>

## Capture screen

<div id='section-id-56'/>

### 📸 ascreenshot
* `ascreenshot` Save screenshot to ~/Desktop
* `ascreenshot -a` Take screenshot on all connected devices

<div id='section-id-60'/>

### 🎥 arecord
1. `arecord` Record screen
2. End recording using `ctrl + c`
3. Save screen video footage to ~/Desktop
4. Records audio by default on devices running Android 12 and up (when using Scrcpy version 2.0.0 or higher)
  * `arecord <custom-name>` Specify your own filename by passing it as argument
  * `arecord -l` Use legacy `-l` option to record using ADB instead of Scrcpy

<div id='section-id-66'/>

## Control device

<div id='section-id-68'/>

### ✏️ apaste
`apaste "john.doe@fakemail.com" password1 "5005 1002 3332 1112" "2/19" 5004`

* `apaste <text>` Insert text into currently focused field
* `apaste "john.doe@fakemail.com" password1 ` Every additional argument will be inserted into subsequent field
* `apaste "This is sample multi-word text."` use "" to insert multi-word text into one field
* `apaste -l` Insert "Lorem Ipsum paragraph"
* `apaste -a <input-text>` Insert any text input (options displayed above) on all connected devices
* `apaste -a -l` Insert "Lorem Ipsum paragraph" on all connected devices

<div id='section-id-78'/>

### 🌐 aurl
* `aurl "google.com"` Open link in web browser or corresponding application
* `aurl -a "google.com"` Open link in web browser or corresponding application on all connected devices

<div id='section-id-82'/>

### 🏴 adarkmode
* `adarkmode` Toggle system dark mode

<div id='section-id-85'/>

### 🔊 atalkback
* `atalkback` Toggle TalkBack screen reader accessiblity option

<div id='section-id-88'/>

### 📐 abounds
* `abounds` Toggle UI layout bounds
* App restart may be necessary on lower APIs

<div id='section-id-92'/>

### 🚗 aanimationspeed
* `aanimationspeed` set slower animation speed or restore default
* `aanimationspeed <speed>` set animation speed multiplier

<div id='section-id-96'/>

### 🔠 afontscale
* `afontscale` set large font scale (1.3x bigger than default) or restore default
* `afontscale <scale>` set font scale multiplier

<div id='section-id-100'/>

### 🎹 acontrol
* `acontrol` start [scrcpy](https://github.com/Genymobile/scrcpy "scrcpy") session
* Provides realtime device screen mirroring and keyboard+mouse control

<div id='section-id-104'/>

### 📷 acamera
* Start the default camera application

<div id='section-id-107'/>

### ⚡️ awireless
* Enable or disable wireless ADB connection
* Use ADB and toolkit without having USB cable attached

<div id='section-id-108'/>

### 👋 apowerbutton
* Lock/Unlock the device (send Power button key event)
* Useful to unlock the device after the screen is locked automatically

<div id='section-id-111'/>

## Manage packages

<div id='section-id-113'/>

### 🚀 alaunch
* `alaunch` List third-party apps and choose one to run it
* `alaunch -s` List all available apps (including os pre-installed) and choose one to run it
* `alaunch com.dummy.package.name.app` Run app by package name

<div id='section-id-118'/>

### 🕵️ aappinfo
* `aappinfo` List foreground app information
	* Package name
	* Version
	* Last update
	* minSdk and targetSdk
	* Permissions
* (Optional) Open application settings
* `aappinfo com.dummy.package.name.app` Target specific app by passing package name as argument

<div id='section-id-128'/>

### 🔪 akill
* `akill` Restart the foreground app
* `akill com.dummy.package.name.app` Target specific app by passing package name as argument

<div id='section-id-132'/>

### 🧽 aerase
* `aerase` Delete all local data of the foreground app and restart it
* `aerase com.dummy.package.name.app` Target specific app by passing package name as argument

<div id='section-id-136'/>

### 🚚 ainstall
* `ainstall some-app-file.apk` Install and run .apk
* `ainstall -a some-app-file.apk` Install and run .apk on all connected devices

<div id='section-id-140'/>

### 🗑 auninstall
* `auninstall` Uninstall third-party app, choose from the list
* `auninstall com.dummy.package.name.app` pass package name as argument
* `auninstall -w` Uninstall all-third party packages
	* Skips some essential apps, edit IGNORED_PACKAGES in this script to customize the list to your needs

<div id='section-id-146'/>

### 🔥 awipe
* Wipe internal storage and delete all third-party apps

<div id='section-id-149'/>

### 🐁 apermissionreset
* Revoke ALL GRANTED runtime permissions for ALL apps
	* You'll have to handle permission requests upon opening almost any app

<div id='section-id-153'/>

### 🛍 agoogleplay
* `agoogleplay "Dummy App"` Search for "Dummy App" on Google Play
* `agoogleplay` Search for currently foreground app on Google Play

<div id='section-id-157'/>

### 🏭 abuildproject
* `abuildproject` Build, install and run Android project located in current directory
* `abuildproject <relative-path>` Build, install and run Android project located in \<relative-path>

<div id='section-id-161'/>

## Manage device

<div id='section-id-163'/>

### ⚙️ aoptions
* `aoptions` Open system settings on a specific activity
* You can choose from quick presets
	* Developer settings
	* Locale settings
	* Date & time
	* Wifi settings
	* Storage management
	* Power usage
	* Root settings activity
* `aoptions A` Choose from exhaustive list of all available options
* `aoptions 1,2,3... | dev | locale | date | wifi | storage | power` Use a preset, choose one

<div id='section-id-176'/>

### 📜 alog
* `alog` Print system log output
* `alog -f <package-name>` Filter log by package name

<div id='section-id-180'/>

### 📋 acheckdevice
* Print genereal device information
* Perform basic safety-checks and toggle "testing firendly" settings
  * 10 minutes screen timeout
  * Highest brightness
  * Automatic date
  * Disabled notification sounds
  * Internet connectivity and WIFI name
  * Font scale
  * enUS locale
* (Optional) Search for the device on [GSMArena](https://www.gsmarena.com/ "GSMArena")

<div id='section-id-192'/>

### 😎 aservices
* Print running background services
* Search for more information via Google

<div id='section-id-196'/>

### ♻ areboot
* Reboot the device

<div id='section-id-199'/>

### 📱 aemulator
**Required**: Make terminal use Android Studio Java
  * **Edit .bash_profile** (or .zshrc if you have zsh shell) `open -e ~/.bash_profile` or `open -e ~/.zshrc`
  * **Add the following line at the end of the file** `export JAVA_HOME='/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home'`

* Android emulator supports all listed scripts by default + extra actions listed below
* `aemulator <option>` Handle various Android emulator activites
  * `start` - choose and launch installed emulator
  * `gprs | edge | 3g` - simulate network latency, choose one
  * `call <number>` - receive fake call
  * `sms <number> <text>` - receive fake sms
  * `gps <lat> <long>` - set manual GPS location
  * `battery <0-100>` - set battery level
  * `telnet <command>` - call command via telnet
	   * example commands `event | redir | sensor | physics | finger | rotate | fold | unfold...` see [Android emulator documentation](https://developer.android.com/studio/run/emulator-console#console-session) for more information

<div id='section-id-215'/>

### 🐒 atestmonkey
* `atestmonkey` Default test with random seed and 15000 input events
* `atestmonkey <event-count>` Test with random seed and custom input event count
* `atestmonkey <event-count> <seed>` Test with custom seed and custom event count
* Perform automated stress test using [Application Excersciser Monkey](https://developer.android.com/studio/test/monkey)
* You can end test prematurely using ctrl^c or `atestmonkeykill` in case something goes wrong
* App under test needs to be pinned to fullscreen mode to prevent unwanted interactions elsewhere
* Screen pinning button location is directly tied to OS version and device manufacturer skin.
	* It may be tricky to turn on, see examples below:<br><br>
	* <details>
			<summary>Google Nexus 5 (Android 6)</summary>
			<br><em>You need to bring the app window to foreground, the button is located in bottom right corner.</em><br><br>
			<img src="/media/Pinning_Nexus_v4.png" width="420"></details>
	* <details>
			<summary>Google Pixel 3 (Android 11)</summary>
			<br><em>You need to click on the app icon, the button is located in popup menu.</em><br><br>
			<img src="/media/Pinning_Pixel_v2.png" width="420"></details>

<div id='section-id-233'/>

# 🍎 iOS Commands

<div id='section-id-235'/>

## Capture screen

<div id='section-id-237'/>

### 📸 iscreenshot
* `iscreenshot` Save screenshot to ~/Desktop
* `iscreenshot -a` Take screenshot on all connected devices

<div id='section-id-241'/>

### 🎥 irecord
**Required**: Install [videosnap](https://github.com/matthutchinson/videosnap/releases "videosnap") -> download and install `videosnap-0.0.8.pkg`<br>
**Required**: Install [ffmpeg](https://www.ffmpeg.org/ "ffmpeg") `brew install ffmpeg`

1. `irecord` Record screen
2. End recording using `ctrl + c`
3. Video footage is saved to ~/Desktop
4. File is compressed using ffmpeg

<div id='section-id-250'/>

### 📹 iquicktime
* Run QuickTime and open video source picker (so you can choose a device right away)
  * You may have to allow security system permission, so the script can access QuickTime application
* This is a fallback script for `irecord` on M1 macs as it is currently not working

<div id='section-id-255'/>

## Manage applications
<div id='section-id-256'/>

### 🚚 iinstall
* `iinstall some-app-file.ipa` Install .ipa (make sure to use properly signed build)
* `iinstall -a some-app-file.ipa` Install .ipa to all connected devices

<div id='section-id-260'/>

### 🗑 iuninstall
* `iuninstall` Uninstall third-party app, choose from the list
* `iuninstall com.dummy.package.name.app` pass bundle name as argument
* `iuninstall -w` Uninstall all third-party packages
  * Skips some essential apps, edit IGNORED_PACKAGES in this script to customize the list to your needs

<div id='section-id-266'/>

### 🚀 ilaunch
* `ilaunch` List third-party apps and choose one to run it
* `ilaunch -s` List os pre-installed apps and choose one to run it
* `ilaunch com.dummy.bundle.id.app` Run app by bundle id

<div id='section-id-271'/>

### 🔪 ikill
* `ikill` List third-party apps and choose one to restart
* `ikill -s` List os pre-installed apps and choose one to restart
* `ikill com.dummy.bundle.id.app` Target specific app by passing bundle id as argument

<div id='section-id-276'/>

## Manage device

<div id='section-id-278'/>

### ⚙️ ioptions
* `ioptions` Open system settings application

<div id='section-id-281'/>

### 💬 ilang
* `ilang <lang>` Change the device language to different one, according to ISO-639 (i.e. "cs")
* `ilang` Change the device language to different one, choose from a list of all supported

<div id='section-id-285'/>

### 📜 ilog
* `ilog` Print system log output

<div id='section-id-288'/>

### 📋 icheckdevice
* Print device information
* (Optional) Search for the device on [GSMArena](https://www.gsmarena.com/ "GSMArena")

<div id='section-id-292'/>

### ♻ ireboot
* Reboot the device

<div id='section-id-295'/>

### 📱 isimulator
* Simulator has limited functionality (no camera, biometrics, Appstore...), but **offers some extra options, unavailable on physical iOS devices**
* `isimulator <option>` Handle various simulator related activites
  * `start` - choose and launch installed simulator
  * `screenshot` - save screenshot to ~/Desktop
  * `record` - save screen recording to ~/Desktop (full resolution and frame rate, without QuickTime hassle)
  * `paste <text>` - insert text into pasteboard
  * `import <file>` - import image or video to simulator gallery app
  * `log` - print simulator log
  * `url <url>` - open link in web browser or corresponding application
  * `wipe` - wipe all simulator data
  * `battery <0-100>` - set battery level displayed in status bar (no functional impact)
  * `time <hh:mm>` - set time displayed in status bar (no functional impact)

<div id='section-id-309'/>

### 🖥 iconsole
* `iconsole` Examine iOS or macOS system logs using Console application

<div id='section-id-312'/>

----

<strong>Feedback & Contribution</strong><br>

<sup>⁉️ [Submit an issue](https://github.com/IntergalacticPenguin/mobile-toolkit/issues/new/choose) to report any bugs, request a feature or ask questions.</sup><br>
<sup>🤝 [Pull requests](https://github.com/IntergalacticPenguin/mobile-toolkit/blob/master/.github/CONTRIBUTING.md "contribution rules") are highly **appreciated**, see the [issue board](https://github.com/IntergalacticPenguin/mobile-toolkit/projects/3).</sup><br>
<sup>💬 Also <strong>visit my [NoMo](https://github.com/IGPenguin/nomo)</strong> project and leave a star.</sup><br>
<sup>🔗 Find me on [LinkedIn](https://www.linkedin.com/in/intergalacticpenguin/) or [Twitter](https://twitter.com/IGPenguin).</sup><br>


================================================
FILE: android/aanimationspeed
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

set_animation_speed(){
  adb -s "$SELECTED_DEVICE" shell settings put global animator_duration_scale "$1"
  adb -s "$SELECTED_DEVICE" shell settings put global transition_animation_scale "$1"
  adb -s "$SELECTED_DEVICE" shell settings put global window_animation_scale "$1"
}

restore_animation_speed(){
  set_animation_speed 1
  echo "🔄 Animation speed restored to default \"1\""
}

android_choose_device
if [ -n "$1" ] 2> /dev/null ;then
  echo "🎬 Setting animation speed to \"$1\" (bigger is slower)..."
  set_animation_speed "$1"
else
  if [ "$(adb -s "$SELECTED_DEVICE" shell settings get global animator_duration_scale)" != "1" ] || [ "$(adb -s "$SELECTED_DEVICE" shell settings get global transition_animation_scale)" != "1" ] || [ "$(adb -s "$SELECTED_DEVICE" shell settings get global window_animation_scale)" != "1" ] ; then
    restore_animation_speed
  else
    echo "🎬 Setting animation speed to \"5\" (bigger is slower)..."
    set_animation_speed 5
  fi
fi


================================================
FILE: android/aappinfo
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

if [ -n "$1" ];
then
    PACKAGE_NAME=$1
    android_is_package_installed "$PACKAGE_NAME"
else
    PACKAGE_NAME=$(android_get_foreground_package)
fi

VERSION=$(adb -s "$SELECTED_DEVICE" shell dumpsys package "$PACKAGE_NAME" | grep versionName | sed 's/versionName=//')
VERSION=$(echo "$VERSION" | awk '{print $1}') # Remove ghost whitespaces
LAST_UPDATE=$(adb -s "$SELECTED_DEVICE" shell dumpsys package "$PACKAGE_NAME" | grep lastUpdateTime | sed 's/lastUpdateTime=//')
LAST_UPDATE=$(echo "$LAST_UPDATE" | awk '{print $1}') # Remove ghost whitespaces
SDK=$(adb -s "$SELECTED_DEVICE" shell dumpsys package "$PACKAGE_NAME" | grep versionCode | grep -o 'minSdk.*')

echo "Package name: $PACKAGE_NAME"
echo "Version: $VERSION"
echo "Last update: $LAST_UPDATE"
echo "SDK: $SDK"
adb -s "$SELECTED_DEVICE" shell dumpsys package "$PACKAGE_NAME" | grep  -v "):\\|perm=\\|installPermissionsFixed=true" | grep -i -e permission
should_proceed "🔨 Do you want to open $PACKAGE_NAME settings?"
adb -s "$SELECTED_DEVICE" shell am start -a "android.settings.APPLICATION_DETAILS_SETTINGS" -d "package:$PACKAGE_NAME" &> /dev/null


================================================
FILE: android/abounds
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

redraw_screen(){
  adb -s "$1" shell service check SurfaceFlinger &> /dev/null
  adb -s "$1" shell service call activity 1599295570 &> /dev/null
}

turn_off_bounds(){
  echo "👋 Turning off layout bounds ..."
  adb -s "$1" shell setprop debug.layout false &> /dev/null
  redraw_screen "$1"
}

turn_on_bounds(){
  echo "📐 Turning on layout bounds..."
  adb -s "$1" shell setprop debug.layout true &> /dev/null
  redraw_screen "$1"
}

BOUNDS_STATE=$(adb -s "$SELECTED_DEVICE" shell getprop debug.layout | tr -cd '[[:alnum:]]._-')
if [ -z "$BOUNDS_STATE" ] || [ "$BOUNDS_STATE" == "false" ]; then
  turn_on_bounds "$SELECTED_DEVICE"
else
  turn_off_bounds "$SELECTED_DEVICE"
fi


================================================
FILE: android/abuildproject
================================================
#!/bin/bash
# shellcheck disable=SC2181
#ignore indirect exit code check at line 54

LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

trap 'cleanup $@' 1 2 3 6 15

cleanup()
{
  if [ -n "$OLD_PWD" ]; then
    cd "$OLD_PWD" || exit
  fi
  rm -f "$TASK_LIST"
  exit
}

list_build_variants(){
  if [ -n "$1" ];
  then
    OLD_PWD=$PWD
    cd "$1" || exit
  fi

  if ! [ -f "gradlew" ];
  then
    echo "🤷‍ No Android project found in current directory"
    exit
  fi

  TASK_LIST="toolkit_task_list.txt"
  echo "⏳ Detecting build variants, this may take a while..."
  ./gradlew tasks | grep '^install' > $TASK_LIST

  if [ "$(nl "$TASK_LIST")" == "" ]; then
    echo "🤷‍ No Android project build variants available! Aborting..."
    exit
  else
    echo "🔨 Available:"
    nl $TASK_LIST
  fi
}

build_project_variant(){
  read -r -p "📝 Choose build variant: " VARIANT_INDEX
  TASK=$(sed "$VARIANT_INDEX"!d $TASK_LIST | awk '{ print $1 }' )
  if [[ $VARIANT_INDEX == "" || $TASK == "" ]]; then
    delete_lastline
    build_project_variant
  else
    echo  "🚀 Triggering new build..."
    ANDROID_SERIAL=$SELECTED_DEVICE ./gradlew "$TASK"
  fi
  if [ $? == "0" ];
  then
    VARIANT=${TASK#"install"}
    echo  "✅ $VARIANT build variant installed successfully"
  else
    echo  "❌ $VARIANT build variant installation failed"
  fi
}

find_apk_path(){
  rm -rf "./outputs/apk/*"
  APK_PATH=$(find . -iname "*.apk" | sort | tail -1)
}

launch(){
  android_detect_package_info "$1"
  echo "🚀 Launching $APP_NAME..."
  adb -s "$SELECTED_DEVICE" shell monkey -p "$PACKAGE_NAME" -c android.intent.category.LAUNCHER 1 &> /dev/null
}

copy_apk_to_desktop(){
  FILENAME=$(echo "$PACKAGE_NAME" | tr . _)
  FILENAME="$FILENAME.apk"
  cp "$APK_PATH" "$HOME/Desktop/$FILENAME"
  echo "💾 \"$FILENAME\" saved to ~/Desktop"
}

android_choose_device
list_build_variants "$@"
build_project_variant
find_apk_path
launch "$APK_PATH"
copy_apk_to_desktop
cleanup


================================================
FILE: android/acamera
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

echo "📷 Opening camera application..."
adb -s "$SELECTED_DEVICE" shell am start -a android.media.action.IMAGE_CAPTURE &> /dev/null


================================================
FILE: android/acheckdevice
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

GSM_URL='https://www.gsmarena.com/res.php3?sSearch='

echo_info(){
  MANUFACTURER=$(adb -s "$SELECTED_DEVICE" shell getprop ro.product.manufacturer | tr -cd '[[:alnum:]]._-')
  MODEL=$(adb -s "$SELECTED_DEVICE" shell getprop ro.product.model | tr -cd '[[:alnum:]]._-')
  VERSION=$(adb -s "$SELECTED_DEVICE" shell getprop ro.build.version.release | tr -cd '[[:alnum:]]._-')
  SDK=$(adb -s "$SELECTED_DEVICE" shell getprop ro.build.version.sdk | tr -cd '[[:alnum:]]._-')
  INFO=$(printf "%s %s %s (API %s)" "$MANUFACTURER" "$MODEL" "$VERSION" "$SDK")

  echo "📱 $INFO"
  echo "  • ID: $SELECTED_DEVICE"
  echo "  • CPU: $(adb -s "$SELECTED_DEVICE" shell getprop ro.product.cpu.abi | tr -cd '[[:alnum:]]._-')"
  echo "  • Display density: $(adb -s "$SELECTED_DEVICE" shell getprop ro.sf.lcd_density | tr -cd '[[:alnum:]]._-')"
}

check_screen_timeout(){
  echo -n "🕑 Checking screen timeout"
  TIMEOUT=$(adb -s "$SELECTED_DEVICE" shell settings get system screen_off_timeout)
  TIMEOUT=${TIMEOUT%$'\r'} # remove trailing carriage return

  if [ "$TIMEOUT" != "600000" ]; then
    yes_or_no " - 🕤 Timeout is $TIMEOUT miliseconds, set to 10 minutes?"; if ! [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then return; fi
    adb -s "$SELECTED_DEVICE" shell settings put system screen_off_timeout 600000
  else
    echo " - ✅ 10 minutes"
  fi
}

check_screen_brightness(){
  echo -n "🔆 Checking screen brightness"
  BRIGHTNESS=$(adb -s "$SELECTED_DEVICE" shell settings get system screen_brightness)
  BRIGHTNESS=${BRIGHTNESS%$'\r'} # remove trailing carriage return
  if [ "$BRIGHTNESS" != "255" ]; then
    yes_or_no " - 🔥 Brightness is $BRIGHTNESS, set manual max brightness?"; if ! [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then return; fi
    adb -s "$SELECTED_DEVICE" shell settings put system screen_brightness_mode 0
    adb -s "$SELECTED_DEVICE" shell settings put system screen_brightness 255
  else
    echo " - ✅ MAX"
  fi
}

check_notification_volume(){
  echo -n "📢 Checking notification volume"
  RINGER_MODE=$(adb -s "$SELECTED_DEVICE" shell settings get global mode_ringer)
  RINGER_MODE=${RINGER_MODE%$'\r'} # remove trailing carriage return
  if [ "$RINGER_MODE" != "1" ]; then
    yes_or_no " - 🔕 Ringer mode is not 1 (~silent), try to set silent mode?"; if ! [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then return; fi
    adb -s "$SELECTED_DEVICE" shell input keyevent 164
  else
    echo " - ✅ Muted"
  fi
}

check_network_name_contains(){
  echo -n "🌐 Checking WIFI network"
  NET_STATS=$(adb -s "$SELECTED_DEVICE" shell dumpsys netstats | grep -E 'iface=wlan.*networkId' )

  if echo "$NET_STATS" | grep -i "$1" &> /dev/null; then
    yes_or_no " - ❗️ $1 network detected, open wifi settings?"; if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then adb -s "$SELECTED_DEVICE" shell am start -a android.net.wifi.PICK_WIFI_NETWORK &> /dev/null; fi
  fi

  if ! echo "$NET_STATS" | grep -E 'iface=wlan.*networkId' &> /dev/null; then
    yes_or_no " - ❌ Disconnected, open wifi settings?"; if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then adb -s "$SELECTED_DEVICE" shell am start -a android.net.wifi.PICK_WIFI_NETWORK &> /dev/null; fi
  else
    echo " - ✅ Connected"
  fi
}

check_automatic_date(){
  echo -n "📆 Checking date"
  AUTO_TIME=$(adb -s "$SELECTED_DEVICE" shell settings get global auto_time)
  AUTO_TIME=${AUTO_TIME%$'\r'} # remove trailing carriage return
  if [ "$AUTO_TIME" == "0" ]; then
    yes_or_no " - ❗️ Date is set manually, open date settings?"
    if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then
      adb -s "$SELECTED_DEVICE" shell am start -a android.settings.DATE_SETTINGS &> /dev/null
    fi
  else
    echo " - ✅ Automatic"
  fi
}

check_locale(){
  DEFAULT_LOCALE="en-US"

  echo -n "👄 Checking locale"
  CURRENT_LOCALE=$(adb -s "$SELECTED_DEVICE" shell getprop persist.sys.locale)
  CURRENT_LOCALE=${CURRENT_LOCALE%$'\r'} # remove trailing carriage return

  if [ "$CURRENT_LOCALE" != "$DEFAULT_LOCALE" ]; then
    yes_or_no " - ❗️ Current locale is $CURRENT_LOCALE, open locale settings?"
    if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then
      adb -s "$SELECTED_DEVICE" shell am start -a android.settings.LOCALE_SETTINGS &> /dev/null
    fi
  else
    echo " - ✅ $CURRENT_LOCALE"
  fi
}

check_font_scale(){
  DEFAULT_SCALE="1.0"

  echo -n "🔠 Checking font scale"
  SCALE=$(adb -s "$SELECTED_DEVICE" shell settings get system font_scale)
  SCALE=${SCALE%$'\r'} # remove trailing carriage return

  if [ "$SCALE" != "$DEFAULT_SCALE" ]; then
    yes_or_no " - 🔍 Current scale is $SCALE, change to $DEFAULT_SCALE"
    if [ "$RESPONSE" == "y" ] || [ "$RESPONSE" != "Y" ]; then
      adb -s "$SELECTED_DEVICE" shell settings put system font_scale "$DEFAULT_SCALE"
    fi
  else
    echo " - ✅ $SCALE"
  fi
}

open_gsmarena(){
  should_proceed "🔍 Search for the device on GSMArena?"
  PHONE_URL=$GSM_URL$MODEL
  open "$PHONE_URL"
}

android_choose_device
echo_info
check_screen_timeout
check_screen_brightness
check_notification_volume
check_automatic_date
check_locale
check_network_name_contains edge
check_font_scale
open_gsmarena

# More settings available at https://developer.android.com/reference/android/provider/Settings.System.html#SCREEN_OFF_TIMEOUT


================================================
FILE: android/acontrol
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

check_dependency "scrcpy"
check_adb_dependency
android_choose_device
android_device_info "$SELECTED_DEVICE"
echo "🔌 Connecting to $MANUFACTURER $MODEL (API $SDK)..."
scrcpy -s "$SELECTED_DEVICE" &> /dev/null &


================================================
FILE: android/adarkmode
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

turn_off_darkmode(){
  echo "⚪️ Turning off dark mode..."
  #adb -s "$SELECTED_DEVICE" shell settings put secure ui_night_mode 1 &> /dev/null #Not working without a reboot
  adb -s "$1" shell cmd uimode night no &> /dev/null
}

turn_on_darkmode(){
  echo "⚫️ Turning on dark mode..."
  #adb -s "$1" shell settings put secure ui_night_mode 2 &> /dev/null
  adb -s "$1" shell cmd uimode night yes &> /dev/null
}

DARKMODE_STATE=$(adb -s "$SELECTED_DEVICE" shell cmd uimode night)

if [ "$DARKMODE_STATE" != "Night mode: yes" ]; then
  turn_on_darkmode "$SELECTED_DEVICE"
else
  turn_off_darkmode "$SELECTED_DEVICE"
fi


================================================
FILE: android/aemulator
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
LOCAL_EMULATOR_LIST=$LOCATION/../data/toolkit_emulator_list.txt
NET_CHANGED=false

trap 'ctrlc $@' 1 2 3 6 15
ctrlc(){
  if $NET_CHANGED ; then
    disable_network_delay
  fi
  exit
}

help(){
  if [[ $1 != "" ]]; then
    echo "🤷‍ Unknown option: $1"
  else
    echo "🤷 Option missing"
  fi
  echo -e "Use one of the following options:\\n  start - choose and launch installed emulator\\n  gprs | edge | 3g - set network latency\\n  call <number> - receive fake call\\n  sms <number> <text> - recieve fake sms\\n  gps <lat> <long> - set manual gps location\\n  battery <0-100> - set manual battery level\\n  telnet <command> - call telnet command (see README.md)"
}

check_java_dependency(){
  if ! java -version &> /dev/null; then
    echo "❌ Java not available, edit your .bash_profile or .zshrc to use Android Studio version"
    echo "📝 Add the following line: export JAVA_HOME='/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home'"
    echo "🔌 Restart terminal afterwards"
    exit 1
  fi
}

check_running_emulator(){
  if ! adb devices | grep emulator &> /dev/null; then
    echo "❌ No running emulators"
    yes_or_no "❓ Do you want to start one?"
    if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]];
    then
      launch_emulator
      android_wait_for_device
      get_token
    fi
    exit
  fi
}

get_emulator_list(){
  echo "⏳ Getting Android emulator list..."
  rm -f "$LOCAL_EMULATOR_LIST"
  "$ANDROID_HOME"/cmdline-tools/latest/bin/avdmanager list avd | grep Name | awk '{print $2}' >> "$LOCAL_EMULATOR_LIST"
  if [ "$(nl "$LOCAL_EMULATOR_LIST")" == "" ]; then
    should_proceed "🤷‍ No emulators installed, install via Android Studio?"
    echo "⏳ Opening Android Studio..."
    open -a /Applications/Android\ Studio.app
    exit
  else
    echo "📱 Available:"
    nl "$LOCAL_EMULATOR_LIST"
  fi
}

launch_emulator(){
  get_emulator_list

  if [ -z "$1" ]; then
    read -r -p "📝 Choose: " EMULATOR_INDEX
  else
    EMULATOR_INDEX=$1
  fi
  EMULATOR_NAME=$(sed "$EMULATOR_INDEX"!d "$LOCAL_EMULATOR_LIST")
  if [[ $EMULATOR_INDEX == "" || $EMULATOR_NAME == "" ]]; then
    delete_lastline
    launch_emulator
  else
    echo  "🚀 Launching emulator..."
    nohup "$ANDROID_HOME"/emulator/emulator -avd "$EMULATOR_NAME" -no-snapshot &> /dev/null &
    rm "$LOCAL_EMULATOR_LIST"
  fi
}

get_token(){
  check_dependency "telnet"
  [ -f ./emulator_console_auth_token ] || echo "localhost 5554" | telnet &> /dev/null
  TOKEN=$(cat "$HOME"/.emulator_console_auth_token)
}

telnet_command() {
  get_token
  if [ -z "$1" ];then
    read -r -p "📝 Insert telnet command: " COMMAND
  else
    COMMAND=$1
  fi
  { echo "o localhost 5554"; sleep 1; echo "auth $TOKEN"; sleep 1; echo "$COMMAND"; } | telnet &> /dev/null
}

call(){
  if [ -z "$1" ];then
    read -r -p "📝 Insert caller number: " NUMBER
  else
    NUMBER=$1
  fi
  echo "📞 Making a call..."
  telnet_command "gsm call $NUMBER"
  echo "🔔 Ringing..."
}

send_sms(){
  if [ -z "$1" ];then
    read -r -p "📝 Insert sender number: " NUMBER
  else
    NUMBER=$1
  fi

  if [ -z "$2" ];then
    read -r -p "📝 Insert text: " TEXT
  else
    TEXT=$2
  fi

  echo "📝 Sending fake SMS..."
  telnet_command "sms send $NUMBER $TEXT"
  echo "✅ Done"
}

set_gps(){
  if [ -z "$1" ];then
    read -r -p "📝 Insert latitude: " LAT
  else
    LAT=$1
  fi

  if [ -z "$2" ];then
    read -r -p "📝 Insert longtitude: " LONG
  else
    LONG=$2
  fi

  echo "🌎 Setting manual GPS location..."
  telnet_command "geo fix $LONG $LAT"
  echo "✅ Done"
}

set_battery_level(){
  if [ -z "$1" ];then
    read -r -p "📝 Insert battery level: " LEVEL
  else
    LEVEL=$1
  fi
    echo "🔋 Setting manual battery level..."
    { echo "o localhost 5554"; sleep 1; echo "auth $TOKEN"; sleep 1; echo "power ac off"; echo "power status discharging"; echo "power capacity $LEVEL"; } | telnet &> /dev/null
    echo "✅ Done"
}

set_network_delay(){
  DELAY=$1
  if [[ "$DELAY" == "3g" ]]; then
    DELAY="umts"
  fi
  NET_CHANGED=true
  echo "🌐 Simulating network limit..."
  { echo "o localhost 5554"; sleep 1; echo "auth $TOKEN"; sleep 1; echo "network delay $DELAY"; echo "network speed $DELAY"; } | telnet &> /dev/null
  read -r -n 1 -p "⚡️ Press ENTER to stop..."
  disable_network_delay
  echo "✅ Done"
}

disable_network_delay(){
  echo "🔄 Disabling network limits..."
  { echo "o localhost 5554"; sleep 1; echo "auth $TOKEN"; sleep 1; echo "network delay none"; echo "network speed full"; } | telnet &> /dev/null
  NET_CHANGED=false
}

check_for_update
check_java_dependency

case $1 in
  'start')
    launch_emulator "$2"
    ;;
  'gprs' | 'edge' | '3g')
    check_running_emulator
    set_network_delay "$1"
    ;;
  'call')
    check_running_emulator
    call "$2"
    ;;
  'sms')
    check_running_emulator
    send_sms "$2" "$3"
    ;;
  'gps')
    check_running_emulator
    set_gps "$2" "$3"
    ;;
  'battery')
    check_running_emulator
    set_battery_level "$2"
    ;;
  'telnet')
    check_running_emulator
    telnet_command "$2"
    ;;
  *)
    help "$1"
    check_running_emulator
    ;;
esac


================================================
FILE: android/aerase
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

if [[ "$1" != "" ]];
then
    APP=$1
    android_is_package_installed "$APP"
else
    APP=$(android_get_foreground_package)
fi

should_proceed "🧽 Do you really want to erase $APP data?"
adb -s "$SELECTED_DEVICE" shell pm clear "$APP" &> /dev/null
echo "🚀 Launching..."
adb -s "$SELECTED_DEVICE" shell monkey -p "$APP" -c android.intent.category.LAUNCHER 1 &> /dev/null


================================================
FILE: android/afontscale
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

set_font_scale(){
  adb -s "$SELECTED_DEVICE" shell settings put system font_scale "$1"
}

android_choose_device
if [ -z "$1" ] 2> /dev/null
then
  CURRENT_FONT_SIZE=$(adb -s "$SELECTED_DEVICE" shell settings get system font_scale)
  if [ "$CURRENT_FONT_SIZE" != "1.0" ]; then
    echo "🔄 Font scale restored to default \"1.0\""
    set_font_scale "1.0"
  else
    echo "🔍 Setting bigger font scale to \"1.3\"..."
    set_font_scale "1.3"
  fi
else
  echo "🔍 Setting font scale to \"$1\"..."
  set_font_scale "$1"
fi


================================================
FILE: android/agoogleplay
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

search_gp(){
  APP_NAME="$1"
  echo "🛒 Searching for $APP_NAME in Google Play..."
  APP_NAME=$(echo "$APP_NAME" | sed -e 's/ /\%20/g')
  GOOGLE_PLAY_URL="http://play.google.com/store/search\\?q\\=$APP_NAME\\&c\\=apps"
  adb -s "$SELECTED_DEVICE" shell am start -a android.intent.action.VIEW -d "$GOOGLE_PLAY_URL" &> /dev/null
}

if [ -z "$1" ] ; then
  search_gp "$(android_get_foreground_package)"
  exit
else
  APP_NAME="'$*'"
  search_gp "$APP_NAME"
fi


================================================
FILE: android/ainstall
================================================
#!/bin/bash
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

install_app(){
  echo "⌛️ Installing \"$PACKAGE_NAME\" to $1..."
  TEMPORARY_FILE="$TEMPORARY_FILE-$(date +%s)"
  adb -s "$1" install -t -r "$2" &> "$TEMPORARY_FILE"
  if grep -q Failure "$TEMPORARY_FILE" ; then
    echo "❌ Installation to $1 failed!"
    echo "🤕 Uninstall existing version or troubleshoot the package"
    echo "🔥 Error details: $(grep 'Failure' "$TEMPORARY_FILE" | sed 's/^.*: //')"
    exit 1
  fi
  echo "🚀 Launching $APP_NAME on $1..."
  adb -s "$1" shell monkey -p "$PACKAGE_NAME" -c android.intent.category.LAUNCHER 1 &> /dev/null
}

check_args_valid(){
  if [[ "$1" != "-a" ]]; then
    FILE=$1
  else
    FILE=$2
  fi

  if [ ! -f "$PWD/$FILE" ] && [ ! -f "$FILE" ]; then
      abort "🤷‍ Installation file not found!"
  fi

  if [[ "$FILE" != *".apk" ]]; then
      abort "🤷 Unsupported file!"
  fi
}

run()
{
  if [[ $1 == "-a" ]]; then
    check_for_update
    android_check_connected
    android_get_devices_auth_dump
    android_detect_package_info "$2"
    for line in $(adb devices | grep -v "List"  | awk '{print $1}')
    do
      DEVICE=$(echo "$line" | awk '{print $1}')
      install_app "$DEVICE" "$2" &
    done
    wait
  else
    android_choose_device
    android_detect_package_info "$1"
    install_app "$SELECTED_DEVICE" "$1"
  fi
}

# "$@" - pass all arguments
check_args_valid "$@"
run "$@"


================================================
FILE: android/akill
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

if [[ "$1" != "" ]];
then
    PACKAGE_NAME=$1
    android_is_package_installed "$PACKAGE_NAME"
else
    PACKAGE_NAME=$(android_get_foreground_package)
fi

echo "🔪 Package \"$PACKAGE_NAME\" process killed"
adb -s "$SELECTED_DEVICE" shell am force-stop "$PACKAGE_NAME"
echo "🚀 Relaunching the app..."
adb -s "$SELECTED_DEVICE" shell monkey -p "$PACKAGE_NAME" -c android.intent.category.LAUNCHER 1 &> /dev/null


================================================
FILE: android/alaunch
================================================
#!/bin/bash
source "$(dirname "$0")"/../common_tools
android_choose_device

if [[ "$1" == "-s" ]]; then
  PACKAGES=($(adb -s "$SELECTED_DEVICE" shell pm list packages -f | sed -e 's/.*=//' | sort))
elif [[ -n "$1" ]]; then
  PACKAGE="$1"
  android_is_package_installed "$PACKAGE"
else
  PACKAGES=($(adb -s "$SELECTED_DEVICE" shell pm list packages -f -3 | sed -e 's/.*=//' | sort))
fi

if [ -z "$PACKAGE" ]; then
  PACKAGES_LISTED=()
  for P in "${PACKAGES[@]}" #removes trailing \r
  do
    P=${P%$'\r'}
    PACKAGES_LISTED+=("$P")
  done

  if [ ${#PACKAGES_LISTED[@]} -eq 0 ]; then
      echo "🤷‍ No third-party apps installed, use \"alaunch -s\" to list system packages"
      exit
  fi

  echo "📋 Choose package to launch:"
  select OPTION in "${PACKAGES_LISTED[@]}"
  do
   case $OPTION in
      *) PACKAGE=$OPTION;break; ;;
    esac
  done
fi

echo "🚀 Launching $PACKAGE..."
adb -s "$SELECTED_DEVICE" shell monkey -p "$PACKAGE" -c android.intent.category.LAUNCHER 1 &> /dev/null


================================================
FILE: android/alog
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

filter_by_package(){
  android_is_package_installed "$1"
  echo "🔍 Filtering by package name: $1"
  PID=$(adb -s "$SELECTED_DEVICE" shell ps | grep -i "$1" | cut -c10-15)
  adb -s "$SELECTED_DEVICE" logcat | grep "$PID"
}

raw_log(){
  echo "📜 Device log:"
  adb -s "$SELECTED_DEVICE" logcat
}

if [ "$1" == "-f" ]; then
  if [ -z "$2" ]; then
    read -r -p "🔬 Insert package name (Android 7+) or leave empty to proceed: " PACKAGE
  else
    PACKAGE=$2
  fi
  filter_by_package "$PACKAGE"
else
    raw_log
fi


================================================
FILE: android/aoptions
================================================
#!/bin/bash
TOOLKIT_LOCATION=$(dirname "$0")
source "$TOOLKIT_LOCATION"/../common_tools
android_choose_device

LOCAL_SETTINGS_LIST_PATH=$TOOLKIT_LOCATION/../data/toolkit_intent_list.txt

import_intents(){
  echo "⏳ Importing settings activity list..."
  ANDROID_TOOLS_SETTINGS_INTENT_LIST_PATH=$(find ~/Library/Android/sdk -name 'activity_actions.txt' | sort | tail -1)
  {
    grep -i "android.settings" "$ANDROID_TOOLS_SETTINGS_INTENT_LIST_PATH" # List settings intents
    grep -i -e "android.net.wifi.PICK_WIFI_NETWORK" -e "android.os.storage.action.MANAGE_STORAGE" -e "android.intent.action.MANAGE_PERMISSIONS" -e "android.intent.action.SET_WALLPAPER" -e "android.intent.action.VIEW_DOWNLOADS" "$ANDROID_TOOLS_SETTINGS_INTENT_LIST_PATH" # List alternate settings intents
    grep -i "android.media.action" "$ANDROID_TOOLS_SETTINGS_INTENT_LIST_PATH" # List media intents
  } >> "$LOCAL_SETTINGS_LIST_PATH"

  wc -l "$LOCAL_SETTINGS_LIST_PATH" | awk '{print $1}' | grep -q 0 && echo "❗️ Imported list is empty, reimporting lower sdk variant..." && rm "$(find ~/Library/Android/sdk -name 'activity_actions.txt' | sort | tail -1)" && import_intents && return
  echo "✅ Importing from $ANDROID_TOOLS_SETTINGS_INTENT_LIST_PATH completed successfully"
}

start_settings_intent(){
  SETTINGS_INTENT=$(sed "$LINE"!d "$LOCAL_SETTINGS_LIST_PATH")
  echo "🚀 Starting $SETTINGS_INTENT"
  adb -s "$SELECTED_DEVICE" shell am start -a "$SETTINGS_INTENT" &> /dev/null
}

search_for_intent(){
  if nl "$LOCAL_SETTINGS_LIST_PATH" | grep -i -q "$1"; then
    if [[ $(grep -c -i "$1" "$LOCAL_SETTINGS_LIST_PATH") -eq 1 ]]; then #If there is only one result
      LINE=$(nl "$LOCAL_SETTINGS_LIST_PATH" | grep -i "$1" | awk '{print $1}')
    else
      nl "$LOCAL_SETTINGS_LIST_PATH" | grep -i "$1"
      read -r -p "📝 Choose option number: " LINE
    fi
    start_settings_intent
  else
    read -r -p "🤷‍ No \"$1\" intent found, try again or leave blank: " KEYWORD
    search_for_intent "$KEYWORD"
  fi
}

handle_preset(){
  case "$1" in
    "S" | "s")
    echo "🚀 Launching system settings app root activity..."
    adb -s "$SELECTED_DEVICE" shell am start -a android.settings.SETTINGS &> /dev/null
    ;;

    "1" | "dev" | "developer")
    echo "🔨 Opening developer settings..."
    adb -s "$SELECTED_DEVICE" shell am start -a android.settings.APPLICATION_DEVELOPMENT_SETTINGS &> /dev/null
    ;;

    "2" | "locale" | "lang" | "language")
    echo "🌍 Opening locale settings..."
    adb -s "$SELECTED_DEVICE" shell am start -a android.settings.LOCALE_SETTINGS &> /dev/null
    ;;

    "4" | "wifi" | "network")
    echo "🌐 Opening WIFI network settings..."
    adb -s "$SELECTED_DEVICE" shell am start -a android.net.wifi.PICK_WIFI_NETWORK &> /dev/null
    ;;

    "5" | "storage")
    echo "📦 Opening storage management ..."
    adb -s "$SELECTED_DEVICE" shell am start -a android.os.storage.action.MANAGE_STORAGE &> /dev/null
    ;;

    "6" | "power")
    echo "🔋 Opening power usage statistics..."
    adb -s "$SELECTED_DEVICE" shell am start -a android.intent.action.POWER_USAGE_SUMMARY &> /dev/null
    ;;

    "3" | "date" | "time" | "datetime")
    echo "📅 Opening date & time settings..."
    adb -s "$SELECTED_DEVICE" shell am start -a android.settings.DATE_SETTINGS &> /dev/null
    ;;

    "0" | "all" | "list" | "A" | "a")
    echo "📜 Available settings activities:"
    nl "$LOCAL_SETTINGS_LIST_PATH"
    read -r -p "📝 Choose option number: " LINE
    start_settings_intent
    ;;

    "U" | "update" | "u")
    rm "$LOCAL_SETTINGS_LIST_PATH"
    import_intents
    ;;

    *)
    search_for_intent "$1"

    ;;
  esac
}

# Initialize available intent list - inspiration here https://stackoverflow.com/questions/8971160/what-is-the-exhaustive-list-of-all-android-intent-action-actions-available-in
if [ ! -f "$LOCAL_SETTINGS_LIST_PATH" ]; then
    echo "✨ Activity list not initialized yet"
    import_intents
fi

# If arugment not passed, show preset chooser
if [[ "$1" == "" ]]; then
  TOOLKIT_INTENT_COUNT=$(wc -l "$LOCAL_SETTINGS_LIST_PATH")
  TOOLKIT_INTENT_COUNT=$(echo "$TOOLKIT_INTENT_COUNT" | awk '{print $1}') # Remove ghost whitespaces 🙃

  echo "📋 Available options:"
  tput setaf 3; echo "A) Show all $TOOLKIT_INTENT_COUNT settings options"; tput sgr0
  echo -e "S) Open system settings application\\n1) Developer\\n2) Locale\\n3) Date & time\\n4) Wifi network\\n5) Storage management\\n6) Power usage\\nU) Update settings list from Android sdk\\nor\\n<text> to search in all settings"
  read -r -p "📝 Enter your selection: " PRESET
  handle_preset "$PRESET"
  exit
else
  handle_preset "$1"
fi


================================================
FILE: android/apaste
================================================
#!/bin/bash
# shellcheck disable=SC1001
#ignore regex - sign occurence
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

LOREM="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
ARGUMENT_COUNT="$#"

check_text(){
  if [ -n "$1" ]
  then
    if ! [[ "$*" =~ [^a-zA-Z0-9\ \-\.\@] ]]; then
      TEXT=$1
    else
      echo "🤷‍ Only basic alphanumeric characters are supported!"
      exit 1
    fi
  else
      echo "🤷‍ No text to insert!"
      exit 1
  fi
}

escape_spaces(){
  ESCAPED_TEXT=$(echo "$1" | sed -e 's/ /\%s/g')
}

send_text(){
  escape_spaces "$1"
  android_get_device_sdk "$SELECTED_DEVICE"
  if (( SDK > 27 )); then
    # Insert only first character
    adb -s "$SELECTED_DEVICE" shell "input text '${ESCAPED_TEXT:0:1}'"
    # Afterwards the rest - workaround for API 28+ edittext char skip bug
    adb -s "$SELECTED_DEVICE" shell "input text '${ESCAPED_TEXT:1:${#ESCAPED_TEXT}}'"
  else
    adb -s "$SELECTED_DEVICE" shell "input text '$ESCAPED_TEXT'"
  fi
}

execute_by_arguments(){
  echo "📲 Sending text to $SELECTED_DEVICE..."
  # Handle arguments
  if [ "$1" == "-l" ];
  then
    send_text "$LOREM"
  elif [ "$ARGUMENT_COUNT" -gt 1 ];
  then
    for ARGUMENT in "$@"
    do
      send_text "$ARGUMENT"
      sleep 0.5
      if ! [ "$ARGUMENT" = "${*: -1}" ]; then #if not last argument, jump to next field
       adb -s "$SELECTED_DEVICE" shell input keyevent 61
      fi
    done
  else
    send_text "$TEXT"
  fi
}

execute_for_all(){
  check_for_update
  android_get_devices_auth_dump

  for DEVICE in $(adb devices | grep -v "List"  | awk '{print $1}')
  do
    (SELECTED_DEVICE=$DEVICE; execute_by_arguments "$@") & # Set var and start command in subshell
  done
  wait
}

check_text "$@"
if [[ "$1" == "-a" ]]; then
  execute_for_all "${@:2}" # All arguments starting from 2nd
else
  android_choose_device
  execute_by_arguments "$@"
fi


================================================
FILE: android/apermissionreset
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

tput setaf 1
should_proceed "🚫 REVOKE ALL GRANTED PERMISSIONS on $SELECTED_DEVICE?"
tput setaf 0
adb -s "$SELECTED_DEVICE" shell pm reset-permissions


================================================
FILE: android/apowerbutton
================================================
#!/bin/bash
source "$(dirname "$0")"/../common_tools
android_choose_device

# Reference for key events - https://gist.github.com/arjunv/2bbcca9a1a1c127749f8dcb6d36fb0bc

echo "👋 Sending power button key event..."
# Send POWER screen key event (key 26)
adb -s "$SELECTED_DEVICE" shell input keyevent POWER &>/dev/null


================================================
FILE: android/areboot
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

should_proceed "🔌 Do you really want to reboot - $SELECTED_DEVICE?"
echo "🔄 Rebooting the device..."
adb -s "$SELECTED_DEVICE" reboot &> /dev/null


================================================
FILE: android/arecord
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

# Trap signal handling
trap 'ctrlc "$@"' 1 2 3 6 15

USE_LEGACY_RECORDING=false

check_dependency "scrcpy"

ctrlc() {
  adb -s "$SELECTED_DEVICE" shell settings put system show_touches 0

  if $RECORDING; then
    sleep 1

    if $USE_LEGACY_RECORDING; then
      if [ -z "$2" ]; then
        android_device_info "$SELECTED_DEVICE"
        FILENAME="$MANUFACTURER-$MODEL-API$SDK-$(date +%Y-%m-%d-%H-%M-%S)"
      else
        FILENAME="$2"
      fi
      echo "📁 Copying the video from the device..."
      adb -s "$SELECTED_DEVICE" pull "$DEVICE_FILE_PATH"/output.mp4 ~/Desktop/"$FILENAME".mp4 &>/dev/null
      adb -s "$SELECTED_DEVICE" shell rm "$DEVICE_FILE_PATH"/output.mp4 &>/dev/null
    else
      # Do nothing
      :
    fi

    echo "✅ Saved into ~/Desktop/$FILENAME.mp4"

  fi
  exit
}

RECORDING=false
android_choose_device

android_get_device_sdk "$SELECTED_DEVICE"
android_get_storage_location_per_SDK "$SDK"

RECORDING=true
echo "📹 Recording screen on $SELECTED_DEVICE, stop it using ctrl^c"

adb -s "$SELECTED_DEVICE" shell settings put system show_touches 1

# Parse the flag option
if [ "$1" == "-l" ]; then
  USE_LEGACY_RECORDING=true
fi

if [ -z "$2" ]; then
  android_device_info "$SELECTED_DEVICE"
  FILENAME="$MANUFACTURER-$MODEL-API$SDK-$(date +%Y-%m-%d-%H-%M-%S)"
else
  FILENAME="$2"
fi

OUTPUT_PATH=~/Desktop/"$FILENAME".mp4

# Perform actions based on flags
if $USE_LEGACY_RECORDING; then
  adb -s "$SELECTED_DEVICE" shell screenrecord "$DEVICE_FILE_PATH"/output.mp4
else
  scrcpy -s "$SELECTED_DEVICE" --verbosity=error --no-window --audio-codec=aac --record=file.mp4 --record="$OUTPUT_PATH" &>/dev/null
fi

# Unset the trap
trap - 1 2 3 6 15


================================================
FILE: android/ascreenshot
================================================
#!/bin/bash
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

screenshot(){
  android_device_info "$1"
  FILENAME="$MANUFACTURER-$MODEL-API$SDK-$(date +%Y-%m-%d-%H-%M-%S).png"

  android_get_storage_location_per_SDK "$SDK"

  echo "📸 Saving screenshot into $FILENAME..."
  adb -s "$1" shell screencap -p "$DEVICE_FILE_PATH"/output.png
  adb -s "$1" pull "$DEVICE_FILE_PATH"/output.png ~/Desktop/"$FILENAME" &>/dev/null
  adb -s "$1" shell rm "$DEVICE_FILE_PATH"/output.png
}

screenshot_all(){
  check_for_update
  android_get_devices_auth_dump
  for line in $(adb devices | grep -v "List"  | awk '{print $1}')
  do
    device=$(echo "$line" | awk '{print $1}')
    screenshot "$device" &
  done
  wait
}

if [ "$1" == "-a" ];
then
  screenshot_all
else
  android_choose_device
  screenshot "$SELECTED_DEVICE"
fi


================================================
FILE: android/aservices
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
SERVICE_LIST=$LOCATION/../data/toolkit_android_services.txt
source "$LOCATION"/../common_tools
android_choose_device

echo "🔍 Getting background service list..."
adb -s "$SELECTED_DEVICE" shell service list | tail -n +2 > "$SERVICE_LIST"
cat "$SERVICE_LIST"

SERVICE_COUNT=$(wc -l < "$SERVICE_LIST")
choose_number "$SERVICE_COUNT" "🔍 Find more about specific service - choose number: "
SERVICE_PACKAGE=$(sed "$CHOICE"!d "$SERVICE_LIST" | cut -d "[" -f2 | cut -d "]" -f1) #get string in brackets
QUERY="android service '$SERVICE_PACKAGE'"
echo "🌐 Performing Google search for: $QUERY"
open https://www.google.com/search?q="$QUERY"

# Potential usage: https://stackoverflow.com/questions/17580199/sending-a-sms-on-android-through-adb/30224091
# More: https://resources.infosecinstitute.com/android-hacking-security-part-3-exploiting-broadcast-receivers/#gref


================================================
FILE: android/atalkback
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

turn_off_talkback(){
  echo "🔇 Turning off TalkBack..."
  adb -s "$1" shell am force-stop com.google.android.marvin.talkback &> /dev/null
}

turn_on_talkback(){
  echo "🔊 Turning on TalkBack..."
  adb -s "$1" shell settings put secure enabled_accessibility_services com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService &> /dev/null
}

TALKBACK_STATE=$(adb -s "$SELECTED_DEVICE" shell pidof com.google.android.marvin.talkback)
if [ -z "$TALKBACK_STATE" ] || [ "$TALKBACK_STATE" == "" ]; then
  turn_on_talkback "$SELECTED_DEVICE"
else
  turn_off_talkback "$SELECTED_DEVICE"
fi


================================================
FILE: android/atestmonkey
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
trap 'ctrlc' 1 2 3 6 15

ctrlc(){
  if $RUNNING_TESTS; then
    MONKEY_TASK_ID=$(adb -s "$SELECTED_DEVICE" shell pidof "com.android.commands.monkey")
    adb -s "$SELECTED_DEVICE" shell kill "$MONKEY_TASK_ID" >/dev/null 2>&1
    echo "🔪 Test monkey terminated, the device is safe to use now!"
  fi

  unlock_app_fullscreen "$SELECTED_DEVICE"
  exit 2
}

unlock_app_fullscreen(){
  adb -s "$1" shell am task lock stop >/dev/null 2>&1
  echo "🔓 App fullscreen pinning disabled"
}

update_package_name(){
  APP_PACKAGE_NAME="$(android_get_foreground_package "$1")"
}

check_screen_pinning(){
  PINNING_ENABLED=$(adb -s "$1" shell settings get system lock_to_app_enabled)
  PINNING_ENABLED=${PINNING_ENABLED%$'\r'} # remove trailing carriage return
  if [ "$PINNING_ENABLED" != "1" ]; then
    adb -s "$1" shell am start -a android.settings.SECURITY_SETTINGS >/dev/null 2>&1
    read -r -p "🔑 Enable \"Screen pinning\" option in security settings, then press enter..."
  fi
}

lock_task_fullscreen(){
  check_screen_pinning "$1"
  adb -s "$1" shell monkey -p "$APP_PACKAGE_NAME" -c android.intent.category.LAUNCHER 1 &> /dev/null
  adb -s "$1" shell input keyevent "KEYCODE_APP_SWITCH"
  read -r -p "📌 Press the \"Pin\" button in \"$APP_PACKAGE_NAME\" window, then press enter... " #TODO add check if already on
}

run_test(){
  SEED="$2"
  EVENT_COUNT="$3"
  tput setaf 3 && should_proceed "🔥 DANGER ZONE ⊗ Send $EVENT_COUNT monkey test events to \"$APP_PACKAGE_NAME\"? (may take a while)" && tput sgr0 #set red and white text color
  echo "🐒 Running monkey stress test... (press ctrl^c to end now)"
  RUNNING_TESTS=true
  LOG_FILE=~/Desktop/"monkey-test-log-$SEED-$APP_PACKAGE_NAME-$MANUFACTURER-$MODEL-API$SDK-$(date +%Y-%m-%d-%H-%M-%S).txt"
  adb -s "$1" shell monkey -p "$APP_PACKAGE_NAME" -s "$SEED" --pct-appswitch 0 --pct-syskeys 0 --pct-anyevent 0 "$EVENT_COUNT" &> "$LOG_FILE"
  unlock_app_fullscreen "$@"
  grep -q "CRASH" "$LOG_FILE" && tput setaf 1 && echo "❌ Test failed, see crash log for details, seed: $SEED" && tput sgr0 && open "$LOG_FILE" && exit 1
  tput setaf 2 && echo "✅ Test passed, successfully executed $EVENT_COUNT events, seed: $SEED" && tput sgr0
}

RUNNING_TESTS=false
EVENT_COUNT=15000 #Converts to $1 if passed
SEED="$RANDOM"

android_choose_device
android_device_info "$SELECTED_DEVICE"
update_package_name "$SELECTED_DEVICE"
lock_task_fullscreen "$SELECTED_DEVICE"

case "$1" in #if $1 not number, LOL
    ''|*[!0-9]*) ;; #nothing left to do
    *) EVENT_COUNT="$1" ;;
esac

case "$2" in #if $2 not number, LOL
    ''|*[!0-9]*) ;; #nothing left to do
    *) SEED="$2" ;;
esac

run_test "$SELECTED_DEVICE" "$SEED" "$EVENT_COUNT"


================================================
FILE: android/atestmonkeykill
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c

android_choose_device

MONKEY_TASK_ID=$(adb -s "$SELECTED_DEVICE" shell pidof "com.android.commands.monkey")

if [ -z "$MONKEY_TASK_ID"  ]; then
  echo "🙈 No test monkey running, device is safe to use"

else
  adb -s "$SELECTED_DEVICE" shell kill "$MONKEY_TASK_ID" >/dev/null 2>&1
  echo "🔪 Test monkey terminated, the device is safe to use now!"
fi


================================================
FILE: android/auninstall
================================================
#!/bin/bash
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

init_uninstalling(){
  echo "⏳ Getting third-party package list..."
  IGNORED_PACKAGES=( "com.microsoft.hockeyapp.testerapp" "io.crash.air" "de.codenauts.hockeyapp" "ar.core" "com.framerjs.android" "com.figma.mirror" "com.invisionapp.ifa" "com.android.chrome" "com.google.ar.core" "no.nordicsemi.android.mcp" "com.x8bit.bitwarden" )
  PACKAGES=($(adb -s "$SELECTED_DEVICE" shell pm list packages -f -3 | sed -e 's/.*=//' | sort))
}

uninstall_all(){
    for PKG in "${PACKAGES[@]}"
    do
      PKG=${PKG%$'\r'} #removes trailing \r
      uninstall_package "$PKG" &
    done
    wait
}

uninstall_package(){
  PACKAGE="$1"
  echo "${PACKAGES[*]}" | grep -w "$PACKAGE" &> /dev/null || { echo "🤷‍ Package \"$PACKAGE\" not installed"; return 1; }
  echo "${IGNORED_PACKAGES[*]}" | grep -w "$PACKAGE" &> /dev/null && { echo "❌ Package \"$PACKAGE\" is whitelisted"; return 1; }
  echo "🔥 Uninstalling \"$PACKAGE\"..."
  adb -s "$SELECTED_DEVICE" shell pm uninstall "$PACKAGE" &> /dev/null
}

select_option(){
    echo "📋 Choose package number:"
    select OPTION in "${PACKAGES_LISTED[@]}"
    do
     case $OPTION in
        *) PACKAGE=$OPTION;break; ;;
      esac
    done
    if [[ -z $PACKAGE ]]; then
      echo "❌ Invalid option picked, retry"
      select_option
    fi
}

filter_packages(){
    PACKAGES_LISTED=()
    for PKG in "${PACKAGES[@]}" #removes trailing \r
    do
      PKG=${PKG%$'\r'}
      echo "${IGNORED_PACKAGES[@]}" | grep -w "$PKG" &> /dev/null && { echo "⏩ Skipped \"$PKG\""; continue; }
      PACKAGES_LISTED+=("$PKG")
    done

    if [ ${#PACKAGES_LISTED[@]} -eq 0 ]; then
        echo "🤷‍ Nothing to uninstall"
        exit
    fi
}

handle_arguments(){
  if [[ -z "$2" ]]; then
    android_choose_device
  else
    SELECTED_DEVICE=$2
  fi

  init_uninstalling

  if [ -z "$1" ]; then
    filter_packages
    select_option
    uninstall_package "$PACKAGE"
  elif [[  "$1" == "-w" ]]; then
    if [[ -z "$2" ]]; then
      tput setaf 1
      should_proceed "💣 Delete all third-party apps on $SELECTED_DEVICE_MODEL - $SELECTED_DEVICE?"
      tput sgr0
    fi
    uninstall_all
  elif [[ -n "$1" ]]; then
    uninstall_package "$1"
  fi
}

handle_arguments "$@"
echo "✅ Done"


================================================
FILE: android/aurl
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

open_url(){
  check_url "$2"
  echo "🌎 Opening url on $1..."
  adb -s "$1" shell am start -a android.intent.action.VIEW -d "$URL" &> /dev/null
}

check_args_valid(){
  if [[ "$1" != "-a" ]]; then
    URL=$1
  else
    URL=$2
  fi
}

run()
{
  if [[ $1 == "-a" ]]; then
    check_for_update
    android_check_connected
    android_get_devices_auth_dump
    for line in $(adb devices | grep -v "List"  | awk '{print $1}')
    do
      DEVICE=$(echo "$line" | awk '{print $1}')
      open_url "$DEVICE" "$URL" &
    done
    wait
  else
    android_choose_device
    open_url "$SELECTED_DEVICE" "$URL"
  fi
}

check_args_valid "$@"
run "$@"


================================================
FILE: android/awipe
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
android_choose_device

prompt(){
  tput setaf 1
  yes_or_no "💣 Wipe all data on $SELECTED_DEVICE_MODEL - $SELECTED_DEVICE?"
  tput sgr0
  if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then
    wipe_apps
    wipe_data
  fi
}

wipe_data(){
  android_device_info "$SELECTED_DEVICE"
  android_get_storage_location_per_SDK "$SDK"

  echo "🔥 Deleting everything in mnt/sdcard..."
  adb -s "$SELECTED_DEVICE" shell rm -rf "$DEVICE_FILE_PATH/*" &>/dev/null
}

wipe_apps(){
  echo "🔥 Deleting all third-party apps..."
  "$LOCATION"/auninstall "-w" "$SELECTED_DEVICE"
}

prompt
echo "✅ Done"


================================================
FILE: android/awireless
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

get_device_ip(){
  android_choose_device
  DEVICE_IP=$(adb -s "$SELECTED_DEVICE" shell ip route | awk '{print $9}')
  DEVICE_IP=$(echo "$DEVICE_IP" | cut -d' ' -f1)
}

setup_network_connection(){
  echo "🌎 Setting up wireless connection..."
  adb -s "$SELECTED_DEVICE" tcpip 5555 &> /dev/null
  echo "🌎 Getting device IP..."
  delete_lastline
  echo "🌎 Device IP: $DEVICE_IP"
  echo "⏳ Connecting via local network..."
  STATUS=$(adb connect "$DEVICE_IP:5555")

  echo "$STATUS" | grep "refused" && echo "❌ Connection refused - already connected?" && return
  echo "✅ Connected successfully, you can unplug the USB cable"
}

setup_usb_connection(){
  echo "🔌 Disabling wireless connection..."
  adb disconnect "$DEVICE_IP:5555" &> /dev/null
  adb usb &> /dev/null
}

get_device_ip

if [[ "$SELECTED_DEVICE" == "emulator"* ]]; then
  echo "🤷‍ Emulator is wireless by default..."
  exit 1
fi

if [ "$SELECTED_DEVICE" == "$DEVICE_IP:5555" ]; then
  setup_usb_connection
else
  setup_network_connection
fi


================================================
FILE: changelog.txt
================================================
  🎉 This is a new (experimental) version 1.4.1!

    📹 arecord was fixed to work with latest scrcpy version
    If you are experiencing issues, try running "brew upgrade scrcpy"

    📷 iscreenshot was fixed to work with iOS 17 or newer
    This change is still experimental - please, report back any issues

    👋 apowerbutton was added based on a pull request by luispinho
    Thanks for your contribution, mate!

    ⭐️ Special thanks to Lenka and František for proposing compatibility fixes
    https://github.com/vrbajiva
    https://github.com/franceskoooo

    💌 Rate Mobile Toolkit and provide optional feedback using this link
    https://forms.gle/nfBHeMSjxEQMs1kv5


================================================
FILE: common_tools
================================================
#!/bin/bash
# shellcheck disable=SC2034

##############################################################################
### VARs

SCRIPT_LOCATION=$(dirname "$0")
LAST_CHECK_DATE_FILE="$SCRIPT_LOCATION/../data/toolkit_last_check_date.txt"
TEMPORARY_FILE="/private/tmp/mobile-toolkit-cache"

REGEX_NUMBER='^[0-9]+$'

##############################################################################
### Android

android_check_connected(){
  android_get_devices
  #No device connected
  if [ ${#DEVICES[@]} -eq 0 ]
  then
    echo "❌ No Android devices detected"
    exit 1
  fi
}

android_wait_for_device(){
  echo "⏳ Waiting for Android device..."
  adb wait-for-any-device
  android_get_devices
}

android_get_devices_auth_dump(){
  DEVICES_DUMP="$SCRIPT_LOCATION/../data/toolkit_adb_devices_dump.txt"
  rm -f $DEVICES_DUMP
  adb devices | grep -v "List" >> $DEVICES_DUMP
  if cat "$DEVICES_DUMP" | grep -q unauthorized ; then
    read -r -p $'🚨 Unauthorized Android device detected!\n🔌 Reconnect it, allow USB debugging and press ENTER...'
    android_get_devices_auth_dump
  fi
  if cat "$DEVICES_DUMP" | grep -q offline ; then
    read -r -p $'🚨 Offline Android device detected!\n🔌 Wait until the startup is complete, then press ENTER...'
    android_get_devices_auth_dump
  fi
}

android_get_devices(){
  #Populate array with device ids
  DEVICES=()
  android_get_devices_auth_dump
  for LINE in $(cat $DEVICES_DUMP | awk '{print $1}')
  do
    DEVICE=$(echo "$LINE" | awk '{print $1}')
    DEVICES+=("$DEVICE")
  done
}

android_get_device_sdk(){
  SDK=$(adb -s "$1" shell getprop ro.build.version.sdk | tr -cd '[[:alnum:]]._-')
}

android_device_info(){
  MANUFACTURER=$(adb -s "$1" shell getprop ro.product.manufacturer | tr -cd '[[:alnum:]]._-')
  MODEL=$(adb -s "$1" shell getprop ro.product.model | tr -cd '[[:alnum:]]._-')
  VERSION=$(adb -s "$1" shell getprop ro.build.version.release | tr -cd '[[:alnum:]]._-')
  SDK=$(adb -s "$1" shell getprop ro.build.version.sdk | tr -cd '[[:alnum:]]._-')
  INFO=$(printf "%s) %s %s %s (API %s) - %s" "$NUMBER" "$MANUFACTURER" "$MODEL" "$VERSION" "$SDK" "$1")
}

android_choose_device() {
  check_for_update
  check_adb_dependency
  android_check_connected

  #Gather device info and choose device
  if [ ${#DEVICES[@]} -gt 1 ]
  then
    NUMBER=1
    echo "📱 Available devices:"
    for ID in "${DEVICES[@]}"
    do
      android_device_info "$ID"
      echo "$INFO"
      ((NUMBER++))
    done

    read -r -p "📝 Select a device: " CHOICE
    while :;
    do
    if [[ ! $CHOICE =~ $REGEX_NUMBER ]] || [ "$CHOICE" -le "0" -o "$CHOICE" -gt "${#DEVICES[@]}" ]; then
      echo -en "\033[1A\033[2K" #deletes last echoed line in terminal
      read -r -p "🤷 Invalid input, try again: " CHOICE
    else
      break
    fi
    done
    SELECTED_DEVICE=${DEVICES[(($CHOICE-1))]}
else
  SELECTED_DEVICE="${DEVICES[0]}"
fi

SELECTED_DEVICE_MODEL=$(adb -s "$SELECTED_DEVICE" shell getprop ro.product.model | tr -cd '[[:alnum:]]._-')
SELECTED_DEVICE_SDK=$(adb -s "$SELECTED_DEVICE" shell getprop ro.build.version.sdk | tr -cd '[[:alnum:]]._-')
}

android_device_unlocked(){
  echo "📱 Checking screen status..."
  adb -s "$1" shell dumpsys power | grep "mWakefulness=" | grep "Awake" &> /dev/null
  return $?
}

android_get_foreground_package(){
  android_get_device_sdk "$SELECTED_DEVICE"
  if (( "$SDK" < 21 )); then
    android_get_foreground_package_sdk_low
  elif (( "$SDK" < 30 )); then
    android_get_foreground_package_sdk_21_plus
  elif (( "$SDK" < 31 )); then
    android_get_foreground_package_sdk_30_plus
  else
    android_get_foreground_package_sdk_31_plus
  fi
}

android_get_foreground_package_sdk_31_plus(){
  adb -s "$SELECTED_DEVICE" shell dumpsys activity recents | grep 'Recent #0' | cut -d= -f3 | cut -d ':' -f2 | cut -d ' ' -f1
}

android_get_foreground_package_sdk_30_plus(){
  adb -s "$SELECTED_DEVICE" shell dumpsys activity recents | grep 'Recent #0' | cut -d= -f6 |  cut -d ':' -f2 | cut -d ' ' -f1
}

android_get_foreground_package_sdk_21_plus(){
  adb -s "$SELECTED_DEVICE" shell dumpsys activity recents | grep 'Recent #0' | cut -d= -f2 | sed 's| .*||' | cut -d '/' -f1
}

android_get_foreground_package_sdk_low(){
  adb -s "$SELECTED_DEVICE" shell dumpsys window windows | grep mCurrentFocus | cut -d'/' -f1 | rev | cut -d' ' -f1 | rev
}

android_get_storage_location_per_SDK(){
  if (( "$1" < 30 )); then
    DEVICE_FILE_PATH="/mnt/sdcard"
  else
    DEVICE_FILE_PATH="/storage/self/primary"
  fi
}

android_is_package_installed() {
  adb -s "$SELECTED_DEVICE" shell pm list packages -f | sed -e 's/.*=//' | grep -w "$1" &> /dev/null
  EXIT_CODE=$?
  if [ $EXIT_CODE -ne 0 ]; then
    echo "🤷‍ Package \"$1\" is not installed"
    exit 1
  fi
}

android_detect_package_info(){
  echo "🔍 Detecting package name..."
  AAPPT_PATH=$(find ~/Library/Android/sdk -name 'aapt' | sort | tail -1)
  PACKAGE_INFO=$($AAPPT_PATH dump badging "$1" > $TEMPORARY_FILE)
  PACKAGE_NAME=$(cat $TEMPORARY_FILE | grep package:\ name);
  PACKAGE_NAME=$(echo "$PACKAGE_NAME" | sed 's/^[^'\'']*'\''//');
  PACKAGE_NAME=$(echo "$PACKAGE_NAME" | sed 's/'\''.*//');
  APP_NAME=$(cat $TEMPORARY_FILE  | grep application-label:)
  APP_NAME=${APP_NAME#"application-label:"}
}

android_unlock_device(){
  # arg1 = DEVICE_ID
  # arg2 = MAX_RETRIES
  MAX_RETRIES="$2"
  UNLOCK_RETRIES=1
until android_device_unlocked "$1";
do
    if [ "$UNLOCK_RETRIES" -le "$MAX_RETRIES" ]; then
      echo "🔆 Screen on attempt $UNLOCK_RETRIES..."
      ((UNLOCK_RETRIES++));
      adb -s "$1" shell input keyevent KEYCODE_POWER
      adb -s "$1" shell input keyevent 82
      sleep 1;
   else
      read -r -p "❌ Screen-wake failed, press ANY KEY after manual unlock..."
      break
   fi
 done

 delete_lastline
 echo "📱 Screen unlocked..."
}

##############################################################################
### iOS

check_go_ios_version(){
  if ! [ -x "$(command -v "go-ios")" ]; then
    install_go_ios
  else
    #Here it complains about the missing agent/tunnel
    GO_IOS_VERSION=$(go-ios --version)
    #echo "Version of go-ios is: $GO_IOS_VERSION"
  fi
}

install_go_ios(){
  echo "⏳ Installing https://github.com/danielpaulus/go-ios..."
  check_dependency "go"
  CURRENT_DIR="$PWD"
  TOOLKIT_IOS_LOCATION=$(dirname "$0")
  git clone "https://github.com/danielpaulus/go-ios.git" "$TOOLKIT_IOS_LOCATION/go-ios" &> /dev/null
  cd "$TOOLKIT_IOS_LOCATION/go-ios"

  go build .
  chmod +x "go-ios"
  mv "go-ios" "go-ios-temp"
  mv "go-ios-temp" ..
  cd ..
  rm -rf "$TOOLKIT_IOS_LOCATION/go-ios"
  mv "go-ios-temp" "go-ios"

  cd "$CURRENT_DIR"
}

prompt_xcode_launch(){
  echo "❌ Developer image is not mounted and/or device screen is locked"
  should_proceed "❓ Do you want to open Xcode to fix it? (make sure you have the latest version)"
  open -a Xcode
  echo "⏳ Waiting for Xcode to launch..."
  while true ; do
    sleep 2
    if [[ $(ps aux | grep -v grep | grep -c Xcode) -ne 0 ]]; then
      break
    fi
  done
  sleep 4
  osascript -e 'quit app "Xcode"'
}

ios_get_devices(){
  check_go_ios_version
  check_dependency "jq"

  if [[ $(ps S | grep -c "go-ios tunnel") -ne 2 ]]; then
    echo "♻️ Launching go-ios tunnel for maximum iOS compatibility (17+)"
    nohup go-ios tunnel start --userspace --nojson >/dev/null 2>&1 &
    GO_IOS_TUNNEL_PID=$!
    #TODO save tunnel port and use it to avoid delays when trying various ports
    disown $GO_IOS_TUNNEL_PID
    sleep 1
  fi

  IOS_USB_DEVICES=( $(go-ios list --nojson | sort -u) )
}

ios_pair_device(){
  go-ios pair --udid="$1" --nojson &> /dev/null
  EXIT_CODE=$?
  if [ $EXIT_CODE -ne 0 ]; then
    read -p "❌ Device is not paired - reconnect it, unlock screen, tap \"Trust\" and press ENTER..."
    ios_pair_device "$1"
  fi
}

ios_check_pairing(){
  go-ios info --udid="$1" &> "$TEMPORARY_FILE"
  if cat "$TEMPORARY_FILE" | grep -q 'UntrustedHostBUID' ; then
    read -r -p "❌ Device is not paired - reconnect it, unlock screen, tap \"Trust\" and press ENTER..."
    ios_pair_device "$1"
  fi
  if cat "$TEMPORARY_FILE" | grep -q 'could not retrieve PairRecord' ; then
    read -r -p "❌ Device is not paired - reconnect it, unlock screen, tap \"Trust\" and press ENTER..."
    ios_pair_device "$1"
  fi
}

ios_check_developer_image(){
  IS_MOUNTED=$((go-ios image list --udid="$1" --nojson) 2>&1)
  if [[ $IS_MOUNTED == *"none"* ]]; then
    prompt_xcode_launch
    ios_check_developer_image "$1"
  fi
}

ios_check_developer_image_and_pairing(){
  ios_check_pairing "$1"
  ios_check_developer_image "$1"
}

ios_device_info(){
  ios_check_developer_image_and_pairing "$1"
  MANUFACTURER="Apple"
  go-ios info --udid="$1" > "$TEMPORARY_FILE"
  MODEL=$(ios_translate_name "$(cat "$TEMPORARY_FILE" | jq -r '.HardwareModel')")
  VERSION=$(cat "$TEMPORARY_FILE" | jq -r '.ProductVersion')
  INFO=$(printf "%s) %s %s %s - %s" "$NUMBER" "$MANUFACTURER" "$MODEL" "$VERSION" "$ID")
}

ios_choose_device(){
  check_for_update
  ios_get_devices

  if [ ${#IOS_USB_DEVICES[@]} -eq 0 ] #No device connected
  then
     echo "❌ No iOS devices detected"
     exit 1
  fi

  if [ ${#IOS_USB_DEVICES[@]} -gt 1 ]
  then
     NUMBER=1
     echo "📱 Available devices:"
     for ID in "${IOS_USB_DEVICES[@]}"
      do
        ios_device_info "$ID"
        echo "$INFO"
        ((NUMBER++))
      done

      read -r -p "📝 Select a device: " CHOICE
      while :;
      do
      if [[ ! $CHOICE =~ $REGEX_NUMBER ]] || [ "$CHOICE" -le "0" -o "$CHOICE" -gt "${#IOS_USB_DEVICES[@]}" ]; then
        echo -en "\033[1A\033[2K" #deletes last echoed line in terminal
        read -r -p "🤷 Invalid input, try again: " CHOICE
      else
        break
      fi
      done
      SELECTED_DEVICE=${IOS_USB_DEVICES[(($CHOICE-1))]}
  else
     SELECTED_DEVICE="${IOS_USB_DEVICES[0]}"
  fi
}

ios_get_installed_package_list(){
  echo "⏳ Getting third-party package list..."
  INSTALLED_PACKAGES=($(go-ios apps --udid="$1" | jq -r '.[] | .CFBundleIdentifier'))
}

ios_get_all_package_list(){
  echo "⏳ Getting all package list..."
  INSTALLED_PACKAGES=($(go-ios apps --udid="$1" | jq -r '.[] | .CFBundleIdentifier'))
  SYSTEM_PACKAGES=($(go-ios apps --udid="$1" --system | jq -r '.[] | .CFBundleIdentifier'))
  ALL_PACKAGES=("${INSTALLED_PACKAGES[@]}" "${SYSTEM_PACKAGES[@]}")
}

ios_is_package_installed(){
  ios_get_all_package_list "$SELECTED_DEVICE"
  echo "${ALL_PACKAGES[*]}" | grep -w "$1" &> /dev/null
  EXIT_CODE=$?
  if [ $EXIT_CODE -ne 0 ]; then
    echo "🤷‍ Package \"$1\" is not installed"
    exit 1
  fi
}

ios_translate_name(){
  # Translations here https://www.theiphonewiki.com/wiki/Models
  NAME=$1
  case $NAME in
    "Purple"*|"purple"*)
      NAME="iPhone"
      ;;
    "M68"*)
      NAME="iPhone"
      ;;
    "N90"*|"N92"*)
      NAME="iPhone4"
      ;;
    "N94"*)
      NAME="iPhone4S"
      ;;
    "N88"*)
      NAME="iPhone3GS"
      ;;
    "N82"*)
      NAME="iPhone3G"
      ;;
    "N71"*)
      NAME="iPhone6S"
      ;;
    "N66"*)
      NAME="iPhone6SPlus"
      ;;
    "N61"*)
      NAME="iPhone6"
      ;;
    "N56"*)
      NAME="iPhone6Plus"
      ;;
    "N51"*|"N53"*)
      NAME="iPhone5S"
      ;;
    "N48"*)
      NAME="iPhone5C"
      ;;
    "N41"*|"N42"*)
      NAME="iPhone5"
      ;;
    "D10"*)
      NAME="iPhone7"
      ;;
    "D11"*)
      NAME="iPhone7Plus"
      ;;
    "D20"*)
      NAME="iPhone8"
      ;;
    "D21"*)
      NAME="iPhone8Plus"
      ;;
    "D22"*|"Ferrari"*|"ferrari"*)
      NAME="iPhoneX"
      ;;
    "D32"*)
      NAME="iPhoneXS"
      ;;
    "D33"*)
      NAME="iPhoneXSMax"
      ;;
    "N104"*)
      NAME="iPhone11"
      ;;
    "D421"*)
      NAME="iPhone11Pro"
      ;;
    "D431"*)
      NAME="iPhone11ProMax"
      ;;
    "D52"*)
      NAME="iPhone12Mini"
      ;;
    "D53g"*)
      NAME="iPhone12"
      ;;
    "D53p"*)
      NAME="iPhone12Pro"
      ;;
    "D54"*)
      NAME="iPhone12ProMax"
      ;;
    "N84"*)
      NAME="iPhoneXR"
      ;;
    "N69"*)
      NAME="iPhoneSEgen1"
      ;;
    "D79"*)
      NAME="iPhoneSEgen2"
      ;;
    "D17"*)
      NAME="iPhone13"
      ;;
    "D16"*)
      NAME="iPhone13Mini"
      ;;
    "D63"*)
      NAME="iPhone13Pro"
      ;;
    "D64"*)
      NAME="iPhone13ProMax"
      ;;
    "J1"*)
      NAME="iPad3gen"
      ;;
    "J2"*)
      NAME="iPad3gen"
      ;;
    "J72"*)
      NAME="iPadAir"
      ;;
    "J82"*)
      NAME="iPadAir2"
      ;;
    "J217"*|"J218"*)
      NAME="iPadAir3gen"
      ;;
    "J307"*|"J308"*)
      NAME="iPadAir4gen"
      ;;
    "J85"*)
      NAME="iPadMiniRetina"
      ;;
    "J96"*)
      NAME="iPadMini4"
      ;;
    "J210"*|"J211"*)
      NAME="iPadMini5gen"
      ;;
    "J310"*|"J311"*)
      NAME="iPadMini6gen"
      ;;
    "J98"*|"J99"*|"J31"*|"J127"*|"J128"*|"J318"*|"J317"*|"J207"*|"J208"*)
      NAME="iPadPro"
      ;;
    "J120"*|"J121"*|"J417"*|"J418"*)
      NAME="iPadPro2gen"
      ;;
    "J320"*|"J321"*|"J517"*|"J518"*)
      NAME="iPadPro3gen"
      ;;
    "J420"*|"J421"*)
      NAME="iPadPro4gen"
      ;;
    "J522"*|"J523"*)
      NAME="iPadPro5gen"
      ;;
    "K48"*)
      NAME="iPad"
      ;;
    "K93"*|"K94"*|"K95"*)
      NAME="iPad2"
      ;;
    "P101"*|"P103"*)
      NAME="iPad4gen"
      ;;
    "P105"*|"P107"*)
      NAME="iPadMini"
      ;;
    "J71s"*|"J71t"*|"J72s"*|"J72t"*)
      NAME="iPad5gen"
      ;;
    "J71b"*|"J72b"*)
      NAME="iPad6gen"
      ;;
    "J171AP"*|"J172AP"*)
      NAME="iPad7gen"
      ;;
    "J171aAP"*|"J172aAP"*)
      NAME="iPad8gen"
      ;;
    "J181"*|"J182"*)
      NAME="iPad9gen"
      ;;
  esac
  echo $NAME
}

##############################################################################
### Commons

check_adb_dependency(){
  if ! [ -x "$(command -v "adb")" ]; then
    echo "🤷‍ Android Debug Bridge required!"
    should_proceed "🔄 Install via homebrew? (this may take a while)"
    brew install --cask "android-platform-tools"
  fi
}

check_dependency(){
  if ! [ -x "$(command -v "$1")" ]; then
    echo "💥 \"$1\" command required!"
    should_proceed "🛒 Install via homebrew? (this may take a while)"
    brew install "$1"
  fi
}

check_for_update(){
  TODAY=$(date +%Y-%m-%d)
  if [ -f "$LAST_CHECK_DATE_FILE" ]; then
    LAST_CHECK_DATE=$(cat "$LAST_CHECK_DATE_FILE")
  else
    echo "$TODAY" > "$LAST_CHECK_DATE_FILE"
  fi

  CURRENT_DIR="$PWD"
  cd "$SCRIPT_LOCATION/.." || exit
  CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)

  if [[ "$CURRENT_BRANCH" == "master" && "$LAST_CHECK_DATE" != "$TODAY" ]]; then
    echo "🔄 Checking for Mobile Toolkit update..."
    echo "$TODAY" > "$LAST_CHECK_DATE_FILE"

    git fetch origin &> /dev/null
    git status -uno | grep "up to date" &> /dev/null
    if [ $? -ne 0 ]; then
      yes_or_no "🆕 Update available, download now?";
      if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]]; then
	echo "⏬ Updating..."
	git pull &> /dev/null
	echo "✨ New features:"
	cat "$SCRIPT_LOCATION"/../changelog.txt
	echo
	echo "✅ Update complete"
  exit 0
      fi
    fi
  fi

  cd "$CURRENT_DIR" || exit
}

delete_lastline(){
  echo -en "\033[1A\033[2K"
}

yes_or_no(){
  read -r -n 1 -p "$1 [y/n] " RESPONSE
  case "$RESPONSE" in
      [yY])
          ;;
      [nN])
          ;;
      *)
        echo
        echo "🤷‍ Invalid option"
        yes_or_no "$1"
        ;;
  esac
  echo
}

should_proceed(){
  read -r -n 1 -p "$1 [y/n] " RESPONSE
  case "$RESPONSE" in
    [yY])
      ;;
    *)
      exit
      ;;
  esac
  echo
}

choose_number(){
  MAX=$1
  if [ -n "$2" ]; then
    read -r -p "$2" CHOICE
  else
    read -r -p "📝 Choose number: " CHOICE
  fi
  while :;
  do
    if [[ -z $CHOICE  || $CHOICE -le 0 || $CHOICE -gt $MAX ]]; then
      delete_lastline
      read -r -p "🤷‍ Invalid choice, try again: " CHOICE
    else
      ((++CHOICE))
      break
    fi
  done
}

check_url(){
  URL=$1

  if [[ $URL == "" ]]; then
    read -r -p "📝 Insert web url: " URL
    check_url "$URL"
  else
    case $1 in
      'http://'*)
        ;;
      'https://'*)
        ;;
      *'://'*)
        ;;
      *)
        URL='http://'$URL
        ;;
    esac
  fi
}

abort(){
  echo "$1"
  exit 1
}


================================================
FILE: data/.gitignore
================================================
toolkit*

================================================
FILE: ios/icheckdevice
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
ios_choose_device
ios_device_info "$SELECTED_DEVICE"

GSM_URL='https://www.gsmarena.com/res.php3?sSearch='

MANUFACTURER=Apple
INFO=$(printf "%s %s - iOS %s" "$MANUFACTURER" "$MODEL" "$VERSION")

PHONE_URL=$GSM_URL$MODEL

echo "📱 $INFO - ID: $SELECTED_DEVICE"

should_proceed "🌐 Search for the device on GSMArena?"
open "$PHONE_URL"


================================================
FILE: ios/iconsole
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

echo "📋 Opening Console app..."
open -a Console


================================================
FILE: ios/iinstall
================================================
#!/bin/bash
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

install_app(){
  echo "⌛️ Installing \"$2\" to $1..."
  TEMPORARY_FILE="$TEMPORARY_FILE-$(date +%s)"
  go-ios install --udid="$1" --path="$PWD/$2" &> "$TEMPORARY_FILE"
  if grep -q 'err' "$TEMPORARY_FILE" ; then
    echo "❌ Installation to $1 failed!"
    echo "🤕 Uninstall existing version or troubleshoot the package"
    echo "🔥 Error details: $(grep 'err' "$TEMPORARY_FILE" | jq -r '.err')"
    exit 1
  fi
  echo "✅ Successfully installed to $1"
  run_app "$1" "$PWD/$2"
}

run_app(){
  echo "🚀 Launching \"$BUNDLE_ID\" on $1..."
  TEMPORARY_FILE="$TEMPORARY_FILE-$(date +%s)"
  go-ios launch "$BUNDLE_ID" --udid="$1" &> "$TEMPORARY_FILE"
  if grep -q 'err' "$TEMPORARY_FILE" ; then
    echo "❌ App launch failed!"
    echo "🤕 Check if developer certificate is trusted in Settings"
    echo "🔥 Error details: $(grep 'error' "$TEMPORARY_FILE" | jq -r '.error')"
    exit 1
  fi
}

check_args_valid(){
  if [[ "$1" != "-a" ]]; then
    FILE=$1
  else
    FILE=$2
  fi

  if [ ! -f "$PWD/$FILE" ] && [ ! -f "$FILE" ]; then
      abort "🤷 Installation file not found!"
  fi

  if [[ "$FILE" != *".ipa" ]]; then
      abort "🤷 Unsupported file!"
  fi
}

get_bundle_id(){
  echo "🔍 Detecting bundle ID..."
  unzip "$1" &> /dev/null
  APP_FILENAME=$(ls "$PWD"/Payload)
  BUNDLE_ID=$(grep "$PWD/Payload/$APP_FILENAME/embedded.mobileprovision" -a -e "[.]com[.]" | sed 's/.[^.]*\.//' | sed 's/\<.*$//')
  if [[ "$BUNDLE_ID" == "" ]]; then # Fallback for bundle identifiers containing "app" instead of "com"
    BUNDLE_ID=$(grep "$PWD/Payload/$APP_FILENAME/embedded.mobileprovision" -a -e "[.]app[.]" | sed 's/.[^.]*\.//' | sed 's/\<.*$//')
  fi
  BUNDLE_ID=$(echo "$BUNDLE_ID" | head -n 1)
  rm -rf "$PWD/Payload"
}

run(){
  if [[ $1 == "-a" ]]; then
    check_for_update
    ios_get_devices
    get_bundle_id "$2"
    for ID in "${IOS_USB_DEVICES[@]}"
     do
      install_app "$ID" "$2" &
    done
    wait
  else
    ios_choose_device
    get_bundle_id "$1"
    install_app "$SELECTED_DEVICE" "$1"
  fi
}

check_args_valid "$@"
run "$@"


================================================
FILE: ios/ikill
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
ios_choose_device

if [[ "$1" == "-s" ]]; then
  PACKAGES=($(go-ios apps --udid="$SELECTED_DEVICE" --system | jq -r '.[] | .CFBundleIdentifier'))
elif [[ -n "$1" ]]; then
  PACKAGE="$1"
  ios_is_package_installed "$PACKAGE"
else
  PACKAGES=($(go-ios apps --udid="$SELECTED_DEVICE" | jq -r '.[] | .CFBundleIdentifier'))
fi

if [ -z "$PACKAGE" ]; then
  PACKAGES_LISTED=()
  for P in "${PACKAGES[@]}" #removes trailing \r
  do
    P=${P%$'\r'}
    PACKAGES_LISTED+=("$P")
  done

  if [ ${#PACKAGES_LISTED[@]} -eq 0 ]; then
      echo "🤷‍ No third-party apps installed, use \"alaunch -s\" to list system packages"
      exit 1
  fi

  echo "📋 Choose application to kill:"
  select OPTION in "${PACKAGES_LISTED[@]}"
  do
   case $OPTION in
      *) PACKAGE=$OPTION;break; ;;
    esac
  done
fi

echo "🔪 Killing $PACKAGE..."
go-ios kill "$PACKAGE" --udid="$SELECTED_DEVICE" &> /dev/null
echo "🚀 Relaunching the app..."
go-ios launch "$PACKAGE" &> /dev/null


================================================
FILE: ios/ilang
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
ios_choose_device
LANGUAGES=( "en" "cs" "sk" "ar" "ca" "hr" "da" "nl" "en-GB" "fi" "fr" "de" "el" "he" "hi" "hu" "id" "it" "ja" "ko" "ms" "nb" "pl" "pt" "pt-BR" "ro" "ru" "es" "sv" "th" "tr" "uk" "vi")

if [ -n "$1" ]; then
  LANG="$1"
else
  echo "📋 Choose desired language:"
  select OPTION in "${LANGUAGES[@]}"
  do
   case $OPTION in
      *) LANG=$OPTION;break; ;;
    esac
  done
fi

echo "💬 Setting language to \"$LANG\" (it might take a while)..."
go-ios lang --udid="$SELECTED_DEVICE" --setlang="$LANG" &> /dev/null

EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
  echo "🤷‍ Supplied language \"$1\" is not supported"
  exit 1
fi


================================================
FILE: ios/ilaunch
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
ios_choose_device

if [[ "$1" == "-s" ]]; then
  PACKAGES=($(go-ios apps --udid="$SELECTED_DEVICE" --system | jq -r '.[] | .CFBundleIdentifier'))
elif [[ -n "$1" ]]; then
  PACKAGE="$1"
  ios_is_package_installed "$PACKAGE"
else
  PACKAGES=($(go-ios apps --udid="$SELECTED_DEVICE" | jq -r '.[] | .CFBundleIdentifier'))
fi

if [ -z "$PACKAGE" ]; then
  PACKAGES_LISTED=()
  for P in "${PACKAGES[@]}" #removes trailing \r
  do
    P=${P%$'\r'}
    PACKAGES_LISTED+=("$P")
  done

  if [ ${#PACKAGES_LISTED[@]} -eq 0 ]; then
      echo "🤷‍ No third-party apps installed, use \"alaunch -s\" to list system packages"
      exit 1
  fi

  echo "📋 Choose application to launch:"
  select OPTION in "${PACKAGES_LISTED[@]}"
  do
   case $OPTION in
      *) PACKAGE=$OPTION;break; ;;
    esac
  done
fi

echo "🚀 Launching \"$PACKAGE\"..."
go-ios launch "$PACKAGE" --udid="$SELECTED_DEVICE" &> /dev/null


================================================
FILE: ios/ilog
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

ios_choose_device
echo "📜 Device log:"
go-ios syslog --udid="$SELECTED_DEVICE" --nojson


================================================
FILE: ios/ioptions
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
ios_choose_device

echo "🔨 Launching system settings..."
go-ios launch "com.apple.Preferences" --udid="$SELECTED_DEVICE" &> /dev/null


================================================
FILE: ios/iquicktime
================================================
#!/bin/bash
echo "📹 Opening QuickTime..."

osascript <<EOD
tell application "QuickTime Player"
	activate
	set newMovieRecording to new movie recording
end tell
tell application "System Events" to tell process "QuickTime Player"
	click button 2 of window 1
end tell
return
EOD


================================================
FILE: ios/ireboot
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
ios_choose_device

should_proceed "🔌 Do you really want to reboot $SELECTED_DEVICE?"
echo "🔄 Restarting the device..."
go-ios reboot --udid="$SELECTED_DEVICE" &> /dev/null


================================================
FILE: ios/irecord
================================================
#!/bin/bash
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
trap 'ctrlc $@' 1 2 3 6 15

RECORDING=false
cd ~/Desktop || exit

ctrlc(){
  osascript -e 'quit app "QuickTime Player"'
  if $RECORDING ; then
    compress_video "$FILENAME"
    echo "✅ Saved into ~/Desktop/$FILENAME"
  fi
  exit
}

check_videosnap_dependency(){ #TODO refactor when videosnap available via Homberew
  if ! [ -x "$(command -v "videosnap")" ]; then
    echo "💥 \"videosnap\" command required!"
    should_proceed "🛒 Install via GitHub? (download and install \"videosnap-0.0.7.pkg\")"
    open "https://github.com/matthutchinson/videosnap/releases/download/v0.0.7/videosnap-0.0.7.pkg"
    exit 1
  fi
}

start_quicktime(){
	echo "🎬 Initializing QuickTime (webcam LED might turn on)..."
	osascript <<EOD
	tell application "QuickTime Player"
		set newMovieRecording to new movie recording
		set miniaturized of window 1 to true
	end tell
EOD
}

pick_recording_device(){
  ios_choose_device
  ios_device_info "$SELECTED_DEVICE"
  DEVICE_NAME=$(go-ios devicename --udid="$SELECTED_DEVICE" --nojson)
  FILENAME="$MANUFACTURER-$MODEL-iOS$VERSION-$(date +%Y-%m-%d-%H-%M-%S).mp4"
}

start_recording(){
  RECORDING=true
  echo "📹 Recording screen on $SELECTED_DEVICE ($DEVICE_NAME), stop it using ctrl^c"
  videosnap -p High -d "$DEVICE_NAME" "$FILENAME" &> /dev/null
}

compress_video(){
  if test -f "$1" ; then
    echo "📦 Compressing video..."
    ffmpeg -i "$1" "LQ-$1" -hide_banner -loglevel error #ultra basic ffmpeg compression
    rm "$1" && mv "LQ-$1" "$1"
  else
    echo "❌ Video not captured possibly due to short recording time..."
    osascript -e 'quit app "QuickTime Player"'
    exit 1
  fi
}

if uname -m | grep -q arm64 ; then
  echo "🤕 This script is currently not working on M1 based macs"
  echo "🔗 See https://github.com/matthutchinson/videosnap/issues/24"
  echo "🩹 You can use \"iquicktime\" temporarily instead"
  exit 1
fi

check_videosnap_dependency
check_dependency "ffmpeg"

start_quicktime
pick_recording_device
start_recording


================================================
FILE: ios/iscreenshot
================================================
#!/bin/bash
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools

screenshot(){
  ios_device_info "$1"
  FILENAME="$MANUFACTURER-$MODEL-iOS$VERSION-$(date +%Y-%m-%d-%H-%M-%S).png"
  echo "📸 Saving screenshot into $FILENAME..."
  go-ios screenshot --udid="$1" --output="$HOME/Desktop/$FILENAME" &> /dev/null
  EXIT_CODE=$?
  if [ $EXIT_CODE -ne 0 ]; then
    echo "🤷 Screenshot failed, error code: $EXIT_CODE"
  fi
}

screenshot_all(){
  check_for_update
  ios_get_devices
  for ID in "${IOS_USB_DEVICES[@]}"
   do
    screenshot "$ID" &
  done
  wait
}

if [ "$1" == "-a" ];
then
  screenshot_all
else
  ios_choose_device
  screenshot "$SELECTED_DEVICE"
fi


================================================
FILE: ios/isimulator
================================================
#!/bin/bash
# shellcheck disable=SC1007
# ignore irrelevant warning "remove space after ="

TOOLKIT_LOCATION=$(dirname "$0")
source "$TOOLKIT_LOCATION"/../common_tools

LOCAL_SIMULATOR_LIST=$TOOLKIT_LOCATION/../data/toolkit_simulator_list.txt

help(){
  if [[ $1 != "" ]]; then
    echo "🤷‍ Unknown option: $1"
  else
    echo "🤷 Argument missing"
  fi
  echo -e "Use one of the following options:\\n  start - choose and launch installed simulator\\n  screenshot <filename> - save screenshot to Desktop\\n  record <filename> - save screen recording to Desktop\\n  paste <text> - insert text into pasteboard\\n  url <url> - open link in web browser\\n  logs - print simulator logs\\n  battery <0-100> - set battery level\\n  time <hh:mm> - set time\\n  import <file> - import photo or video into gallery\\n  wipe - wipe all simulator data"
}

import_simulator_list(){
  xcrun simctl list -v devices | grep -i booted | sed 's/ (Booted) //g' > "$LOCAL_SIMULATOR_LIST"
  RUNNING_SIMULATOR_COUNT=$(wc -l < "$LOCAL_SIMULATOR_LIST")
}

choose_simulator(){
  read -r -p "📝 Choose: " SIMULATOR_INDEX
  SIMULATOR_ID=$(sed "$SIMULATOR_INDEX"!d "$LOCAL_SIMULATOR_LIST")
  if [[ $SIMULATOR_INDEX == "" || $SIMULATOR_ID != *"("* ]]; then
    delete_lastline
    choose_simulator
  fi
  SIMULATOR_ID=$(echo "$SIMULATOR_ID" | sed 's/.*(\(.*\))/\1/')
}

choose_running_simulator(){
  import_simulator_list

  if [[ $RUNNING_SIMULATOR_COUNT -le 0 ]]; then
    echo "❌ No running simulators"
    yes_or_no "❓ Do you want to start one?"
    if [[ "$RESPONSE" == "y" ||  "$RESPONSE" == "Y" ]];
    then
      start_simulator
    fi
    exit
  elif [[ $RUNNING_SIMULATOR_COUNT -eq 1 ]]; then
    SIMULATOR_ID=$(sed 1!d "$LOCAL_SIMULATOR_LIST" | sed 's/.*(\(.*\))/\1/')
    return
  fi

  echo "📱 Available simulators:"
  nl "$LOCAL_SIMULATOR_LIST"
  choose_simulator
}

start_simulator(){
  echo "⏳ Getting iOS simulator list..."
  rm -f "$LOCAL_SIMULATOR_LIST"
  xcrun simctl list | grep -i "shutdown\\|booted\\|-- iOS\\|-- tvOS\\|-- watchOS" | grep -v "unavailable\\|Watch:\\|Phone:" | sed 's/ (Shutdown) //g' | sed 's/ (Booted) //g' >> "$LOCAL_SIMULATOR_LIST"

  echo "📱 Available:"
  nl "$LOCAL_SIMULATOR_LIST"
  choose_simulator
  echo  "🚀 Launching simulator..."
  xcrun simctl boot "$SIMULATOR_ID"
  open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/
  rm "$LOCAL_SIMULATOR_LIST"
}

open_url(){
  check_url "$1"
  echo "🌎 Opening link in a web browser..."
  xcrun simctl openurl "$SIMULATOR_ID" "$URL" &> /dev/null
}

launch_app(){
  if [[ $1 == "" || $1 != "com."*"."* ]]; then
    read -r -p "📝 Enter bundle id: " BUNDLE_ID
    launch_app "$BUNDLE_ID"
  else
    echo "🚀 Launching the app..."
    xcrun simctl launch "$SIMULATOR_ID" "$1"
  fi
}

insert_clipboard(){
  if [[ $1 == "" ]]; then
    read -r -p "📝 Enter pasteboard text: " TEXT
    insert_clipboard "$TEXT"
  else
    echo "📥 Inserting text into clipboard..."
    echo "$1" | xcrun simctl pbcopy "$SIMULATOR_ID" &> /dev/null
  fi
}

wipe_contents(){
  should_proceed "💣 Wipe all simulator data?"
  echo "🚫 Shutting down the device..."
  xcrun simctl shutdown "$SIMULATOR_ID"
  echo "🔥 Wiping data..."
  xcrun simctl erase "$SIMULATOR_ID"
  echo "🚀 Booting..."
  xcrun simctl boot "$SIMULATOR_ID"
}

record_screen(){
  echo "📹 Recording screen on $SIMULATOR_ID, stop it with ctrl^c"
  if [ -z "$1" ] ; then
    FILENAME="Simulator-$(date +%Y-%m-%d-%H-%M-%S).mp4"
  else
    FILENAME="$1.mp4"
  fi
  if ! xcrun simctl io "$SIMULATOR_ID" recordVideo "$HOME/DESKTOP/$FILENAME"; then
    echo "❌ Recording failed!"
  else
    echo "✅ Video saved to ~/Desktop/$FILENAME"
  fi
}

take_screenshot(){
  echo "📸 Taking screenshot on $SIMULATOR_ID"
  if [ -z "$1" ] ; then
    FILENAME="Simulator-$(date +%Y-%m-%d-%H-%M-%S).png"
  else
    FILENAME=$1
  fi
  xcrun simctl io "$SIMULATOR_ID" screenshot "$HOME/Desktop/$FILENAME" &> /dev/null
  echo "✅ Screenshot saved to ~/Desktop/$FILENAME"
}

import_file(){
  if [ -z "$1" ] ; then
    echo "🤷‍ No file for import, use - isimulator import <file>"
    exit
  fi
  echo "⏬ Importing file to $SIMULATOR_ID..."
  xcrun simctl addmedia "$SIMULATOR_ID" "$PWD/$1"
  echo "✅ File imported successfully"
}

print_logs(){
  echo "📜 Simulator logs:"
  xcrun simctl spawn "$SIMULATOR_ID" log stream --level=debug
}

check_xcode_version(){
  if [ "$(FirefoxmdlsVersion= mdls -name kMDItemVersion /Applications/Xcode.app | tr -d "." | grep -oE '[0-9]+')" -lt 110 ]; then
    should_proceed "🔄 Xcode 11 or later is required, open App Store?"
    open -a "App Store"
    exit 1
  fi
}

set_battery_level(){
  check_xcode_version
  if [[ -z "$1" ]]; then
    read -r -p "📝 Enter battery level: " LEVEL
    set_battery_level "$LEVEL"
  else
    echo "🔋 Setting battery level to: $1"
    xcrun simctl status_bar "$SIMULATOR_ID" override --batteryLevel "$1" # --batteryState charged
  fi
}

set_time(){
  check_xcode_version
  if [[ -z "$1" ]]; then
    read -r -p "📝 Enter battery level: " TIME
    set_time "$TIME"
  else
    xcrun simctl status_bar "$SIMULATOR_ID" override --time "$1"
  fi
}

check_for_update

case $1 in
  'start')
    import_simulator_list
    start_simulator "$2"
    ;;
  'screenshot')
    choose_running_simulator
    take_screenshot "$2"
    ;;
  'record')
    choose_running_simulator
    record_screen "$2"
    ;;
  'import')
    choose_running_simulator
    import_file "$2"
    ;;
  'url')
    choose_running_simulator
    open_url "$2"
    ;;
  'battery')
    choose_running_simulator
    set_battery_level "$2"
    ;;
  'time')
    choose_running_simulator
    set_time "$2"
    ;;
  'launch')
    choose_running_simulator
    launch_app "$2"
    ;;
  'paste')
    choose_running_simulator
    insert_clipboard "$2"
    ;;
  'logs'|'log')
    choose_running_simulator
    print_logs "$2"
    ;;
  'wipe')
    choose_running_simulator
    wipe_contents
    ;;
   *)
    help "$1"
    choose_running_simulator
    ;;
esac


================================================
FILE: ios/iuninstall
================================================
#!/bin/bash
trap "kill 0" SIGINT # Kill all spawned subprocesses on ctrl^c
LOCATION=$(dirname "$0")
source "$LOCATION"/../common_tools
IGNORED_PACKAGES=( "com.apple.TestFlight" "motif.FramerPreview" "com.figma.FigmaMirror" "com.invisionapp.InVisionApp-iOS" "com.8bit.bitwarden" )
ios_choose_device


select_option(){
  echo "📋 Choose package number:"
  select OPTION in "${INSTALLED_PACKAGES[@]}"
  do
   case $OPTION in
      *) PACKAGE=$OPTION;break; ;;
    esac
  done
  if [[ -z $PACKAGE ]]; then
    echo "❌ Invalid option picked, retry"
    select_option
  fi
}

uninstall_package(){
  PACKAGE="$1"
  echo "${INSTALLED_PACKAGES[*]}" | grep -w "$PACKAGE" &> /dev/null || { echo "🤷‍ Package \"$PACKAGE\" not installed"; return 1; }
  echo "${IGNORED_PACKAGES[*]}" | grep -w "$PACKAGE" &> /dev/null && { echo "❌ Package \"$PACKAGE\" is whitelisted"; return 1; }

  echo "🔥 Uninstalling \"$PACKAGE\"..."
  go-ios uninstall --udid="$SELECTED_DEVICE" "$PACKAGE" &> /dev/null
  return 0
}

uninstall_all(){
  tput setaf 1
  should_proceed "💣 Delete all third-party apps on $SELECTED_DEVICE?"
  tput sgr0
  for PKG in "${INSTALLED_PACKAGES[@]}"
  do
    uninstall_package "$PKG" &
  done
  wait
}

handle_arguments(){
  if [[  "$1" == "-w" ]]; then
    uninstall_all
  elif [[ -n "$1" ]]; then
    uninstall_package "$1"
  fi

  if [ -z "$1" ]; then
    if [ ${#INSTALLED_PACKAGES[@]} -eq 0 ]; then
        echo "🤷‍ Nothing to uninstall"
        exit
    fi
    select_option
    uninstall_package "$PACKAGE"
  fi
}


ios_get_installed_package_list "$SELECTED_DEVICE"
handle_arguments "$@"
echo "✅ Done"
Download .txt
gitextract_fh0eifjr/

├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── count_lines.yml
│       └── shellcheck.yml
├── .gitignore
├── LICENCE.md
├── README.md
├── android/
│   ├── aanimationspeed
│   ├── aappinfo
│   ├── abounds
│   ├── abuildproject
│   ├── acamera
│   ├── acheckdevice
│   ├── acontrol
│   ├── adarkmode
│   ├── aemulator
│   ├── aerase
│   ├── afontscale
│   ├── agoogleplay
│   ├── ainstall
│   ├── akill
│   ├── alaunch
│   ├── alog
│   ├── aoptions
│   ├── apaste
│   ├── apermissionreset
│   ├── apowerbutton
│   ├── areboot
│   ├── arecord
│   ├── ascreenshot
│   ├── aservices
│   ├── atalkback
│   ├── atestmonkey
│   ├── atestmonkeykill
│   ├── auninstall
│   ├── aurl
│   ├── awipe
│   └── awireless
├── changelog.txt
├── common_tools
├── data/
│   └── .gitignore
└── ios/
    ├── icheckdevice
    ├── iconsole
    ├── iinstall
    ├── ikill
    ├── ilang
    ├── ilaunch
    ├── ilog
    ├── ioptions
    ├── iquicktime
    ├── ireboot
    ├── irecord
    ├── iscreenshot
    ├── isimulator
    └── iuninstall
Condensed preview — 56 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (107K chars).
[
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 645,
    "preview": "## 💬 Contributing\n\nIf you consider contributing to this repository, please first discuss the desired changes via issue, "
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 257,
    "preview": "### 🐞 Whats wrong\n~Describe requested functionality or existing bug~\n### 🌈 Desired state\n~Describe desired code and func"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 310,
    "preview": "## ⚠️ Progress checklist\n- [ ] 🏗 **Features fully completed**\n- [ ] 🔬 **Shellcheck issues resolved**\n- [ ] 🔨 **All chang"
  },
  {
    "path": ".github/workflows/count_lines.yml",
    "chars": 444,
    "preview": "name: Count lines\non: [push]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n   "
  },
  {
    "path": ".github/workflows/shellcheck.yml",
    "chars": 514,
    "preview": "name: 'Shellcheck'\non: push\njobs:\n  shellcheck:\n    name: Shellcheck\n    runs-on: ubuntu-latest\n    steps:\n    - uses: a"
  },
  {
    "path": ".gitignore",
    "chars": 58,
    "preview": "*DS_Store\nios/go-ios\nios/nohup.out\nios/selfidentity.plist\n"
  },
  {
    "path": "LICENCE.md",
    "chars": 1091,
    "preview": "MIT License\n\nCopyright (c) 2019 IntergalacticPenguin - Adam Svoboda\n\nPermission is hereby granted, free of charge, to an"
  },
  {
    "path": "README.md",
    "chars": 17953,
    "preview": "![Header](/media/header_v2.png?raw=true)\n<div id='section-id-2'/>\n\n## What is its purpose?\n🛠 **Control Android & iOS dev"
  },
  {
    "path": "android/aanimationspeed",
    "chars": 1045,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\nset_animation_speed(){\n  adb -s \"$SELECTED_DEVI"
  },
  {
    "path": "android/aappinfo",
    "chars": 1207,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nif [ -n \"$1\" ];\nthen\n    "
  },
  {
    "path": "android/abounds",
    "chars": 769,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nredraw_screen(){\n  adb -s"
  },
  {
    "path": "android/abuildproject",
    "chars": 1948,
    "preview": "#!/bin/bash\n# shellcheck disable=SC2181\n#ignore indirect exit code check at line 54\n\nLOCATION=$(dirname \"$0\")\nsource \"$L"
  },
  {
    "path": "android/acamera",
    "chars": 226,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\necho \"📷 Opening camera ap"
  },
  {
    "path": "android/acheckdevice",
    "chars": 5322,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\nGSM_URL='https://www.gsmarena.com/res.php3?sSea"
  },
  {
    "path": "android/acontrol",
    "chars": 283,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\ncheck_dependency \"scrcpy\"\ncheck_adb_dependency\n"
  },
  {
    "path": "android/adarkmode",
    "chars": 711,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nturn_off_darkmode(){\n  ec"
  },
  {
    "path": "android/aemulator",
    "chars": 5127,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nLOCAL_EMULATOR_LIST=$LOCATION/../data/toolkit_em"
  },
  {
    "path": "android/aerase",
    "chars": 464,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nif [[ \"$1\" != \"\" ]];\nthen"
  },
  {
    "path": "android/afontscale",
    "chars": 590,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\nset_font_scale(){\n  adb -s \"$SELECTED_DEVICE\" s"
  },
  {
    "path": "android/agoogleplay",
    "chars": 551,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nsearch_gp(){\n  APP_NAME=\""
  },
  {
    "path": "android/ainstall",
    "chars": 1473,
    "preview": "#!/bin/bash\ntrap \"kill 0\" SIGINT # Kill all spawned subprocesses on ctrl^c\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/."
  },
  {
    "path": "android/akill",
    "chars": 503,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nif [[ \"$1\" != \"\" ]];\nthen"
  },
  {
    "path": "android/alaunch",
    "chars": 986,
    "preview": "#!/bin/bash\nsource \"$(dirname \"$0\")\"/../common_tools\nandroid_choose_device\n\nif [[ \"$1\" == \"-s\" ]]; then\n  PACKAGES=($(ad"
  },
  {
    "path": "android/alog",
    "chars": 605,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nfilter_by_package(){\n  an"
  },
  {
    "path": "android/aoptions",
    "chars": 4610,
    "preview": "#!/bin/bash\nTOOLKIT_LOCATION=$(dirname \"$0\")\nsource \"$TOOLKIT_LOCATION\"/../common_tools\nandroid_choose_device\n\nLOCAL_SET"
  },
  {
    "path": "android/apaste",
    "chars": 2337,
    "preview": "#!/bin/bash\n# shellcheck disable=SC1001\n#ignore regex - sign occurence\ntrap \"kill 0\" SIGINT # Kill all spawned subproces"
  },
  {
    "path": "android/apermissionreset",
    "chars": 245,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\ntput setaf 1\nshould_proce"
  },
  {
    "path": "android/apowerbutton",
    "chars": 317,
    "preview": "#!/bin/bash\nsource \"$(dirname \"$0\")\"/../common_tools\nandroid_choose_device\n\n# Reference for key events - https://gist.gi"
  },
  {
    "path": "android/areboot",
    "chars": 242,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nshould_proceed \"🔌 Do you "
  },
  {
    "path": "android/arecord",
    "chars": 1746,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\n# Trap signal handling\ntrap 'ctrlc \"$@\"' 1 2 3 "
  },
  {
    "path": "android/ascreenshot",
    "chars": 887,
    "preview": "#!/bin/bash\ntrap \"kill 0\" SIGINT # Kill all spawned subprocesses on ctrl^c\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/."
  },
  {
    "path": "android/aservices",
    "chars": 894,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nSERVICE_LIST=$LOCATION/../data/toolkit_android_services.txt\nsource \"$LOCATION\"/../c"
  },
  {
    "path": "android/atalkback",
    "chars": 704,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nturn_off_talkback(){\n  ec"
  },
  {
    "path": "android/atestmonkey",
    "chars": 2731,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\ntrap 'ctrlc' 1 2 3 6 15\n\nctrlc(){\n  if $RUNNING_"
  },
  {
    "path": "android/atestmonkeykill",
    "chars": 486,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\ntrap \"kill 0\" SIGINT # Kill all spawned subproce"
  },
  {
    "path": "android/auninstall",
    "chars": 2335,
    "preview": "#!/bin/bash\ntrap \"kill 0\" SIGINT # Kill all spawned subprocesses on ctrl^c\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/."
  },
  {
    "path": "android/aurl",
    "chars": 711,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\nopen_url(){\n  check_url \"$2\"\n  echo \"🌎 Opening "
  },
  {
    "path": "android/awipe",
    "chars": 660,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nandroid_choose_device\n\nprompt(){\n  tput setaf 1\n"
  },
  {
    "path": "android/awireless",
    "chars": 1075,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\nget_device_ip(){\n  android_choose_device\n  DEVI"
  },
  {
    "path": "changelog.txt",
    "chars": 675,
    "preview": "  🎉 This is a new (experimental) version 1.4.1!\n\n    📹 arecord was fixed to work with latest scrcpy version\n    If you a"
  },
  {
    "path": "common_tools",
    "chars": 16052,
    "preview": "#!/bin/bash\n# shellcheck disable=SC2034\n\n##############################################################################\n"
  },
  {
    "path": "data/.gitignore",
    "chars": 8,
    "preview": "toolkit*"
  },
  {
    "path": "ios/icheckdevice",
    "chars": 405,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nios_choose_device\nios_device_info \"$SELECTED_DEV"
  },
  {
    "path": "ios/iconsole",
    "chars": 121,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\necho \"📋 Opening Console app...\"\nopen -a Console"
  },
  {
    "path": "ios/iinstall",
    "chars": 2173,
    "preview": "#!/bin/bash\ntrap \"kill 0\" SIGINT # Kill all spawned subprocesses on ctrl^c\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/."
  },
  {
    "path": "ios/ikill",
    "chars": 1025,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nios_choose_device\n\nif [[ \"$1\" == \"-s\" ]]; then\n "
  },
  {
    "path": "ios/ilang",
    "chars": 707,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nios_choose_device\nLANGUAGES=( \"en\" \"cs\" \"sk\" \"ar"
  },
  {
    "path": "ios/ilaunch",
    "chars": 965,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nios_choose_device\n\nif [[ \"$1\" == \"-s\" ]]; then\n "
  },
  {
    "path": "ios/ilog",
    "chars": 161,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\n\nios_choose_device\necho \"📜 Device log:\"\ngo-ios s"
  },
  {
    "path": "ios/ioptions",
    "chars": 206,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nios_choose_device\n\necho \"🔨 Launching system sett"
  },
  {
    "path": "ios/iquicktime",
    "chars": 276,
    "preview": "#!/bin/bash\necho \"📹 Opening QuickTime...\"\n\nosascript <<EOD\ntell application \"QuickTime Player\"\n\tactivate\n\tset newMovieRe"
  },
  {
    "path": "ios/ireboot",
    "chars": 244,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\nios_choose_device\n\nshould_proceed \"🔌 Do you real"
  },
  {
    "path": "ios/irecord",
    "chars": 2037,
    "preview": "#!/bin/bash\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/../common_tools\ntrap 'ctrlc $@' 1 2 3 6 15\n\nRECORDING=false\ncd ~"
  },
  {
    "path": "ios/iscreenshot",
    "chars": 727,
    "preview": "#!/bin/bash\ntrap \"kill 0\" SIGINT # Kill all spawned subprocesses on ctrl^c\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/."
  },
  {
    "path": "ios/isimulator",
    "chars": 5948,
    "preview": "#!/bin/bash\n# shellcheck disable=SC1007\n# ignore irrelevant warning \"remove space after =\"\n\nTOOLKIT_LOCATION=$(dirname \""
  },
  {
    "path": "ios/iuninstall",
    "chars": 1602,
    "preview": "#!/bin/bash\ntrap \"kill 0\" SIGINT # Kill all spawned subprocesses on ctrl^c\nLOCATION=$(dirname \"$0\")\nsource \"$LOCATION\"/."
  }
]

About this extraction

This page contains the full source code of the IGPenguin/mobile-toolkit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 56 files (94.1 KB), approximately 29.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!