├── .gitignore
├── LICENSE
├── README.md
├── configs
├── .dialogrcDark
└── .dialogrcLight
├── fetch_link.sh
├── fetch_patches.sh
├── fetch_versions.sh
├── install.sh
├── main.sh
├── revancify
├── revancify.keystore
├── root_util.sh
└── sources.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.json
2 | !sources.json
3 | *.jar
4 | *.apk
5 | *.sh
6 | !main.sh
7 | !root_util.sh
8 | !fetch*.sh
9 | !install.sh
10 | *.txt
11 | pup
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Revancify 🛠️
2 | ### A TUI wrapper for Revanced CLI with amazing features.
3 |
4 | [](https://t.me/revancifychat)
5 |
6 | ## Termux
7 | | Android Version | Download Link|
8 | | ---- | ----- |
9 | | Android 8+ | [Termux Monet](https://github.com/HardcodedCat/termux-monet/releases/latest) (Strictly Recommended)
10 | | Android 4+ | [Termux](https://github.com/termux/termux-app/releases/latest)
11 |
12 | # Features
13 | 1. Auto updates Patches and CLI
14 | 2. Interactive and Easy to use
15 | 3. Inbuilt scrapper for [ApkMirror](https://apkmirror.com)
16 | > Only support apps available on apkmirror. However, you can still download app manually and use the apk file to patch
17 | 4. Contains User-friendly Patch-options Editor
18 | 5. Conserve selected patches
19 | 6. Supports App Version downgrade for devices with Signature Spoof enabled
20 | 7. Convenient Installation and usage
21 | 6. Lightweight and faster than any other tool
22 |
23 | # Guide
24 |
25 | ## Installation
26 | 1. Open Termux.
27 | 2. Copy and paste this command.
28 | ```
29 | curl -sL "https://raw.githubusercontent.com/decipher3114/Revancify/main/install.sh" | bash
30 | ```
31 |
32 |
33 | If the above one doesn't work, use this.
34 |
35 | ```
36 | pkg update -y -o Dpkg::Options::="--force-confnew" && pkg install git -y && git clone --depth=1 https://github.com/decipher3114/Revancify.git && ./Revancify/revancify
37 | ```
38 |
39 |
40 | ## Usage
41 | After installation, type `revancify` in termux and press enter.
42 |
43 | Or use with arguments. Check them with `revancify -h` or `revancify --help`
44 |
45 | # Thanks & Credits
46 | [Revanced](https://github.com/revanced)
47 | [Revanced Extended](https://github.com/inotia00)
48 |
--------------------------------------------------------------------------------
/configs/.dialogrcDark:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/configs/.dialogrcLight:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/fetch_link.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | UserAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
4 |
5 | arch=$(getprop ro.product.cpu.abi)
6 | developer="$1"
7 | appName="$2"
8 | appVer="$3"
9 |
10 | page1=$(curl -vsL -A "$UserAgent" "https://www.apkmirror.com/apk/$developer/$appName/$appName-$appVer-release" 2>&1)
11 |
12 | canonicalUrl=$(pup -p --charset utf-8 'link[rel="canonical"] attr{href}' <<<"$page1")
13 | if [[ "$canonicalUrl" == *"apk-download"* ]]; then
14 | url1=("${canonicalUrl/"https://www.apkmirror.com/"//}")
15 | else
16 | grep -q 'class="error404"' <<<"$page1" && echo noversion >&2 && exit 1
17 |
18 | page2=$(pup -p --charset utf-8 ':parent-of(:parent-of(span:contains("APK")))' <<<"$page1")
19 |
20 | [[ "$(pup -p --charset utf-8 ':parent-of(div:contains("noarch"))' <<<"$page2")" == "" ]] || arch=noarch
21 | [[ "$(pup -p --charset utf-8 ':parent-of(div:contains("universal"))' <<<"$page2")" == "" ]] || arch=universal
22 |
23 | readarray -t url1 < <(pup -p --charset utf-8 ":parent-of(div:contains(\"$arch\")) a.accent_color attr{href}" <<<"$page2")
24 |
25 | [ "${#url1[@]}" -eq 0 ] && echo noapk >&2 && exit 1
26 | fi
27 | echo 33
28 |
29 | url2=$(curl -sL -A "$UserAgent" "https://www.apkmirror.com${url1[-1]}" | pup -p --charset utf-8 'a:contains("Download APK") attr{href}')
30 |
31 | [ "$url2" == "" ] && echo error >&2 && exit 1
32 | echo 66
33 |
34 | url3=$(curl -sL -A "$UserAgent" "https://www.apkmirror.com$url2" | pup -p --charset UTF-8 'a[data-google-vignette="false"][rel="nofollow"] attr{href}')
35 |
36 | [ "$url3" == "" ] && echo error >&2 && exit 1
37 | echo 100
38 |
39 | echo "https://www.apkmirror.com$url3" >&2
--------------------------------------------------------------------------------
/fetch_patches.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source="$1"
4 | storagePath="$3"
5 |
6 | patchesJson=$(jq '.' "$source"-patches-*.json)
7 |
8 | savedJson=$(jq '.' "$storagePath/$source-patches.json" 2>/dev/null || jq -n '[]')
9 |
10 | if ! jq -n --argjson savedJson "$savedJson" '$savedJson' > /dev/null 2>&1; then
11 | savedJson=$(jq -n '[]')
12 | fi
13 |
14 | allPackages=$(echo "$patchesJson" | jq '[.[].compatiblePackages | if . != null then .[].name else empty end]')
15 |
16 | pkgs=$(jq -n --argjson allPackages "$allPackages" '[$allPackages | to_entries[] | .value as $pkg | map($allPackages | to_entries[] | select(.value == $pkg).key)[0] as $index | if $index == .key then $pkg else empty end]')
17 |
18 | generatedJson=$(jq --null-input --argjson pkgs "$pkgs" --argjson savedJson "$savedJson" --slurpfile patchesFile "$source"-patches-*.json '
19 | [
20 | $patchesFile[] | . as $patchesJson |
21 | $pkgs[] | . as $pkgName |
22 | {
23 | "pkgName": .,
24 | "appName": (
25 | if (($savedJson | length) != 0) then
26 | ($savedJson[] | select(.pkgName == $pkgName) | .appName) // null
27 | else
28 | null
29 | end
30 | ),
31 | "apkmirrorAppName": (
32 | if (($savedJson | length) != 0) then
33 | ($savedJson[] | select(.pkgName == $pkgName) | .apkmirrorAppName) // null
34 | else
35 | null
36 | end
37 | ),
38 | "developerName": (
39 | if (($savedJson | length) != 0) then
40 | ($savedJson[] | select(.pkgName == $pkgName) | .developerName) // null
41 | else
42 | null
43 | end
44 | ),
45 | "versions": (
46 | [
47 | $patchesJson[] |
48 | .compatiblePackages |
49 | if . != null then
50 | if ((map(.name) | index($pkgName)) != null) then
51 | .[(map(.name) | index($pkgName))].versions | if . != null then .[] else empty end
52 | else
53 | empty
54 | end
55 | else
56 | empty
57 | end] |
58 | unique
59 | ),
60 | "includedPatches": (
61 | if (($savedJson | length) != 0) then
62 | [
63 | (
64 | ($savedJson[] | select(.pkgName == $pkgName)) |
65 | if ((.includedPatches | length) == 0) then
66 | (
67 | $patchesJson[] |
68 | .name as $patchName |
69 | .use as $use |
70 | .excluded as $excluded |
71 | .compatiblePackages |
72 | if . != null then
73 | if ((((map(.name) | index($pkgName)) != null) or (length == 0)) and (($use == true) or ($excluded == false))) then
74 | $patchName
75 | else
76 | empty
77 | end
78 | else
79 | empty
80 | end
81 | )
82 | else
83 | .includedPatches[]
84 | end) //
85 | (
86 | $patchesJson[] |
87 | .name as $patchName |
88 | .use as $use |
89 | .excluded as $excluded |
90 | .compatiblePackages |
91 | if . != null then
92 | if ((((map(.name) | index($pkgName)) != null) or (length == 0)) and (($use == true) or ($excluded == false))) then
93 | $patchName
94 | else
95 | empty
96 | end
97 | else
98 | "hi"
99 | end
100 | )
101 | ]
102 | else
103 | [(
104 | $patchesJson[] |
105 | .name as $patchName |
106 | .use as $use |
107 | .excluded as $excluded |
108 | .compatiblePackages |
109 | if . != null then
110 | if ((((map(.name) | index($pkgName)) != null) or (length == 0)) and (($use == true) or ($excluded == false))) then
111 | $patchName
112 | else
113 | empty
114 | end
115 | else
116 | empty
117 | end
118 | )]
119 | end
120 | )
121 | }
122 | ]')
123 |
124 | if [ "$2" == "online" ]; then
125 | response=$(curl --fail-early --connect-timeout 2 --max-time 5 -s 'https://www.apkmirror.com/wp-json/apkm/v1/app_exists/' \
126 | -H 'Accept: application/json' \
127 | -H 'Content-Type: application/json' \
128 | -H 'Authorization: Basic YXBpLXRvb2xib3gtZm9yLWdvb2dsZS1wbGF5OkNiVVcgQVVMZyBNRVJXIHU4M3IgS0s0SCBEbmJL' \
129 | -H 'User-Agent: Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.86 Mobile Safari/537.36' \
130 | -d "$(jq -n --argjson pkgs "$pkgs" '{"pnames": $pkgs}')")
131 |
132 | if ! responseJson=$(
133 | echo "$response" | jq '[.data[] |
134 | if .exists then
135 | ({
136 | "key": (.pname),
137 | "value": {
138 | "appName": (.app.name | sub("( -)|( &)|:"; ""; "g") | sub("[()\\|]"; ""; "g") | sub(" *[-, ] *"; "-"; "g") | sub("-Wear-OS"; ""; "g")),
139 | "apkmirrorAppName": (.app.link | sub("-wear-os"; "") | match("(?<=\\/)(((?!\\/).)*)(?=\\/$)").string),
140 | "developerName": (.app.link | match("(?<=apk\\/).*?(?=\\/)").string)
141 | }
142 | })
143 | else
144 | empty
145 | end] | from_entries' 2>/dev/null
146 | ); then
147 | echo error
148 | exit 1
149 | fi
150 | generatedJson=$(
151 | jq -n --argjson generatedJson "$generatedJson" --argjson responseJson "$responseJson" '[
152 | $generatedJson[] | .pkgName as $pkgName | (.appName = ($responseJson[$pkgName].appName)) | (.apkmirrorAppName = ($responseJson[$pkgName].apkmirrorAppName)) | (.developerName = ($responseJson[$pkgName].developerName))]'
153 | 2>/dev/null || { echo error && exit 1 ;})
154 | fi
155 |
156 | echo "$generatedJson" | jq '.' >"$storagePath/$source-patches.json"
157 |
--------------------------------------------------------------------------------
/fetch_versions.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | UserAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
4 |
5 | appName="$1"
6 | apkmirrorAppName="$2"
7 | patchesSource="$3"
8 | currentVersion="$4"
9 | storagePath="$5"
10 |
11 | page1=$(curl --fail-early --connect-timeout 2 --max-time 5 -sL -A "$UserAgent" "https://www.apkmirror.com/uploads/?appcategory=$apkmirrorAppName" 2>&1)
12 |
13 | [ "$page1" == "" ] && echo error && exit 1
14 |
15 | readarray -t versions < <(pup -p 'div.widget_appmanager_recentpostswidget h5 a.fontBlack text{}' <<<"$page1")
16 |
17 | supportedVers=$(jq -r --arg apkmirrorAppName "$apkmirrorAppName" '[.[] | select(.apkmirrorAppName == $apkmirrorAppName).versions[] | sub(" *[-, ] *"; "-"; "g") | sub(":"; ""; "g")]' "$storagePath/$patchesSource-patches.json")
18 |
19 | jq -r -n --arg appName "$appName-"\
20 | --arg currentVersion "$currentVersion"\
21 | --argjson supportedVers "$supportedVers"\
22 | '($currentVersion | sub(" *[-, ] *"; "-"; "g") | sub(":"; ""; "g")) as $installedVer |
23 | [
24 | [
25 | $ARGS.positional[] |
26 | sub("( -)|( &)|:"; ""; "g") |
27 | sub("[()\\|]"; ""; "g") |
28 | sub(" *[-, ] *"; "-"; "g") |
29 | sub($appName; "")
30 | ] |
31 | . |= . + $supportedVers |
32 | unique |
33 | reverse |
34 | index($installedVer) as $index |
35 | if $index == null then
36 | .[]
37 | else
38 | .[0:($index + 1)][]
39 | end | . as $version |
40 | if (($supportedVers | index($version)) != null) then
41 | $version, "[RECOMMENDED]"
42 | elif ($version | test("beta|Beta|BETA")) then
43 | $version | sub("(?<=[0-9])-[a-zA-Z]*$"; ""), "[BETA]"
44 | elif ($version | test("alpha|Alpha|ALPHA")) then
45 | $version | sub("(?<=[0-9])-[a-zA-Z]$"; ""), "[ALPHA]"
46 | else
47 | $version, "[STABLE]"
48 | end
49 | ] |
50 | if (. | index($installedVer)) != null then
51 | .[-1] |= "[INSTALLED]"
52 | else
53 | .
54 | end |
55 | if ((. | index("[RECOMMENDED]")) != null) then
56 | . |= ["Auto Select", "[RECOMMENDED]"] + .
57 | else
58 | .
59 | end |
60 | .[]' --args "${versions[@]}"
61 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | servers=("google.com" "raw.githubusercontent.com")
4 |
5 | for server in "${servers[@]}"; do
6 | if ! ping -c 1 -W 3 "$server"&> /dev/null; then
7 | echo -e "\e[1;31m$server is not reachable with your current network.\nChange your network configuration.\e[0m"
8 | fi
9 | done
10 |
11 | if [ -z "$TERMUX_VERSION" ]; then
12 | echo -e "\e[1;31mTermux not detected !!\e[0m\n\e[1;31mInstall aborted !!\e[0m"
13 | exit 1
14 | fi
15 |
16 | if [ -d "$HOME/Revancify" ]; then
17 | ./Revancify/revancify
18 | exit 0
19 | fi
20 |
21 | if ! command -v git &> /dev/null; then
22 | if ! pkg update -y -o Dpkg::Options::="--force-confnew"; then
23 | echo -e "\e[1;31mOops !!
24 | Possible causes of error:
25 | 1. Termux from Playstore is not maintained. Download Termux from github.
26 | 2. Unstable internet Connection.
27 | 3. Repository issues. Clear Termux Data and retry."
28 | exit 1
29 | fi
30 | pkg install git -y -o Dpkg::Options::="--force-confnew"
31 | fi
32 |
33 | if git clone --depth=1 https://github.com/decipher3114/Revancify.git; then
34 | $HOME/Revancify/revancify
35 | else
36 | echo -e "\e[1;31mInstall Failed !!\e[0m"
37 | echo "Please Try again"
38 | exit 1
39 | fi
--------------------------------------------------------------------------------
/main.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 | clear
8 | exit "${1:-1}"
9 | }
10 | trap terminate SIGTERM SIGINT SIGABRT
11 |
12 | setEnv() {
13 | if [ ! -f "$4" ]; then
14 | : > "$4"
15 | fi
16 | if ! grep -q "${1}=" "$4"; then
17 | echo "$1=$2" >> "$4"
18 | elif [ "$3" == "update" ]; then
19 | sed -i "s/$1=.*/$1=$2/" "$4"
20 | fi
21 | }
22 |
23 | initialize() {
24 | internalStorage="/storage/emulated/0"
25 | storagePath="$internalStorage/Revancify"
26 | [ ! -d "$storagePath" ] && mkdir -p "$storagePath"
27 | [ ! -d apps ] && mkdir -p apps
28 | arch=$(getprop ro.product.cpu.abi)
29 | repoDir="$HOME/Revancify"
30 | header=(dialog --backtitle "Revancify | [Arch: $arch, Root: $root]" --no-shadow)
31 | envFile=config.cfg
32 | [ ! -f "apps/.appSize" ] && : > "apps/.appSize"
33 | userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
34 |
35 | AutocheckToolsUpdate="" Riplibs="" LightTheme="" ShowConfirmPatchesMenu="" LaunchAppAfterMount="" AllowVersionDowngrade="" FetchPreReleasedTools=""
36 | setEnv AutocheckToolsUpdate false init "$envFile"
37 | setEnv Riplibs true init "$envFile"
38 | setEnv LightTheme false init "$envFile"
39 | setEnv ShowConfirmPatchesMenu false init "$envFile"
40 | setEnv LaunchAppAfterMount true init "$envFile"
41 | setEnv AllowVersionDowngrade false init "$envFile"
42 | setEnv FetchPreReleasedTools false init "$envFile"
43 | # shellcheck source=/dev/null
44 | source "$envFile"
45 | if [ -z "$source" ]; then
46 | readarray -t allSources < <(jq -r --arg source "$source" 'to_entries | .[] | .key,"["+.value.projectName+"]","on"' "$repoDir"/sources.json)
47 | source=$("${header[@]}" --begin 2 0 --title '| Source Selection Menu |' --no-cancel --ok-label "Done" --radiolist "Use arrow keys to navigate; Press Spacebar to select option" -1 -1 0 "${allSources[@]}" 2>&1 >/dev/tty)
48 | setEnv source "$source" update "$envFile"
49 | fi
50 | [ "$root" == true ] && menuEntry="Uninstall Patched app" || menuEntry="Download Microg"
51 |
52 | [ "$LightTheme" == true ] && theme=Light || theme=Dark
53 | export DIALOGRC="$repoDir/configs/.dialogrc$theme"
54 |
55 | # shellcheck source=/dev/null
56 | source <(jq -r --arg source "$source" '.[$source].sources | to_entries[] | .key+"Source="+.value.org' "$repoDir"/sources.json)
57 | sourceName=$(jq -r --arg source "$source" '.[$source].projectName' "$repoDir"/sources.json)
58 |
59 | if [ "$online" != false ]; then
60 | if [ "$AutocheckToolsUpdate" == true ]; then
61 | getTools || terminate 1
62 | else
63 | checkTools || terminate 1
64 | fi
65 | fi
66 |
67 | if [ -f "$storagePath/$source-patches.json" ]; then
68 | bash "$repoDir/fetch_patches.sh" "$source" offline "$storagePath" &> /dev/null
69 | refreshJson || return 1
70 | fi
71 | }
72 |
73 | internet() {
74 | if ! ping -c 1 google.com &> /dev/null; then
75 | "${header[@]}" --msgbox "Oops! No Internet Connection available.\n\nConnect to Internet and try again later." 12 45
76 | return 1
77 | fi
78 | }
79 |
80 | fetchToolsAPI() {
81 | internet || return 1
82 |
83 | "${header[@]}" --infobox "Please Wait !!\nFetching tools data from github API..." 12 45
84 | readarray -t tools < <(jq -r --arg source "$source" '.[$source].sources | keys_unsorted[]' "$repoDir"/sources.json)
85 | readarray -t links < <(jq -r --arg source "$source" '.[$source].sources[] | .org+"/"+.repo' "$repoDir"/sources.json)
86 | : >".${source}-data"
87 | [ "$FetchPreReleasedTools" == false ] && stableRelease="/latest" || stableRelease=""
88 | i=0 && for tool in "${tools[@]}"; do
89 | curl -s --fail-early --connect-timeout 2 --max-time 5 "https://api.github.com/repos/${links[$i]}/releases$stableRelease" | jq -r --arg tool "$tool" 'if type == "array" then .[0] else . end | $tool+"Latest="+.tag_name, (.assets[] | if .content_type == "application/json" then "jsonUrl="+.browser_download_url, "jsonSize="+(.size|tostring) else $tool+"Url="+.browser_download_url, $tool+"Size="+(.size|tostring) end)' >>".${source}-data"
90 | i=$(("$i" + 1))
91 | done
92 |
93 | if [ "$(wc -l <".${source}-data")" -lt "11" ]; then
94 | "${header[@]}" --msgbox "Oops! Unable to connect to Github.\n\nRetry or change your Network." 12 45
95 | return 1
96 | fi
97 | # shellcheck source=/dev/null
98 | source ./".${source}-data"
99 |
100 | cliAvailableSize=$(ls "$cliSource"-cli-*.jar &> /dev/null && du -b "$cliSource"-cli-*.jar | cut -d $'\t' -f 1 || echo 0)
101 | patchesAvailableSize=$(ls "$patchesSource"-patches-*.jar &> /dev/null && du -b "$patchesSource"-patches-*.jar | cut -d $'\t' -f 1 || echo 0)
102 | integrationsAvailableSize=$(ls "$integrationsSource"-integrations-*.apk &> /dev/null && du -b "$integrationsSource"-integrations-*.apk | cut -d $'\t' -f 1 || echo 0)
103 | }
104 |
105 | getTools() {
106 | fetchToolsAPI || return 1
107 | if [ -f "$patchesSource-patches-$patchesLatest.jar" ] && [ -f "$patchesSource-patches-$patchesLatest.json" ] && [ -f "$cliSource-cli-$cliLatest.jar" ] && [ -f "$integrationsSource-integrations-$integrationsLatest.apk" ] && [ "$cliSize" == "$cliAvailableSize" ] && [ "$patchesSize" == "$patchesAvailableSize" ] && [ "$integrationsSize" == "$integrationsAvailableSize" ]; then
108 | if [ "$(bash "$repoDir/fetch_patches.sh" "$source" online "$storagePath")" == "error" ]; then
109 | "${header[@]}" --msgbox "Tools are successfully downloaded but Apkmirror API is not accessible. So, patches are not successfully synced.\nRevancify may crash.\n\nChange your network." 12 45
110 | return 1
111 | fi
112 | "${header[@]}" --msgbox "Tools are already downloaded !!\n\nPatches are successfully synced." 12 45
113 | return 0
114 | fi
115 | [ -f "$patchesSource-patches-$patchesLatest.jar" ] || { rm "$patchesSource"-patches-*.jar &> /dev/null && rm "$patchesSource"-patches-*.json &> /dev/null && patchesAvailableSize=0 ;}
116 | [ -f "$cliSource-cli-$cliLatest.jar" ] || { rm "$cliSource"-cli-*.jar &> /dev/null && cliAvailableSize=0 ;}
117 | [ -f "$integrationsSource-integrations-$integrationsLatest.apk" ] || { rm "$integrationsSource"-integrations-*.apk &> /dev/null && integrationsAvailableSize=0 ;}
118 | [ "$cliSize" != "$cliAvailableSize" ] &&
119 | wget -q -c "$cliUrl" -O "$cliSource"-cli-"$cliLatest".jar --show-progress --user-agent="$userAgent" 2>&1 | stdbuf -o0 cut -b 63-65 | stdbuf -o0 grep '[0-9]' | "${header[@]}" --begin 2 0 --gauge "Source : $cliSource\nTool : CLI\nVersion : $cliLatest\nSize : $(numfmt --to=iec --format="%0.1f" "$cliSize")\n\nDownloading..." -1 -1 "$(($((cliAvailableSize * 100)) / cliSize))" && tput civis
120 |
121 | [ "$cliSize" != "$(ls "$cliSource"-cli-*.jar &> /dev/null && du -b "$cliSource"-cli-*.jar | cut -d $'\t' -f 1 || echo 0)" ] && "${header[@]}" --msgbox "Oops! Unable to download completely.\n\nRetry or change your Network." 12 45 && return 1
122 |
123 | [ "$patchesSize" != "$patchesAvailableSize" ] &&
124 | wget -q -c "$patchesUrl" -O "$patchesSource"-patches-"$patchesLatest".jar --show-progress --user-agent="$userAgent" 2>&1 | stdbuf -o0 cut -b 63-65 | stdbuf -o0 grep '[0-9]' | "${header[@]}" --begin 2 0 --gauge "Source : $patchesSource\nTool : Patches\nVersion : $patchesLatest\nSize : $(numfmt --to=iec --format="%0.1f" "$patchesSize")\n\nDownloading..." -1 -1 "$(($((patchesAvailableSize * 100 / patchesSize))))" && tput civis && patchesUpdated=true
125 |
126 | wget -q -c "$jsonUrl" -O "$patchesSource"-patches-"$patchesLatest".json --user-agent="$userAgent"
127 |
128 | [ "$patchesSize" != "$(ls "$patchesSource"-patches-*.jar &> /dev/null && du -b "$patchesSource"-patches-*.jar | cut -d $'\t' -f 1 || echo 0)" ] && "${header[@]}" --msgbox "Oops! Unable to download completely.\n\nRetry or change your Network." 12 45 && return 1
129 |
130 | [ "$integrationsSize" != "$integrationsAvailableSize" ] &&
131 | wget -q -c "$integrationsUrl" -O "$integrationsSource"-integrations-"$integrationsLatest".apk --show-progress --user-agent="$userAgent" 2>&1 | stdbuf -o0 cut -b 63-65 | stdbuf -o0 grep '[0-9]' | "${header[@]}" --begin 2 0 --gauge "Source : $integrationsSource\nTool : Integrations\nVersion : $integrationsLatest\nSize : $(numfmt --to=iec --format="%0.1f" "$integrationsSize")\n\nDownloading..." -1 -1 "$(($((integrationsAvailableSize * 100 / integrationsSize))))" && tput civis
132 |
133 | [ "$integrationsSize" != "$(ls "$integrationsSource"-integrations-*.apk &> /dev/null && du -b "$integrationsSource"-integrations-*.apk | cut -d $'\t' -f 1 || echo 0)" ] && "${header[@]}" --msgbox "Oops! File not downloaded.\n\nRetry or change your Network." 12 45 && return 1
134 |
135 | if [ "$patchesUpdated" == true ]; then
136 | "${header[@]}" --infobox "Updating patches and options file..." 12 45
137 | if [ "$(bash "$repoDir/fetch_patches.sh" "$source" online "$storagePath")" == "error" ]; then
138 | "${header[@]}" --msgbox "Tools are successfully downloaded but Apkmirror API is not accessible. So, patches are not successfully synced.\nRevancify may crash.\n\nChange your network." 12 45
139 | return 1
140 | fi
141 | java -jar "$cliSource"-cli-*.jar options -ou -p "$storagePath/$source-options.json" "$patchesSource"-patches-*.jar &> /dev/null
142 | fi
143 |
144 | refreshJson || return 1
145 | }
146 |
147 | changeSource() {
148 | internet || return 1
149 | readarray -t allSources < <(jq -r --arg source "$source" 'to_entries | .[] | if .key == $source then .key,"["+.value.projectName+"]","on" else .key,"["+.value.projectName+"]","off" end' "$repoDir"/sources.json)
150 | selectedSource=$("${header[@]}" --begin 2 0 --title '| Source Selection Menu |' --no-cancel --ok-label "Done" --radiolist "Use arrow keys to navigate; Press Spacebar to select option" -1 -1 0 "${allSources[@]}" 2>&1 >/dev/tty)
151 | if [ "$source" != "$selectedSource" ]; then
152 | source="$selectedSource"
153 | # shellcheck source=/dev/null
154 | source <(jq -r --arg source "$source" '.[$source].sources | to_entries[] | .key+"Source="+.value.org' "$repoDir"/sources.json)
155 | setEnv source "$selectedSource" update "$envFile"
156 | sourceName=$(jq -r --arg source "$source" '.[$source].projectName' "$repoDir"/sources.json)
157 | checkTools || return 1
158 | fi
159 | }
160 |
161 | selectApp() {
162 | [ "$1" == "storage" ] && helpTag=(--help-button --help-label "From Storage") || helpTag=()
163 | previousAppName="$appName"
164 | readarray -t availableApps < <(jq -n -r --argjson appsArray "$appsArray" '$appsArray[] | .index, .appName, .pkgName')
165 | appIndex=$("${header[@]}" --begin 2 0 --title '| App Selection Menu |' --item-help --default-item "$appIndex" "${helpTag[@]}" --ok-label "Select" --cancel-label "Back" --menu "Use arrow keys to navigate\nSource: $sourceName" $(($(tput lines) - 3)) -1 15 "${availableApps[@]}" 2>&1 >/dev/tty)
166 | appSelectResult=$?
167 | case $appSelectResult in
168 | 0)
169 | readarray -t appSelectedResult < <(jq -n -r --arg appIndex "$appIndex" --argjson appsArray "$appsArray" '$appsArray[] | select(.index == ($appIndex | tonumber)) | .appName, .pkgName, .developerName, .apkmirrorAppName')
170 | appName="${appSelectedResult[0]}"
171 | pkgName="${appSelectedResult[1]}"
172 | developerName="${appSelectedResult[2]}"
173 | apkmirrorAppName="${appSelectedResult[3]}"
174 | appType=downloaded
175 | ;;
176 | 1)
177 | return 1
178 | ;;
179 | 2)
180 | appType=local
181 | unset appName appVer
182 | ;;
183 | esac
184 | if [ "$previousAppName" != "$appName" ]; then
185 | unset appVerList
186 | fi
187 | }
188 |
189 | selectPatches() {
190 | while true; do
191 | readarray -t patchesInfo < <(
192 | jq -n -r --arg pkgName "$pkgName" \
193 | --slurpfile patchesFile "$patchesSource"-patches-*.json \
194 | --argjson includedPatches "$includedPatches" \
195 | '$patchesFile[][] |
196 | .name as $patchName |
197 | .description as $desc |
198 | .compatiblePackages |
199 | if . != null then
200 | if (((map(.name) | index($pkgName)) != null) or (length == 0)) then
201 | (
202 | if ((($includedPatches | length) != 0) and (($includedPatches[] | select(.pkgName == $pkgName).includedPatches | index($patchName)) != null)) then
203 | $patchName, "on", $desc
204 | else
205 | $patchName, "off", $desc
206 | end
207 | )
208 | else
209 | empty
210 | end
211 | else
212 | if ((($includedPatches | length) != 0) and (($includedPatches[] | select(.pkgName == $pkgName).includedPatches | index($patchName)) != null)) then
213 | $patchName, "on", $desc
214 | else
215 | $patchName, "off", $desc
216 | end
217 | end'
218 | )
219 | grep -qoP "(?<=\s)off(?=\s)" <<< "${patchesInfo[@]}" && toogleName="Include All" || toogleName="Exclude All"
220 | choices=$("${header[@]}" --begin 2 0 --title '| Patch Selection Menu |' --item-help --no-items --separate-output --ok-label "$1" --cancel-label "$toogleName" --help-button --help-label "Recommended" --checklist "Use arrow keys to navigate; Press Spacebar to toogle patch\nSource: $sourceName; AppName: $appName" $(($(tput lines) - 3)) -1 15 "${patchesInfo[@]}" 2>&1 >/dev/tty)
221 | selectPatchStatus=$?
222 | readarray -t choices <<< "$choices"
223 | patchSaver || break
224 | done
225 | }
226 |
227 | patchSaver() {
228 | case "$selectPatchStatus" in
229 | 0 )
230 | includedPatches=$(jq -n --arg pkgName "$pkgName" --argjson includedPatches "$includedPatches" '[$includedPatches[] | select(.pkgName == $pkgName).includedPatches = $ARGS.positional]' --args "${choices[@]}")
231 | echo "$includedPatches" >"$storagePath/$source-patches.json" && return 1
232 | ;;
233 | 1 )
234 | if [ "$toogleName" == "Include All" ]; then
235 | includedPatches=$(jq -n --arg pkgName "$pkgName" --slurpfile patchesFile "$patchesSource"-patches-*.json --argjson includedPatches "$includedPatches" '
236 | [
237 | $includedPatches[] |
238 | select(.pkgName == $pkgName).includedPatches = [
239 | $patchesFile[][] |
240 | .name as $patchName |
241 | .compatiblePackages |
242 | if . != null then
243 | if (((map(.name) | index($pkgName)) != null) or (length == 0)) then
244 | $patchName
245 | else
246 | empty
247 | end
248 | else
249 | $patchName
250 | end
251 | ]
252 | ]'
253 | )
254 | elif [ "$toogleName" == "Exclude All" ]; then
255 | includedPatches=$(jq -n --arg pkgName "$pkgName" --argjson includedPatches "$includedPatches" '[$includedPatches[] | select(.pkgName == $pkgName).includedPatches = []]')
256 | fi
257 | ;;
258 | 2 )
259 | includedPatches=$(jq -n --arg pkgName "$pkgName" --slurpfile patchesFile "$patchesSource"-patches-*.json --argjson includedPatches "$includedPatches" '
260 | [
261 | $includedPatches[] |
262 | select(.pkgName == $pkgName).includedPatches = [
263 | $patchesFile[][] |
264 | .name as $patchName |
265 | .use as $use |
266 | .excluded as $excluded |
267 | .compatiblePackages |
268 | if . != null then
269 | if ((((map(.name) | index($pkgName)) != null) or (length == 0)) and (($use == true) or ($excluded == false))) then
270 | $patchName
271 | else
272 | empty
273 | end
274 | else
275 | $patchName
276 | end
277 | ]
278 | ]')
279 | ;;
280 | esac
281 | }
282 |
283 | editPatchOptions() {
284 | if [ ! -f "$storagePath/$source-options.json" ]; then
285 | java -jar "$cliSource"-cli-*.jar options -ou -p "$storagePath/$source-options.json" "$patchesSource"-patches-*.jar &> /dev/null
286 | fi
287 | currentPatch="none"
288 | optionsJson=$(jq '.' "$storagePath/$source-options.json")
289 | readarray -t patchNames < <(jq -n -r --argjson optionsJson "$optionsJson" '$optionsJson[].patchName')
290 | while true; do
291 | if [ "$currentPatch" == "none" ]; then
292 | if ! currentPatch=$("${header[@]}" --begin 2 0 --title '| Patch Options Menu |' --no-items --ok-label "Select" --cancel-label "Back" --menu "Select Patch to edit options" -1 -1 0 "${patchNames[@]}" 2>&1 >/dev/tty); then
293 | jq -n --argjson optionsJson "$optionsJson" '$optionsJson' > "$storagePath/$source-options.json"
294 | break
295 | fi
296 | else
297 | tput cnorm
298 | readarray -t patchOptionEntries < <(jq -n -r --arg currentPatch "$currentPatch" --argjson optionsJson "$optionsJson" '$optionsJson[] | select(.patchName == $currentPatch) | .options | to_entries[] | .key as $key | (.value | (.key | length) as $wordLength | ((($key+1) | tostring) + ". " + .key + ":"), ($key*2)+1, 0, .value, ($key*2)+1, ($wordLength + 6), 100, 100)')
299 | readarray -t newValues < <("${header[@]}" --begin 2 0 --title '| Patch Options Form |' --ok-label "Save" --cancel-label "Back" --form "Edit patch options for \"$currentPatch\" patch" -1 -1 0 "${patchOptionEntries[@]}" 2>&1 >/dev/tty)
300 | if [ "${newValues[*]}" != "" ]; then
301 | optionsJson=$(jq -n -r --arg currentPatch "$currentPatch" --argjson optionsJson "$optionsJson" '$optionsJson | map((select(.patchName == $currentPatch) | .options) |= [(to_entries[] | .key as $key | .value.value = (if $ARGS.positional[$key] == "" then null elif $ARGS.positional[$key] == "null" then null elif $ARGS.positional[$key] == "true" then true elif $ARGS.positional[$key] == "false" then false else $ARGS.positional[$key] end)) | .value])' --args "${newValues[@]}")
302 | fi
303 | currentPatch="none"
304 | tput civis
305 | fi
306 | done
307 | }
308 |
309 | initInstall() {
310 | if [ "$root" == true ];
311 | then
312 | "${header[@]}" --infobox "Please Wait !!\nInstalling Patched $appName..." 12 45
313 | if ! su -mm -c "/system/bin/sh $repoDir/root_util.sh mount $pkgName $appName $appVer $sourceName" > /dev/null 2>&1; then
314 | "${header[@]}" --msgbox "Installation Failed !!\nLogs saved to \"Internal-Storage/Revancify/install_log.txt\". Share the Install logs to developer." 12 45
315 | return 1
316 | else
317 | "${header[@]}" --msgbox "$appName installed Successfully !!" 12 45
318 | fi
319 | if [ "$LaunchAppAfterMount" == true ]; then
320 | su -c "settings list secure | sed -n -e 's/\/.*//' -e 's/default_input_method=//p' | xargs pidof | xargs kill -9 && pm resolve-activity --brief $pkgName | tail -n 1 | xargs am start -n && pidof com.termux | xargs kill -9" &> /dev/null
321 | fi
322 | else
323 | "${header[@]}" --infobox "Copying $appName $sourceName $selectedVer to Internal Storage..." 12 45
324 | [ -d "$storagePath/$appName-$appVer" ] || mkdir -p "$storagePath/$appName-$appVer"
325 | cp "apps/$appName-$appVer/base-$sourceName.apk" "$storagePath/$appName-$appVer" &> /dev/null
326 | termux-open "$storagePath/$appName-$appVer/base-$sourceName.apk"
327 | return 1
328 | fi
329 | }
330 |
331 | rootUninstall() {
332 | selectApp normal || return 1
333 | su -mm -c "/system/bin/sh $repoDir/root_util.sh unmount $pkgName" &> /dev/null
334 | unmountStatus=$?
335 | if [ "$unmountStatus" -eq "2" ]; then
336 | "${header[@]}" --msgbox "Patched $appName is not installed(mounted) in your device." 12 45
337 | return 1
338 | else
339 | "${header[@]}" --infobox "Uninstalling Patched $appName by Unmounting..." 12 45
340 | sleep 2
341 | [ "$unmountStatus" -ne "0" ] && "${header[@]}" --msgbox "Unmount failed !! Something went wrong." 12 45 && sleep 1 && return 1
342 | fi
343 | "${header[@]}" --msgbox "Unmount Successful !!" 12 45
344 | sleep 1
345 | }
346 |
347 | refreshJson() {
348 | if ! ls "$patchesSource"-patches-*.json &> /dev/null; then
349 | getTools || return 1
350 | return 0
351 | fi
352 | if [ ! -f "$storagePath/$source-patches.json" ]; then
353 | internet || return 1
354 | "${header[@]}" --infobox "Please Wait !!" 12 45
355 | if [ "$(bash "$repoDir/fetch_patches.sh" "$source" online "$storagePath")" == "error" ]; then
356 | "${header[@]}" --msgbox "Oops !! Apkmirror API is not accessible. Patches are not successfully synced.\nRevancify may crash.\n\nChange your network." 12 45
357 | return 1
358 | fi
359 | fi
360 | includedPatches=$(jq '.' "$storagePath/$source-patches.json" 2>/dev/null || jq -n '[]')
361 | appsArray=$(jq -n --argjson includedPatches "$includedPatches" --arg pkgName "$pkgName" '$includedPatches | map(select(.appName != null)) | to_entries | map({"index": (.key + 1), "appName": (.value.appName), "pkgName" :(.value.pkgName), "developerName" :(.value.developerName), "apkmirrorAppName" :(.value.apkmirrorAppName)})')
362 | }
363 |
364 | checkTools() {
365 | if [ -f ".${source}-data" ]; then
366 | # shellcheck source=/dev/null
367 | source ./".${source}-data"
368 | else
369 | getTools || return 1
370 | fi
371 | if [ "$cliSize" == "$(ls "$cliSource"-cli-*.jar &> /dev/null && du -b "$cliSource"-cli-*.jar | cut -d $'\t' -f 1 || echo 0)" ] && [ "$patchesSize" == "$(ls "$patchesSource"-patches-*.jar &> /dev/null && du -b "$patchesSource"-patches-*.jar | cut -d $'\t' -f 1 || echo 0)" ] && [ "$integrationsSize" == "$(ls "$integrationsSource"-integrations-*.apk &> /dev/null && du -b "$integrationsSource"-integrations-*.apk | cut -d $'\t' -f 1 || echo 0)" ] && ls "$storagePath/$source-patches.json" &> /dev/null; then
372 | refreshJson || return 1
373 | else
374 | getTools || return 1
375 | fi
376 | }
377 |
378 | getAppVer() {
379 | if [ "$root" == true ] && su -c "pm list packages | grep -q $pkgName" && [ "$AllowVersionDowngrade" == false ]; then
380 | selectedVer=$(su -c dumpsys package "$pkgName" | sed -n '/versionName/s/.*=//p' | sed -n '1p')
381 | appVer="${selectedVer// /-}"
382 | fi
383 | if [ "${#appVerList[@]}" -lt 2 ]; then
384 | internet || return 1
385 | "${header[@]}" --infobox "Please Wait !!\nScraping versions list for $appName from apkmirror.com..." 12 45
386 | readarray -t appVerList < <(bash "$repoDir/fetch_versions.sh" "$appName" "$apkmirrorAppName" "$source" "$selectedVer" "$storagePath")
387 | fi
388 | versionSelector || return 1
389 | }
390 |
391 | versionSelector() {
392 | if [ "${appVerList[0]}" == "error" ]; then
393 | "${header[@]}" --msgbox "Unable to fetch link !!\nThere is some problem with your internet connection. Disable VPN or Change your network." 12 45
394 | return 1
395 | fi
396 | selectedVer=$("${header[@]}" --begin 2 0 --title '| Version Selection Menu |' --default-item "$selectedVer" --ok-label "Select" --cancel-label "Back" --menu "Use arrow keys to navigate\nSource: $sourceName; AppName: $appName" -1 -1 0 "${appVerList[@]}" 2>&1 >/dev/tty) || return 1
397 | if [ "$selectedVer" == "Auto Select" ]; then
398 | selectedVer=$(jq -n -r --argjson includedPatches "$includedPatches" --arg pkgName "$pkgName" '$includedPatches[] | select(.pkgName == $pkgName) | .versions[-1]')
399 | fi
400 | appVer="${selectedVer// /-}"
401 | }
402 |
403 | checkPatched() {
404 | if [ -f "apps/$appName-$appVer/base-$sourceName.apk" ]; then
405 | "${header[@]}" --begin 2 0 --title '| Patched apk found |' --no-items --defaultno --yes-label 'Patch' --no-label 'Install' --help-button --help-label 'Back' --yesno "Current directory already contains Patched $appName version $selectedVer.\n\n\nDo you want to patch $appName again?" -1 -1
406 | apkFoundPrompt=$?
407 | case "$apkFoundPrompt" in
408 | 0 )
409 | rm "apps/$appName-$appVer/base-$sourceName.apk"
410 | ;;
411 | 1 )
412 | initInstall
413 | return 1
414 | ;;
415 | 2 )
416 | return 1
417 | ;;
418 | esac
419 | else
420 | rm "apps/$appName-$appVer/base-$sourceName.apk" &> /dev/null
421 | return 0
422 | fi
423 | }
424 |
425 | selectFile() {
426 | newPath=""
427 | while [ ! -f "$newPath" ]; do
428 | currentPath=${currentPath:-$internalStorage}
429 | dirList=()
430 | files=()
431 | if [ "$currentPath" != "$internalStorage" ]; then
432 | dirUp=(1 ".." "GO BACK TO PREVIOUS DIRECTORY")
433 | num=1
434 | else
435 | unset dirUp
436 | num=0
437 | fi
438 | while read -r itemName; do
439 | if [ -d "$currentPath/$itemName" ]; then
440 | files+=("$itemName")
441 | [ "${#itemName}" -gt $(("$(tput cols)" - 24)) ] && itemNameDisplay=${itemName:0:$(("$(tput cols)" - 34))}...${itemName: -10} || itemNameDisplay="$itemName"
442 | dirList+=("$((++num))" "$itemNameDisplay/" "DIR: $itemName/")
443 | elif [ "${itemName##*.}" == "apk" ]; then
444 | files+=("$itemName")
445 | [ "${#itemName}" -gt $(("$(tput cols)" - 24)) ] && itemNameDisplay=${itemName:0:$(("$(tput cols)" - 34))}...${itemName: -10} || itemNameDisplay=$itemName
446 | dirList+=("$((++num))" "$itemNameDisplay" "APK: $itemName")
447 | fi
448 | done < <(ls -1 --group-directories-first "$currentPath")
449 | pathIndex=$("${header[@]}" --begin 2 0 --title '| Apk File Selection Menu |' --item-help --ok-label "Select" --menu "Use arrow keys to navigate\nCurrent Path: $currentPath/" $(($(tput lines) - 3)) -1 15 "${dirUp[@]}" "${dirList[@]}" 2>&1 >/dev/tty)
450 | exitstatus=$?
451 | [ "$exitstatus" -eq 1 ] && break
452 | if [ "$currentPath" != "$internalStorage" ] && [ "$pathIndex" -eq 1 ]; then
453 | newPath=".."
454 | elif [ "$currentPath" != "$internalStorage" ] && [ "$pathIndex" -ne 1 ]; then
455 | newPath=${files[$pathIndex - 2]}
456 | else
457 | newPath=${files[$pathIndex - 1]}
458 | fi
459 | if [ "$newPath" == ".." ]; then
460 | newPath=${currentPath%/*}
461 | else
462 | newPath=$currentPath/$newPath
463 | fi
464 | if [ -d "$newPath" ]; then
465 | currentPath=$newPath
466 | fi
467 | done
468 | [ "$exitstatus" -eq 1 ] && return 1
469 | return 0
470 | }
471 |
472 | fetchCustomApk() {
473 | selectedVer="" installedVer=""
474 | selectFile || return 1
475 | "${header[@]}" --infobox "Please Wait !!\nExtracting data from \"$(basename "$newPath")\"" 12 45
476 | if ! aaptData=$(./aapt2 dump badging "$newPath"); then
477 | "${header[@]}" --msgbox "The apkfile you selected is not an valid app. Download the apk again and retry." 12 45
478 | return 1
479 | fi
480 | pkgName=$(grep "package:" <<<"$aaptData" | sed -e 's/package: name='\''//' -e 's/'\'' versionCode.*//')
481 | if [ "$(jq -n --arg pkgName "$pkgName" --argjson includedPatches "$includedPatches" '$includedPatches[] | select(.pkgName == $pkgName) | .patches')" == "" ]; then
482 | "${header[@]}" --msgbox "The app you selected is not supported for patching by $sourceName patches !!" 12 45
483 | return 1
484 | fi
485 | fileAppName=$(grep "application-label:" <<<"$aaptData" | sed -e 's/application-label://' -e 's/'\''//g')
486 | appName="$(sed 's/\./-/g;s/ /-/g' <<<"$fileAppName")"
487 | selectedVer=$(grep "package:" <<<"$aaptData" | sed -e 's/.*versionName='\''//' -e 's/'\'' platformBuildVersionName.*//')
488 | appVer="${selectedVer// /-}"
489 | if [ "$root" == true ] && su -c "pm list packages | grep -q $pkgName" && [ "$AllowVersionDowngrade" == false ]; then
490 | installedVer=$(su -c dumpsys package "$pkgName" | sed -n '/versionName/s/.*=//p' | sed -n '1p')
491 | if [ "$installedVer" != "$selectedVer" ]; then
492 | sorted=$(jq -nr --arg installedVer "$installedVer" --arg selectedVer "$selectedVer" '[$installedVer, $selectedVer] | sort | .[0]')
493 | if [ "${sorted[0]}" != "$installedVer" ];then
494 | "${header[@]}" --msgbox "The selected version $selectedVer is lower then version $installedVer installed on your device.\nPlease Select a higher version !!" 12 45
495 | return 1
496 | fi
497 | fi
498 | fi
499 | [ -d "apps/$appName-$appVer" ] || mkdir -p "apps/$appName-$appVer"
500 | cp "$newPath" "apps/$appName-$appVer/base.apk"
501 | if [ "$(jq -n -r --argjson includedPatches "$includedPatches" --arg pkgName "$pkgName" '$includedPatches[] | select(.pkgName == $pkgName) | .versions | length')" -eq 0 ]; then
502 | if ! "${header[@]}" --begin 2 0 --title '| Proceed |' --no-items --yesno "The following data is extracted from the apk file you provided.\nApp Name : $fileAppName\nPackage Name: $pkgName\nVersion : $selectedVer\nDo you want to proceed with this app?" -1 -1; then
503 | return 1
504 | fi
505 | else
506 | if [ "$(jq -n -r --arg selectedVer "$selectedVer" --arg pkgName "$pkgName" --argjson includedPatches "$includedPatches" '$includedPatches[] | select(.pkgName == $pkgName) | .versions | index($selectedVer)')" != "null" ]; then
507 | if ! "${header[@]}" --begin 2 0 --title '| Proceed |' --no-items --yesno "The following data is extracted from the apk file you provided.\nApp Name : $fileAppName\nPackage Name: $pkgName\nVersion : $selectedVer\nDo you want to proceed with this app?" -1 -1; then
508 | return 1
509 | fi
510 | else
511 | if ! "${header[@]}" --begin 2 0 --title '| Proceed |' --no-items --yesno "The following data is extracted from the apk file you provided.\nApp Name : $fileAppName\nPackage Name: $pkgName\nVersion : $selectedVer\n\nThe version $selectedVer is not supported. Supported versions are: \n$(jq -n -r --arg pkgName "$pkgName" --argjson includedPatches "$includedPatches" '$includedPatches[] | select(.pkgName == $pkgName).versions | length as $array_length | to_entries[] | if .key != ($array_length - 1) then .value + "," else .value end')\n\nDo you still want to proceed with version $selectedVer for $appName?" -1 -1; then
512 | return 1
513 | fi
514 | fi
515 | fi
516 | checkPatched || return 1
517 | }
518 |
519 | fetchApk() {
520 | selectedVer="" appVer=""
521 | getAppVer || return 1
522 | if [ "$(jq -n -r --argjson includedPatches "$includedPatches" --arg selectedVer "$selectedVer" --arg pkgName "$pkgName" '$includedPatches[] | select(.pkgName == $pkgName) | .versions | if ((. | length) == 0) then 0 elif ((. | index($selectedVer)) != null) then 0 else 1 end')" -eq 0 ]; then
523 | if ! "${header[@]}" --begin 2 0 --title '| Proceed |' --no-items --yesno "Do you want to proceed with version $selectedVer for $appName?" -1 -1; then
524 | return 1
525 | fi
526 | else
527 | if ! "${header[@]}" --begin 2 0 --title '| Proceed |' --no-items --yesno "The version $selectedVer is not supported. Supported versions are: \n$(jq -n -r --arg pkgName "$pkgName" --argjson includedPatches "$includedPatches" '$includedPatches[] | select(.pkgName == $pkgName).versions | length as $array_length | to_entries[] | if .key != ($array_length - 1) then .value + "," else .value end')\n\nDo you still want to proceed with version $selectedVer for $appName?" -1 -1; then
528 | return 1
529 | fi
530 | fi
531 | checkPatched || return 1
532 | if [ -f "apps/$appName-$appVer/base.apk" ]; then
533 | # shellcheck source=/dev/null
534 | if [ "$(source "apps/.appSize"; eval echo \$"${appName//-/_}"Size)" == "$([ -f "apps/$appName-$appVer/base.apk" ] && du -b "apps/$appName-$appVer/base.apk" | cut -d $'\t' -f 1 || echo 0)" ]; then
535 | return 0
536 | fi
537 | else
538 | rm -rf "apps/$appName"* &> /dev/null
539 | fi
540 | downloadApp || return 1
541 | }
542 |
543 | downloadApp() {
544 | internet || return 1
545 | appUrl=$( (bash "$repoDir/fetch_link.sh" "$developerName" "$apkmirrorAppName" "$appVer" 2>&3 | "${header[@]}" --begin 2 0 --gauge "App : $appName\nVersion: $selectedVer\n\nScraping Download Link..." -1 -1 0 >&2) 3>&1)
546 | tput civis
547 | case $appUrl in
548 | "error" )
549 | "${header[@]}" --msgbox "Unable to fetch link !!\nThere is some problem with your internet connection. Disable VPN or Change your network." 12 45
550 | return 1
551 | ;;
552 | "noapk" )
553 | if [ "$root" == false ]; then
554 | "${header[@]}" --msgbox "No apk found on apkmirror.com for version $selectedVer !!\nTry selecting other version." 12 45
555 | return 1
556 | else
557 | "${header[@]}" --msgbox "No apk found on apkmirror.com for version $selectedVer !!\nPlease upgrade or degrade the version to patch it.\n\nSuggestion: Download apk manually and use that file to patch." 12 45
558 | return 1
559 | fi
560 | ;;
561 | "noversion" )
562 | "${header[@]}" --msgbox "This version is not uploaded on apkmirror.com!!\nPlease upgrade or degrade the version to patch it.\n\nSuggestion: Download apk manually and use that file to patch." 12 45
563 | return 1
564 | ;;
565 | esac
566 | appSize="$(curl -sLI "$appUrl" -A "$userAgent" | sed -n '/Content-Length/s/[^0-9]*//p' | tr -d '\r')"
567 | [ "$appSize" == "" ] && return 1
568 | setEnv "${appName//-/_}Size" "$appSize" update "apps/.appSize"
569 | [ -d "apps/$appName-$appVer" ] || mkdir -p "apps/$appName-$appVer"
570 | wget -q -c "$appUrl" -O "apps/$appName-$appVer/base.apk" --show-progress --user-agent="$userAgent" 2>&1 | stdbuf -o0 cut -b 63-65 | stdbuf -o0 grep '[0-9]' | "${header[@]}" --begin 2 0 --gauge "App : $appName\nVersion: $selectedVer\nSize : $(numfmt --to=iec --format="%0.1f" "$appSize")\n\nDownloading..." -1 -1 "$(($(( "$([ -f "apps/$appName-$appVer/base.apk" ] && du -b "apps/$appName-$appVer/base.apk" | cut -d $'\t' -f 1 || echo 0)" * 100)) / appSize))"
571 | tput civis
572 | sleep 0.5s
573 | if [ "$appSize" != "$(du -b "apps/$appName-$appVer/base.apk" | cut -d $'\t' -f 1)" ]; then
574 | "${header[@]}" --msgbox "Oh No !!\nUnable to complete download. Please Check your internet connection and Retry." 12 45
575 | return 1
576 | fi
577 | }
578 |
579 | downloadMicrog() {
580 | microgName=mMicroG microgRepo=inotia00
581 | if "${header[@]}" --begin 2 0 --title '| MicroG Prompt |' --no-items --defaultno --yesno "$microgName is used to run MicroG services without root.\nYouTube and YouTube Music won't work without it.\nIf you already have $microgName, You don't need to download it.\n\n\n\n\n\nDo you want to download $microgName app?" -1 -1; then
582 | internet || return 1
583 | readarray -t microgheaders < <(curl -s "https://api.github.com/repos/$microgRepo/$microgName/releases/latest" | jq -r --arg regex ".*$arch.*" '(.assets[] | if .name | test($regex) then .browser_download_url, .size else empty end), .tag_name')
584 | wget -q -c "${microgheaders[0]}" -O "$microgName-${microgheaders[2]}.apk" --show-progress --user-agent="$userAgent" 2>&1 | stdbuf -o0 cut -b 63-65 | stdbuf -o0 grep '[0-9]' | "${header[@]}" --begin 2 0 --gauge "App : $microgName \nVersion : ${microgheaders[2]}\nSize : $(numfmt --to=iec --format="%0.1f" "${microgheaders[1]}")\n\nDownloading..." -1 -1 && tput civis
585 | ls $microgName* &> /dev/null && mv $microgName* "$storagePath/" && termux-open "$storagePath/$microgName-${microgheaders[2]}.apk"
586 | fi
587 | }
588 |
589 | patchApp() {
590 | if [ "$cliSource" == "inotia00" ] && [ "$Riplibs" == true ]; then
591 | riplibArgs="--rip-lib=x86_64 --rip-lib=x86 --rip-lib=armeabi-v7a --rip-lib=arm64-v8a "
592 | riplibArgs="${riplibArgs//--rip-lib=$arch /}"
593 | else
594 | riplibArgs=""
595 | fi
596 | includedPatches=$(jq '.' "$storagePath/$source-patches.json" 2>/dev/null || jq -n '[]')
597 | readarray -t patchesArg < <(jq -n -r --argjson includedPatches "$includedPatches" --arg pkgName "$pkgName" '$includedPatches[] | select(.pkgName == $pkgName).includedPatches | if ((. | length) != 0) then (.[] | "-i", .) else empty end')
598 | java -jar "$cliSource"-cli-*.jar patch -fpw -b "$patchesSource"-patches-*.jar -m "$integrationsSource"-integrations-*.apk -o "apps/$appName-$appVer/base-$sourceName.apk" $riplibArgs "${patchesArg[@]}" --keystore "$repoDir"/revancify.keystore --alias "decipher" --signer "decipher" --keystore-entry-password "revancify" --keystore-password "revancify" --custom-aapt2-binary ./aapt2 --options "$storagePath/$source-options.json" --exclusive "apps/$appName-$appVer/base.apk" 2>&1 | tee "$storagePath/patch_log.txt" | "${header[@]}" --begin 2 0 --ok-label "Continue" --cursor-off-label --programbox "Patching $appName $selectedVer.apk" -1 -1
599 | echo -e "\n\n\nRooted: $root\nArch: $arch\nApp: $appName v$appVer\nCLI: $(ls "$cliSource"-cli-*.jar)\nPatches: $(ls "$patchesSource"-patches-*.jar)\nIntegrations: $(ls "$integrationsSource"-integrations-*.apk)\nPatches argument: ${patchesArg[*]}" >>"$storagePath/patch_log.txt"
600 | tput civis
601 | sleep 1
602 | if [ ! -f "apps/$appName-$appVer/base-$sourceName.apk" ]; then
603 | "${header[@]}" --msgbox "Oops, Patching failed !!\nLogs saved to \"Internal Storage/Revancify/patch_log.txt\". Share the Patchlog to developer." 12 45
604 | return 1
605 | fi
606 | }
607 |
608 | deleteComponents() {
609 | while true; do
610 | delComponentPrompt=$("${header[@]}" --begin 2 0 --title '| Delete Components Menu |' --ok-label "Select" --cancel-label "Back" --menu "Use arrow keys to navigate\nSource: $sourceName" -1 -1 0 1 "Tools" 2 "Apps" 3 "Patch Options" 2>&1 >/dev/tty) || break
611 | case "$delComponentPrompt" in
612 | 1 )
613 | if "${header[@]}" --begin 2 0 --title '| Delete Tools |' --no-items --defaultno --yesno "Please confirm to delete the tools.\nIt will delete the $sourceName CLI, patches and integrations." -1 -1; then
614 | rm "$cliSource"-cli-*.jar &> /dev/null
615 | rm "$patchesSource"-patches-*.jar &> /dev/null
616 | rm "$patchesSource"-patches-*.json &> /dev/null
617 | rm "$integrationsSource"-integrations-*.apk &> /dev/null
618 | "${header[@]}" --msgbox "All $sourceName Tools successfully deleted !!" 12 45
619 | fi
620 | ;;
621 | 2 )
622 | if "${header[@]}" --begin 2 0 --title '| Delete Apps |' --no-items --defaultno --yesno "Please confirm to delete all the downloaded and patched apps." -1 -1; then
623 | rm -rf "apps"/*
624 | "${header[@]}" --msgbox "All Apps are successfully deleted !!" 12 45
625 | fi
626 | ;;
627 | 3 )
628 | if "${header[@]}" --begin 2 0 --title '| Delete Patch Options |' --no-items --defaultno --yesno "Please confirm to delete the patch options file for $sourceName patches." -1 -1; then
629 | rm "$storagePath/$source-options.json" &> /dev/null
630 | "${header[@]}" --msgbox "Options file successfully deleted for current source !!" 12 45
631 | fi
632 | ;;
633 | esac
634 | done
635 | }
636 |
637 | preferences() {
638 | [ "$cliSource" == "inotia00" ] && RiplibsPref=("Riplibs" "$Riplibs" "Removes extra libs from app") || RiplibsPref=()
639 | prefsArray=("LightTheme" "$LightTheme" "Use Light theme for Revancify" "${RiplibsPref[@]}" "AutocheckToolsUpdate" "$AutocheckToolsUpdate" "Check for tools update at startup" "ShowConfirmPatchesMenu" "$ShowConfirmPatchesMenu" "Shows Patches Menu before Patching starts" "LaunchAppAfterMount" "$LaunchAppAfterMount" "[Root] Launches app automatically after mount" AllowVersionDowngrade "$AllowVersionDowngrade" "[Root] Allows downgrading version if any such module is present" "FetchPreReleasedTools" "$FetchPreReleasedTools" "Fetches the pre-release version of tools")
640 | readarray -t prefsArray < <(for pref in "${prefsArray[@]}"; do sed 's/false/off/;s/true/on/' <<< "$pref"; done)
641 | read -ra newPrefs < <("${header[@]}" --begin 2 0 --title '| Preferences Menu |' --item-help --no-items --no-cancel --ok-label "Save" --checklist "Use arrow keys to navigate; Press Spacebar to toogle patch" $(($(tput lines) - 3)) -1 15 "${prefsArray[@]}" 2>&1 >/dev/tty)
642 | sed -i 's/true/false/' "$envFile"
643 | for newPref in "${newPrefs[@]}"; do
644 | setEnv "$newPref" true update "$envFile"
645 | done
646 | # shellcheck source=/dev/null
647 | source "$envFile"
648 | [ "$LightTheme" == true ] && theme=Light || theme=Dark
649 | export DIALOGRC="$repoDir/configs/.dialogrc$theme"
650 | }
651 |
652 | buildApk() {
653 | if [ "$appType" == "downloaded" ]; then
654 | fetchApk || return 1
655 | else
656 | fetchCustomApk || return 1
657 | selectPatches Proceed
658 | fi
659 | if [ "$appType" == "downloaded" ] && [ "$ShowConfirmPatchesMenu" == true ]; then
660 | selectPatches Proceed
661 | fi
662 | patchApp || return 1
663 | initInstall
664 | }
665 |
666 | mainMenu() {
667 | mainMenu=$("${header[@]}" --begin 2 0 --title '| Main Menu |' --default-item "$mainMenu" --ok-label "Select" --cancel-label "Exit" --menu "Use arrow keys to navigate\nSource: $sourceName" -1 -1 0 1 "Patch App" 2 "Select Patches" 3 "Change Source" 4 "Fetch Tools" 5 "Edit Patch Options" 6 "$menuEntry" 7 "Delete Components" 8 "Preferences" 2>&1 >/dev/tty) || terminate 0
668 | case "$mainMenu" in
669 | 1 )
670 | while true; do
671 | selectApp storage || break
672 | buildApk
673 | done
674 | ;;
675 | 2 )
676 | while true; do
677 | selectApp normal || break
678 | selectPatches Save || break
679 | done
680 | ;;
681 | 3 )
682 | changeSource
683 | ;;
684 | 4 )
685 | getTools
686 | ;;
687 | 5 )
688 | editPatchOptions
689 | ;;
690 | 6 )
691 | if [ "$root" == true ]; then
692 | rootUninstall
693 | else
694 | downloadMicrog
695 | fi
696 | ;;
697 | 7 )
698 | deleteComponents
699 | ;;
700 | 8 )
701 | preferences
702 | ;;
703 | esac
704 | }
705 |
706 | if su -c exit &> /dev/null; then
707 | [ "$1" == false ] && root=false || root=true
708 | else
709 | root=false
710 | fi
711 |
712 | online="$2"
713 |
714 | initialize
715 |
716 | while true; do
717 | unset appVerList appVer appName pkgName
718 | mainMenu
719 | done
--------------------------------------------------------------------------------
/revancify:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | HOME=/data/data/com.termux/files/home
4 | BIN=/data/data/com.termux/files/usr/bin
5 |
6 | repo="$HOME/Revancify"
7 | cp "$repo/revancify" "$BIN/revancify" >/dev/null 2>&1
8 | root=default
9 | online="Checking..."
10 | DATA="$HOME/revancify-data"
11 | [ ! -d $DATA ] && mkdir -p "$DATA"
12 | # shellcheck source=/dev/null
13 | source "$DATA/config.cfg" > /dev/null 2>&1
14 | [ "${LightTheme:-false}" == "true" ] && theme=Light || theme=Dark
15 | export DIALOGRC="$repo/configs/.dialogrc$theme"
16 |
17 | help() {
18 | if [ "$1" == "1" ]; then
19 | readarray -t invalidArgs < <(for i in "${FLAG_ERR[@]}"; do
20 | if grep -q "unrecognized" <<< "$i"; then
21 | grep -oP "(?<=\`).*(?=')" <<< "$i"
22 | else
23 | cut -b 27 <<< "$i" | xargs -0 -I {} echo -n "-{}"
24 | fi
25 | done)
26 | IFS=","
27 | echo -e "\e[1;31mInvalid Argument(s) Passed: ${invalidArgs[*]}\n"
28 | fi
29 | echo -e "revancify\n
30 | Usage: revancify [OPTION]\n
31 | Options:
32 | -n, --no-root Run without SU permissions
33 | -o, --offline Run without updating revancify
34 | -r, --reinstall Reinstall revancify
35 | -h, --help Prints help statement"
36 | exit "$1"
37 | }
38 |
39 | if ! ARGS=$(getopt -o nohr -l "no-root,offline,help,reinstall" -- "$@" 2>/dev/null); then
40 | readarray -t FLAG_ERR < <(getopt -o nohr -l no-root,offline,help,reinstall -- "$@" 2>&1 1>&-)
41 | help 1
42 | fi
43 | eval set -- "$ARGS"
44 | while [ $# -gt 0 ]
45 | do
46 | case "$1" in
47 | "-h" | "--help")
48 | help 0
49 | ;;
50 | "-n" | "--no-root")
51 | root=false
52 | shift
53 | ;;
54 | "-r" | "--reinstall")
55 | if ping -c 1 google.com >/dev/null 2>&1; then
56 | rm -rf $DATA
57 | cd "$repo"/.. && rm -rf "$repo" && git clone --depth=1 https://github.com/decipher3114/Revancify.git && "$repo/revancify" && exit
58 | else
59 | echo "No internet Connection !!"
60 | exit 1
61 | fi
62 | ;;
63 | "-o" | "--offline")
64 | online=false
65 | shift
66 | ;;
67 | "--")
68 | shift
69 | break
70 | ;;
71 | esac
72 | shift
73 | done
74 |
75 | if [ "$(getprop ro.product.cpu.abi)" = "armeabi-v7a" ]; then
76 | echo "CPU architecture \"armeabi-v7a\" of your device is not supported for patching."
77 | echo "You may get build errors."
78 | read -N 1 -s -r -p $'Press ENTER to CONTINUE or SPACE to EXIT...\n' key
79 | if [ "$key" = " " ]; then
80 | echo "Script terminated"
81 | exit
82 | fi
83 | fi
84 |
85 | terminate() {
86 | clear
87 | echo "Script terminated !!"
88 | tput cnorm
89 | exit 1
90 | }
91 | trap terminate SIGTERM SIGINT SIGABRT
92 |
93 | checkdependencies() {
94 | if [ -f "$DATA/aapt2" ] && bins=$(ls "$BIN") && grep -q java <<<"$bins" && grep -q wget <<<"$bins" && grep -q tput <<<"$bins" && grep -q dialog <<<"$bins" && grep -q pup <<<"$bins" && grep -q jq <<<"$bins" && ls "$BIN/revancify" >/dev/null 2>&1 && ls "$HOME/storage" >/dev/null 2>&1; then
95 | return 0
96 | else
97 | if ping -c 1 google.com >/dev/null 2>&1; then
98 | installdependencies || (echo "Dependencies not installed !!" && exit 1)
99 | else
100 | cp "$repo/revancify" "$BIN/revancify"
101 | echo -e "Dependencies not installed !!\nRun again with internet connection."
102 | exit 1
103 | fi
104 | fi
105 | }
106 |
107 | installdependencies() {
108 | clear
109 | echo "Installing dependencies..."
110 | arch=$(getprop ro.product.cpu.abi)
111 | cp "$repo/revancify" "$BIN/revancify"
112 | sleep 1
113 | [ ! -d "$HOME/storage" ] && termux-setup-storage
114 | pkg update -y -o Dpkg::Options::="--force-confnew" || return 1
115 | pkg install openjdk-17 wget ncurses-utils dialog pup jq -y -o Dpkg::Options::="--force-confnew" || return 1
116 | git config --global pull.rebase true
117 | sed -i '/allow-external-apps/s/# //' "$HOME/.termux/termux.properties"
118 | [ ! -f "$DATA/aapt2" ] && wget "https://github.com/decipher3114/binaries/releases/download/v0.1/aapt2_$arch" -O "$DATA/aapt2" && chmod +x "$DATA/aapt2"
119 | echo "Dependencies installed successfully."
120 | return 0
121 | }
122 |
123 | checkrevancify() {
124 | if [ -d "$repo" ]; then
125 | cd "$repo" >/dev/null 2>&1 || true
126 | rm -rf ./*cache*
127 | return 0
128 | else
129 | echo -e "\e[1;31mRevancify dir is not found !!"
130 | echo -e "\e[1;31mDo you want to reinstall Revancify?"
131 | read -r -p "[Y/N]: " choice
132 | case "$choice" in
133 | y | Y)
134 | rm -rf "$repo"
135 | git clone --depth=1 https://github.com/decipher3114/Revancify.git && $repo/revancify && exit
136 | ;;
137 | n | N)
138 | echo "Removing revancify completely !!"
139 | sleep 0.5s
140 | rm "$BIN/revancify"
141 | echo "Successfully Uninstalled revancify."
142 | exit 0
143 | ;;
144 | ?)
145 | echo "Invalid option !!"
146 | exit 1
147 | ;;
148 | esac
149 | fi
150 | }
151 |
152 | checkdependencies
153 | checkrevancify
154 | tput civis
155 | dialog --no-shadow --infobox "\n █▀█ █▀▀ █░█ ▄▀█ █▄░█ █▀▀ █ █▀▀ █▄█\n █▀▄ ██▄ ▀▄▀ █▀█ █░▀█ █▄▄ █ █▀░ ░█░ \n\nDeveloper : decipher\nLast Updated : Checking...\nOnline : $online" 10 42
156 |
157 | if [ "$online" != false ]; then
158 | ping -c 1 google.com >/dev/null 2>&1 && online=true || online=false
159 | fi
160 |
161 | if [ "$online" == true ]; then
162 | if ! git pull >/dev/null 2>&1;then
163 | git fetch --all >/dev/null 2>&1 &&\
164 | git reset --hard "@{u}" >/dev/null 2>&1
165 | fi
166 | fi
167 | dialog --no-shadow --infobox "\n █▀█ █▀▀ █░█ ▄▀█ █▄░█ █▀▀ █ █▀▀ █▄█\n █▀▄ ██▄ ▀▄▀ █▀█ █░▀█ █▄▄ █ █▀░ ░█░ \n\nDeveloper : decipher\nLast Updated : $(git log -1 --pretty='format:%cd' --date=format:'%b %d, %Y | %H:%M')\nOnline : $online" 10 42
168 | cd "$DATA" >/dev/null 2>&1 || true
169 | bash "$repo/main.sh" "$root" "$online"
170 | exitstatus=$?
171 | clear
172 | cd "$HOME" || :
173 | if [ $exitstatus -eq 0 ]; then
174 | echo "Script exited !!"
175 | else
176 | echo "Script terminated !!"
177 | rm -rf -- *cache >/dev/null 2>&1
178 | fi
179 | tput cnorm
180 |
--------------------------------------------------------------------------------
/revancify.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YT-Advanced/Revancify/f661a28617efd854f8f62503d670dd9d2d9d927c/revancify.keystore
--------------------------------------------------------------------------------
/root_util.sh:
--------------------------------------------------------------------------------
1 | #!/system/bin/sh
2 |
3 | pkgName="$2"
4 | appName="$3"
5 | appVer="$4"
6 | sourceName="$5"
7 |
8 |
9 | if [ "$1" = "unmount" ]; then
10 | grep -q "$pkgName" /proc/mounts || exit 2
11 | am force-stop "$pkgName"
12 | grep "$pkgName" /proc/mounts | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -l
13 | stockApp=$(pm path "$pkgName" | sed -n "/base/s/package://p")
14 | am force-stop "$pkgName"
15 | rm "/data/adb/service.d/mount_revanced_$pkgName.sh"
16 | rm "/data/adb/post-fs-data.d/umount_revanced_$pkgName.sh"
17 | rm -rf "/data/local/tmp/revancify/$pkgName.apk"
18 | grep -q "$pkgName" /proc/mounts && exit 1
19 | exit 0
20 | fi
21 |
22 | [ -d /data/local/tmp/revancify/ ] || mkdir -p /data/local/tmp/revancify/
23 | [ -d /data/adb/post-fs-data.d/ ] || mkdir -p /data/adb/post-fs-data.d/
24 | [ -d /data/adb/service.d/ ] || mkdir -p /data/adb/service.d/
25 |
26 | rm "/data/adb/post-fs-data.d/umount_revanced_$pkgName.sh"
27 | rm "/data/adb/service.d/mount_revanced_$pkgName.sh"
28 | rm "/data/local/tmp/revancify/$pkgName.apk"
29 |
30 |
31 | if pm list packages | grep -q "$pkgName" && [ "$(dumpsys package "$pkgName" | sed -n '/versionName/s/.*=//p' | sed 's/ /./1p')" = "$appVer" ]; then
32 | :
33 | else
34 | pm install --user 0 -r "apps/$appName-$appVer/base.apk"
35 | fi
36 |
37 | pm list packages | grep -q "$pkgName" || exit 1
38 |
39 | stockApp=$(pm path "$pkgName" | sed -n "/base/s/package://p")
40 | revancedApp="/data/local/tmp/revancify/$pkgName.apk"
41 |
42 | am force-stop "$pkgName"
43 |
44 | {
45 | grep "$pkgName" /proc/mounts | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -vl
46 | cp "apps/$appName-$appVer/base-$sourceName.apk" "/data/local/tmp/revancify/$pkgName.apk"
47 | chmod -v 644 "$revancedApp" && chown -v system:system "$revancedApp"
48 | chcon -v u:object_r:apk_data_file:s0 "$revancedApp"
49 | mount -vo bind "$revancedApp" "$stockApp"
50 | } > /storage/emulated/0/Revancify/install_log.txt 2>&1
51 |
52 | am force-stop "$pkgName"
53 |
54 | grep -q "$pkgName" /proc/mounts || exit 1
55 |
56 | cat <"/data/adb/service.d/mount_revanced_$pkgName.sh"
57 | #!/system/bin/sh
58 | while [ "\$(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 5; done
59 |
60 | base_path="$revancedApp"
61 | stock_path="\$(pm path $pkgName | sed -n '/base/s/package://p')"
62 | am force-stop "$pkgName"
63 | chcon u:object_r:apk_data_file:s0 "\$base_path"
64 | [ ! -z "\$stock_path" ] && mount -o bind "\$base_path" "\$stock_path"
65 | am force-stop $pkgName
66 | EOF
67 | cat <"/data/adb/post-fs-data.d/umount_revanced_$pkgName.sh"
68 | #!/system/bin/sh
69 | stock_path="\$(pm path $pkgName | sed -n '/base/s/package://p')"
70 | [ ! -z "\$stock_path" ] && umount -l "\$stock_path"
71 | grep $pkgName /proc/mounts | cut -d " " -f 2 | sed "s/apk.*/apk/" | xargs -r umount -l
72 | EOF
73 | chmod 0744 "/data/adb/service.d/mount_revanced_$pkgName.sh"
74 | chmod 0744 "/data/adb/post-fs-data.d/umount_revanced_$pkgName.sh"
75 |
--------------------------------------------------------------------------------
/sources.json:
--------------------------------------------------------------------------------
1 | {
2 | "revanced":
3 | {
4 | "projectName": "Revanced",
5 | "sources":
6 | {
7 | "cli":
8 | {
9 | "org": "revanced",
10 | "repo": "revanced-cli"
11 | },
12 | "patches":
13 | {
14 | "org": "revanced",
15 | "repo": "revanced-patches"
16 | },
17 | "integrations":
18 | {
19 | "org": "revanced",
20 | "repo": "revanced-integrations"
21 | }
22 | }
23 | },
24 | "YT-Advanced":
25 | {
26 | "projectName": "ReX",
27 | "sources":
28 | {
29 | "cli":
30 | {
31 | "org": "inotia00",
32 | "repo": "revanced-cli"
33 | },
34 | "patches":
35 | {
36 | "org": "YT-Advanced",
37 | "repo": "ReX-patches"
38 | },
39 | "integrations":
40 | {
41 | "org": "YT-Advanced",
42 | "repo": "ReX-integrations"
43 | }
44 | }
45 | },
46 | "inotia00":
47 | {
48 | "projectName": "Revanced-Extended",
49 | "sources":
50 | {
51 | "cli":
52 | {
53 | "org": "inotia00",
54 | "repo": "revanced-cli"
55 | },
56 | "patches":
57 | {
58 | "org": "inotia00",
59 | "repo": "revanced-patches"
60 | },
61 | "integrations":
62 | {
63 | "org": "inotia00",
64 | "repo": "revanced-integrations"
65 | }
66 | }
67 | },
68 | "kitadai31":
69 | {
70 | "projectName": "RVX-Android_6-7",
71 | "sources":
72 | {
73 | "cli":
74 | {
75 | "org": "kitadai31",
76 | "repo": "rvcli"
77 | },
78 | "patches":
79 | {
80 | "org": "kitadai31",
81 | "repo": "revanced-patches-android6-7"
82 | },
83 | "integrations":
84 | {
85 | "org": "kitadai31",
86 | "repo": "revanced-integrations"
87 | }
88 | }
89 | },
90 | "d4n3436":
91 | {
92 | "projectName": "RVX-Android-5",
93 | "sources":
94 | {
95 | "cli":
96 | {
97 | "org": "kitadai31",
98 | "repo": "rvcli"
99 | },
100 | "patches":
101 | {
102 | "org": "d4n3436",
103 | "repo": "revanced-patches-android5"
104 | },
105 | "integrations":
106 | {
107 | "org": "d4n3436",
108 | "repo": "revanced-integrations"
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------