├── .info ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── .shellcheckrc ├── .gitignore ├── modules ├── json │ ├── constants.sh │ ├── parse.sh │ ├── apps.sh │ ├── api.sh │ ├── patches.sh │ ├── cli.sh │ └── options.sh ├── app │ ├── delete.sh │ ├── antisplit.sh │ ├── select.sh │ ├── import.sh │ ├── version.sh │ └── download.sh ├── system │ ├── install.sh │ └── root.sh ├── constants.sh ├── source.sh ├── utils.sh ├── preferences.sh ├── workflow.sh ├── patch.sh └── assets.sh ├── install.sh ├── root ├── umount.sh └── mount.sh ├── sources.json ├── README.md ├── main.sh ├── config ├── .DIALOGRC_DARK └── .DIALOGRC_LIGHT └── revancify /.info: -------------------------------------------------------------------------------- 1 | VERSION='v2.7.2' 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: 'decipher3114' 2 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | disable=SC1090,SC1091,SC2011,SC2034,SC2044,SC2153,SC2154,SC2178 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.* 2 | */** 3 | 4 | !main.sh 5 | !install.sh 6 | !sources.json 7 | !modules/** 8 | !root/** -------------------------------------------------------------------------------- /modules/json/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | STRING="String" 4 | NUMBER="Number" 5 | BOOLEAN="Boolean" 6 | STRINGARRAY="StringArray" 7 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | [ -z "$TERMUX_VERSION" ] && echo -e "Termux not detected !!" && exit 1 3 | BIN="$PREFIX/bin/revancify" 4 | curl -sL "https://github.com/decipher3114/Revancify/raw/refs/heads/main/revancify" -o "$BIN" 5 | [ -e "$BIN" ] && chmod +x "$BIN" && "$BIN" 6 | -------------------------------------------------------------------------------- /modules/app/delete.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | deleteApps() { 4 | if "${DIALOG[@]}" \ 5 | --title '| Delete Assets |' \ 6 | --defaultno \ 7 | --yesno "Please confirm to delete the apps.\nIt will delete all the downloaded and patched apps." -1 -1; then 8 | rm -rf "apps"/* "$STORAGE"/Patched/* &> /dev/null 9 | fi 10 | } 11 | -------------------------------------------------------------------------------- /root/umount.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | PKG_NAME="$1" 4 | 5 | am force-stop "$PKG_NAME" 6 | grep "$PKG_NAME" /proc/mounts | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -l 7 | am force-stop "$PKG_NAME" 8 | pm clear --cache-only "$PKG_NAME" 9 | rm -f "/data/adb/service.d/mount_$PKG_NAME.sh" 10 | rm -f "/data/adb/post-fs-data.d/umount_$PKG_NAME.sh" 11 | rm -f "/data/local/tmp/revancify/$PKG_NAME.apk" 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /modules/system/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | installApp() { 4 | local CANONICAL_VER 5 | if [ "$ROOT_ACCESS" == true ]; then 6 | mountApp 7 | else 8 | notify info "Copying patched $APP_NAME apk to Internal Storage..." 9 | CANONICAL_VER=${APP_VER//:/} 10 | cp -f "apps/$APP_NAME/$APP_VER-$SOURCE.apk" "$STORAGE/Patched/$APP_NAME-$CANONICAL_VER-$SOURCE.apk" &> /dev/null 11 | termux-open --view "$STORAGE/Patched/$APP_NAME-$CANONICAL_VER-$SOURCE.apk" 12 | fi 13 | unset PKG_NAME APP_NAME APKMIRROR_APP_NAME APP_VER 14 | } 15 | -------------------------------------------------------------------------------- /modules/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | STORAGE="$HOME/storage/shared/Revancify" 4 | 5 | ARCH=$(getprop ro.product.cpu.abi) 6 | DPI=$(getprop ro.sf.lcd_density) 7 | 8 | USER_AGENT="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" 9 | 10 | DIALOG=(dialog --backtitle 'Revancify' --no-shadow --begin 2 0) 11 | 12 | CURL=(curl -sL --fail-early --connect-timeout 2 --max-time 5 -H 'Cache-Control: no-cache') 13 | 14 | WGET=(wget -qc --show-progress --user-agent="$USER_AGENT") 15 | 16 | NAVIGATION_HINT="Navigate with [↑] [↓] [←] [→]" 17 | SELECTION_HINT="Select with [SPACE]" 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Detailed information about the bug encountered 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: decipher3114 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Patch Log** 21 | If bug is related to patching 22 | 23 | **Screenshots or Recordings** 24 | Upload a Screenshot or Recording displaying the bug 25 | 26 | **Smartphone (please complete the following information):** 27 | - Device: [e.g. iPhone6] 28 | - Android Version: [e.g. iOS8.1] 29 | - Revancify Version: [e.g. 22] 30 | -------------------------------------------------------------------------------- /modules/source.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | changeSource() { 4 | local SELECTED_SOURCE SOURCES_INFO 5 | readarray -t SOURCES_INFO < <(jq -r --arg SOURCE "$SOURCE" '.[] | .source | ., if . == $SOURCE then "on" else "off" end' sources.json) 6 | SELECTED_SOURCE=$( 7 | "${DIALOG[@]}" \ 8 | --title '| Source Selection Menu |' \ 9 | --no-cancel \ 10 | --no-items \ 11 | --ok-label 'Done' \ 12 | --radiolist "$NAVIGATION_HINT\n$SELECTION_HINT" -1 -1 0 \ 13 | "${SOURCES_INFO[@]}" 2>&1 > /dev/tty 14 | ) 15 | 16 | [ "$SOURCE" == "$SELECTED_SOURCE" ] && return 17 | SOURCE="$SELECTED_SOURCE" 18 | setEnv SOURCE "$SOURCE" update .config 19 | unset AVAILABLE_PATCHES APPS_INFO APPS_LIST ENABLED_PATCHES 20 | } 21 | -------------------------------------------------------------------------------- /modules/utils.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | terminate() { 4 | killall -9 java &> /dev/null 5 | killall -9 dialog &> /dev/null 6 | killall -9 WGET &> /dev/null 7 | rm -rf -- *temporary* 8 | tput cnorm 9 | clear 10 | exit "${1:-0}" 11 | } 12 | 13 | setEnv() { 14 | if [ ! -f "$4" ]; then 15 | : > "$4" 16 | fi 17 | if ! grep -q "${1}=" "$4"; then 18 | echo "$1='$2'" >> "$4" 19 | elif [ "$3" == "update" ]; then 20 | sed -i "s|^$1=.*|$1='${2//&/\\&}'|" "$4" 21 | fi 22 | } 23 | 24 | notify() { 25 | dialog --backtitle 'Revancify' --"$1"box "$2" 12 45 26 | } 27 | 28 | internet() { 29 | if ! ping -c 1 google.com &> /dev/null; then 30 | notify msg "Oops! No Internet Connection available.\n\nConnect to Internet and try again later." 31 | return 1 32 | fi 33 | } 34 | -------------------------------------------------------------------------------- /modules/preferences.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | configure() { 4 | local CONFIG_OPTS UPDATED_CONFIG THEME 5 | CONFIG_OPTS=("LIGHT_THEME" "$LIGHT_THEME" "PREFER_SPLIT_APK" "$PREFER_SPLIT_APK" "LAUNCH_APP_AFTER_MOUNT" "$LAUNCH_APP_AFTER_MOUNT" ALLOW_APP_VERSION_DOWNGRADE "$ALLOW_APP_VERSION_DOWNGRADE") 6 | 7 | readarray -t UPDATED_CONFIG < <( 8 | "${DIALOG[@]}" \ 9 | --title '| Configure |' \ 10 | --no-items \ 11 | --separate-output \ 12 | --no-cancel \ 13 | --ok-label 'Save' \ 14 | --checklist "$NAVIGATION_HINT\n$SELECTION_HINT" -1 -1 -1 \ 15 | "${CONFIG_OPTS[@]}" \ 16 | 2>&1 > /dev/tty 17 | ) 18 | 19 | sed -i "s|='on'|='off'|" .config 20 | 21 | for CONFIG_OPT in "${UPDATED_CONFIG[@]}"; do 22 | setEnv "$CONFIG_OPT" on update .config 23 | done 24 | 25 | source .config 26 | 27 | [ "$LIGHT_THEME" == "on" ] && THEME="LIGHT" || THEME="DARK" 28 | export DIALOGRC="config/.DIALOGRC_$THEME" 29 | } 30 | -------------------------------------------------------------------------------- /modules/workflow.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | initiateWorkflow() { 4 | TASK="CHOOSE_APP" 5 | while true; do 6 | case "$TASK" in 7 | "CHOOSE_APP") 8 | chooseApp || break 9 | ;; 10 | "DOWNLOAD_APP") 11 | downloadApp || continue 12 | TASK="MANAGE_PATCHES" 13 | ;; 14 | "IMPORT_APP") 15 | importApp || continue 16 | TASK="MANAGE_PATCHES" 17 | ;; 18 | "MANAGE_PATCHES") 19 | managePatches || continue 20 | TASK="EDIT_OPTIONS" 21 | ;; 22 | "EDIT_OPTIONS") 23 | editOptions || continue 24 | TASK="PATCH_APP" 25 | ;; 26 | "PATCH_APP") 27 | patchApp || break 28 | TASK="INSTALL_APP" 29 | ;; 30 | "INSTALL_APP") 31 | installApp 32 | break 33 | ;; 34 | esac 35 | done 36 | } 37 | -------------------------------------------------------------------------------- /modules/app/antisplit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | antisplitApp() { 4 | local APP_DIR LOCALE 5 | 6 | notify info "Please Wait !!\nReducing app size..." 7 | 8 | APP_DIR="apps/$APP_NAME/$APP_VER" 9 | 10 | if [ ! -e "$APP_DIR" ]; then 11 | LOCALE=$(getprop persist.sys.locale | sed 's/-.*//g') 12 | unzip -qqo \ 13 | "apps/$APP_NAME/$APP_VER.apkm" \ 14 | "base.apk" \ 15 | "split_config.${ARCH//-/_}.apk" \ 16 | "split_config.${LOCALE}.apk" \ 17 | split_config.*dpi.apk \ 18 | -d "$APP_DIR" 2> /dev/null 19 | fi 20 | 21 | java -jar bin/APKEditor.jar m -i "$APP_DIR" -o "apps/$APP_NAME/$APP_VER.apk" &> /dev/null 22 | 23 | if [ ! -e "apps/$APP_NAME/$APP_VER.apk" ]; then 24 | rm -rf "$APP_DIR" &> /dev/null 25 | notify msg "Unable to run merge splits!!\nApkEditor is not working properly." 26 | return 1 27 | fi 28 | rm "apps/$APP_NAME/$APP_VER.apkm" &> /dev/null 29 | 30 | if [ "$ROOT_ACCESS" == false ]; then 31 | rm -rf "apps/$APP_NAME/$APP_VER" 32 | fi 33 | setEnv "APP_SIZE" "$(stat -c %s "apps/$APP_NAME/$APP_VER.apk")" update "apps/$APP_NAME/.data" 34 | } 35 | -------------------------------------------------------------------------------- /sources.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "source": "ReVanced", 4 | "repository": "ReVanced/revanced-patches", 5 | "api": { 6 | "json": "https://api.revanced.app/v4/patches/list", 7 | "version": "https://api.revanced.app/v4/patches/version" 8 | } 9 | }, 10 | { 11 | "source": "ReVanced-Extended", 12 | "repository": "inotia00/revanced-patches", 13 | "api": { 14 | "json": "https://raw.githubusercontent.com/inotia00/revanced-patches/refs/heads/revanced-extended/patches.json", 15 | "version": null 16 | } 17 | }, 18 | { 19 | "source": "Anddea", 20 | "repository": "anddea/revanced-patches", 21 | "api": { 22 | "json": "https://raw.githubusercontent.com/anddea/revanced-patches/refs/heads/main/patches.json", 23 | "version": null 24 | } 25 | }, 26 | { 27 | "source": "RVX-Android-6-7", 28 | "repository": "kitadai31/revanced-patches-android6-7", 29 | "api": { 30 | "json": "https://raw.githubusercontent.com/kitadai31/revanced-patches-android6-7/refs/heads/revanced-extended/patches.json", 31 | "version": null 32 | } 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /modules/app/select.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | chooseApp() { 4 | local PREVIOUS_APP SELECTED_APP EXIT_CODE 5 | fetchAssets || return 1 6 | unset PKG_NAME APP_NAME APKMIRROR_APP_NAME 7 | PREVIOUS_APP="$APP_NAME" 8 | SELECTED_APP=$( 9 | "${DIALOG[@]}" \ 10 | --title '| App Selection Menu |' \ 11 | --no-tags \ 12 | --ok-label 'Select' \ 13 | --cancel-label 'Back' \ 14 | --help-button \ 15 | --help-label 'Import' \ 16 | --default-item "$SELECTED_APP" \ 17 | --menu "$NAVIGATION_HINT" -1 -1 0 \ 18 | "${APPS_LIST[@]}" \ 19 | 2>&1 > /dev/tty 20 | ) 21 | EXIT_CODE=$? 22 | case "$EXIT_CODE" in 23 | 0) 24 | source <(jq -nrc --argjson SELECTED_APP "$SELECTED_APP" ' 25 | $SELECTED_APP | 26 | "PKG_NAME=\(.pkgName) 27 | APP_NAME=\(.appName) 28 | APKMIRROR_APP_NAME=\(.apkmirrorAppName)" 29 | ') 30 | TASK="DOWNLOAD_APP" 31 | ;; 32 | 1) 33 | return 1 34 | ;; 35 | 2) 36 | TASK="IMPORT_APP" 37 | ;; 38 | esac 39 | if [ "$APP_NAME" != "$SELECTED_APP" ]; then 40 | unset VERSIONS_LIST 41 | fi 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Revancify 🛠️ (Archived) 2 | ### A TUI wrapper for Revanced CLI with amazing features. 3 | 4 | [![TelegramChannel](https://img.shields.io/badge/Telegram_Support_Chat-2CA5E0?style=for-the-badge&logo=Telegram&logoColor=FFFFFF)](https://t.me/decipher_projects) 5 | 6 | ## Alternative 7 | ### 1. [ReVanced Manager (Official)](https://github.com/ReVanced/revanced-manager) 8 | ### 2. [Universal ReVanced Manager](https://github.com/Jman-Github/Universal-ReVanced-Manager) 9 | 10 | ## Termux 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Download Link
Termux Official (Recommended)Termux Monet (Deprecated)
21 | 22 | 23 | # Features 24 | 1. Auto fetches Patches and CLI 25 | 2. Interactive and Easy to use 26 | 3. Support APK download from ApkMirror or Importing from storage 27 | 4. User-friendly Patch-options Editor 28 | 5. Convenient Installation and usage 29 | 6. Lightweight and faster than any other tool 30 | 31 | # Guide 32 | 33 | ## Installation 34 | 1. Download and Install [Termux](#termux). 35 | 2. Open Termux. 36 | 3. Copy and paste this command. 37 | ``` 38 | curl -sL https://github.com/decipher3114/Revancify/raw/refs/heads/main/install.sh | bash 39 | ``` 40 | 41 | ## Usage 42 | After installation, type `revancify` in termux and press enter. 43 | 44 | Or use with arguments. Check them with `revancify -h` 45 | 46 | # Thanks & Credits 47 | [Revanced](https://github.com/revanced) 48 | [Revanced Extended](https://github.com/inotia00) 49 | 50 | -------------------------------------------------------------------------------- /modules/json/parse.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | parsePatchesJson() { 4 | while [ ! -e "assets/$SOURCE/Patches-$PATCHES_VERSION.json" ]; do 5 | if [ -n "$JSON_URL" ]; then 6 | parseJsonFromAPI 7 | continue 8 | fi 9 | parseJsonFromCLI | "${DIALOG[@]}" --gauge "Please Wait!!\nParsing JSON file for $SOURCE patches from CLI Output.\nThis might take some time." -1 -1 0 10 | tput civis 11 | done 12 | 13 | [ -n "$AVAILABLE_PATCHES" ] || AVAILABLE_PATCHES=$(jq -rc '.' "assets/$SOURCE/Patches-$PATCHES_VERSION.json") 14 | 15 | [ -n "$ENABLED_PATCHES" ] || ENABLED_PATCHES=$(jq -erc '.' "$STORAGE/$SOURCE-patches.json" 2> /dev/null || echo '[]') 16 | 17 | while [ -z "$APPS_LIST" ]; do 18 | if [ -e "assets/$SOURCE/Apps-$PATCHES_VERSION.json" ]; then 19 | readarray -t APPS_LIST < <( 20 | jq -rc ' 21 | reduce .[] as $APP_INFO ( 22 | []; 23 | if any(.[]; .[1] == $APP_INFO.appName) then 24 | . += [[$APP_INFO, "\($APP_INFO.appName) [\($APP_INFO.pkgName)]"]] | 25 | .[-2] |= (.[0] as $APP_INFO | .[1] as $APP_NAME | [$APP_INFO, "\($APP_NAME) [\($APP_INFO.pkgName)]"]) 26 | else 27 | . += [[$APP_INFO, $APP_INFO.appName]] 28 | end 29 | ) | 30 | .[][] 31 | ' "assets/$SOURCE/Apps-$PATCHES_VERSION.json" 32 | ) 33 | fi 34 | if [ ${#APPS_LIST[@]} -eq 0 ]; then 35 | unset APPS_LIST 36 | rm assets/"$SOURCE"/Apps-*.json &> /dev/null 37 | fetchAppsInfo || return 1 38 | fi 39 | done 40 | } 41 | -------------------------------------------------------------------------------- /modules/system/root.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | getInstalledVersion() { 4 | if [ "$ROOT_ACCESS" == true ] && su -c "pm list packages | grep -q $PKG_NAME"; then 5 | INSTALLED_VERSION=$(su -c dumpsys package "$PKG_NAME" | sed -n '/versionName/s/.*=//p' | sed -n '1p') 6 | fi 7 | } 8 | 9 | mountApp() { 10 | notify info "Please Wait !!\nMounting $APP_NAME..." 11 | if su -mm -c "/system/bin/sh root/mount.sh $PKG_NAME $APP_NAME $APP_VER $SOURCE" &> /dev/null; then 12 | notify msg "$APP_NAME Mounted Successfully !!" 13 | else 14 | notify msg "Installation Failed !!\nShare logs to developer." 15 | termux-open --send "$STORAGE/mount_log.txt" 16 | return 0 17 | fi 18 | if [ "$LAUNCH_APP_AFTER_MOUNT" == "on" ]; then 19 | su -c "settings list secure | sed -n -e 's/\/.*//' -e 's/default_input_method=//p' | xargs pidof | xargs kill -9 && pm resolve-activity --brief $PKG_NAME | tail -n 1 | xargs am start -n && pidof com.termux | xargs kill -9" &> /dev/null 20 | fi 21 | } 22 | 23 | umountApp() { 24 | local PKG_NAME 25 | readarray -t MOUNTED_PKGS < <(su -c 'ls /data/local/tmp/revancify | xargs basename -s ".apk" -a 2> /dev/null') 26 | if [ ${#MOUNTED_PKGS[@]} == 0 ]; then 27 | notify msg "No mounted app present!!" 28 | return 29 | fi 30 | if ! PKG_NAME=$( 31 | "${DIALOG[@]}" \ 32 | --title '| Unmount App |' \ 33 | --no-items \ 34 | --ok-label 'Select' \ 35 | --cancel-label 'Back' \ 36 | --menu "$NAVIGATION_HINT" -1 -1 0 \ 37 | "${MOUNTED_PKGS[@]}" \ 38 | 2>&1 > /dev/tty 39 | ); then 40 | return 41 | fi 42 | su -mm -c "/system/bin/sh root/umount.sh $PKG_NAME" &> /dev/null 43 | notify msg "Unmount Successful !!" 44 | unset MOUNTED_PKGS PKG_NAME 45 | } 46 | -------------------------------------------------------------------------------- /main.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | main() { 4 | 5 | setEnv SOURCE "ReVanced" init .config 6 | setEnv LIGHT_THEME "off" init .config 7 | setEnv PREFER_SPLIT_APK "on" init .config 8 | setEnv LAUNCH_APP_AFTER_MOUNT "on" init .config 9 | setEnv ALLOW_APP_VERSION_DOWNGRADE "off" init .config 10 | source .config 11 | 12 | mkdir -p "assets" "apps" "$STORAGE" "$STORAGE/Patched" "$STORAGE/Stock" 13 | 14 | [ "$ROOT_ACCESS" == true ] && MENU_ENTRY=(7 "Unmount Patched app") 15 | 16 | [ "$LIGHT_THEME" == "on" ] && THEME="LIGHT" || THEME="DARK" 17 | export DIALOGRC="config/.DIALOGRC_$THEME" 18 | 19 | while true; do 20 | MAIN=$( 21 | "${DIALOG[@]}" \ 22 | --title '| Main Menu |' \ 23 | --ok-label 'Select' \ 24 | --cancel-label 'Exit' \ 25 | --menu "$NAVIGATION_HINT" -1 -1 0 1 "Patch App" 2 "Update Assets" 3 "Change Source" 4 "Configure" 5 "Delete Assets" 6 "Delete Apps" "${MENU_ENTRY[@]}" \ 26 | 2>&1 > /dev/tty 27 | ) || break 28 | case "$MAIN" in 29 | 1) 30 | initiateWorkflow 31 | ;; 32 | 2) 33 | fetchAssetsInfo || break 34 | fetchAssets 35 | ;; 36 | 3) 37 | changeSource 38 | ;; 39 | 4) 40 | configure 41 | ;; 42 | 5) 43 | deleteAssets 44 | ;; 45 | 6) 46 | deleteApps 47 | ;; 48 | 7) 49 | umountApp 50 | ;; 51 | esac 52 | done 53 | } 54 | 55 | tput civis 56 | ROOT_ACCESS="$1" 57 | 58 | for MODULE in $(find modules -type f -name "*.sh"); do 59 | source "$MODULE" 60 | done 61 | 62 | trap terminate SIGTERM SIGINT SIGABRT 63 | main || terminate 1 64 | terminate "$?" 65 | -------------------------------------------------------------------------------- /modules/patch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | findPatchedApp() { 4 | if [ -e "apps/$APP_NAME/$APP_VER-$SOURCE.apk" ]; then 5 | "${DIALOG[@]}" \ 6 | --title '| Patched apk found |' \ 7 | --defaultno \ 8 | --yes-label 'Patch' \ 9 | --no-label 'Install' \ 10 | --help-button \ 11 | --help-label 'Back' \ 12 | --yesno "Current directory already contains Patched $APP_NAME version $APP_VER.\n\n\nDo you want to patch $APP_NAME again?" -1 -1 13 | case "$?" in 14 | 0) 15 | rm "apps/$APP_NAME/$APP_VER-$SOURCE.apk" 16 | ;; 17 | 1) 18 | TASK="INSTALL_APP" 19 | return 1 20 | ;; 21 | 2) 22 | return 1 23 | ;; 24 | esac 25 | else 26 | return 0 27 | fi 28 | } 29 | 30 | patchApp() { 31 | if [ ! -e "apps/$APP_NAME/$APP_VER.apk" ]; then 32 | notify msg "Apk not found !!\nTry importing Apk from Storage." 33 | return 1 34 | fi 35 | 36 | readarray -t ARGUMENTS < <( 37 | jq -nrc --arg PKG_NAME "$PKG_NAME" --argjson ENABLED_PATCHES "$ENABLED_PATCHES" ' 38 | $ENABLED_PATCHES[] | 39 | select(.pkgName == $PKG_NAME) | 40 | .options as $OPTIONS | 41 | .patches[] | 42 | . as $PATCH_NAME | 43 | "--enable", 44 | $PATCH_NAME, 45 | ( 46 | $OPTIONS[] | 47 | if .patchName == $PATCH_NAME then 48 | "--options=" + 49 | .key + "=" + 50 | ( 51 | .value | 52 | if . != null then 53 | . | tostring 54 | else 55 | empty 56 | end 57 | ) 58 | else 59 | empty 60 | end 61 | ) 62 | ' 63 | ) 64 | 65 | echo -e "Root Access: $ROOT_ACCESS\nArchitecture: $ARCH\nApp: $APP_NAME v$APP_VER\nCLI: $CLI_FILE\nPatches: $PATCHES_FILE\nArguments: ${ARGUMENTS[*]}\n\nLogs:\n" > "$STORAGE/patch_log.txt" 66 | 67 | java -jar "$CLI_FILE" patch \ 68 | --force --exclusive --purge --patches="$PATCHES_FILE" \ 69 | --out="apps/$APP_NAME/$APP_VER-$SOURCE.apk" \ 70 | "${ARGUMENTS[@]}" \ 71 | --custom-aapt2-binary="./bin/aapt2" \ 72 | --keystore="$STORAGE/revancify.keystore" \ 73 | "apps/$APP_NAME/$APP_VER.apk" |& 74 | tee -a "$STORAGE/patch_log.txt" | 75 | "${DIALOG[@]}" \ 76 | --ok-label 'Continue' \ 77 | --extra-button \ 78 | --extra-label 'Share Logs' \ 79 | --cursor-off-label \ 80 | --programbox "Patching $APP_NAME $APP_VER" -1 -1 81 | EXIT_CODE=$? 82 | tput civis 83 | 84 | if [ $EXIT_CODE -eq 3 ]; then 85 | termux-open --send "$STORAGE/patch_log.txt" 86 | fi 87 | 88 | if [ ! -f "apps/$APP_NAME/$APP_VER-$SOURCE.apk" ]; then 89 | notify msg "Patching failed !!\nInstallation Aborted." 90 | return 1 91 | fi 92 | } 93 | -------------------------------------------------------------------------------- /modules/json/apps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | fetchAppsInfo() { 4 | local RESPONSE_JSON 5 | 6 | notify info "Fetching apps info from apkmirror.com..." 7 | 8 | if RESPONSE_JSON=$( 9 | "${CURL[@]}" 'https://www.apkmirror.com/wp-json/apkm/v1/app_exists/' \ 10 | -A "$USER_AGENT" \ 11 | -H 'Accept: application/json' \ 12 | -H 'Content-Type: application/json' \ 13 | -H 'Authorization: Basic YXBpLXRvb2xib3gtZm9yLWdvb2dsZS1wbGF5OkNiVVcgQVVMZyBNRVJXIHU4M3IgS0s0SCBEbmJL' \ 14 | -d "$(jq -nr --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" ' 15 | { 16 | "pnames": ( 17 | $AVAILABLE_PATCHES | 18 | map( 19 | .pkgName | 20 | if . != null then 21 | . 22 | else 23 | empty 24 | end 25 | ) 26 | ) 27 | } 28 | ')" | 29 | jq -c ' 30 | reduce .data[] as { 31 | pname: $PKG_NAME, 32 | exists: $EXISTS, 33 | app: { 34 | name: $APP_NAME, 35 | link: $APP_URL 36 | } 37 | } ( 38 | []; 39 | if $EXISTS then 40 | . += [{ 41 | "pkgName": $PKG_NAME, 42 | "appName": $APP_NAME, 43 | "appUrl": $APP_URL 44 | }] 45 | else 46 | . 47 | end 48 | ) 49 | ' 2> /dev/null 50 | ); then 51 | rm assets/"$SOURCE"/Apps-*.json &> /dev/null 52 | 53 | echo "$RESPONSE_JSON" | 54 | jq -c ' 55 | reduce .[] as {pkgName: $PKG_NAME, appName: $APP_NAME, appUrl: $APP_URL} ( 56 | []; 57 | . += [{ 58 | "pkgName": $PKG_NAME, 59 | "appName": ( 60 | $APP_NAME | 61 | sub("( -)|( &)|:"; ""; "g") | 62 | sub("[()\\|]"; ""; "g") | 63 | sub(" *[-, ] *"; "-"; "g") | 64 | sub("-Wear-OS|-Android-Automotive"; ""; "g") 65 | ) | 66 | split("-")[:4] | 67 | join("-"), 68 | "apkmirrorAppName": ( 69 | $APP_URL | 70 | sub("-wear-os|-android-automotive"; "") | 71 | match("(?<=\\/)(((?!\\/).)*)(?=\\/$)").string 72 | ), 73 | }] 74 | ) 75 | ' > "assets/$SOURCE/Apps-$PATCHES_VERSION.json" \ 76 | 2> /dev/null 77 | else 78 | notify msg "API request failed for apkmirror.com.\nTry again later..." 79 | return 1 80 | fi 81 | } 82 | -------------------------------------------------------------------------------- /modules/app/import.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | selectFile() { 4 | readarray -t STOCK_APPS < <(ls "$STORAGE/Stock/"*.apk "$STORAGE/Stock"/*.apkm 2> /dev/null | xargs basename -a 2> /dev/null) 5 | if [ "${#STOCK_APPS[@]}" -eq 0 ]; then 6 | notify msg "No apk found in Stock Apps directory !!\nMove app to 'Revancify/Stock' to import." 7 | TASK="CHOOSE_APP" 8 | return 1 9 | fi 10 | if ! SELECTED_FILE=$( 11 | "${DIALOG[@]}" \ 12 | --title '| Import App |' \ 13 | --no-items \ 14 | --ok-label 'Select' \ 15 | --cancel-label 'Back' \ 16 | --menu "$NAVIGATION_HINT" -1 -1 0 \ 17 | "${STOCK_APPS[@]}" \ 18 | 2>&1 > /dev/tty 19 | ); then 20 | TASK="CHOOSE_APP" 21 | return 1 22 | fi 23 | } 24 | 25 | extractMeta() { 26 | local APP_INFO 27 | FILE_PATH="$STORAGE/Stock/$SELECTED_FILE" 28 | if [ "${SELECTED_FILE##*.}" == "apk" ]; then 29 | notify info "Please Wait !!\nExtracting data from \"$(basename "$FILE_PATH")\"" 30 | if ! APP_INFO=$(./bin/aapt2 dump badging "$FILE_PATH"); then 31 | notify msg "The Apk you selected is not valid. Download again and retry." 32 | return 1 33 | fi 34 | APP_EXT="apk" 35 | PKG_NAME=$(grep -oP "(?<=package: name=')[^']+" <<< "$APP_INFO") 36 | APP_NAME=$(grep -oP "(?<=application-label:')[^']+" <<< "$APP_INFO" | sed -E 's/[.: ]+/-/g') 37 | SELECTED_VERSION=$(grep -oP "(?<=versionName=')[^']+" <<< "$APP_INFO") 38 | else 39 | if ! APP_INFO=$(unzip -qqp "$FILE_PATH" info.json 2> /dev/null); then 40 | notify msg "The Bundle you selected is not valid. Download again and retry." 41 | return 1 42 | fi 43 | if jq -e --arg ARCH "$ARCH" '.arches | index($ARCH) == null' <<< "$APP_INFO" &> /dev/null; then 44 | notify msg "The selected Apk Bundle doesn't contain $ARCH lib.\nChoose another file." 45 | fi 46 | APP_EXT="apkm" 47 | source <(jq -rc ' 48 | "APP_NAME=\(.app_name) 49 | PKG_NAME=\(.pname) 50 | SELECTED_VERSION=\(.release_version)" 51 | ' <<< "$APP_INFO") 52 | fi 53 | } 54 | 55 | importApp() { 56 | unset PKG_NAME APP_NAME APP_VER 57 | local SELECTED_FILE FILE_PATH APP_EXT SELECTED_VERSION 58 | selectFile || return 1 59 | extractMeta || return 1 60 | APP_VER="${SELECTED_VERSION// /-}" 61 | getInstalledVersion 62 | if [ "$ALLOW_APP_VERSION_DOWNGRADE" == "off" ] && 63 | jq -e '.[0] > .[1]' <<< "[\"${INSTALLED_VERSION:-0}\", \"$SELECTED_VERSION\"]" \ 64 | &> /dev/null; then 65 | notify msg "The selected version $SELECTED_VERSION is lower then version $INSTALLED_VERSION installed on your device.\nPlease Select a higher version !!" 66 | return 1 67 | fi 68 | if ! "${DIALOG[@]}" \ 69 | --title '| Proceed |' \ 70 | --yes-label 'Import' \ 71 | --no-label 'Back' \ 72 | --yesno "The following data is extracted from the apk file you provided.\nApp Name : $APP_NAME\nPackage Name: $PKG_NAME\nVersion : $SELECTED_VERSION\nDo you want to proceed with this app?" -1 -1; then 73 | return 1 74 | fi 75 | mkdir -p "apps/$APP_NAME" &> /dev/null 76 | rm -rf apps/"$APP_NAME"/* &> /dev/null 77 | cp "$FILE_PATH" "apps/$APP_NAME/$APP_VER.$APP_EXT" 78 | if [ "$APP_EXT" == "apkm" ]; then 79 | antisplitApp || return 1 80 | fi 81 | findPatchedApp || return 1 82 | } 83 | -------------------------------------------------------------------------------- /config/.DIALOGRC_DARK: -------------------------------------------------------------------------------- 1 | # 2 | # Run-time configuration file for dialog 3 | # 4 | # Custom Dark Config by "decipher3114" 5 | # 6 | # 7 | # Types of values: 8 | # 9 | # Number - 10 | # String - "string" 11 | # Boolean - 12 | # Attribute - (foreground,background,highlight?,underline?,reverse?) 13 | 14 | # Set aspect-ration. 15 | aspect = 0 16 | 17 | # Set separator (for multiple widgets output). 18 | separate_widget = "" 19 | 20 | # Set tab-length (for textbox tab-conversion). 21 | tab_len = 0 22 | 23 | # Make tab-traversal for checklist, etc., include the list. 24 | visit_items = OFF 25 | 26 | # Shadow dialog boxes? This also turns on color. 27 | use_shadow = OFF 28 | 29 | # Turn color support ON or OFF 30 | use_colors = ON 31 | 32 | # Screen color 33 | screen_color = (WHITE,BLACK,OFF) 34 | 35 | # Shadow color 36 | shadow_color = (BLACK,BLACK,OFF) 37 | 38 | # Dialog box color 39 | dialog_color = screen_color 40 | 41 | # Dialog box title color 42 | title_color = screen_color 43 | 44 | # Dialog box border color 45 | border_color = screen_color 46 | 47 | # Active button color 48 | button_active_color = (BLACK,WHITE,OFF) 49 | 50 | # Inactive button color 51 | button_inactive_color = screen_color 52 | 53 | # Active button key color 54 | button_key_active_color = button_active_color 55 | 56 | # Inactive button key color 57 | button_key_inactive_color = screen_color 58 | 59 | # Active button label color 60 | button_label_active_color = button_active_color 61 | 62 | # Inactive button label color 63 | button_label_inactive_color = screen_color 64 | 65 | # Input box color 66 | inputbox_color = screen_color 67 | 68 | # Input box border color 69 | inputbox_border_color = screen_color 70 | 71 | # Search box color 72 | searchbox_color = screen_color 73 | 74 | # Search box title color 75 | searchbox_title_color = title_color 76 | 77 | # Search box border color 78 | searchbox_border_color = border_color 79 | 80 | # File position indicator color 81 | position_indicator_color = title_color 82 | 83 | # Menu box color 84 | menubox_color = screen_color 85 | 86 | # Menu box border color 87 | menubox_border_color = border_color 88 | 89 | # Item color 90 | item_color = screen_color 91 | 92 | # Selected item color 93 | item_selected_color = button_active_color 94 | 95 | # Tag color 96 | tag_color = title_color 97 | 98 | # Selected tag color 99 | tag_selected_color = button_label_active_color 100 | 101 | # Tag key color 102 | tag_key_color = button_key_inactive_color 103 | 104 | # Selected tag key color 105 | tag_key_selected_color = button_active_color 106 | 107 | # Check box color 108 | check_color = screen_color 109 | 110 | # Selected check box color 111 | check_selected_color = button_active_color 112 | 113 | # Up arrow color 114 | uarrow_color = screen_color 115 | 116 | # Down arrow color 117 | darrow_color = uarrow_color 118 | 119 | # Item help-text color 120 | itemhelp_color = screen_color 121 | 122 | # Active form text color 123 | form_active_text_color = (WHITE,BLACK,OFF,ON) 124 | 125 | # Form text color 126 | form_text_color = screen_color 127 | 128 | # Readonly form item color 129 | form_item_readonly_color = screen_color 130 | 131 | # Dialog box gauge color 132 | gauge_color = title_color 133 | 134 | # Dialog box border2 color 135 | border2_color = border_color 136 | 137 | # Input box border2 color 138 | inputbox_border2_color = screen_color 139 | 140 | # Search box border2 color 141 | searchbox_border2_color = screen_color 142 | 143 | # Menu box border2 color 144 | menubox_border2_color = screen_color 145 | -------------------------------------------------------------------------------- /config/.DIALOGRC_LIGHT: -------------------------------------------------------------------------------- 1 | # 2 | # Run-time configuration file for dialog 3 | # 4 | # Custom Light Config by "decipher3114" 5 | # 6 | # 7 | # Types of values: 8 | # 9 | # Number - 10 | # String - "string" 11 | # Boolean - 12 | # Attribute - (foreground,background,highlight?,underline?,reverse?) 13 | 14 | # Set aspect-ration. 15 | aspect = 0 16 | 17 | # Set separator (for multiple widgets output). 18 | separate_widget = "" 19 | 20 | # Set tab-length (for textbox tab-conversion). 21 | tab_len = 0 22 | 23 | # Make tab-traversal for checklist, etc., include the list. 24 | visit_items = OFF 25 | 26 | # Shadow dialog boxes? This also turns on color. 27 | use_shadow = OFF 28 | 29 | # Turn color support ON or OFF 30 | use_colors = ON 31 | 32 | # Screen color 33 | screen_color = (BLACK,WHITE,OFF) 34 | 35 | # Shadow color 36 | shadow_color = (WHITE,WHITE,OFF) 37 | 38 | # Dialog box color 39 | dialog_color = screen_color 40 | 41 | # Dialog box title color 42 | title_color = screen_color 43 | 44 | # Dialog box border color 45 | border_color = screen_color 46 | 47 | # Active button color 48 | button_active_color = (WHITE,BLACK,OFF) 49 | 50 | # Inactive button color 51 | button_inactive_color = screen_color 52 | 53 | # Active button key color 54 | button_key_active_color = button_active_color 55 | 56 | # Inactive button key color 57 | button_key_inactive_color = screen_color 58 | 59 | # Active button label color 60 | button_label_active_color = button_active_color 61 | 62 | # Inactive button label color 63 | button_label_inactive_color = screen_color 64 | 65 | # Input box color 66 | inputbox_color = screen_color 67 | 68 | # Input box border color 69 | inputbox_border_color = screen_color 70 | 71 | # Search box color 72 | searchbox_color = screen_color 73 | 74 | # Search box title color 75 | searchbox_title_color = title_color 76 | 77 | # Search box border color 78 | searchbox_border_color = border_color 79 | 80 | # File position indicator color 81 | position_indicator_color = title_color 82 | 83 | # Menu box color 84 | menubox_color = screen_color 85 | 86 | # Menu box border color 87 | menubox_border_color = border_color 88 | 89 | # Item color 90 | item_color = screen_color 91 | 92 | # Selected item color 93 | item_selected_color = button_active_color 94 | 95 | # Tag color 96 | tag_color = title_color 97 | 98 | # Selected tag color 99 | tag_selected_color = button_label_active_color 100 | 101 | # Tag key color 102 | tag_key_color = button_key_inactive_color 103 | 104 | # Selected tag key color 105 | tag_key_selected_color = button_active_color 106 | 107 | # Check box color 108 | check_color = screen_color 109 | 110 | # Selected check box color 111 | check_selected_color = button_active_color 112 | 113 | # Up arrow color 114 | uarrow_color = screen_color 115 | 116 | # Down arrow color 117 | darrow_color = uarrow_color 118 | 119 | # Item help-text color 120 | itemhelp_color = screen_color 121 | 122 | # Active form text color 123 | form_active_text_color = (BLACK,WHITE,OFF,ON) 124 | 125 | # Form text color 126 | form_text_color = screen_color 127 | 128 | # Readonly form item color 129 | form_item_readonly_color = screen_color 130 | 131 | # Dialog box gauge color 132 | gauge_color = title_color 133 | 134 | # Dialog box border2 color 135 | border2_color = border_color 136 | 137 | # Input box border2 color 138 | inputbox_border2_color = screen_color 139 | 140 | # Search box border2 color 141 | searchbox_border2_color = screen_color 142 | 143 | # Menu box border2 color 144 | menubox_border2_color = screen_color 145 | -------------------------------------------------------------------------------- /root/mount.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | PKG_NAME="$1" 4 | APP_NAME="$2" 5 | APP_VER="$3" 6 | SOURCE="$4" 7 | 8 | log() { 9 | echo "- $1" >> "/storage/emulated/0/Revancify/mount_log.txt" 10 | } 11 | 12 | rm "/storage/emulated/0/Revancify/mount_log.txt" 13 | 14 | log "START" 15 | 16 | for DIR in /data/local/tmp/revancify/ /data/adb/post-fs-data.d/ /data/adb/service.d/; do 17 | if [ ! -e $DIR ]; then 18 | mkdir "$DIR" 19 | log "$DIR created." 20 | fi 21 | done 22 | 23 | for FILE in "/data/adb/post-fs-data.d/umount_$PKG_NAME.sh" "/data/adb/service.d/mount_$PKG_NAME.sh" "/data/local/tmp/revancify/$PKG_NAME.apk"; do 24 | if [ -e "$FILE" ]; then 25 | rm "$FILE" 26 | log "$FILE deleted." 27 | fi 28 | done 29 | 30 | log "Checking if $APP_NAME $APP_VER is installed" 31 | if ! (pm list packages | grep -q "$PKG_NAME" && [ "$(dumpsys package "$PKG_NAME" | sed -n '/versionName/s/.*=//p' | sed 's/ /./1p')" = "$APP_VER" ]); then 32 | 33 | log "$APP_NAME $APP_VER is NOT installed !!" 34 | log "Installing $APP_NAME $APP_VER..." 35 | 36 | if [ -e "apps/$APP_NAME/$APP_VER" ]; then 37 | pm install --user 0 -r "apps/$APP_NAME/$APP_VER/"* 38 | log "$APP_NAME $APP_VER [split] installed." 39 | else 40 | pm install --user 0 -r "apps/$APP_NAME/$APP_VER.apk" 41 | log "$APP_NAME $APP_VER installed." 42 | fi 43 | fi 44 | 45 | if ! pm list packages | grep -q "$PKG_NAME"; then 46 | log "$APP_NAME $APP_VER installation failed !!" 47 | log "Exit !!" 48 | exit 1 49 | fi 50 | 51 | STOCK_APP_PATH=$(pm path "$PKG_NAME" | sed -n "/base/s/package://p") 52 | PATCHED_APP_PATH="/data/local/tmp/revancify/$PKG_NAME.apk" 53 | 54 | log "Force stopping..." 55 | am force-stop "$PKG_NAME" 56 | 57 | log "Unmounting previous mounts..." 58 | grep "$PKG_NAME" /proc/mounts | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -l 59 | 60 | log "Copying apk to $PATCHED_APP_PATH..." 61 | cp -f "apps/$APP_NAME/$APP_VER-$SOURCE.apk" "$PATCHED_APP_PATH" 62 | if [ ! -e "$PATCHED_APP_PATH" ]; then 63 | log "Path: $PATCHED_APP_PATH does not exist !!" 64 | log "Exit !!" 65 | exit 1 66 | fi 67 | 68 | log "Setting up permissions..." 69 | chmod 644 "$PATCHED_APP_PATH" 70 | chown system:system "$PATCHED_APP_PATH" 71 | chcon u:object_r:apk_data_file:s0 "$PATCHED_APP_PATH" 72 | 73 | log "Mounting app..." 74 | mount -o bind "$PATCHED_APP_PATH" "$STOCK_APP_PATH" 75 | 76 | if ! grep -q "$PKG_NAME" /proc/mounts; then 77 | log "Mount failed !!" 78 | log "Exit !!" 79 | exit 1 80 | fi 81 | 82 | log "Force stopping..." 83 | am force-stop "$PKG_NAME" 84 | 85 | log "Clearing cache..." 86 | pm clear --cache-only "$PKG_NAME" 87 | 88 | log "Creating boot scripts..." 89 | cat << EOF > "/data/adb/service.d/mount_$PKG_NAME.sh" 90 | #!/system/bin/sh 91 | while [ "\$(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 5; done 92 | 93 | BASE_PATH="$PATCHED_APP_PATH" 94 | STOCK_PATH="\$(pm path $PKG_NAME | sed -n '/base/s/package://p')" 95 | am force-stop "$PKG_NAME" 96 | chcon u:object_r:apk_data_file:s0 "\$BASE_PATH" 97 | [ ! -z "\$STOCK_PATH" ] && mount -o bind "\$BASE_PATH" "\$STOCK_PATH" 98 | am force-stop "$PKG_NAME" 99 | EOF 100 | 101 | cat << EOF > "/data/adb/post-fs-data.d/umount_$PKG_NAME.sh" 102 | #!/system/bin/sh 103 | STOCK_PATH="\$(pm path "$PKG_NAME" | sed -n '/base/s/package://p')" 104 | [ ! -z "\$STOCK_PATH" ] && umount -l "\$STOCK_PATH" 105 | grep "$PKG_NAME" /proc/mounts | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -l 106 | EOF 107 | 108 | chmod 0755 "/data/adb/service.d/mount_$PKG_NAME.sh" 109 | chmod 0755 "/data/adb/post-fs-data.d/umount_$PKG_NAME.sh" 110 | 111 | log "Install Successful." 112 | log "End." 113 | 114 | exit 0 115 | -------------------------------------------------------------------------------- /modules/json/api.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | parseJsonFromAPI() { 4 | local RESPONSE 5 | 6 | notify info "Please Wait!!\nParsing JSON file for $SOURCE patches from API." 7 | 8 | if ! AVAILABLE_PATCHES=$( 9 | "${CURL[@]}" "$JSON_URL" | 10 | jq -c \ 11 | --arg STRING "$STRING" \ 12 | --arg NUMBER "$NUMBER" \ 13 | --arg BOOLEAN "$BOOLEAN" \ 14 | --arg STRINGARRAY "$STRINGARRAY" ' 15 | reduce .[] as { 16 | name: $PATCH, 17 | use: $USE, 18 | compatiblePackages: $COMPATIBLE_PKGS, 19 | options: $OPTIONS 20 | } ( 21 | []; 22 | ( 23 | $OPTIONS | 24 | if length != 0 then 25 | map( 26 | . |= {"patchName": $PATCH} + . | 27 | .type |= ( 28 | if test("List") then 29 | $STRINGARRAY 30 | elif test("Boolean") then 31 | $BOOLEAN 32 | elif test("Long|Int|Float") then 33 | $NUMBER 34 | else 35 | $STRING 36 | end 37 | ) | 38 | .values |= ( 39 | if . != null then 40 | [to_entries[] | (.value | tostring) + " (" + .key + ")"] 41 | else 42 | [] 43 | end 44 | ) 45 | ) 46 | else 47 | . 48 | end 49 | ) as $OPTIONS | 50 | [ 51 | $COMPATIBLE_PKGS | 52 | if . == null then 53 | {"name": null, "versions":[]} 54 | else 55 | to_entries[] | 56 | {"name": .key, "versions": (.value // [])} 57 | end 58 | ] as $COMPATIBLE_PKGS | 59 | reduce $COMPATIBLE_PKGS[] as {name: $PKG_NAME, versions: $VERSIONS} ( 60 | .; 61 | if any(.[]; .pkgName == $PKG_NAME) then 62 | . 63 | else 64 | . |= .[0:-1] + [ 65 | { 66 | "pkgName": $PKG_NAME, 67 | "versions": [], 68 | "patches": { 69 | "recommended": [], 70 | "optional": [] 71 | }, 72 | "options": [] 73 | } 74 | ] + .[-1:] 75 | end | 76 | map( 77 | if .pkgName == $PKG_NAME then 78 | .versions |= (. += $VERSIONS | unique | sort) | 79 | .patches |= ( 80 | if $USE then 81 | .recommended += [$PATCH] 82 | else 83 | .optional += [$PATCH] 84 | end 85 | ) | 86 | .options += $OPTIONS 87 | else 88 | . 89 | end 90 | ) 91 | ) 92 | ) 93 | ' 2> /dev/null 94 | ); then 95 | unset JSON_URL AVAILABLE_PATCHES 96 | return 1 97 | fi 98 | 99 | echo "$AVAILABLE_PATCHES" > "assets/$SOURCE/Patches-$PATCHES_VERSION.json" 100 | } 101 | -------------------------------------------------------------------------------- /modules/app/version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | scrapeVersionsList() { 4 | local PAGE_CONTENTS PAGE_JSON MERGED_JSON 5 | local IDX MAX_PAGE_COUNT 6 | 7 | MAX_PAGE_COUNT=5 8 | 9 | for ((IDX = 1; IDX <= MAX_PAGE_COUNT; IDX++)); do 10 | TMP_FILES[IDX]=$(mktemp) 11 | "${CURL[@]}" -A "$USER_AGENT" "https://www.apkmirror.com/uploads/page/$IDX/?appcategory=$APKMIRROR_APP_NAME" > "${TMP_FILES[$IDX]}" 2> /dev/null & 12 | done 13 | wait 14 | 15 | for ((IDX = 1; IDX <= MAX_PAGE_COUNT; IDX++)); do 16 | PAGE_CONTENTS[IDX]=$(cat "${TMP_FILES[$IDX]}") 17 | rm -f "${TMP_FILES[$IDX]}" 18 | done 19 | 20 | for ((IDX = 1; IDX <= MAX_PAGE_COUNT; IDX++)); do 21 | PAGE_JSON[IDX]=$( 22 | pup -c 'div.widget_appmanager_recentpostswidget div.listWidget div:not([class]) json{}' <<< "${PAGE_CONTENTS[$IDX]}" | 23 | jq -rc ' 24 | .[].children as $CHILDREN | 25 | { 26 | version: $CHILDREN[1].children[0].children[1].text, 27 | info: $CHILDREN[0].children[0].children[1].children[0].children[0].children[0] 28 | } | 29 | { 30 | version: .version, 31 | tag: ( 32 | .info.text | ascii_downcase | 33 | if test("beta") then 34 | "[BETA]" 35 | elif test("alpha") then 36 | "[ALPHA]" 37 | else 38 | "[STABLE]" 39 | end 40 | ), 41 | url: .info.href 42 | } 43 | ' 44 | ) 45 | done 46 | 47 | MERGED_JSON=$(jq -s '.' <<< "$(printf '%s\n' "${PAGE_JSON[@]}")") 48 | 49 | if [[ "$MERGED_JSON" == "[]" ]]; then 50 | notify msg "Unable to fetch versions !!\nThere is some problem with your internet connection. Disable VPN or Change your network." 51 | TASK="CHOOSE_APP" 52 | return 1 53 | fi 54 | 55 | readarray -t VERSIONS_LIST < <( 56 | jq -rc \ 57 | --arg PKG_NAME "$PKG_NAME" \ 58 | --arg INSTALLED_VERSION "$INSTALLED_VERSION" \ 59 | --arg ALLOW_APP_VERSION_DOWNGRADE "$ALLOW_APP_VERSION_DOWNGRADE" \ 60 | --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" ' 61 | . as $ALL_VERSIONS | 62 | ( 63 | $AVAILABLE_PATCHES[] | 64 | select(.pkgName == $PKG_NAME) | 65 | .versions 66 | ) as $SUPPORTED_VERSIONS | 67 | $ALL_VERSIONS | 68 | map( 69 | .version as $VERSION | 70 | if ($SUPPORTED_VERSIONS | index($VERSION)) != null then 71 | .tag = "[RECOMMENDED]" 72 | elif .version == $INSTALLED_VERSION then 73 | .tag = "[INSTALLED]" 74 | else 75 | . 76 | end 77 | ) | 78 | ( 79 | if any(.[]; .tag == "[RECOMMENDED]") then 80 | (first(.[] | select(.tag == "[RECOMMENDED]"))), "Auto Select|[RECOMMENDED]" 81 | elif $INSTALLED_VERSION != "" then 82 | .[-1], "Auto Select|[INSTALLED]" 83 | else 84 | empty 85 | end 86 | ), 87 | ( 88 | .[] | 89 | ., "\(.version)|\(.tag)" 90 | ) 91 | ' <<< "$MERGED_JSON" 92 | ) 93 | } 94 | 95 | chooseVersion() { 96 | unset APP_VER APP_DL_URL 97 | local INSTALLED_VERSION SELECTED_VERSION 98 | internet || return 1 99 | getInstalledVersion 100 | if [ "${#VERSIONS_LIST[@]}" -eq 0 ]; then 101 | notify info "Please Wait !!\nScraping versions list for $APP_NAME from apkmirror.com..." 102 | scrapeVersionsList || return 1 103 | fi 104 | if ! SELECTED_VERSION=$( 105 | "${DIALOG[@]}" \ 106 | --title '| Version Selection Menu |' \ 107 | --no-tags \ 108 | --column-separator "|" \ 109 | --default-item "$SELECTED_VERSION" \ 110 | --ok-label 'Select' \ 111 | --cancel-label 'Back' \ 112 | --menu "$NAVIGATION_HINT" -1 -1 0 \ 113 | "${VERSIONS_LIST[@]}" \ 114 | 2>&1 > /dev/tty 115 | ); then 116 | TASK="CHOOSE_APP" 117 | return 1 118 | fi 119 | APP_VER=$(jq -nrc --argjson SELECTED_VERSION "$SELECTED_VERSION" '$SELECTED_VERSION.version | sub(" "; ""; "g")') 120 | APP_DL_URL=$(jq -nrc --argjson SELECTED_VERSION "$SELECTED_VERSION" '"https://www.apkmirror.com" + $SELECTED_VERSION.url') 121 | } 122 | -------------------------------------------------------------------------------- /modules/json/patches.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | managePatches() { 4 | local ENABLED_PATCHES_LIST BUTTON_TEXT PATCHES_ARRAY UPDATED_PATCHES CHOICES EXIT_CODE 5 | readarray -t ENABLED_PATCHES_LIST < <( 6 | jq -nrc --arg PKG_NAME "$PKG_NAME" --argjson ENABLED_PATCHES "$ENABLED_PATCHES" ' 7 | $ENABLED_PATCHES | 8 | if any(.[]; .pkgName == $PKG_NAME) then 9 | .[] | select(.pkgName == $PKG_NAME) | .patches[] 10 | else 11 | empty 12 | end' 13 | ) 14 | BUTTON_TEXT="Recommended" 15 | 16 | while true; do 17 | 18 | readarray -t PATCHES_ARRAY < <( 19 | jq -nrc \ 20 | --arg PKG_NAME "$PKG_NAME" \ 21 | --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" ' 22 | $AVAILABLE_PATCHES[] | 23 | select(.pkgName == $PKG_NAME or .pkgName == null) | 24 | .patches | 25 | .recommended[], .optional[] | 26 | . as $PATCH | 27 | if ($ARGS.positional | index($PATCH)) != null then 28 | $PATCH, "on" 29 | else 30 | $PATCH, "off" 31 | end 32 | ' --args "${ENABLED_PATCHES_LIST[@]}" 33 | ) 34 | 35 | CHOICES=$( 36 | "${DIALOG[@]}" \ 37 | --title '| Patch Selection Menu |' \ 38 | --no-items \ 39 | --separate-output \ 40 | --ok-label 'Done' \ 41 | --cancel-label "$BUTTON_TEXT" \ 42 | --help-button \ 43 | --help-label "Back" \ 44 | --checklist "$NAVIGATION_HINT\n$SELECTION_HINT" -1 -1 0 \ 45 | "${PATCHES_ARRAY[@]}" 2>&1 > /dev/tty 46 | ) 47 | EXIT_CODE=$? 48 | 49 | [ "$CHOICES" != "" ] && readarray -t ENABLED_PATCHES_LIST <<< "$CHOICES" 50 | 51 | case "$EXIT_CODE" in 52 | 0) 53 | if [ ${#ENABLED_PATCHES_LIST[@]} -eq 0 ]; then 54 | notify msg "No patches enabled!!\nPatches selection couldn't be empty. Enable some patches to continue." 55 | continue 56 | fi 57 | break 58 | ;; 59 | 1) 60 | if [ "$BUTTON_TEXT" == "Recommended" ]; then 61 | readarray -t ENABLED_PATCHES_LIST < <(jq -nrc --arg PKG_NAME "$PKG_NAME" --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" '$AVAILABLE_PATCHES[] | select(.pkgName == $PKG_NAME or .pkgName == null) | .patches | .recommended[]') 62 | BUTTON_TEXT="Enable All" 63 | elif [ "$BUTTON_TEXT" == "Enable All" ]; then 64 | readarray -t ENABLED_PATCHES_LIST < <(jq -nrc --arg PKG_NAME "$PKG_NAME" --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" '$AVAILABLE_PATCHES[] | select(.pkgName == $PKG_NAME or .pkgName == null) | .patches | .recommended[], .optional[]') 65 | BUTTON_TEXT="Disable All" 66 | elif [ "$BUTTON_TEXT" == "Disable All" ]; then 67 | ENABLED_PATCHES_LIST=() 68 | BUTTON_TEXT="Recommended" 69 | fi 70 | ;; 71 | 2) 72 | TASK="CHOOSE_APP" 73 | return 1 74 | ;; 75 | esac 76 | done 77 | 78 | clear 79 | 80 | UPDATED_PATCHES=$( 81 | jq -nc --arg PKG_NAME "$PKG_NAME" --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" --argjson ENABLED_PATCHES "$ENABLED_PATCHES" ' 82 | [ 83 | $AVAILABLE_PATCHES[] | 84 | select(.pkgName == $PKG_NAME or .pkgName == null) | 85 | .options[] 86 | ] as $AVAILABLE_OPTIONS | 87 | $ENABLED_PATCHES | 88 | if any(.[]; .pkgName == $PKG_NAME) then 89 | . 90 | else 91 | . += [{"pkgName": $PKG_NAME}] 92 | end | 93 | map( 94 | if .pkgName == $PKG_NAME then 95 | .patches |= [$ARGS.positional | if (.[0] == "") then empty else .[] end] | 96 | .options |= ( 97 | . as $SAVED_OPTIONS | [ 98 | $AVAILABLE_OPTIONS[] | 99 | . as $OPTION | 100 | .patchName as $PATCH_NAME | 101 | if ($ARGS.positional | index($PATCH_NAME)) != null then 102 | .title as $TITLE | 103 | .key as $KEY | 104 | .default as $DEFAULT | 105 | { 106 | "title": $TITLE, 107 | "patchName": $PATCH_NAME, 108 | "key": $KEY, 109 | "value": (($SAVED_OPTIONS[]? | select(.key == $KEY and .patchName == $PATCH_NAME) | .value) // $DEFAULT) 110 | } 111 | else 112 | empty 113 | end 114 | ] 115 | ) 116 | else 117 | . 118 | end 119 | ) 120 | ' --args "${ENABLED_PATCHES_LIST[@]}" 121 | ) 122 | 123 | echo "$UPDATED_PATCHES" > "$STORAGE/$SOURCE-patches.json" 124 | ENABLED_PATCHES="$UPDATED_PATCHES" 125 | } 126 | -------------------------------------------------------------------------------- /revancify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | SRC="$HOME/Revancify" 4 | 5 | HELP="revancify 6 | 7 | Usage: revancify [OPTION] 8 | 9 | Options: 10 | -f: Force re-install Revancify 11 | -u: Disable Update Check 12 | -r: Disable Root access 13 | -v: Print current version 14 | -h: Print help statement" 15 | 16 | while getopts ":furvh" OPT 2> /dev/null; do 17 | case $OPT in 18 | f) 19 | rm "$SRC/.info" &> /dev/null 20 | ;; 21 | u) 22 | INTERNET_ACCESS=false 23 | ;; 24 | r) 25 | ROOT_ACCESS=false 26 | ;; 27 | v) 28 | if [ -e "$SRC" ]; then 29 | source "$SRC/.info" 30 | echo "$VERSION" 31 | else 32 | echo "Revancify not installed !!" 33 | fi 34 | exit 35 | ;; 36 | h) 37 | echo -e "$HELP" 38 | exit 39 | ;; 40 | ?) 41 | echo -e "Invalid option specified: -${OPTARG}" 42 | echo -e "$HELP" 43 | exit 1 44 | ;; 45 | esac 46 | done 47 | 48 | terminate() { 49 | killall -9 curl &> /dev/null 50 | killall -9 wget &> /dev/null 51 | clear 52 | echo "Script terminated !!" 53 | exit 1 54 | } 55 | trap terminate SIGTERM SIGINT SIGABRT 56 | 57 | installDependencies() { 58 | local BINS BIN CTR RESPONSE 59 | echo "Checking dependencies..." 60 | 61 | [ -e "$HOME/storage" ] || termux-setup-storage 62 | 63 | BINS=$(ls "$PREFIX/bin") 64 | grep -q java <<< "$BINS" || PKGS+=("openjdk-21") 65 | grep -q wget <<< "$BINS" || PKGS+=("wget") 66 | grep -q tput <<< "$BINS" || PKGS+=("ncurses-utils") 67 | grep -q dialog <<< "$BINS" || PKGS+=("dialog") 68 | grep -q pup <<< "$BINS" || PKGS+=("pup") 69 | grep -q jq <<< "$BINS" || PKGS+=("jq") 70 | grep -q unzip <<< "$BINS" || PKGS+=("unzip") 71 | 72 | if [ "${#PKGS[@]}" -ne 0 ]; then 73 | pkg update || return 1 74 | yes | pkg install "${PKGS[@]}" || return 1 75 | fi 76 | 77 | sed -i '/allow-external-apps/s/# //' "$HOME/.termux/termux.properties" 78 | 79 | [ -e "$SRC/bin" ] || mkdir -p "$SRC/bin" 80 | 81 | AAPT2="$SRC/bin/aapt2" 82 | APK_EDITOR="$SRC/bin/APKEditor.jar" 83 | 84 | CTR=0 && while [ ! -e "$AAPT2" ]; do 85 | [ $CTR -gt 2 ] && return 1 86 | echo -e "\nDownloading aapt2...\n" 87 | readarray -t RESPONSE < <(curl -s "https://api.github.com/repos/decipher3114/binaries/releases/latest" | jq -r --arg ARCH "$(getprop ro.product.cpu.abi)" '.assets[] | if (.name | test($ARCH)) then (.browser_download_url, .size) else empty end' 2> /dev/null) 88 | [ "${#RESPONSE[@]}" -eq 0 ] && return 1 89 | wget -q --show-progress "${RESPONSE[0]}" -O "$AAPT2" 90 | chmod +x "$AAPT2" 91 | if [ "${RESPONSE[1]}" == "$(stat -c %s "$AAPT2" 2> /dev/null)" ]; then 92 | break 93 | else 94 | rm "$AAPT2" 95 | fi 96 | ((CTR++)) 97 | done 98 | 99 | CTR=0 && while [ ! -e "$APK_EDITOR" ]; do 100 | [ $CTR -gt 2 ] && return 1 101 | echo -e "\nDownloading APKEditor...\n" 102 | readarray -t RESPONSE < <(curl -s "https://api.github.com/repos/REAndroid/APKEditor/releases/latest" | jq -r '.assets[0] | .browser_download_url, .size' 2> /dev/null) 103 | [ "${#RESPONSE[@]}" -eq 0 ] && return 1 104 | wget -q --show-progress "${RESPONSE[0]}" -O "$APK_EDITOR" 105 | if [ "${RESPONSE[1]}" == "$(stat -c %s "$APK_EDITOR" 2> /dev/null)" ]; then 106 | break 107 | else 108 | rm "$APK_EDITOR" 109 | yes | pkg uninstall -y openjdk-21 110 | yes | pkg install openjdk-17 111 | fi 112 | ((CTR++)) 113 | done 114 | 115 | return 0 116 | } 117 | 118 | fetchSrc() { 119 | [ -e "$SRC/.info" ] && source "$SRC/.info" 120 | 121 | [ "$INTERNET_ACCESS" == false ] && return 122 | 123 | ping -c 1 google.com &> /dev/null || return 124 | 125 | echo "Checking Latest Release..." 126 | 127 | TAG=$(curl -s 'https://api.github.com/repos/decipher3114/Revancify/releases/latest' 2> /dev/null | jq -r '.tag_name') 128 | 129 | [ "$TAG" == "$VERSION" ] && return 130 | 131 | echo "Revancify $TAG is available..." 132 | 133 | echo "Installing..." 134 | 135 | wget -qc "https://github.com/decipher3114/Revancify/archive/refs/tags/$TAG.zip" -O "$TAG.zip" 136 | if [ -e "$TAG.zip" ]; then 137 | unzip -qo "$TAG.zip" 138 | rm -rf "$TAG.zip" 139 | for CONTENT in Revancify-*/* Revancify-*/.*; do 140 | rm -rf "${SRC:?}/$(basename "$CONTENT")" 141 | mv "$CONTENT" "$SRC/" 142 | done 143 | rm -rf Revancify-* &> /dev/null 144 | cp -f "$SRC/revancify" "$PREFIX/bin/revancify" 145 | chmod +x "$PREFIX/bin/revancify" 146 | echo -e "Revancify $TAG is now installed.\nRun 'revancify -h' for help." 147 | exit 148 | else 149 | echo -e "Unable to install Revancify $TAG !!\nPlease try again with proper Internet" 150 | exit 1 151 | fi 152 | } 153 | 154 | clear 155 | 156 | if ! installDependencies; then 157 | echo -e "Dependencies not installed !!\nRun again with stable internet connection." 158 | exit 1 159 | fi 160 | 161 | fetchSrc 162 | 163 | if [ "$ROOT_ACCESS" != false ] && su -c 'exit' &> /dev/null; then 164 | ROOT_ACCESS=true 165 | else 166 | ROOT_ACCESS=false 167 | fi 168 | 169 | cd "$SRC" &> /dev/null || terminate 170 | 171 | bash main.sh "$ROOT_ACCESS" 172 | EXIT_CODE=$? 173 | 174 | exit "$EXIT_CODE" 175 | -------------------------------------------------------------------------------- /modules/assets.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | fetchAssetsInfo() { 4 | unset CLI_VERSION CLI_URL CLI_SIZE PATCHES_VERSION PATCHES_URL PATCHES_SIZE JSON_URL 5 | local SOURCE_INFO VERSION PATCHES_API_URL 6 | 7 | internet || return 1 8 | 9 | if [ "$("${CURL[@]}" "https://api.github.com/rate_limit" | jq -r '.resources.core.remaining')" -gt 5 ]; then 10 | 11 | mkdir -p "assets/$SOURCE" 12 | 13 | rm "assets/$SOURCE/.data" "assets/.data" &> /dev/null 14 | 15 | notify info "Fetching Assets Info..." 16 | 17 | if ! "${CURL[@]}" "https://api.github.com/repos/ReVanced/revanced-cli/releases/latest" | 18 | jq -r ' 19 | "CLI_VERSION='\''\(.tag_name)'\''", 20 | ( 21 | .assets[] | 22 | if (.name | endswith(".jar")) then 23 | "CLI_URL='\''\(.browser_download_url)'\''", 24 | "CLI_SIZE='\''\(.size|tostring)'\''" 25 | else 26 | empty 27 | end 28 | ) 29 | ' > assets/.data 2> /dev/null; then 30 | notify msg "Unable to fetch latest CLI info from API!!\nRetry later." 31 | return 1 32 | fi 33 | 34 | source <( 35 | jq -r --arg SOURCE "$SOURCE" ' 36 | .[] | select(.source == $SOURCE) | 37 | "REPO=\(.repository)", 38 | ( 39 | .api // empty | 40 | ( 41 | (.json // empty | "JSON_URL=\(.)"), 42 | (.version // empty | "VERSION_URL=\(.)") 43 | ) 44 | ) 45 | ' sources.json 46 | ) 47 | 48 | if [ -n "$VERSION_URL" ]; then 49 | if VERSION=$("${CURL[@]}" "$VERSION_URL" | jq -r '.version' 2> /dev/null); then 50 | PATCHES_API_URL="https://api.github.com/repos/$REPO/releases/tags/$VERSION" 51 | else 52 | notify msg "Unable to fetch latest version from API!!\nRetry later." 53 | return 1 54 | fi 55 | else 56 | PATCHES_API_URL="https://api.github.com/repos/$REPO/releases/latest" 57 | fi 58 | 59 | if ! "${CURL[@]}" "$PATCHES_API_URL" | 60 | jq -r ' 61 | if type == "array" then .[0] else . end | 62 | "PATCHES_VERSION='\''\(.tag_name)'\''", 63 | ( 64 | .assets[] | 65 | if (.name | endswith(".rvp")) then 66 | "PATCHES_URL='\''\(.browser_download_url)'\''", 67 | "PATCHES_SIZE='\''\(.size|tostring)'\''" 68 | else 69 | empty 70 | end 71 | ) 72 | ' > "assets/$SOURCE/.data" \ 73 | 2> /dev/null; then 74 | notify msg "Unable to fetch latest Patches info from API!!\nRetry later." 75 | return 1 76 | fi 77 | 78 | [ -n "$JSON_URL" ] && setEnv JSON_URL "$JSON_URL" init "assets/$SOURCE/.data" 79 | else 80 | notify msg "Unable to check for update.\nYou are probably rate-limited at this moment.\nTry again later or Run again with '-o' argument." 81 | return 1 82 | fi 83 | source "assets/.data" 84 | source "assets/$SOURCE/.data" 85 | } 86 | 87 | fetchAssets() { 88 | local CTR 89 | 90 | if [ -e "assets/.data" ] && [ -e "assets/$SOURCE/.data" ]; then 91 | source "assets/.data" 92 | source "assets/$SOURCE/.data" 93 | else 94 | fetchAssetsInfo || return 1 95 | fi 96 | 97 | CLI_FILE="assets/CLI-$CLI_VERSION.jar" 98 | [ -e "$CLI_FILE" ] || rm -- assets/CLI-* &> /dev/null 99 | 100 | CTR=2 && while [ "$CLI_SIZE" != "$(stat -c %s "$CLI_FILE" 2> /dev/null || echo 0)" ]; do 101 | if [ $CTR -eq 0 ]; then 102 | rm "$CLI_FILE" &> /dev/null 103 | notify msg "Oops! Unable to download completely.\n\nRetry or change your Network." 104 | return 1 105 | fi 106 | ((CTR--)) 107 | "${WGET[@]}" "$CLI_URL" -O "$CLI_FILE" |& 108 | stdbuf -o0 cut -b 63-65 | 109 | stdbuf -o0 grep '[0-9]' | 110 | "${DIALOG[@]}" --gauge "File : CLI-$CLI_VERSION.jar\nSize : $(numfmt --to=iec --format="%0.1f" "$CLI_SIZE")\n\nDownloading..." -1 -1 "$(($(($(stat -c %s "$CLI_FILE" 2> /dev/null || echo 0) * 100)) / CLI_SIZE))" 111 | tput civis 112 | done 113 | 114 | PATCHES_FILE="assets/$SOURCE/Patches-$PATCHES_VERSION.rvp" 115 | [ -e "$PATCHES_FILE" ] || rm -- assets/"$SOURCE"/Patches-* &> /dev/null 116 | 117 | CTR=2 && while [ "$PATCHES_SIZE" != "$(stat -c %s "$PATCHES_FILE" 2> /dev/null || echo 0)" ]; do 118 | if [ $CTR -eq 0 ]; then 119 | rm "$PATCHES_FILE" &> /dev/null 120 | notify msg "Oops! Unable to download completely.\n\nRetry or change your Network." 121 | return 1 122 | fi 123 | ((CTR--)) 124 | "${WGET[@]}" "$PATCHES_URL" -O "$PATCHES_FILE" |& 125 | stdbuf -o0 cut -b 63-65 | 126 | stdbuf -o0 grep '[0-9]' | 127 | "${DIALOG[@]}" --gauge "File : Patches-$PATCHES_VERSION.rvp\nSize : $(numfmt --to=iec --format="%0.1f" "$PATCHES_SIZE")\n\nDownloading..." -1 -1 "$(($(($(stat -c %s "$PATCHES_FILE" 2> /dev/null || echo 0) * 100)) / PATCHES_SIZE))" 128 | tput civis 129 | done 130 | 131 | parsePatchesJson || return 1 132 | } 133 | 134 | deleteAssets() { 135 | if "${DIALOG[@]}" \ 136 | --title '| Delete Assets |' \ 137 | --defaultno \ 138 | --yesno "Please confirm to delete the assets.\nIt will delete the CLI and patches." -1 -1 \ 139 | ; then 140 | unset CLI_VERSION CLI_URL CLI_SIZE PATCHES_VERSION PATCHES_URL PATCHES_SIZE JSON_URL 141 | rm -rf assets &> /dev/null 142 | mkdir assets 143 | fi 144 | } 145 | -------------------------------------------------------------------------------- /modules/app/download.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | scrapeAppInfo() { 4 | PAGE1=$( 5 | "${CURL[@]}" \ 6 | -A "$USER_AGENT" \ 7 | "$APP_DL_URL" 8 | ) 9 | unset APP_DL_URL 10 | 11 | CANONICAL_URL=$(pup -p --charset utf-8 'link[rel="canonical"] attr{href}' <<< " $PAGE1" 2> /dev/null) 12 | 13 | if grep -q "apk-download" <<< "$CANONICAL_URL"; then 14 | URL1="${CANONICAL_URL/"https://www.apkmirror.com/"//}" 15 | else 16 | if [ "$PREFER_SPLIT_APK" == "on" ]; then 17 | APP_FORMAT="BUNDLE" 18 | else 19 | APP_FORMAT="APK" 20 | fi 21 | 22 | readarray -t VARIANT_INFO < <( 23 | pup -p --charset utf-8 'div.variants-table json{}' <<< "$PAGE1" | 24 | jq -r \ 25 | --arg ARCH "$ARCH" \ 26 | --arg DPI "$DPI" \ 27 | --arg APP_FORMAT "$APP_FORMAT" ' 28 | [ 29 | .[].children[1:][].children | 30 | if (.[1].text | test("universal|noarch|\($ARCH)")) and 31 | ( 32 | .[3].text | 33 | test("nodpi") or 34 | ( 35 | capture("(?\\d+)-(?\\d+)dpi") | 36 | (($DPI | tonumber) <= (.high | tonumber)) and (($DPI | tonumber) >= (.low | tonumber)) 37 | ) 38 | ) 39 | then 40 | .[0].children 41 | else 42 | empty 43 | end 44 | ] | 45 | if length != 0 then 46 | [ 47 | if any(.[]; .[1].text == $APP_FORMAT) then 48 | .[] | 49 | if (.[1].text == $APP_FORMAT) then 50 | [.[1].text, .[0].href] 51 | else 52 | empty 53 | end 54 | else 55 | .[] | 56 | [.[1].text, .[0].href] 57 | end 58 | ][-1][] 59 | else 60 | empty 61 | end 62 | ' 63 | ) 64 | 65 | [ "${#VARIANT_INFO[@]}" -eq 0 ] && echo 1 >&2 && exit 66 | 67 | APP_FORMAT="${VARIANT_INFO[0]}" 68 | URL1="${VARIANT_INFO[1]}" 69 | fi 70 | echo 33 71 | 72 | PAGE2=$("${CURL[@]}" -A "$USER_AGENT" "https://www.apkmirror.com$URL1") 73 | readarray -t DL_URLS < <(pup -p --charset utf-8 'a.downloadButton attr{href}' <<< "$PAGE2" 2> /dev/null) 74 | 75 | if [ "$APP_FORMAT" == "APK" ]; then 76 | URL2="${DL_URLS[0]}" 77 | else 78 | URL2="${DL_URLS[-1]}" 79 | fi 80 | 81 | APP_SIZE=$(pup -p --charset utf-8 ':parent-of(:parent-of(svg[alt="APK file size"])) div text{}' <<< "$PAGE2" 2> /dev/null | sed -n 's/.*(//;s/ bytes.*//;s/,//gp' 2> /dev/null) 82 | [ "$URL2" == "" ] && echo 2 >&2 && exit 83 | echo 66 84 | 85 | URL3=$("${CURL[@]}" -A "$USER_AGENT" "https://www.apkmirror.com$URL2" | pup -p --charset UTF-8 'a:contains("here") attr{href}' 2> /dev/null | head -n 1) 86 | [ "$URL3" == "" ] && echo 2 >&2 && exit 87 | 88 | APP_URL="https://www.apkmirror.com$URL3" 89 | setEnv APP_FORMAT "$APP_FORMAT" update "apps/$APP_NAME/.data" 90 | setEnv APP_SIZE "$APP_SIZE" update "apps/$APP_NAME/.data" 91 | setEnv APP_URL "$APP_URL" update "apps/$APP_NAME/.data" 92 | echo 100 93 | } 94 | 95 | fetchDownloadURL() { 96 | local EXIT_CODE 97 | 98 | internet || return 1 99 | 100 | mkdir -p "apps/$APP_NAME" 101 | if [ $((($(date +%s) - $(stat -c %Y "apps/$APP_NAME/.data" 2> /dev/null || echo 0)) / 60)) -le 5 ]; then 102 | if [ "$APP_FORMAT" == "BUNDLE" ]; then 103 | export APP_EXT="apkm" 104 | else 105 | export APP_EXT="apk" 106 | fi 107 | return 0 108 | fi 109 | 110 | EXIT_CODE=$( 111 | { 112 | scrapeAppInfo 2>&3 | 113 | "${DIALOG[@]}" --gauge "App : $APP_NAME\nVersion: $APP_VER\n\nScraping Download Link..." -1 -1 0 2>&1 > /dev/tty 114 | } 3>&1 115 | ) 116 | if [ $(($(date +%s) - $(stat -c %Y "apps/$APP_NAME/.data" 2> /dev/null || echo 0))) -le 5 ]; then 117 | source "apps/$APP_NAME/.data" 118 | if [ "$APP_FORMAT" == "BUNDLE" ]; then 119 | export APP_EXT="apkm" 120 | else 121 | export APP_EXT="apk" 122 | fi 123 | else 124 | case $EXIT_CODE in 125 | 1) 126 | notify msg "No apk or bundle found matching device architecture. Please select a different version." 127 | ;; 128 | 2) 129 | notify msg "Unable to fetch link !!\nEither there is some problem with your internet connection or blocked by cloudflare protection. Disable VPN or Change your network." 130 | ;; 131 | esac 132 | return 1 133 | fi 134 | tput civis 135 | } 136 | 137 | downloadAppFile() { 138 | "${WGET[@]}" "$APP_URL" -O "apps/$APP_NAME/$APP_VER.$APP_EXT" |& 139 | stdbuf -o0 cut -b 63-65 | 140 | stdbuf -o0 grep '[0-9]' | 141 | "${DIALOG[@]}" \ 142 | --gauge "File: $APP_NAME-$APP_VER.$APP_EXT\nSize: $(numfmt --to=iec --format="%0.1f" "$APP_SIZE")\n\nDownloading..." -1 -1 \ 143 | "$(($(($(stat -c %s "apps/$APP_NAME/$APP_VER.$APP_EXT" || echo 0) * 100)) / APP_SIZE))" 144 | tput civis 145 | if [ "$APP_SIZE" != "$(stat -c %s "apps/$APP_NAME/$APP_VER.$APP_EXT" || echo 0)" ]; then 146 | notify msg "Oh No !!\nUnable to complete download. Please Check your internet connection and Retry." 147 | return 1 148 | fi 149 | } 150 | 151 | downloadApp() { 152 | local APP_FORMAT APP_EXT APP_SIZE APP_URL 153 | if ! chooseVersion; then 154 | TASK="CHOOSE_APP" 155 | return 1 156 | fi 157 | 158 | findPatchedApp || return 1 159 | 160 | [ -e "apps/$APP_NAME/.data" ] && source "apps/$APP_NAME/.data" 161 | 162 | if [ "$(stat -c %s "apps/$APP_NAME/$APP_VER.apk" 2> /dev/null || echo 0)" == "$APP_SIZE" ]; then 163 | if "${DIALOG[@]}" \ 164 | --title '| App Found |' \ 165 | --defaultno \ 166 | --yesno "Apk file already exists!!\nDo you want to download again?" -1 -1; then 167 | rm -rf "apps/$APP_NAME" &> /dev/null 168 | else 169 | return 0 170 | fi 171 | elif [ -e "apps/$APP_NAME/$APP_VER" ]; then 172 | antisplitApp && return 0 || return 1 173 | elif ! ls "apps/$APP_NAME/$APP_VER"* &> /dev/null; then 174 | rm -rf "apps/$APP_NAME" &> /dev/null 175 | fi 176 | 177 | fetchDownloadURL || return 1 178 | downloadAppFile || return 1 179 | 180 | if [ "$APP_FORMAT" == "BUNDLE" ]; then 181 | antisplitApp || return 1 182 | fi 183 | } 184 | -------------------------------------------------------------------------------- /modules/json/cli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | parseJsonFromCLI() { 4 | local PACKAGES PATCHES TOTAL CTR OPTIONS_ARRAY 5 | 6 | AVAILABLE_PATCHES='[]' 7 | 8 | readarray -d '' -t PACKAGES < <(java -jar "$CLI_FILE" list-versions "$PATCHES_FILE" -u | sed 's/INFO: //' | awk -v RS='' -v ORS='\0' '1') 9 | 10 | readarray -d '' -t PATCHES < <(java -jar "$CLI_FILE" list-patches "$PATCHES_FILE" -iopd | sed 's/INFO: //' | awk -v RS='' -v ORS='\0' '1') 11 | 12 | TOTAL=$((${#PACKAGES[@]} + ${#PATCHES[@]})) 13 | 14 | CTR=0 15 | 16 | for PACKAGE in "${PACKAGES[@]}"; do 17 | 18 | PKG_NAME=$(grep '^P' <<< "$PACKAGE" | sed 's/.*: //') 19 | 20 | readarray -t PKG_VERSIONS < <(grep $'\t' <<< "$PACKAGE" | sed 's/\t//') 21 | 22 | AVAILABLE_PATCHES=$( 23 | jq -nc --arg PKG_NAME "$PKG_NAME" --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" ' 24 | $AVAILABLE_PATCHES + [{ 25 | "pkgName": $PKG_NAME, 26 | "versions": ( 27 | $ARGS.positional | 28 | if .[0] == "Any" then 29 | [] 30 | else 31 | [ .[] | match(".*(?= \\()").string ] 32 | end | 33 | sort 34 | ), 35 | "patches": { 36 | "recommended": [], 37 | "optional": [] 38 | }, 39 | "options": [] 40 | }] 41 | ' --args "${PKG_VERSIONS[@]}" 42 | ) 43 | unset PACKAGE PKG_NAME PKG_VERSIONS 44 | 45 | ((CTR++)) 46 | echo "$(((CTR * 100) / TOTAL))" 47 | done 48 | unset PACKAGES 49 | 50 | AVAILABLE_PATCHES=$( 51 | jq -nc --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" ' 52 | $AVAILABLE_PATCHES + [{ 53 | "pkgName": null, 54 | "versions": [], 55 | "patches": { 56 | "recommended": [], 57 | "optional": [] 58 | }, 59 | "options": [] 60 | }] 61 | ' 62 | ) 63 | 64 | for PATCH in "${PATCHES[@]}"; do 65 | 66 | PATCH_NAME=$(grep '^Name:' <<< "$PATCH" | sed 's/.*: //') 67 | USE=$(grep '^Enabled:' <<< "$PATCH" | sed 's/.*: //') 68 | PATCH=$(sed '/^Name:/d;/^Enabled:/d' <<< "$PATCH") 69 | 70 | if grep -q '^C' <<< "$PATCH"; then 71 | readarray -t PACKAGES < <(grep $'^\tPackage name:' <<< "$PATCH" | sed 's/.*: //;s/ //g') 72 | PATCH=$(sed '/^Compatible packages:/d;/^\tPackage name:/d' <<< "$PATCH") 73 | fi 74 | 75 | OPTIONS_ARRAY='[]' 76 | if grep -q "Options:" <<< "$PATCH"; then 77 | PATCH=$(sed '/^Options:/d;s/^\t//g' <<< "$PATCH") 78 | readarray -d '' -t OPTIONS < <(awk -v RS='\n\nTitle' -v ORS='\0' '1' <<< "$PATCH") 79 | 80 | for OPTION in "${OPTIONS[@]}"; do 81 | 82 | KEY=$(grep '^Key:' <<< "$OPTION" | sed 's/.*: //;s/ //g') 83 | TITLE=$(grep -E '^Title:|^:' <<< "$OPTION" | sed 's/.*: //;') 84 | REQUIRED=$(grep '^Required:' <<< "$OPTION" | sed 's/.*: //') 85 | DEFAULT=$(grep '^Default:' <<< "$OPTION" | sed 's/.*: //') 86 | TYPE=$(grep '^Type:' <<< "$OPTION" | sed 's/.*: //;s/ //') 87 | 88 | if grep -q "^Possible values:" <<< "$OPTION"; then 89 | readarray -t VALUES < <(grep $'^\t' <<< "$OPTION" | sed 's/\t//') 90 | fi 91 | 92 | OPTION=$(sed '/^Key:/d;/^Title:/d;/^:/d;/^Required:/d;/^Default:/d;/^Type:/d;/^Possible values:/d;/^\t/d' <<< "$OPTION") 93 | 94 | DESCRIPTION=$(sed 's/^Description: //;s/\n/\\n/g' <<< "$OPTION") 95 | 96 | OPTIONS_ARRAY=$( 97 | jq -nc \ 98 | --arg PATCH_NAME "$PATCH_NAME" \ 99 | --arg KEY "$KEY" \ 100 | --arg TITLE "$TITLE" \ 101 | --arg DESCRIPTION "$DESCRIPTION" \ 102 | --arg REQUIRED "$REQUIRED" \ 103 | --arg DEFAULT "$DEFAULT" \ 104 | --arg TYPE "$TYPE" \ 105 | --arg STRING "$STRING" \ 106 | --arg NUMBER "$NUMBER" \ 107 | --arg BOOLEAN "$BOOLEAN" \ 108 | --arg STRINGARRAY "$STRINGARRAY" \ 109 | --argjson OPTIONS_ARRAY "$OPTIONS_ARRAY" ' 110 | ( 111 | $TYPE | 112 | if test("List") then 113 | $STRINGARRAY 114 | elif test("Boolean") then 115 | $BOOLEAN 116 | elif test("Long|Int|Float") then 117 | $NUMBER 118 | else 119 | $STRING 120 | end 121 | ) as $TYPE | 122 | ( 123 | $DEFAULT | 124 | if . != "" then 125 | ( 126 | if $TYPE == $STRING then 127 | tostring 128 | elif $TYPE == $NUMBER then 129 | tonumber 130 | elif $TYPE == $BOOLEAN then 131 | toboolean 132 | elif $TYPE == $STRINGARRAY then 133 | (gsub("(?([^,\\[\\] ]+))" ; "\"" + .a + "\"") | fromjson) 134 | end 135 | ) 136 | else 137 | null 138 | end 139 | ) as $DEFAULT | 140 | $OPTIONS_ARRAY + [{ 141 | "patchName": $PATCH_NAME, 142 | "key": $KEY, 143 | "title": $TITLE, 144 | "description": $DESCRIPTION, 145 | "required": ($REQUIRED | toboolean), 146 | "type": $TYPE, 147 | "default": $DEFAULT, 148 | "values": $ARGS.positional 149 | }] 150 | ' --args "${VALUES[@]}" 151 | ) 152 | unset TITLE KEY DESCRIPTION REQUIRED DEFAULT TYPE VALUES 153 | done 154 | unset OPTIONS 155 | fi 156 | 157 | AVAILABLE_PATCHES=$( 158 | jq -nc --arg PATCH_NAME "$PATCH_NAME" --arg USE "$USE" --argjson OPTIONS_ARRAY "$OPTIONS_ARRAY" --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" ' 159 | $ARGS.positional as $COMPATIBLE_PACKAGES | 160 | $AVAILABLE_PATCHES | 161 | reduce ($COMPATIBLE_PACKAGES[] // null) as $PKG_NAME ( 162 | .; 163 | map( 164 | if .pkgName == $PKG_NAME then 165 | .patches |= ( 166 | if ($USE | toboolean) then 167 | .recommended += [$PATCH_NAME] 168 | else 169 | .optional += [$PATCH_NAME] 170 | end 171 | ) | 172 | .options += $OPTIONS_ARRAY 173 | else 174 | . 175 | end 176 | ) 177 | ) 178 | ' --args "${PACKAGES[@]}" 179 | ) 180 | unset PATCH PATCH_NAME PACKAGES OPTIONS_ARRAY 181 | 182 | ((CTR++)) 183 | echo "$(((CTR * 100) / TOTAL))" 184 | done 185 | 186 | unset TOTAL CTR PATCHES 187 | 188 | echo "$AVAILABLE_PATCHES" > "assets/$SOURCE/Patches-$PATCHES_VERSION.json" 189 | } 190 | -------------------------------------------------------------------------------- /modules/json/options.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | editOptions() { 4 | local OPTIONS_JSON OPTIONS_LIST CURRENT_VALUE TYPE DESCRIPTION VALUE ALLOWED_VALUES NEW_VALUE EXIT_CODE TEMP_FILE UPDATED_OPTIONS UPDATED_PATCHES 5 | 6 | OPTIONS_JSON=$( 7 | jq -nc \ 8 | --arg PKG_NAME "$PKG_NAME" \ 9 | --argjson ENABLED_PATCHES "$ENABLED_PATCHES" ' 10 | $ENABLED_PATCHES[] | select(.pkgName == $PKG_NAME) | .options 11 | ' 12 | ) 13 | 14 | [ "$OPTIONS_JSON" == '[]' ] && return 15 | 16 | readarray -t OPTIONS_LIST < <( 17 | jq -r ' 18 | .[] | 19 | ( 20 | { 21 | "key": .key, 22 | "patchName": .patchName 23 | } | 24 | tostring 25 | ), 26 | .title 27 | ' <<< "$OPTIONS_JSON" 28 | ) 29 | while true; do 30 | 31 | unset EXIT_CODE 32 | 33 | if [ -z "$SELECTED_OPTION" ]; then 34 | SELECTED_OPTION="$( 35 | "${DIALOG[@]}" \ 36 | --title '| Select Option Key |' \ 37 | --no-tags \ 38 | --ok-label 'Edit' \ 39 | --cancel-label 'Done' \ 40 | --help-button \ 41 | --help-label 'Back' \ 42 | --menu "$NAVIGATION_HINT" -1 -1 0 \ 43 | "${OPTIONS_LIST[@]}" 2>&1 > /dev/tty 44 | )" 45 | case "$?" in 46 | 1) 47 | break 48 | ;; 49 | 2) 50 | TASK="MANAGE_PATCHES" 51 | unset OPTIONS_JSON SELECTED_OPTION CURRENT_VALUE NEW_VALUE 52 | return 1 53 | ;; 54 | esac 55 | else 56 | 57 | readarray -t CURRENT_VALUE < <( 58 | jq -r --arg SELECTED_OPTION "$SELECTED_OPTION" ' 59 | .[] | 60 | select( 61 | .key as $KEY | 62 | .patchName as $PATCH_NAME | 63 | $SELECTED_OPTION | 64 | fromjson | 65 | .key == $KEY and .patchName == $PATCH_NAME 66 | ) | 67 | .value | 68 | if (. | type) == "array" then 69 | .[] 70 | else 71 | . 72 | end | 73 | if . != null then 74 | . 75 | else 76 | empty 77 | end 78 | ' <<< "$OPTIONS_JSON" 79 | ) 80 | 81 | source <( 82 | jq -nrc \ 83 | --arg PKG_NAME "$PKG_NAME" \ 84 | --arg SELECTED_OPTION "$SELECTED_OPTION" \ 85 | --arg CURRENT_VALUE "${CURRENT_VALUE[0]}" \ 86 | --argjson AVAILABLE_PATCHES "$AVAILABLE_PATCHES" ' 87 | $AVAILABLE_PATCHES[] | 88 | select(.pkgName == $PKG_NAME or .pkgName == null) | 89 | .options[] | 90 | select( 91 | .key as $KEY | 92 | .patchName as $PATCH_NAME | 93 | $SELECTED_OPTION | 94 | fromjson | 95 | .key == $KEY and .patchName == $PATCH_NAME 96 | ) | 97 | "TYPE=\(.type)", 98 | "DESCRIPTION=\"\(.description | gsub("\n"; "\\n") | gsub("\""; "\\\""))\"", 99 | "VALUES=( 100 | \( 101 | [ 102 | .values | 103 | if (length != 0) then ( 104 | if any(.[]; match(".*?(?= \\()").string == $CURRENT_VALUE) then 105 | ( 106 | .[] | 107 | if match(".*?(?= \\()").string == $CURRENT_VALUE then 108 | ., "on" 109 | else 110 | ., "off" 111 | end 112 | ) else ( 113 | (.[] | ., "off"), "\($CURRENT_VALUE) (Custom)", "on" 114 | ) end 115 | ) else 116 | empty 117 | end 118 | ] | 119 | map("\"\(.)\"") | 120 | join(" ") 121 | ) 122 | )" 123 | ' 124 | ) 125 | 126 | while true; do 127 | if [ "$TYPE" == "$BOOLEAN" ] || [ "${VALUES[0]}" != "" ]; then 128 | if [ "$TYPE" != "$BOOLEAN" ]; then 129 | ALLOWED_VALUES=("${VALUES[@]}" "Custom Value" "off") 130 | else 131 | if [ "${CURRENT_VALUE[0]}" == "true" ]; then 132 | ALLOWED_VALUES=("true" "on" "false" "off") 133 | else 134 | ALLOWED_VALUES=("true" "off" "false" "on") 135 | fi 136 | fi 137 | NEW_VALUE=$( 138 | "${DIALOG[@]}" \ 139 | --title '| Choose Option Value |' \ 140 | --no-items \ 141 | --ok-label 'Done' \ 142 | --cancel-label 'Cancel' \ 143 | --help-button \ 144 | --help-label 'Description' \ 145 | --radiolist "$NAVIGATION_HINT\n$SELECTION_HINT" -1 -1 0 \ 146 | "${ALLOWED_VALUES[@]}" 2>&1 > /dev/tty 147 | ) 148 | EXIT_CODE=$? 149 | unset ALLOWED_VALUES 150 | 151 | case "$EXIT_CODE" in 152 | 1) 153 | unset NEW_VALUE 154 | break 155 | ;; 156 | 2) 157 | "${DIALOG[@]}" \ 158 | --title '| Option Description |' \ 159 | --msgbox "Value Type : $TYPE\nDescription: $DESCRIPTION" -1 -1 160 | continue 161 | ;; 162 | esac 163 | NEW_VALUE=${NEW_VALUE%% (*} 164 | if [ "$NEW_VALUE" == "Custom Value" ]; then 165 | unset NEW_VALUE 166 | fi 167 | fi 168 | 169 | if [ -z "$NEW_VALUE" ]; then 170 | tput cnorm 171 | if [ "$TYPE" == "$STRINGARRAY" ]; then 172 | TEMP_FILE="$(mktemp)" 173 | printf "%s\n" "${CURRENT_VALUE[@]}" > "$TEMP_FILE" 174 | tput cnorm 175 | NEW_VALUE=$( 176 | "${DIALOG[@]}" \ 177 | --title '| Edit Option Value |' \ 178 | --help-button \ 179 | --help-label "Description" \ 180 | --editbox "$TEMP_FILE" -1 -1 \ 181 | 2>&1 1>&2 1> /dev/tty 182 | ) 183 | EXIT_CODE=$? 184 | rm "$TEMP_FILE" 185 | readarray -t NEW_VALUE <<< "$NEW_VALUE" 186 | else 187 | NEW_VALUE=$( 188 | "${DIALOG[@]}" \ 189 | --title '| Edit Option Value |' \ 190 | --help-button \ 191 | --help-label \ 192 | "Description" \ 193 | --inputbox "Enter $TYPE\nLeave empty to set as null" -1 -1 \ 194 | "${CURRENT_VALUE[@]}" \ 195 | 2>&1 1>&2 1> /dev/tty 196 | ) 197 | EXIT_CODE=$? 198 | fi 199 | 200 | tput civis 201 | case "$EXIT_CODE" in 202 | 1) 203 | unset NEW_VALUE 204 | break 205 | ;; 206 | 2) 207 | "${DIALOG[@]}" \ 208 | --title '| Option Description |' \ 209 | --msgbox "Value Type : $TYPE\n# Each line represents an individual value.\nDescription: $DESCRIPTION" -1 -1 210 | continue 211 | ;; 212 | esac 213 | fi 214 | 215 | if [ "${NEW_VALUE[*]}" == "${CURRENT_VALUE[*]}" ]; then 216 | break 217 | fi 218 | 219 | if [[ $TYPE == "$NUMBER" && ! "${NEW_VALUE[*]}" =~ ^[0-9]+$ ]]; then 220 | notify msg "This field should contain only numbers." 221 | continue 222 | fi 223 | 224 | if UPDATED_OPTIONS=$( 225 | jq -e \ 226 | --arg SELECTED_OPTION "$SELECTED_OPTION" \ 227 | --arg TYPE "$TYPE" \ 228 | --arg STRING "$STRING" \ 229 | --arg NUMBER "$NUMBER" \ 230 | --arg BOOLEAN "$BOOLEAN" ' 231 | map( 232 | .key as $KEY | 233 | .patchName as $PATCH_NAME | 234 | if ($SELECTED_OPTION | fromjson | .key == $KEY and .patchName == $PATCH_NAME) then 235 | .value |= ( 236 | $ARGS.positional | 237 | if length == 0 then 238 | null 239 | elif length == 1 then 240 | if $TYPE == $BOOLEAN then 241 | .[0] | toboolean 242 | elif $TYPE == $NUMBER then 243 | .[0] | tonumber 244 | elif $TYPE == $STRING then 245 | .[0] | tostring 246 | else 247 | . 248 | end 249 | else 250 | . 251 | end 252 | ) 253 | else 254 | . 255 | end 256 | ) 257 | ' --args "${NEW_VALUE[@]}" <<< "$OPTIONS_JSON" 2> /dev/null 258 | ); then 259 | OPTIONS_JSON="$UPDATED_OPTIONS" 260 | fi 261 | break 262 | done 263 | unset CURRENT_VALUE TYPE DESCRIPTION VALUES ALLOWED_VALUES SELECTED_OPTION NEW_VALUE UPDATED_OPTIONS 264 | fi 265 | done 266 | 267 | UPDATED_PATCHES=$(jq -c \ 268 | --arg PKG_NAME "$PKG_NAME" \ 269 | --argjson ENABLED_PATCHES "$ENABLED_PATCHES" ' 270 | . as $OPTIONS_JSON | 271 | $ENABLED_PATCHES | 272 | map( 273 | if .pkgName == $PKG_NAME then 274 | .options |= $OPTIONS_JSON 275 | else 276 | . 277 | end 278 | ) 279 | ' <<< "$OPTIONS_JSON") 280 | 281 | echo "$UPDATED_PATCHES" > "$STORAGE/$SOURCE-patches.json" 282 | 283 | ENABLED_PATCHES="$UPDATED_PATCHES" 284 | } 285 | --------------------------------------------------------------------------------