├── .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 | [](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 |
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 |
--------------------------------------------------------------------------------