Compare commits

..

668 Commits

Author SHA1 Message Date
dfb309ca45 oops 2025-05-11 19:29:41 +02:00
bc9e8c16f5 fix: launch callback in correct scope 2025-05-11 19:27:54 +02:00
e9b2dd6104 Merge branch 'compose-dev' into compose/fix-process-runtime 2025-05-11 18:58:40 +02:00
3f5d04a513 feat: Order bundles by number of patches 2025-05-11 13:10:11 +02:00
3ad027007f style: Apply formatting 2025-05-10 23:07:46 +02:00
813ec4dbe2 build: Sign releases using keystore if available 2025-05-10 20:00:01 +02:00
9fac5126df feat: Use "Debug" and "Debug signed" for build names respectively 2025-05-10 20:00:00 +02:00
5e03cf2307 feat: Move safeguards above patcher preference group 2025-05-10 20:00:00 +02:00
a3af817358 i cant access the class, which makes me very sad 2025-05-08 18:38:25 +02:00
4d522664b6 fix: Reset cached theme on theme change to avoid broken colors (#2527) 2025-05-08 15:31:34 +02:00
8fdd5074a0 feat(Compose): Move developer options to top level (#2528) 2025-05-07 18:37:30 +02:00
5555373deb build: Remove repos that are not required 2025-05-05 19:37:10 +02:00
6e57a6e977 fix: Ignore long click when already in delete mode
closes #2503
2025-04-30 13:34:33 +03:00
10001b492b feat: add network checks for features that require it 2025-04-28 18:10:01 +02:00
d37ed050bc feat: move plugin api to another repository 2025-04-28 17:19:07 +02:00
ee07621e37 fix: Do not poll battery optimization status (#2491) 2025-04-23 19:26:04 +02:00
fc05f95837 feat: Improve update screen design (#2487) 2025-04-23 20:09:05 +03:00
d5c63ead26 fix: Use compatible rather than support when referring to patch compatibility (#2422) 2025-02-13 00:40:47 +07:00
1956982060 feat: Improve APK file name formatting on save (#2421) 2025-02-13 00:40:36 +07:00
e10e5e4e3f build(deps): bump the gradle-compose group with 16 updates (#2407) 2025-02-01 10:45:14 +07:00
ede1ab5ed4 feat: Reorder Import & Export settings (#2403) 2025-02-01 04:05:28 +03:00
1092188ab0 feat: TopAppBar scroll behavior (#2397) 2025-01-31 15:03:50 +03:00
f348eba115 ci: Generate release artifact provenance (#2324)
Signed-off-by: validcube <pun.butrach@gmail.com>
2025-01-29 20:43:16 +01:00
3d234820a3 fix: improve keystore import error handling and show toast 2025-01-29 19:58:38 +01:00
cd06d36f68 build: Enable pseudo locale for debug variant 2025-01-29 23:22:24 +07:00
242c4570ce chore: Update project's dependencies to latest 2025-01-29 23:17:34 +07:00
71b73a3b42 fix: show install button when installation has been cancelled 2025-01-29 15:17:41 +01:00
067020f38f feat: Screen slide transition (#2396) 2025-01-27 19:28:55 +07:00
2aef67872d fix: Offset badge 2025-01-27 03:56:15 +03:00
818dc09aa4 build: Bump AGP to 8.8.0
build: Bump AGP to 8.8.0
2025-01-19 17:47:56 +07:00
a762969966 docs: Merge documentation from Flutter to Compose 2025-01-19 17:08:07 +07:00
74338931b8 feat: Redesign the patches screen (#2381) 2025-01-18 17:03:38 +01:00
0ab424bfdb fix: available updates dialog list item color 2025-01-05 00:12:00 +01:00
fff1a41fee refactor: use EventEffect for legacy import 2025-01-05 00:08:29 +01:00
7644a74648 feat: add required options screen (#2378) 2025-01-03 22:26:40 +01:00
9db3bd5b3f feat: Add confirm dialogs when toggling dangerous settings (#2072)
Co-authored-by: Ax333l <main@axelen.xyz>
2024-12-23 16:35:27 +01:00
b81bd17fbc chore: add .kotlin to gitignore 2024-12-23 14:40:28 +01:00
cf3866f892 fix: remove battery optimization notification if user grants the permission 2024-12-23 14:39:57 +01:00
5d3a81f4b9 feat: switch to androidx.navigation (#2362) 2024-12-23 14:31:31 +01:00
f9831d4da5 refactor: remove unnecessary function 2024-12-23 13:13:08 +01:00
8a20d8cf9b fix: contributors screen repository name 2024-12-22 22:32:15 +01:00
49f75f9edd fix: process death resilience and account for android 11 bug (#2355) 2024-12-22 22:28:54 +01:00
9916e4da4d fix: Screen turns off while patching due to wrong WakeLock (#2147) 2024-12-21 10:25:59 +01:00
2ec1c0238d feat: Add downloader plugin system (#2041) 2024-12-19 21:41:04 +01:00
9dc716b1c8 feat: switch to revanced api v4 2024-12-12 17:52:21 +01:00
31fb8b1404 chore: Nitpick on misspelling of comment 2024-12-01 01:13:03 +07:00
0685479d53 feat: Make patch bundles list scrollable (#2322) 2024-11-22 16:57:23 +03:00
20c13ee71c chore: update dependencies
🦀 integrations are gone! 🦀
2024-11-13 22:11:36 +01:00
cf322147d5 fix: only perform haptics on events 2024-11-12 21:17:02 +01:00
b4c37e6ddc feat: Add haptic feedback (#1457)
Co-authored-by: Ushie <ushiekane@gmail.com>
2024-11-06 23:48:40 +03:00
697386c36c fix: Match "Installation incompatible" dialog message with Flutter Manager (#2231) 2024-09-30 18:51:22 +02:00
f6f72387b9 feat(patcher): Improve installation (#2185) 2024-09-19 21:17:38 +02:00
d201bdc422 feat: Add installer status dialog (#1473)
Co-authored-by: Benjamin Halko <benjaminhalko@hotmail.com>
Co-authored-by: Benjamin <73490201+BenjaminHalko@users.noreply.github.com>
Co-authored-by: Ushie <ushiekane@gmail.com>
Co-authored-by: Ax333l <main@axelen.xyz>
2024-08-29 18:28:13 +02:00
2055400565 feat: View bundle patches (#2065) 2024-08-25 01:23:13 +03:00
10e7e4b39f feat: Open the app-specific manage all files permission dialog (#2148) 2024-08-18 17:54:07 +07:00
edb4e421e2 feat: Improve patch bundle screen (#2070) 2024-08-17 01:58:43 +03:00
747017a5f9 feat: Improve Settings order (#2060)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: Ax333l <main@axelen.xyz>
2024-08-15 14:59:06 +02:00
e992a99783 fix: remove the unique constraint for patch bundle names 2024-08-12 22:42:55 +02:00
e869db0555 fix: Move temporary files outside of the cache directory (#2122) 2024-08-12 18:23:31 +02:00
936a9efd0b refactor: Add parameters for custom rotation values in ArrowButton 2024-08-08 02:49:07 +03:00
edc11b6e1d ci: Actually enable caching of Gradle 2024-08-07 22:51:04 +07:00
32f2710763 fix: Turn off filters by default (#2079) 2024-08-06 03:46:50 +03:00
4257c32bf5 fix: ExtendedFloatingActionButton not accessible by screen readers (#2080) 2024-08-06 03:46:41 +03:00
c18901c35b feat: Improve unsupported patch warnings (#2066)
Closes #2052
2024-07-29 18:21:10 +03:00
f126fe9fa8 fix: show available and selected patches in patch selector screen 2024-07-29 15:00:24 +02:00
4cbd480e84 feat: Add reset button to custom API (#2076)
Closes #2051
2024-07-29 13:14:13 +03:00
afc72ffd85 feat: Show manager update dialog (#2069)
Closes #1963, closes #1958
2024-07-29 00:09:56 +03:00
cdc69ea8ff fix: Support patching on ARMv7 by updating AAPT2 (#2084) 2024-07-28 22:43:10 +02:00
52a89b1638 feat: Improve update setting tile titles
Closes #1968
2024-07-24 04:48:53 +03:00
641d518b6e build: Enable Gradle Configuration Cache (#2059) 2024-07-22 15:41:50 +07:00
2eca45d397 fix: always use default patch selection if customization is disabled 2024-07-20 16:17:41 +02:00
de6fddf405 fix: android icon not loading in app selector 2024-07-20 12:24:21 +02:00
45f32040f8 feat: Improve custom API URL dialog (#2033)
Signed-off-by: validcube <pun.butrach@gmail.com>
2024-07-18 04:22:37 +03:00
42ef2ca99d fix: Broken header padding in AlertDialogExtended when using an Icon 2024-07-18 00:56:18 +03:00
563f2f37f7 fix: Remove unnecessary screen padding
Closes #2062
2024-07-18 00:55:15 +03:00
881b13740a feat: Improve overall UI and fix inconsistencies (#2028) 2024-07-11 01:09:45 +03:00
d39804f7ed Merge branch 'compose-dev' into fix/minor-issues 2024-07-11 01:08:34 +03:00
b8a85c4891 refactor: Use lastIndex
Co-authored-by: Ax333l <main@axelen.xyz>
2024-07-11 01:07:57 +03:00
37e9630b9c fix: Inconsistent padding for battery optimisation warning
The problem came after moving the card to DashboardScreen, this is because the card specified padding modifier but others does not. This commit remove the modifier completely.
2024-07-07 17:58:51 +07:00
40492b67d1 refactor: Use TextButton instead of FilledButton for consistency 2024-07-07 17:54:04 +07:00
f655a6e03a ci: Bump dependencies to latest (#2039) 2024-07-07 15:42:56 +07:00
36f864efbb chore: update dependencies 2024-07-06 22:47:27 +02:00
a995f43b7b fix: move battery warning to dashboard 2024-07-06 18:12:44 +02:00
1ee1330e47 feat: improve the safeguards (#2038) 2024-07-06 15:43:37 +00:00
3f4a234915 fix: run props flow on correct dispatcher (#2035) 2024-07-05 18:41:29 +00:00
032ca39cf6 feat: Automatic language detection (#2032) 2024-07-05 18:34:13 +00:00
6f9a984541 fix: improve bundle page strings 2024-07-05 20:31:49 +02:00
e6e043f168 fix: cleanup advanced settings screen 2024-07-05 19:41:27 +02:00
7c7fb7b343 feat: dont ask for root on launch 2024-07-05 19:06:10 +02:00
b26fe30861 Merge branch 'compose-dev' of https://github.com/ReVanced/revanced-manager into fix/minor-issues 2024-07-04 23:34:22 +03:00
f99cdfe926 feat: improve UX for failed or missing bundles 2024-07-04 19:36:31 +02:00
ec0a077539 feat: implement more patch option types (#2015) 2024-07-04 17:34:55 +00:00
a22158d070 fix: crash when removing used bundles 2024-07-04 13:58:55 +02:00
48fe3a707e fix: import export screen UX 2024-07-03 14:46:00 +02:00
8d3d500b7b feat: add ability to share debug logs 2024-07-03 13:54:37 +02:00
d8248cc915 fix: import bundles on another thread 2024-07-03 12:27:39 +02:00
397a1f8f9c Merge branch 'compose-dev' of https://github.com/ReVanced/revanced-manager into fix/minor-issues 2024-07-03 03:55:12 +03:00
a0f187354f feat: Remove tag from changelog 2024-07-03 03:48:19 +03:00
1bf004ddee feat: Progressive AlertDialog for adding bundles
Closes #1992
2024-07-03 03:48:04 +03:00
495100dea9 fix: Use the correct icon in API URL dialog
Closes #1972
2024-07-03 02:49:16 +03:00
e3bd8a8b22 feat: Add sensitivity to isScrollingUp 2024-07-03 02:46:01 +03:00
d987ac6c7a feat: Add isScrollingUp support for ScrollState 2024-07-03 02:45:45 +03:00
9883dcd0a7 fix: Use FAB instead of ListItem to patch in App Overview
Closes #1995
2024-07-03 02:44:10 +03:00
d63133189d feat: Improve device information in debugging section
Closes #1977
2024-07-03 01:34:01 +03:00
39fbb87010 fix: Change the title in the Update screen from "Updates" to "Update"
Closes #1960
2024-07-03 01:28:33 +03:00
f197760efd feat: Change "Update" to "Show" in Update Available notification
Closes #1959
2024-07-03 01:27:13 +03:00
fa13d4a538 feat: Highlight links in Markdown
Closes #1962
2024-07-03 01:26:05 +03:00
71b5f539c1 feat: Improve initial update popup wording
Closes #1956
2024-07-03 01:25:25 +03:00
603a827e45 chore: Remove unused ARMv7 AAPT binary
Closes #1954
2024-07-03 01:24:55 +03:00
47bdc69a43 refactor: Improve naming consistency in libs.version.toml
Closes #1953
2024-07-03 01:24:10 +03:00
1ce56af3b1 feat: get bundle information from jar manifest (#2027) 2024-07-02 19:50:28 +00:00
a12c5c583b fix: add bounds checks in patch selector 2024-07-02 15:43:06 +02:00
5455cf20ab feat: rename main bundle to Default 2024-06-25 23:54:59 +02:00
75fcdb139c fix: use proper update icon 2024-06-25 23:49:28 +02:00
269fa79136 feat: improve patcher screen labels 2024-06-23 21:01:20 +02:00
16fea59605 fix: scrolling in patch selector 2024-06-23 20:17:42 +02:00
c7d183ee8d feat: rename debug build to ReVanced Manager (dev) 2024-06-23 17:27:40 +02:00
413083c58d fix(downloader): versions not loading correctly 2024-05-29 23:25:15 +02:00
1f671aba33 fix: automatically focus search views 2024-05-27 22:49:08 +02:00
6bfd9098d6 feat: move update to notification card (#1917) 2024-05-27 21:50:02 +02:00
4e7d96e91d feat: revert to blue theme colors 2024-05-22 20:05:27 +02:00
ac0a036035 refactor: fix more warnings 2024-04-05 20:04:27 +02:00
2a1445d61f build(deps): update ksp 2024-04-05 19:22:54 +02:00
0df39a1136 refactor: replace deprecated functions 2024-04-05 19:09:39 +02:00
634d793839 fix: crash caused by compose inlining bug
This is a bug in jetpack compose. Inlining this function wasn't very
important anyways so it's best to just stop inlining it to avoid the
crash.
2024-04-04 17:50:31 +02:00
afd6c5d6b7 build(deps): bump aboutLibrariesGradlePlugin from 11.1.0 to 11.1.1 (#1813)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 15:28:19 +00:00
ab0682cc5c build(deps): bump androidx.compose.ui:ui-tooling from 1.6.3 to 1.6.4 (#1814)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 15:27:59 +00:00
60ca901ac7 build(deps): bump androidx.compose:compose-bom from 2024.02.02 to 2024.03.00 (#1812)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 15:27:45 +00:00
91ca5be57a build(deps): bump libsu from 5.2.1 to 5.2.2 (#1810)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 15:27:24 +00:00
ac47a7eaa4 build(deps): bump plugin.serialization from 1.9.22 to 1.9.23 (#1811)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 15:26:17 +00:00
ce134224a8 fix: correctly patch apk files 2024-04-03 17:07:29 +02:00
ca49d3a465 feat: add external process runtime (#1799) 2024-03-29 15:00:52 +00:00
5d7f9d1387 feat: check if the version being used is the recommended version (#1675) 2024-03-15 17:57:53 +00:00
8d5d86fea8 feat: add social links (#1294)
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: Ax333l <main@axelen.xyz>
2024-03-12 17:09:39 -07:00
6709505e9e chore: Upgrade dependencies (#1761)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-10 11:05:47 -07:00
088de60c91 chore: upgrade dependencies (#1670) 2024-03-05 06:35:13 -08:00
ef041869e5 fix(VersionSelector): use correct LazyColumn item key 2024-03-05 14:34:45 +01:00
16cdc7aca4 refactor: Disable update for dev build (#1673)
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
2024-02-17 18:17:27 +00:00
39536c0e18 feat: Collapse ExtendedFAB on scroll (#1630) 2024-02-03 19:12:12 +00:00
607d8b67c9 feat: add toast feedback to the bundle update button 2024-01-21 14:34:18 +01:00
0b9889ea44 fix: patch options reset button being broken 2024-01-21 14:24:39 +01:00
4acef776b2 refactor: use consistent wording for the version compat check 2024-01-20 17:48:08 +01:00
e186dfdaa9 docs(security): init (#1612)
Co-authored-by: Ax333l <main@axelen.xyz>
2024-01-20 16:35:32 +00:00
c0f3d02e6f refactor: fix terminology and wording related to patches (#1623) 2024-01-18 19:50:24 +00:00
36c8f59d6f feat: Scrollbars (#1479) 2024-01-08 01:28:16 +03:00
f38b31a591 fix: progress bar not updating 2024-01-07 13:29:09 +01:00
3232bb10e6 feat: improve patcher UI (#1494) 2024-01-06 16:51:11 +01:00
b7cb6b94f5 feat: updater UI and code improvements (#1597) 2024-01-05 22:05:02 +00:00
aa6e612fba feat: Select bundle type before adding bundle (#1490) 2024-01-04 21:54:44 +00:00
d9d7b98409 feat: Purple default theme (#1601) 2023-12-27 16:35:23 -08:00
4fdd6bbe5f chore: upgrade AGP to 8.2.0 + migrate deprecated functions (#1574) 2023-12-26 20:37:42 +00:00
439f6250f3 chore(deps): update jetpack compose 2023-12-25 23:28:42 +01:00
d55abf5dda feat(app-selector): show patchable installed apps first (#1496) 2023-12-14 11:50:14 +01:00
a8b9d9316f docs: update revanced url 2023-12-02 17:18:21 +07:00
55c7800f39 build: bump Gradle to v8.5
build: update Gradle wrapper
2023-12-02 17:17:03 +07:00
d9eb1c42bc refactor: slight formatting of build.gradle.kts 2023-12-02 17:00:52 +07:00
8cd617e32d chore(template): update label name for feature 2023-12-02 16:58:47 +07:00
a17a05995a ci: caching with gradle-build-action
Allow for automatic capture of buildscan in job summary, and smarter
caching than the one provided by `setup-java`.
2023-12-02 16:55:04 +07:00
de4e616dcc chore(deps): bump revanced patcher and library 2023-12-01 13:21:20 +01:00
12b00e5c8d fix: specify multithreadingDexFileWriter in PatcherOptions (#1402)
Co-authored-by: Ax333l <main@axelen.xyz>
2023-11-29 21:33:00 +00:00
9cab91959e fix: load patch bundles earlier 2023-11-29 22:11:57 +01:00
bd9778a3d1 feat(Update Screen): changelogs & handle states (#1464)
Co-authored-by: Ax333l <main@axelen.xyz>
2023-11-19 23:28:28 +03:00
62a5fce66c feat(Contributors Screen): implement design from Figma (#1465)
Co-authored-by: Robert <72943079+CnC-Robert@users.noreply.github.com>
Co-authored-by: Ax333l <main@axelen.xyz>
2023-11-19 23:27:13 +03:00
2bd84636d6 fix: parcel error for nullable types 2023-11-15 21:32:54 +01:00
ac561e7aca feat: Use correct casing in module description 2023-11-07 23:59:33 +01:00
59daceef99 chore: bump patcher 2023-11-06 19:33:06 +01:00
1dc41badd9 feat: check for updates on startup (#1462) 2023-11-05 13:19:55 +01:00
1a83315424 feat(Changelogs): overall improvement (#1429) 2023-11-03 18:03:14 +00:00
3c5776214f feat(Installer): use BottomAppBar (#1428) 2023-11-03 09:15:17 +00:00
5fff0a2923 fix: option state crash (#1456)
Co-authored-by: Ax333l <main@axelen.xyz>
2023-11-02 15:46:53 +00:00
8df7f2992d refactor(ui-components): deduplicate colors and move to settings folder 2023-11-01 21:57:00 +01:00
7741394c9c feat(NotificationCard): rewrite & consistent usage (#1426) 2023-11-01 20:54:06 +03:00
25bd91debc feat(Settings): use SettingsListItem consistently and overall improvements (#1427) 2023-11-01 20:11:43 +03:00
7fe4724e10 feat: remember patch options (#1449) 2023-10-31 20:16:02 +00:00
123ae37524 chore: add issue template (#1432) 2023-10-28 12:54:33 +07:00
172604fcdb feat(installer): sign apk in patcher worker 2023-10-27 23:30:45 +02:00
7d887c73e8 fix: use correct checksum 2023-10-27 17:33:11 +02:00
6abaac25d9 chore: upgrade dependencies (#1401) 2023-10-26 07:25:12 -07:00
cc897840e2 fix: perform selected app operations in the correct order 2023-10-26 09:06:42 +02:00
757840b76f feat(bundles tab): add BackHandler 2023-10-26 09:03:26 +02:00
50e8d1f8f4 docs: clarify license 2023-10-21 16:10:10 +02:00
65f8d38c59 feat: show toast when no patches are selected 2023-10-20 23:16:00 +02:00
e70c10adbd feat: add checkboxes to the downloaded apps page 2023-10-20 23:02:35 +02:00
64ec73d821 fix: more android 34 fixes 2023-10-20 22:59:16 +02:00
32e8a37f33 fix: handle exceptions when checking for bundle updates 2023-10-20 19:43:26 +02:00
5290713504 feat(patch-selector): remove TODO about an unplanned feature 2023-10-20 19:24:17 +02:00
18cfb56b45 fix: bundles not loading on Android 14 2023-10-20 18:49:44 +02:00
4b12ae1531 fix: jvm signature clash error 2023-10-20 17:21:59 +02:00
9df98edca5 fix: use upsert when modifying installed apps 2023-10-19 22:01:32 +02:00
c3af6acb2c feat: selected app info page (#1395) 2023-10-19 19:44:50 +00:00
7ba00cafd9 refactor: move mount code to when block 2023-10-17 09:21:06 +02:00
5aefb3bc59 fix: hide patch button (#1284) 2023-10-16 12:48:51 -07:00
212e55ffd8 feat: add user agent (#1382) 2023-10-16 17:39:17 +00:00
bf54d38c91 chore: bump patcher 2023-10-15 14:03:53 +02:00
cee2240cdc chore: bump compose 2023-10-15 13:17:07 +02:00
4c1ad868a9 fix: broken logo in about page on release builds 2023-10-15 00:22:12 +02:00
f5b3b29d6d feat: hide unfinished pages in release mode 2023-10-14 18:48:07 +02:00
8f5449527d feat: armv7 warning 2023-10-14 18:24:40 +02:00
8f6d720454 refactor(downloaders): improve file system code (#1379) 2023-10-14 15:42:10 +00:00
56a4a7043d feat: settings migration (compose) (#1309) 2023-10-13 10:39:10 -07:00
5762859906 feat: add patches selector bottom sheet (#1360) 2023-10-13 16:11:40 +00:00
608bac6854 feat: use revanced api for changelogs 2023-10-07 17:07:19 +02:00
723f9cd98c fix: delete temporary files (#1341) 2023-10-07 14:09:02 +00:00
abf4d91703 fix: use correct classes to determine option type
I can't believe this happened
2023-10-06 16:41:00 +02:00
d8392ad3eb feat(settings): move experimental patches option to advanced 2023-10-05 22:16:58 +02:00
39caad18a5 feat(installer): adjust arrow icon size 2023-10-05 22:06:20 +02:00
6437f7bb65 feat(installer): adjust step icon size and alignment 2023-10-05 21:51:48 +02:00
e232044157 chore: switch to revanced library and bump patcher (#1314) 2023-10-05 15:36:33 +00:00
f78b56ef0a feat(patch-selector): default patches selection (#1272) 2023-10-01 18:56:16 +00:00
ca3c9af3b8 feat: remove dead help icons
These never did anything and were removed from the figma a while ago.
2023-10-01 19:02:11 +02:00
b8b2e74151 chore: fully remove idea project files 2023-10-01 18:12:08 +02:00
1f8341ac42 fix: remove misc.xml and kotlinc.xml 2023-09-27 10:20:18 -07:00
0964f15475 docs: init (#1224) 2023-09-26 16:54:02 -07:00
63fd7957c6 ci: Add release workflow (#1235) 2023-09-25 19:02:18 +02:00
65377ffd9e fix: Updates popup shows incorrect names (#1283) 2023-09-23 19:42:27 -07:00
f79320c013 fix: use ReVanced ring logo in about section (#1302) 2023-09-22 14:19:48 -07:00
cf71ea26ec feat: implement Submit Issue button (#1276) 2023-09-21 19:18:42 +00:00
ee96c37c20 refactor: update progress onBackClick function (#1277) 2023-09-21 19:16:39 +00:00
a86923aee1 fix: disable WebView history (#1278) 2023-09-21 19:13:57 +00:00
e0f8d06152 fix(ui): make entire patches view button selectable (#1271) 2023-09-21 19:13:25 +00:00
4cb4ce298a feat: change appID and name of debug builds 2023-09-20 20:15:55 -07:00
36de61a57f ci: build pull requests (#1228) 2023-09-20 20:14:26 -07:00
6f2ca5bb89 fix: typo in string name import_keystore_description (#1273) 2023-09-18 16:59:03 +07:00
940885768d fix: contributors screen fix (#1256) 2023-09-15 16:33:14 +00:00
fc577b4c3e chore: update dependencies (#1247) 2023-09-15 16:30:46 +00:00
bf10af2ae2 feat: root installation (#1243) 2023-09-09 13:18:00 +00:00
b4dfcf1bb4 fix: minify crash on building release (#1245) 2023-09-06 17:49:11 +00:00
0b0ba21852 fix: providers.gradleProperty (#1223) 2023-09-06 17:48:48 +00:00
42e0346e25 feat: make bundles selectable (#1237) 2023-09-04 07:05:55 +02:00
212db84d0b ci(config): appreciation for first-time contributors
Show appreciation message for new contributors
2023-09-03 21:47:05 +07:00
e6eb8accf2 docs: update readme badges (#1227) 2023-09-03 09:40:54 +07:00
8bd73c3afa ci(release): don't build when not necessary
Add paths-ignore to all markdown files, and .idea folder
2023-09-02 21:59:23 +07:00
5369a25fa2 docs: update badge's repository
The repository was moved from `revanced-manager-compose` to the main one, which is `revanced-manager`.

The organisation's name has also switched to `ReVanced` (used to be `revanced`).
2023-09-02 21:51:48 +07:00
eeae46a415 chore: bump kotlinx.serialization plugin and patcher 2023-09-01 10:50:13 +02:00
c0badbe96b build: updates (#85) 2023-08-26 14:03:44 +00:00
2bb51c136a fix(deps): use correct work-runtime version string 2023-08-26 15:52:40 +02:00
3cfa4ea6d6 feat: more info for the select from application screen (#81) 2023-08-23 20:05:21 +02:00
f01adf5eb0 ci(release): migrate from node12 to node16
This bump `actions/upload-artifact`@v2 to `actions/upload-artifact`@v3
2023-08-19 16:00:01 +07:00
a0b92554e9 feat: store patched apps (#79)
* feat: store patched apps

* fix: missing string

* feat: save patch selection

* feat: things

* fix: fix broken query

* fix: remove redundant `withContext`

* fix: fix
2023-08-17 17:42:10 +02:00
ac4c7e06e7 ci(release): use correct vars context object
why am i so stupid
2023-08-17 19:30:31 +07:00
0f9a6f4340 ci(release): no longer store keystore alias in secrets
fixes an issue where GitHub Actions logs would be censored
2023-08-17 19:13:18 +07:00
9586a9c0dd fix: patches not being reloaded 2023-08-14 18:29:56 +02:00
f6563b265b fix: permission error when using installed app 2023-08-12 14:52:34 +02:00
7aea9473de feat: patch options UI (#80) 2023-08-12 08:41:22 +00:00
3f059d7748 feat: switch to the new api (#75) 2023-08-07 09:03:50 +00:00
7e3c31c4b2 chore: bump patcher 2023-08-04 12:55:14 +02:00
1707a9690a feat: improve bundle dialog UI 2023-08-04 12:46:07 +02:00
379ce917a9 feat: finish implementing the sources system (#70) 2023-08-03 11:15:42 +00:00
299aaa2b68 fix: library info not being embedded 2023-08-01 21:14:15 +02:00
5cf5e87fa8 ci(release): task naming consistency 2023-08-01 15:30:56 +07:00
55f22562eb fix: don't store app list in parcel 2023-07-31 13:24:49 +02:00
272d911464 fix(installer): progress tracking 2023-07-31 12:16:13 +02:00
6beb34baa8 ci: init 2023-07-30 20:23:51 +00:00
61de0b67fa feat: show installed app in version selector 2023-07-30 19:45:40 +02:00
aec8cec9b8 feat: download apps in patcher screen (#73) 2023-07-30 10:29:22 +00:00
Pun
83b9573b52 docs(readme): minor fix to displaying url
When you hover on Commit & Activity badges, `revanced` will appear in url display, but on License badge, `ReVanced` will display

This commit fix that by changing the organisation to what we're supposed to be using which is {org_name}/{repo_name} (ReVanced/...)
2023-07-24 16:24:21 +07:00
21d99a1f24 feat: add patch bundle info screen (#55) 2023-07-23 15:27:07 +00:00
1331479072 fix: serialization not working 2023-07-17 16:48:29 +02:00
b472a36a9a fix: buildfile syntax (#66)
Signed-off-by: Patryk Miś <foss@patrykmis.com>
2023-07-17 14:41:17 +00:00
3238fcdae7 build: updates (#63)
* Update Java base to 17
* update Kotlin to 1.8.22
* update Bouncycastle
* update all dependencies
* follow the manifest on jni libs packaging
* enhance app optimization by specifying resource configurations, excluding dependencies info and unnessesary files
* Remove obsolete SDK check as we are already using minSdk 26

Signed-off-by: Patryk Miś <foss@patrykmis.com>
2023-07-17 12:27:19 +00:00
cd2587b1fd feat: improve accessibility (#64)
* Label Back button
* Mark group section headings as headings

Signed-off-by: Patryk Miś <foss@patrykmis.com>
2023-07-17 12:20:54 +00:00
879884a9fa feat: switch to Preferences DataStore (#60) 2023-07-15 09:52:12 +00:00
5d3b963682 feat: disable filter chips when there are no patches 2023-07-14 21:37:50 +02:00
955e7a4f1c feat: ReVanced theme colors 2023-07-14 21:35:17 +02:00
d2dcd4209d fix: release builds not working properly 2023-07-14 13:11:34 +02:00
6299ff5b48 chore: migrate dependencies to version catalogs (#58) 2023-07-14 10:33:42 +00:00
94a4dbaba1 feat: app downloader (#43) 2023-07-14 08:54:42 +00:00
c36deea045 build: update gradle to v8.2.1 2023-07-12 20:36:23 +07:00
Pun
7030d43aa5 docs(readme): minor changes to how badges works
* Better description for the repository license badge

* Clicking on badges open you the relevant url
2023-07-08 20:43:10 +07:00
aa02e9f8cf feat: improve keystore UI and UX (#52) 2023-07-07 18:48:36 +00:00
37e177b56e revert: downgrade Kotlin to 1.8.21
"A what? 1.8.22 isn't compatible, but the version bump indicate that it's supposed to be bug fixes????"
2023-07-07 23:16:07 +07:00
453f4da8ec feat: advanced settings page with device info (#51) 2023-07-07 15:35:36 +00:00
400163b820 build: update dependencies
There are 9 dependencies update, changelog of this commit are available
below here.

Android Gradle Plugin: 8.0.1 -> 8.0.2
Kotlin: 1.8.21 -> 1.8.22
Android Compose BOM: 2023.05.01 -> 2023.06.01
Room: 2.5.1 -> 2.5.2
ReVanced Patcher: 11.0.1 -> 11.0.4
APKsig: 8.2.0-alpha05 -> 8.2.0-alpha10
Koin (Android, workmanager): 3.4.0 -> 3.4.2
Koin (Androidx Compose): 3.4.4 -> 3.4.5
Ktor: 2.3.0 -> 2.3.1
2023-07-07 20:27:53 +07:00
4ae9904c8a fix(installer): sign and install on threads
This is needed to avoid ANRs because it takes a while if the Apk is 100+
MB.
2023-07-07 12:31:31 +02:00
fe5e191cb5 feat: updater changelogs (#48)
---------

Co-authored-by: Aunali321 <aunvakil.aa@gmail.com>
2023-07-07 08:56:04 +00:00
d9d83df9de feat: allow user to save logs 2023-07-06 20:01:44 +02:00
8dd8f88d2b feat: save patch options and selected patches in bundle (#50) 2023-07-04 09:09:16 +00:00
01fd4c8ffa feat: patch options (#45) 2023-07-03 09:12:34 +00:00
7ac3bb74e0 refactor: use getDir instead of filesDir directly 2023-07-03 10:09:01 +02:00
3b65cd0edc fix: use correct directory 2023-07-01 16:02:34 +02:00
a9606728bf build: bump patcher 2023-07-01 15:09:21 +02:00
4d4f1a242c build: update gradle to v8.2 2023-07-01 12:40:01 +07:00
6b7143dd8f feat: licenses screen (#47) 2023-06-29 20:05:43 +00:00
7e4ee00cb2 chore: update links in about page 2023-06-29 18:10:40 +02:00
4868c45b43 feat: animate the arrow button 2023-06-29 17:26:49 +02:00
81f485da6b refactor: use correct coroutine scopes 2023-06-29 10:35:23 +02:00
18cbe51e6b fix(installer): save step incorrectly being marked as completed 2023-06-29 10:11:44 +02:00
149c8cc8b2 fix: sources screen being misaligned during transitions 2023-06-29 10:03:15 +02:00
0dccb8c27b feat: contributors screen (#42)
* Contributors page
- https://github.com/revanced/revanced-manager-compose/issues/34

* feat: adding ContributorScreen as clickable icons like the website

* feat: adding ContributorScreen
- Made changes that were asked for in prev PR
- Currently just waiting on a git merge to get ArrowButton in

* feat: adding ContributorScreen
- Made changes that were asked for in prev PR
- ArrowButton is also in use

* feat: adding ContributorScreen
- Made changes that were asked for in prev PR
- ArrowButton is also in use
- Fixed other PR comment changes

* Apply suggestions from code review

* Remove unused string resources

---------

Co-authored-by: Ax333l <main@axelen.xyz>
2023-06-28 19:51:12 +00:00
4302ea8832 fix: pass worker inputs without serialization (#44)
Because androidx.work.Data sucks and causes our app to crash.
2023-06-27 14:39:30 +00:00
1eac42dab8 fix(installer): make the correct column scrollable 2023-06-27 15:12:55 +02:00
9dd74f1f22 feat: experimental patches setting 2023-06-27 15:05:31 +02:00
923ce74735 feat: save patch selection using room db (#38) 2023-06-22 10:20:30 +00:00
2d9f9adfee refactor: better PatchBundle docs and naming 2023-06-17 13:59:37 +02:00
9a55e51a3a build: bump patcher 2023-06-17 13:47:55 +02:00
5681c917c5 feat: show stacktrace in installer ui (#36) 2023-06-17 11:45:52 +00:00
6309e8bdf5 feat: filter options for patches 2023-06-15 22:20:17 +02:00
535efa3d73 fix: run blocking IO operations in the correct context 2023-06-11 17:52:43 +02:00
b8a51d32f5 fix(patcher): add notification and wakelock to worker; chore: add app icon 2023-06-11 17:49:42 +02:00
919b6b7014 feat: keystore import/export (#30) 2023-06-11 14:38:56 +00:00
971277ed39 fix(installer): properly track worker state (#32) 2023-06-09 15:34:10 +00:00
7ce4de7a8b feat(koin): use the android logger 2023-06-06 12:27:42 +02:00
9591f4e14f feat: ProGuard 2023-06-04 19:37:23 +02:00
27426b1390 feat: rename package to app.revanced.manager 2023-06-04 18:27:40 +02:00
fcb75dd780 feat: improved compose stability 2023-06-04 17:50:40 +02:00
1be9c9c1bd fix: use correct getViewModel 2023-06-04 17:42:21 +02:00
e088d053ab feat: rename ViewModels for consistency 2023-06-03 23:27:01 +02:00
ffa8d9c063 feat: hide tabs when 1 bundle is used 2023-06-03 20:12:03 +02:00
7a5596a281 refactor: PackageManager (#31)
* refactor: refactor `PM`

* feat: use plurals for patch count

* fix: support apk's from storage

* feat: use ViewModel for loading apps and bundles

* fix: fix file selector that has no reason to be broken

* refactor: rename parameter

* refactor: `MainViewModel`

* feat: make all apps use `path`

* build: target java 11
2023-06-03 18:03:14 +00:00
9f46f74357 refactor(logs): use consistent tag 2023-05-26 15:39:56 +02:00
36c4e2dfe0 refactor(di): use constructor DSL for VMs
Instead of doing it manually with viewModel { }
2023-05-26 15:25:08 +02:00
5cb31dbe9d chore(deps): bump revanced-patcher to 9.0.0 2023-05-26 15:17:57 +02:00
399fc98dec feat: better installer ui (#29)
based cossale

Co-authored-by: Aunali321 <aunvakil.aa@gmail.com>
2023-05-26 13:14:21 +00:00
c22371e0c5 feat: patch bundle sources system (#24) 2023-05-26 12:58:14 +00:00
a4842c078b feat: in-app updater (#25) 2023-05-23 11:02:22 +02:00
c332760786 feat(settings screen): add battery optimization notification 2023-05-22 05:17:26 +05:30
ea4247c688 feat(update screen): complete main update screen 2023-05-22 04:14:43 +05:30
fec8c0cc14 feat(about screen): complete about screen 2023-05-22 03:23:14 +05:30
9b585c73fb feat(settings screen): match typography from figma 2023-05-22 02:34:07 +05:30
c695fa525f refactor(settings screen): clean code up a bit 2023-05-22 02:29:19 +05:30
93f3e27d48 fix: dont crash when the bundle cannot be downloaded 2023-05-20 17:14:05 +02:00
52ab7937bd feat(installer): apk signing and installation 2023-05-20 12:30:24 +02:00
762bfa8514 fix(patches selector): copy the selected patches list 2023-05-20 09:47:42 +02:00
ca20996b62 refactor(ui): move PatchItem to the only file where it is used 2023-05-19 21:21:37 +02:00
ad14818de8 refactor(net apis): remove unnecessary interfaces
Having interfaces like that is only really useful if you have unit
tests, which we don't.

Other similar compose projects don't make interfaces either.
Not having them is more readable.
2023-05-19 21:01:33 +02:00
32839656f8 style: run formatter 2023-05-19 20:58:44 +02:00
a48faad17a build: updates (#23) 2023-05-19 18:54:21 +00:00
40487923f9 feat: integrate revanced patcher (#22) 2023-05-19 18:49:32 +00:00
f1656c6d1e feat: improved dashboard screen 2023-05-18 13:46:59 +02:00
4c3dbbd8d5 feat: patches selector screen 2023-05-18 13:44:19 +02:00
4088ed747e feat: settings screen 2023-05-18 13:38:02 +02:00
bca8df8efd build: updates (#21)
* perf: obsolete sdk check

Signed-off-by: Patryk Mis <foss@patrykmis.com>

* chore: bump dependencies

Signed-off-by: Patryk Mis <foss@patrykmis.com>

---------

Signed-off-by: Patryk Mis <foss@patrykmis.com>
2023-05-13 12:00:06 +00:00
54f0a69596 feat: app selector screen 2023-05-06 12:42:30 +02:00
9065c0d260 feat: Dashboard Screen (#18)
* feat: add Dashboard Screen and Sources Screen

* fix: fix tab onClick not working

* refactor: remove AppBar

---------

Co-authored-by: CnC-Robert <CnC.Rob3rt@gmail.com>
2023-04-30 19:27:14 +00:00
cb0150a0f9 fix: gradlew permissions on unix 2023-04-23 14:45:07 +02:00
ec0f7e3f7a build: dependency and syntax updates (#17)
* build: Update Gradle to v8.1.1

* build: Bump dependencies

* build: move repo configurations to settings

---------

Co-authored-by: Patryk Mis <24607131+PatrickMis@users.noreply.github.com>
2023-04-23 12:36:52 +00:00
e5d898f025 feat: backend 2023-03-18 11:53:25 +01:00
52bdb1cd6a Create README.md 2023-01-31 19:14:18 -03:00
49f9dfcf95 feat: splash screen 2023-01-28 02:30:39 +03:00
9536cdcae1 feat: implement navigation 2023-01-28 02:28:39 +03:00
57e2632f38 feat: implement DI 2023-01-28 02:19:44 +03:00
b372f7ee84 feat: initialize project 2023-01-28 02:00:52 +03:00
70e8253b63 Migrate to compose branch
This commit was made to allow cherry-picking the first commit of the followup commits according to https://github.com/ReVanced/revanced-manager-compose/issues/65#issue-1806335545
2023-08-26 18:07:57 +02:00
d9953b1473 fix(installer): use correct elevation level 2023-08-24 19:00:50 +07:00
c6ac898390 fix: universal patches not selectable 2023-08-17 18:25:23 +05:45
43f98cec43 build: make variant more identifiable
Release variant will use the "ReVanced Manager" app name, Debug will use "ReVanced Manager Debug"
2023-08-15 21:31:34 +07:00
63175cdec6 ci(build): create debug variant instead of release 2023-08-15 20:55:11 +07:00
6436a1ec61 build: debug apk come with application suffix
Instead of using the same one as Release, app.revanced.manager.flutter - We add .debug to it, yay, co-installation of two variants!
2023-08-15 20:37:43 +07:00
c400619338 feat: disable changing patches selection by default (#1132)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-08-15 16:05:27 +07:00
dcd5ba41cf build: add mavenlocal to repositories 2023-08-14 03:40:05 +03:00
7525e52fab fix(installer): use correct bg colour for dialog 2023-08-12 15:07:04 +07:00
72fd24e624 fix: use i18n translation for installer page 2023-08-12 15:06:17 +07:00
11d8f9fd30 refactor: apply changes according to Dart
trailing commas
2023-08-12 14:53:54 +07:00
4b0c8cecc8 ci(build): update event trigger
This add ".github/workflows/pr-build.yml" to build on every change to the CI Build to check if there's no error when it got modified.

And also remove "fastlane/**" because that's not related to the ReVanced Manager's code
2023-08-12 13:56:18 +07:00
372ce174c9 fix: overlapping issue in application selection page (#1128) 2023-08-11 19:19:44 +03:00
381daff980 fix: exclude x86 aapt2 binary from release builds (#1126) 2023-08-11 19:19:33 +03:00
94acebbebd docs(fixed): replaced some emoji icons, not rendering on chromium based browsers on windows (#1123) 2023-08-11 10:17:46 +07:00
f90f6e81ee feat: patch apps without internet (#1114) 2023-08-11 08:11:19 +07:00
97e37f304b refactor: migrate MediaQuery properties to InheritedModel (#1115)
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-08-10 15:25:07 +03:00
f5e45ead26 ci(build): don't build on every pull request unrelated to application 2023-08-10 19:14:13 +07:00
14f765f4b4 ci(build): don't cache Gradle
Gradle take up so much space in cache that it wouldn't make sense for us to cache it since we are rapidly hitting the cache limit every time.
2023-08-10 19:12:15 +07:00
b77d46b2c2 feat(installer): redesign utility options (#1062)
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-08-10 19:02:30 +07:00
1442916b20 build: bump version to 1.8.0 2023-08-10 03:01:03 +03:00
c6a5f42d23 chore: merge dev to main (#1120) 2023-08-10 03:00:27 +03:00
580d50eb8d fix: use right label for auto patch 2023-08-10 00:05:47 +03:00
9a66b6e50d fix(root-install): create service and postfs directories if they dont exist (#1116)
Fixes #860
2023-08-09 23:57:09 +03:00
acec064cb7 feat(patcher-view): show notice for removed patches that were used previously (#1107) 2023-08-09 22:32:13 +03:00
ea05d13a1f fix(patches-selector): New tag showing for old patches (#1113) 2023-08-09 22:31:37 +03:00
fd741f2ccf build: bump version to v1.7.0 2023-08-08 03:52:03 +03:00
055c52178f chore: merge dev branch to main branch (#1096) 2023-08-08 03:51:05 +03:00
722a5859a5 fix: pair integrations and patches updates (#1102) 2023-08-08 03:30:48 +03:00
1714c3fa86 feat(patches-selector): show New tag for new patches (#1099) 2023-08-07 22:28:09 +07:00
131df28110 docs(managing): update to reflect current changes
The chip was removed because it was not implemented for too long.
2023-08-07 20:13:23 +07:00
1be284f8d8 feat: switch to the new ReVanced API (#1101) 2023-08-07 17:14:16 +07:00
218e53ae75 refactor(analysis): migrate deprecated rule
deprecated since Dart 2.0, yike
2023-08-06 14:41:15 +07:00
264d8d90c4 refactor: apply changes according to analysis
trailing commas, and sort import alphabetically
2023-08-06 14:39:46 +07:00
0b529c2629 fix(patches-selector): separate all universal patches to the bottom (#1092) 2023-08-06 12:12:01 +07:00
5abcc7191f fix: experimental/universal patches being used even when turned off (#1090)
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
2023-08-06 12:10:57 +07:00
5346f6e1bf fix(patch-item): hide universal patches if not enabled (#1087) 2023-08-05 20:19:36 +07:00
2f471b3de4 fix: use user friendly strings (#1088) 2023-08-05 20:19:03 +07:00
3cf06efddf build: bump version to v1.6.1 2023-08-04 03:33:19 +03:00
1f4f0a3bb7 build: revert patcher to v11.0.4 2023-08-04 03:32:50 +03:00
c07f8eae9b build: bump version to v1.6.0 2023-08-04 00:54:13 +03:00
374eb3d06d feat: abort patching process at any time (#1072) 2023-08-04 00:40:08 +03:00
fe75b75ddc feat: dpi responsive layout (#361) 2023-08-04 00:38:38 +03:00
d3790bf64b refactor: remove unimplemented features (#1061)
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-08-03 23:58:50 +03:00
f905a52988 feat: allow control over patches update (#1063)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-08-03 23:23:56 +03:00
e55f427b05 build: bump patcher version to v12.1.1 2023-08-04 01:53:19 +05:45
0f000fc4d1 build: bump version to v1.5.2 2023-08-04 01:52:24 +05:45
a0339e3c19 chore: merge dev to main (#1077) 2023-08-03 05:24:25 +03:00
9cfa274d81 build: bump version to v1.5.1 2023-08-03 05:23:47 +03:00
8c79f5e371 build: bump patcher to v12.1.0 2023-08-03 05:23:16 +03:00
0d716513d7 fix: keystore password dialog showing up before importing (#1068) 2023-08-03 08:36:39 +07:00
08f6724060 chore: merge dev branch to main branch (#1060) 2023-08-03 02:20:24 +03:00
29daf51e64 build: bump version to 2023-08-03 02:19:14 +03:00
b3b2b01c0f ci(build): use pr event trigger for now
I just heard of cache eviction policy :)
2023-08-01 20:20:27 +07:00
c3212d0308 ci(build): update event trigger
Run on every PR

Run on every Push, ignore main branch, and ignore tag
2023-08-01 19:32:52 +07:00
aaa114ba13 ci(pr-build): reinstate functionality
PR Build was never use because it have to be manually trigger
2023-08-01 19:26:26 +07:00
8ca6418630 refactor(settings): improve clarity of the warning dialogs (#1058) 2023-07-31 21:53:23 +07:00
95632b7f55 chore(deps): update revanced-patcher to v12.0.0 2023-07-30 11:49:01 +07:00
64744b2abf fix: original.apk not found despite existing (#1052) 2023-07-30 10:35:34 +07:00
096b315701 fix(patch-item): remove redundant patch version completely (#1059)
https://github.com/ReVanced/revanced-patches/pull/2709
2023-07-29 16:21:30 +07:00
c27ca08d3a docs: leftovers from 6495687841524cd82ab65990c9c44d53d377db6e 2023-07-29 16:08:54 +07:00
0011222371 chore:fix incorrect version 2023-07-23 14:25:11 +05:30
29db947e25 fix: using non const value in a const variable 2023-07-23 14:04:35 +05:30
fd43ac7581 chore: merge dev branch to main branch (#1032) 2023-07-23 13:53:25 +05:30
e49c19b3cd feat: support older version of android for reddit client patches 2023-07-23 13:51:36 +05:30
Pun
06f0e59967 chore(deps): update libsu to v5.2.0 2023-07-22 13:21:13 +07:00
Pun
c5fc5ee93b build: speed up compilation time
faster build faster build faster build faster build faster build
2023-07-19 18:30:50 +07:00
159c85bd1f fix: close previous dialog when user reset the API URL (#1025) 2023-07-16 21:04:35 +07:00
Pun
2460acf0f4 ci(analyze): don't run when PR is in draft 2023-07-16 17:10:29 +07:00
Pun
6495687841 docs: correct misspelling (EN_US)
This correct spelling to English American, because by default, we use English US.
2023-07-16 16:31:08 +07:00
Pun
d229ccb36c ci(analyze): clarify job name that the job do static analysis & format checking 2023-07-16 16:21:16 +07:00
Pun
4d6a57ddcf ci(analyze): restore run when commit push to dev branch 2023-07-16 16:17:15 +07:00
d161d55aaf fix: patched applications not showing at launch (#1031) 2023-07-16 12:46:01 +07:00
Pun
768ad0c9bc chore: merge dev branch to main branch (#1011)
ReVanced Manager v1.4.1
2023-07-15 20:33:30 +07:00
29323d4e20 build: update app version to v1.4.1 2023-07-15 12:55:59 +07:00
630b22e193 refactor: applies changes from formatter & linter 2023-07-15 12:41:03 +07:00
79116f9e67 fix(patched-applications): non-patched app showing on Installed section (#1022) 2023-07-15 08:11:51 +07:00
eb58475259 fix: showing Installed when it's actually not (#1021) 2023-07-15 08:10:36 +07:00
a879ac30fb fix: prevent unsupported operation exception (#1018) 2023-07-14 17:55:04 +07:00
c5b0621323 fix(navigation-view): back button closing the app from any page (#1019) 2023-07-13 19:25:48 +05:45
0462815014 fix(app-selector): fix text overflow on small screen (#1017) 2023-07-13 00:16:53 +05:45
e64318c947 fix(app-selector): fix text overflow on small screen 2023-07-13 00:14:47 +05:45
b784482788 chore: update libsu to 5.1.0
This should fixes some issues with root
2023-07-11 19:28:59 +07:00
2834e8b348 fix: patchable apps not showing if none of them is installed (#1009) 2023-07-11 17:11:45 +07:00
b23dfd4289 chore: merge dev branch to main branch (#1010) 2023-07-11 01:24:54 +05:30
217d525cb2 build: update app version to v1.4.0 2023-07-11 01:22:57 +05:30
85b166cbda fix: update hardcoded patch name 2023-07-11 01:21:45 +05:30
9a57f8b858 ci(build): use correct JDK version
There's no reasons to go higher than JDK 11 since we compile the application using JDK 11
2023-07-10 21:42:24 +07:00
Pun
3bfdc932c2 chore: update app description
👍👍 Patch your favourite apps, right on your device.
2023-07-10 21:27:20 +07:00
48c878af21 chore: merge dev branch to main branch (#996) 2023-07-10 16:56:50 +03:00
490a7a58fc build: update app version to v1.3.9
ReVanced Manager, 1.3.9 (10030009)

See PR #996
2023-07-10 20:40:40 +07:00
567b1a3ace refactor: improve code readability according to linter 2023-07-10 19:45:50 +07:00
6a45db8a38 refactor: improve code readability according to formatter 2023-07-10 19:36:50 +07:00
Pun
b272988929 build: always compile with the latest SDK supported by Flutter
`targetSdkVersion` is still the same, we want to test the application first before bumping up to latest.
2023-07-10 18:43:07 +07:00
9828857570 fix: fixed typo 2023-07-10 12:16:51 +05:45
ecb54d8e44 docs: improve clarity of speech in issue template (#1005)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-07-09 23:15:20 +02:00
344717b021 fix: app crash after custom source (#1003) 2023-07-08 23:07:19 +05:45
da6cf585c0 fix: app crash after custom source 2023-07-08 22:26:31 +05:45
9e93177afd feat: show warning dialog when resetting stored patches
This prevent user from accidentally resetting stored patches by showing
them warning dialog.
2023-07-08 11:01:10 +07:00
16318efb01 refactor: seperate translation string by context 2023-07-08 10:45:13 +07:00
e83e13b6d4 refactor: fix more problem with analyser
Missing trailing comma in app_selector_view
2023-07-07 22:21:22 +07:00
7c2c695d88 refactor: apply changes from analyser 2023-07-07 22:15:12 +07:00
c7b227529d chore: disable unnecessary tooltip
Remove tooltip in Navigation View, there's no impact to accessibility.

Tested using: Android TalkBack
2023-07-07 22:11:13 +07:00
14f49e9d30 docs: fix description for next page 2023-07-05 15:43:40 +02:00
1875c4ee73 fix: update button being clickable when offline (#987) 2023-07-05 19:59:53 +07:00
76c68baa1f build: update gradle to v7.6.2 2023-07-01 18:21:49 +07:00
68876a4414 build: bump version to v1.3.8 and patcher dep 2023-07-01 14:33:25 +05:30
26d7e5b60f build: bump version to v1.3.7 and patcher dep 2023-07-01 02:46:43 +03:00
e78af6ae99 chore: merge dev to main (#976) 2023-07-01 02:41:57 +03:00
6fe05cd86e feat: show all the unseen changelogs on changelog section (#970) 2023-07-01 02:41:03 +03:00
4100d7a391 fix: different message when trying to patch spilt apk (#973) 2023-07-01 02:40:30 +03:00
c1cc812ea4 build: bump version to v1.3.6 2023-06-24 19:17:59 +03:00
9663e3f0f4 chore: merge dev branch to main branch (#945) 2023-06-24 19:16:22 +03:00
941f618153 fix(app-selector): remove direct use of strings (#944) 2023-06-23 16:39:12 +03:00
716a30bf7b fix: experimental patches stay selected when toggled off (#946) 2023-06-23 16:37:23 +03:00
d051ae576b feat(updater): download successful dialog (#938) 2023-06-23 16:36:36 +03:00
35e99cb014 feat(mount): use /data/adb/revanced again and ensure migration scenario (#948) 2023-06-23 01:03:03 +03:00
af054fba49 docs: improve clarity of footnotes [skip ci] (#950) 2023-06-17 14:48:57 +02:00
58d837d641 feat(app-selector): Improve app item UI to avoid overflow issues (#943) 2023-06-16 10:04:55 +03:00
6cc1bd21cd fix(updater): ability to start again after cancelling (#937) 2023-06-16 10:04:07 +03:00
cebfa7c8ae docs: simplify steps 2023-06-16 05:09:52 +02:00
e2d7ab8f8f chore: fix broken docs link 2023-06-15 16:42:09 +02:00
ec77987fcd feat(app-selector): improve skeleton UI (#939) 2023-06-15 12:57:39 +03:00
b161608d02 feat(patches-selector): improve disabled card UI (#941) 2023-06-15 12:54:49 +03:00
5e7458ff1c fix: import patches selects unsupported patches (#942) 2023-06-14 21:34:11 +07:00
Pun
ed06aaa1f5 docs(readme): fix invalid url for building manager from source
📜 fix invalid URL for building manager from source.

Old: https://github.com/revanced/revanced-manager/blob/docs/docs/5_building-from-source.md
New: https://github.com/revanced/revanced-manager/blob/main/docs/4_building.md
2023-06-14 19:52:10 +07:00
13b7179941 fix: fix not asking for permission 2023-06-12 09:56:49 +05:30
da0d88d86f build: bump patcher 2023-06-12 09:08:29 +05:30
b7347c312a build: bump version to v1.3.4 2023-06-12 08:59:49 +05:30
c876f2f7e3 feat: add permission to manage storage 2023-06-12 08:59:14 +05:30
adad5fd8ff build: bump version to v1.3.3 2023-06-12 07:02:49 +05:30
2aaa7ae8c9 chore: merge dev to main (#935) 2023-06-12 07:01:12 +05:30
1f64ea37bd fix: using wrong string 2023-06-12 06:59:43 +05:30
cac1525da0 build: bump patcher 2023-06-12 06:02:16 +05:30
1ad906fedc refactor: improve code 2023-06-12 06:01:57 +05:30
20ffef39a3 fix: update button being shown as clickable on launch (#932) 2023-06-12 05:55:53 +05:30
185460c054 chore(fastlane): update description (#933)
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-06-12 03:21:33 +03:00
0079e74d77 feat: clarify suggested version in app list (#934)
Co-authored-by: Aunali321 <48486084+Aunali321@users.noreply.github.com>
2023-06-12 05:35:02 +05:30
a8e019482f ci: add workflow to update documentation repository 2023-06-10 23:41:36 +02:00
f01b8e47aa fix: aborting message changed (#928)
Change aborting message from "aborting..." to "aborted..."

----------------------
Co-authored-by: Ushie <github@ushie.dev>
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-06-09 17:34:55 +07:00
Pun
e43dfb7599 docs(troubleshooting): fix markdown styling (#929)
`docs`: `84a9168` - Self-explanatory, fix markdown styling issue
2023-06-09 17:24:31 +07:00
6b8cfe2b49 build: bump version to v1.3.2 2023-06-09 11:42:06 +03:00
7f7b14bae3 fix: use correct variable name for armv7 check 2023-06-09 11:40:54 +03:00
810b02d9fd build: bump version to v1.3.1 2023-06-09 10:45:22 +03:00
62813145b2 chore: merge dev to main (#926) 2023-06-09 01:59:18 +03:00
4877058253 chore(fastlane): add banner 2023-06-09 01:58:02 +03:00
2a9fd3abb8 chore(fastlane): add missing directory 2023-06-09 01:57:49 +03:00
a244f7b598 chore: merge dev to main 2023-06-08 21:31:41 +03:00
5396457ad5 chore: move fastlane to correct directory 2023-06-08 21:30:44 +03:00
0186b6ea61 chore: merge dev to main (#924) 2023-06-07 20:18:21 +03:00
fe
f6e99f7e88 fix: return fetched patch version if non default patch repo is used (#922) 2023-06-07 20:16:25 +03:00
Pun
c24a3828be ci(analyze): don't run when pushed to repository (#920) 2023-06-07 20:15:43 +03:00
Pun
2e38a4567a feat: clarify architecture in about section
This commit changes `Arch` to `Supported Arch(s)`
2023-06-07 20:13:42 +03:00
67c5d67a61 feat: update icons for the new logo
fixed #918
2023-06-07 20:10:14 +03:00
9592dde534 feat: add fastlane for F-Droid (#889)
Co-authored-by: Ushie <ushiekane@gmail.com>
2023-06-07 19:59:17 +03:00
d030b0af70 docs: init (#911)
Co-authored-by: afn <_@afn.lol>
2023-06-07 12:01:36 +02:00
4ccb9ac94d fix: remove redundant "v" in the downloader screen (#895) 2023-05-26 13:54:52 +03:00
8af62b917c build: bump version to 1.3.0 (#892) 2023-05-24 23:33:04 +03:00
311f114132 chore: update v1.3.0 2023-05-24 21:46:00 +02:00
d015bd03f7 chore: merge dev to main (#891) 2023-05-24 22:17:16 +03:00
a61b9de0fa chore(deps): bump revanced-patcher to 9.0.0 2023-05-24 02:02:59 +02:00
ef1b283917 chore(deps): bump revanced-patcher to 8.0.0 (#880) 2023-05-24 01:07:22 +05:30
c677f00105 build: bump version to v1.2.0 2023-05-20 02:30:18 +03:00
8d2f778dfe chore: merge dev to main (#881) 2023-05-20 02:28:58 +03:00
c549d102f6 build: migrate to dart 3 (#871) 2023-05-20 04:07:53 +05:30
39a9ee4e9d chore: remove coreLibraryDesugaring (#873) 2023-05-20 04:05:46 +05:30
a27dc6ad1c feat: migrate to dart3 2023-05-19 14:23:48 +05:45
8ccb75fc8d chore: remove coreLibraryDesugaring 2023-05-17 15:38:03 +09:00
8fc86dbe02 feat: allow selecting installed if app is full apk 2023-05-13 04:54:35 +03:00
359f052608 fix: export keystore not working in some conditions (#862) 2023-05-12 19:36:47 +03:00
4150e2265c fix: use correct version in update download dialog (#859) 2023-05-10 13:23:02 +03:00
b803ce7435 fix: system navigation overlapping UI (#853) 2023-05-10 13:18:51 +03:00
289c6cd7a9 chore: merge dev to main (#857) 2023-05-08 21:46:33 +05:30
31fc7b74c2 build: bump version to v1.1.0 2023-05-08 21:38:19 +05:30
3e565f25be fix(appselector): closing dialog closes app selector 2023-05-08 21:27:07 +05:30
e509be4e21 chore(deps): bump patcher to 7.1.1 2023-05-08 21:19:45 +05:30
170fc537ac fix: fix armv7 dialog shown for x86, x86_64 2023-05-07 04:14:57 +05:30
3fe5882145 feat: remove cronet 2023-05-06 05:39:46 +05:30
a290791410 chore: merge dev to main (#844) 2023-05-06 03:26:14 +05:30
Pun
2ebd38ff68 feat: add checksums verification to Gradle (#813) 2023-05-03 21:38:47 +03:00
cd987a5b19 fix: broken filename when saving files with the same name (#837) 2023-05-03 21:34:30 +03:00
7f1dab7ee1 ci: temporarily restore old release workflow 2023-05-02 17:50:07 +03:00
bed3945aa5 feat: clarify warning and consequences for ARMv7 users (#836) 2023-05-02 16:41:11 +07:00
15a32a18b7 fix: fix broken manager update implmentation 2023-04-30 17:38:08 +07:00
d2e8e7dd5d chore: comply with review changes 2023-04-30 01:03:36 +07:00
ce12ec89c4 ci(release): automatically bump version codes 2023-04-29 20:53:54 +07:00
b286444ad9 fix(i18n): update translation for refreshSucess 2023-04-29 20:43:02 +07:00
941263102d refactor: change name from "recommendedVersion" to "suggestedVersion" 2023-04-29 20:43:02 +07:00
e2ed296dc7 chore(deps): constrain get_it to 7.2.0 2023-04-29 19:46:07 +07:00
cfc866bef2 feat: change continue anyways to cancel 2023-04-29 15:37:30 +03:00
affba669ce feat: add continue anyway button to select from storage dialog (#810) 2023-04-29 15:36:25 +03:00
7230152ab8 chore: fix issues with gitignore [skip ci] 2023-04-29 17:42:19 +07:00
21fee7171f chore: update pubspec version
skip-checks: true
2023-04-29 15:40:25 +07:00
ad17995f28 feat: improve suggested app version text (#822) 2023-04-28 17:41:28 +03:00
ac830cbe7f fix: resized monochrome icon to match the original (#789)
cherry-pick from 6281ae82876c69ff4194a782e4b08b398a1285d6
2023-04-27 18:12:48 +07:00
65da6af3f9 fix: update pubspec version 2023-04-21 14:56:31 +03:00
3d90bf7588 ci: bypass push permission on protected branches with owner PAT 2023-04-21 14:55:29 +03:00
62ef1c88fe feat: resetting source to default dismiss the sources pop-up (#797)
* feat: resetting source to default dismiss the sources pop-up

* chore: format using `dart format`
2023-04-21 00:29:37 +05:30
d6918920b6 chore: add missing translations 2023-04-20 23:53:24 +05:30
cdfb09fbfa feat: warning for armv7 devices 2023-04-20 23:20:30 +05:30
bb681e31c9 feat: disable selecting installed apps for nonroot 2023-04-20 22:25:29 +05:30
c7483936ec docs: remove alpha disclaimer 2023-04-20 05:07:46 +05:30
0a1f2da33d feat: appreciation message for new contributors 2023-04-20 05:04:37 +05:30
f5aafdb7d6 feat: progress bar for manager updates 2023-04-20 04:45:46 +05:30
c9adf1c492 style: sort imports 2023-04-19 01:32:43 +05:30
4c9cb560e3 feat: auto select default patches 2023-04-19 01:21:08 +05:30
f0b028279c fix: open contributor links externally (#791) 2023-04-18 19:46:16 +05:30
197770b68b chore: update dependencies (#772)
* chore: updated some dependencies

* refactor: reimplemented cache interceptor

* Revert "Updated dependencies & migrated breaking changes"

This reverts commit e6743b0d6b2552fdbf1c99d23e158e682362dd5d.

* chore: migrated flutter_local_notifications

* revert: reimplemented cache interceptor
2023-04-18 19:45:29 +05:30
37b583f560 feat: trim extra space when setting custom source (#771) 2023-04-18 17:15:04 +07:00
dca2d4fe12 feat: add option to import/export keystore (#776)
* feat: add option to import/export keystore

* change the order of import/export keystore buttons

* feat: add option to change the keystore password
2023-04-18 15:08:10 +05:30
3b677f8ae3 feat: improve ux (#752)
* feat: restart app toast when changing sources, api url

* fix: potentially fix manager stuck on black screen

* feat: remove select all patches chip

* feat: show all apps and recommended versions

* chore(i18n): remove unused strings

remove unused strings left out in 7e05bca

* feat: select install type before patching

* feat: update patches button (#782)

* feat: update patches button

* feat: show toast when force refresh

* chore: don't translate "ReVanced Manager" and "ReVanced Patches"

* Revert "feat: select install type before patching"

This reverts commit 74e0c09b543fe70b70cd917e6e21ceb0f46f3533.

* feat: rename recommended patches to default patches

* feat: add missing localization

* feat: display restart app toast for resetting source

---------

Co-authored-by: EvadeMaster <93124920+EvadeMaster@users.noreply.github.com>
2023-04-18 13:27:26 +05:30
0b952578d1 ci(release): update semantic-release 2023-04-02 02:37:49 +07:00
054afbbedd feat: confirmation dialog for deleting keystore (#764)
* feat: confirmation dialog for deleting keystore

* refactor(i18n): apply suggestion from code-reviewer

Co-authored-by: Ushie <github@ushie.dev>

* refactor: apply suggestion from code-reviewer

Co-authored-by: Mipirakas <borismichiels@gmail.com>

---------

Co-authored-by: Ushie <github@ushie.dev>
Co-authored-by: Mipirakas <borismichiels@gmail.com>
2023-04-01 21:02:28 +07:00
866a6e4a44 chore(CHANGELOG): reset changelog 2023-03-30 23:29:26 +07:00
8ea7dd478b ci(release): update node dependencies 2023-03-30 23:20:31 +07:00
7839252934 ci(analyze): only run when necessary (#766)
* ci(analyser): only run on lib/`.dart` changes

* ci(analyser): runs on workflow changes

* ci(analyze): run on all dart file changes

---------

Co-authored-by: Palm <palmpasuthorn@gmail.com>
2023-03-29 20:51:08 +03:00
fa4063220f ci: dart analyser (#761)
* ci: dart analyser

* ci(analyser): clarify the dart analysing step

* refactor: ignore generated files

* ci(analyser): apply suggestion from code-reviewer

Co-Authored-By: Palm <palmpasuthorn@gmail.com>

* ci(analyser): apply suggestion from code-reviewer

Co-Authored-By: Palm <palmpasuthorn@gmail.com>

* ci(analyser): apply suggestion from code-reviewer

Co-authored-by: Palm <palmpasuthorn@gmail.com>

---------

Co-authored-by: Palm <palmpasuthorn@gmail.com>
2023-03-28 15:47:34 +07:00
d214a02abd ci(release): fix argument parsing of npm exec 2023-03-26 23:02:57 +07:00
d1c12edd1b ci(release): use appropriate npm commands for ci environment 2023-03-26 23:02:57 +07:00
ded1a44c37 ci(release): fix step not moving artifact to accessible path 2023-03-26 23:02:57 +07:00
790a6cd1e3 ci(release): remove unnecessary variables in flutter build step 2023-03-26 18:57:27 +03:00
a79f883a0f ci: use caching to speed up workflows (#760)
Use caching to speed up workflows: 9-14 minutes (ish) to 6-8 minutes (ish)

Commits:
* ci(release): use GitHub cache

* ci(release): restore signing
2023-03-26 20:02:56 +07:00
d9c5a540a3 ci(release): fix wrong artifact name and ci not uploading build 2023-03-24 01:01:44 +07:00
276f33b9ec ci: use semantic-release (#746) 2023-03-23 17:34:52 +01:00
ded59d2da0 feat(ci): update crowdin workflow to use new main branch 2023-03-20 22:39:29 +03:00
62f7a820d8 feat: remove notice about stale development [skip ci] 2023-03-20 18:03:58 +03:00
7063ffa013 build: bump version to v0.0.57 2023-03-14 21:00:48 +03:00
bf4dc3c095 chore: bump stacked
stacked: ^3.0.0 -> ^3.2.0
stacked_generator: ^0.8.0 -> ^1.0.0
stacked_services: ^0.9.3 -> ^1.0.0
2023-03-14 21:00:28 +03:00
c10e5848bf Merge pull request #707 from revanced/dev
chore: merge branch `dev` to `flutter`
2023-03-14 22:07:35 +07:00
92a3b0d6e0 feat(style): use native switch & chip (#732)
* chore: remove useless themedata

* feat(style): new switch

* feat(style): use native chip components

* chore: remove unused import

* feat(accessibility): set tooltip

* chore: remove unneeded themedata

* chore: fix theme

* feat(i18n): add 3 new strings

* feat(style): correct material 3 theme on nondynamic
2023-03-14 21:53:42 +07:00
b475bd25c8 build: remove env setup step 2023-03-11 23:01:26 +05:30
d318224a6f fix: black screen after resetting custom sources 2023-03-06 18:08:48 +05:30
0074fee865 build: new ci (#731)
New Build CI

Commits:
* buid: re-do ci

* build: ignore tags

* build: get the latest flutter version automatically && formatting

---------

Co-authored-by: EvadeMaster <93124920+EvadeMaster@users.noreply.github.com>
2023-03-05 16:23:51 +07:00
5617535a63 refactor: remove sentry and crowdin (#730)
We no longer use sentry and crowdin.
2023-03-05 16:12:46 +07:00
68ccefc59f revert: "refactor: update deprecated and minor code refactors (#710)"
This reverts commit 6829d3cdeac94fb305b8885570840fe25769b6a6.

Signed-off-by: EvadeMaster <93124920+EvadeMaster@users.noreply.github.com>
2023-03-03 18:06:24 +07:00
6d60541626 chore(deps): meet patcher breaking changes 2023-03-03 03:11:44 +03:00
a635e5b8d0 chore: addFiles -> addIntegrations (#725)
fix #721
2023-03-02 14:51:35 +07:00
48a10440fe ci(build): remove environment on PR build 2023-03-02 11:06:15 +07:00
8e3ba88318 chore: bump gradle 7.5-rc.1 -> 7.6.1 (#717)
* chore: upgrade gradle from 7.5-rc.1 -> 7.6.1

* chore: remove unrelated changes
2023-02-27 19:26:10 +07:00
ab8fccc544 chore(deps): bump patcher version from 6.4.3 -> 7.0.0 2023-02-27 19:19:19 +07:00
8319dc9164 fix: improperly sized monochrome icon (#715) 2023-02-27 19:07:44 +07:00
6829d3cdea refactor: update deprecated and minor code refactors (#710)
Improve code readability & additional refactoring

Commits:
chore: exclude generated from analyzer

refactor: add SharedPreferences to locator

refactor: access shared pref from locator, and code refactor

refactor: remove unwanted `await`

refactor: remove `const` from `CacheConfig`
2023-02-27 19:06:05 +07:00
3ae4d69110 chore: migrate deprecation code && code cleanup (#708)
Fixes all issues in `flutter analyze`.
<Reviewed>

Commits:
* chore: migrate deprecated text style

* chore: migrate `toggleableActiveColor` to individual theme

* chore: don't use 'BuildContext's across async gaps
2023-02-20 16:53:53 +07:00
dc665f227e fix: use high resolution adaptive icons (#675)
Use high-resolution icons instead of the low-resolution ones on ReVanced Manager.

Commits:
* High resolution adaptive icons, built using revanced-logo-no-background.svg

Same icons currently used for ReVanced YouTube.

* tweak background color to match revanced-logo-no-background.svg

* recreated foreground using 'revanced-logo-shape.svg'

* updated full resolution icons, using same ratio as original SVG

* updated icons with gradient border
2023-02-17 19:28:01 +07:00
a83496568f chore: code cleanup (#681)
Improve the readability of the code.

Commits:
* chore: correct typos `fragement` to `fragment`

* chore: put a single newline at end of file.
2023-02-17 18:53:23 +07:00
12d25570af fix: Long patch description truncated (#702)
fix: long patch description truncated (#702)

Allow flexible height
2023-02-14 18:42:24 +07:00
378c947654 chore: bump version to v0.0.56 2023-02-12 13:04:34 +07:00
bd39a3140e feat: fix patch bundle version no longer displayed (#686) 2023-02-12 00:02:30 +07:00
7d3ca3dec1 fix: display patches version on first load (#687) 2023-02-12 00:00:01 +07:00
1cb556c8f8 chore: bump version to 0.0.55 2023-02-11 23:59:53 +07:00
8c8f96de1c build: bump patcher version 2023-02-11 23:59:47 +07:00
318cd87a9a feat(style): use the correct m3 theming (partially) (#680) 2023-02-07 15:46:29 +03:00
5d63d5c2d3 feat: potentially fix apps disappearing when update is available (#674) 2023-01-31 15:42:00 +03:00
7d347fccc6 build: bump version to v0.0.54 2023-01-30 15:57:06 +03:00
a54ca799b9 feat: update rules of analysis_options.yaml. and solved all problems (#665)
* update rules of analysis_options.yaml. and solved all problems

* refactor: fix remaining problems

---------

Co-authored-by: Ushie <ushiekane@gmail.com>
2023-01-30 15:35:06 +03:00
f5bc1a996f feat: remove select all icon from searchbar (#669) 2023-01-30 15:13:25 +03:00
8591bc4d01 chore: update cr_file_saver (#668)
Co-authored-by: Ushie <github@ushie.dev>
2023-01-30 15:11:31 +03:00
40888c07f3 feat: fix overflow text & separate version from patch name (#666)
* chore: Add note

* feat: Fix overflow text & seperate version from patch title

* feat: Fix overflow text & separate version from patch name
2023-01-30 15:07:25 +03:00
1c965c3788 refactor: cleanup remember patches feature (#630) 2023-01-30 15:03:55 +03:00
4df690c2a2 feat: monochrome icon + predictive back gesture (#591)
* Add monochrome icon

* Enable predictive back gesture

* feat: monochrome icon + predictive back gesture
2023-01-30 01:23:56 +03:00
d6abb61e2b ci(build): update workflow actions (#662)
- Bump upload-artifact to v3

- Update crowdin/github-action according to new versioning format [https://github.com/crowdin/github-action/releases/tag/v1.6.0]
2023-01-29 23:56:13 +03:00
3434c862e9 feat: improve note [skip ci] 2023-01-28 07:49:57 +01:00
ea8af926fa chore: update broken documentation link (#659)
* chorus: fix invalid documentation link

* chorus: Update broken documentation link
2023-01-22 16:18:41 +03:00
c3df48174c feat: warn user for selecting all patches (#649) 2023-01-21 16:37:28 +01:00
f1e60f96c4 chore: bump version to 0.0.53 2023-01-18 22:07:00 +01:00
cdd852678b build: bump patcher version 2023-01-18 22:07:00 +01:00
bf518b5467 bump: kotlin gradle plugin 2023-01-18 22:07:00 +01:00
ffd53fab26 feat: clarify acknowledgement label (#608) 2023-01-14 20:23:49 +03:00
5aad7dad35 chore: fix incorrect wording 2023-01-12 21:03:11 +01:00
b1c1a9f4e1 feat: stale development notice [skip ci] 2023-01-07 14:24:43 +01:00
d847a8e0b2 build: bump version to v0.0.52 2022-12-19 01:39:48 +01:00
9668730b5d build: bump patcher version (#610) 2022-12-19 01:35:58 +01:00
dc049cf26a New Crowdin translations by Github Action (#601)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-16 22:41:26 +03:00
82c2b2f128 build: bump version to v0.0.51 2022-12-15 21:24:06 +03:00
5f81d65911 feat: sort by amount of patches, display patches count, setting to enable universal patches (#593)
* feat: sort by amount of patches

* feat: display patches count in application card

* feat: setting to enable universal patches
2022-12-15 23:35:45 +05:30
19f990c564 build: bump version to v0.0.50 2022-12-14 14:17:38 +03:00
62467007b2 Revert "feat: display app's patch count in appcard"
This reverts commit 8d4e4ba6c9c30fdade2fcbf79aaaba34c68458ac.
2022-12-14 13:20:17 +03:00
d7624e5e1f Revert "feat: filter apps by patch count"
This reverts commit d78868b4625747b8c30e278d5a6a8fbb49bdc19d.
2022-12-14 13:19:59 +03:00
4505f10e50 build: bump version number 2022-12-14 06:04:42 +01:00
3ce3df5e19 build: bump patcher version 2022-12-14 06:04:10 +01:00
8d4e4ba6c9 feat: display app's patch count in appcard 2022-12-14 02:12:57 +03:00
d78868b462 feat: filter apps by patch count 2022-12-14 01:52:06 +03:00
01a681ad00 build: bump version to v0.0.48 2022-12-12 22:34:39 +03:00
adfeb61eab fix: print patch fail cause if message is null 2022-12-12 22:32:11 +03:00
8c3faac343 build: bump version to v0.0.47 2022-12-12 17:09:32 +03:00
c81acce31c feat: improve patch fail logging 2022-12-12 17:09:32 +03:00
fe629ce77c Merge branch 'flutter' of https://github.com/revanced/revanced-manager into flutter 2022-12-11 18:04:06 +05:30
5c27add2b2 bump:bump version to v0.0.46 2022-12-11 18:04:00 +05:30
ff90dae695 feat: add support for shared patches (#577)
* fix: avoid npe if a patch has empty compatible package.

* feat: support for shared patches

* fix: incorrect bool check and cleanup

Co-authored-by: Aunali321 <aunvakil.aa@gmail.com>
2022-12-11 18:00:44 +05:30
4f8aec6a05 build: bump version to v0.0.45 2022-12-09 17:47:51 +05:30
ba8df57580 Update broken link (#569) 2022-12-09 17:42:09 +05:30
3bab1940c1 Support Gitea repositories (#570) 2022-12-09 17:40:43 +05:30
e300c92215 build: bump version to v0.0.44 2022-12-06 03:15:23 +01:00
99764e25ed build: bump patcher dependency version 2022-12-06 03:14:35 +01:00
b5dcae11a4 Merge branch 'flutter' of https://github.com/revanced/revanced-manager into flutter 2022-11-29 21:16:54 +05:30
f69f475ab9 build: bump version to v0.0.43 2022-11-29 21:16:42 +05:30
061e929705 feat: Changelog in update dialog (#551)
* add update confirmation dialog

* fix loader placement
2022-11-29 21:15:44 +05:30
9e8193a6ac fix: selected patches not being remembered. 2022-11-29 21:12:46 +05:30
1601796c6c build: bump patcher dependency. 2022-11-29 21:03:12 +05:30
a0af0dde0a fix: disable changing languages for now. 2022-11-29 17:45:42 +05:30
c5ba6a238a ci(crowdin): upload sources only 2022-11-29 14:29:30 +03:00
3c3ebe7d9d build: bump version to v0.0.42 2022-11-26 22:14:56 +03:00
b7227bfad7 fix(build): use correct step id 2022-11-26 22:14:42 +03:00
447c16cff1 fix(build): migrate env name to new workflow 2022-11-26 21:17:57 +03:00
47529ae3f5 build: workflows for PRs & all commits 2022-11-26 21:16:29 +03:00
149af96d51 New Crowdin translations by Github Action (#541)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-11-26 16:43:08 +03:00
14e9dd1c04 fix: update workflow actions (#482)
* fix: update workflow actions

* fix: sign apk parameters
2022-11-26 16:34:14 +03:00
ec9ef98981 feat(apiUrl): fix textbox hint (#521) 2022-11-26 16:33:18 +03:00
922f474b59 fix: apply correct patch if patch names are same (#535) 2022-11-24 20:14:04 +03:00
795a1c78df build: bump version to v0.0.41 2022-11-24 00:36:03 +03:00
6ea055d486 feat: use secrets for keys (#528) 2022-11-24 00:27:00 +03:00
b0c4567cb8 feat: use github to get latest patches version if custom sources are used. 2022-11-23 10:03:56 +05:30
6e05120aa5 feat: add chips for patches selection. 2022-11-23 09:48:10 +05:30
c5958f1257 docs: add translation field 2022-11-15 14:20:23 +03:00
0faf86c9e9 refactor: restructure codebase. (#501)
* refactor: separate language selection to own widget.

* feat: separate theme changer to own widget.

* refactor: move Appearance UI to separate class.

* refactor: move language selection UI to separate class.

* refactor: move sources selection to separate file.

* refactor: move sources selection to separate file.

* refactor: split settings sections in separate files.

* refactor: move logging section to separate file.

* fix: show toast on bottom.

* fix: recommended patches not being selected by default.

* fix: patch selection selecting non recommended patches.

* fix: experimental toggle not updating.
2022-11-12 21:25:33 +05:30
ee689922a3 feat: do not update on tracing logs 2022-11-11 16:15:23 +01:00
be77a181ec feat: EXPERIMENTAL language switching. 2022-11-10 23:03:07 +05:30
30376c960f build: bump version to v0.0.40 2022-11-10 12:14:50 +05:30
405147b1c5 fix(i18n): locale capitalization and grammar (#496)
* fix(i18n): locale capitalization and grammar

* fix(i18n): add some more changes requested
2022-11-09 19:07:28 +05:30
d545dfe49b refactor: move exporting patches to another section 2022-11-09 14:20:32 +05:30
c571cf2c53 feat: ability to store and load selected patches (#469)
* feat: ability to store and load selected patches

* fix: I18n

* fix: do not append but truncate file

* fix: use json file, minor fixes

* fix: better ui

* WIP

* feat: load patches selection after app selection

* feat: import/export json file

* fix: reformat code

* fix: rare bug on import feature fixed

* fix: move export/ipmort to settings page & import full json

* fix: minor improvements

* fix: minor code quality improvements

* fix: export filename fix

* fix: select list element istead of removing it
2022-11-09 13:06:04 +05:30
fd5d71e24d fix: delete cached apk files when picking new one (#481) 2022-11-09 13:05:26 +05:30
2c3809d2bc feat: show selected patches count in patches selector view & patch selector card (#466)
* feat: show selected patches count in patches selector view & patch selector card

* fix: add Widget on row

* fix: formatting

* fix: same text style for count
2022-11-09 13:02:39 +05:30
0fc8e7cbc8 feat: crowdin workflow 2022-11-05 01:31:56 +03:00
787e47f634 Update Crowdin configuration file 2022-11-04 23:14:55 +03:00
dc47da75f2 feat: Adding support for Exporting APK (#439)
* feat: Adding Export APK support

* Using cr_file_saver to simplify export
2022-11-02 17:22:40 +05:30
348 changed files with 19684 additions and 8459 deletions

3
.env
View File

@ -1,3 +0,0 @@
sentryDSN=
apiKey=
appId=

View File

@ -1,120 +1,61 @@
name: 🐞 Bug report
description: Report a very clearly broken issue.
title: 'bug: <title>'
labels: [bug]
body:
- type: markdown
attributes:
value: |
# ReVanced Manager bug report
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-manager/labels/bug).
- type: dropdown
attributes:
label: Type
options:
- Error while running the manager
- Error at runtime
- Cosmetic
- Other
validations:
required: true
- type: textarea
attributes:
label: Bug description
description: How did you find the bug? Any additional details that might help?
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Add the steps to reproduce this bug including your environment.
placeholder: Step 1. Download some files. Step 2. ...
validations:
required: true
- type: textarea
attributes:
label: Android version
description: Android version used.
validations:
required: true
- type: textarea
attributes:
label: Manager version
description: Manager version used.
validations:
required: true
- type: textarea
attributes:
label: Target package name
description: App you tried to patch.
validations:
required: true
- type: textarea
attributes:
label: Target package version.
description: Version of the app you tried to patch.
validations:
required: true
- type: dropdown
attributes:
label: Installation type
options:
- Non-root
- Root
validations:
required: true
- type: textarea
attributes:
label: Patches selected.
description: Patches you selected for the app.
validations:
required: true
- type: textarea
attributes:
label: Device logs (exported using Manager settings).
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
validations:
required: true
- type: textarea
attributes:
label: Installer logs (exported using Installer menu option) [unneeded if issue is not during patching].
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
validations:
required: false
- type: textarea
attributes:
label: Screenshots or videos
description: Add screenshots or videos that show the bug here.
placeholder: Drag and drop the screenshots/videos into this box.
validations:
required: false
- type: textarea
attributes:
label: Solution
description: If applicable, add a possible solution.
validations:
required: false
- type: textarea
attributes:
label: Additional context
description: Add additional context here.
validations:
required: false
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Your issue will be closed if you haven't done these steps.
options:
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
required: true
- label: I have written a short but informative title.
required: true
- label: I filled out all of the requested information in this issue properly.
required: true
- label: The issue is related solely to the ReVanced Manager
required: true
name: 🐞 Bug report
description: Create a new bug report.
title: 'bug: <title>'
labels: [bug]
body:
- type: markdown
attributes:
value: |
# ReVanced Manager bug report
Please check for existing issues [here](https://github.com/revanced/revanced-manager/labels/bug) before creating a new one.
- type: textarea
attributes:
label: Bug description
description: |
- Describe your bug in detail
- Add steps to reproduce the bug if possible (Step 1. Download some files. Step 2. ...)
- Add images and videos if possible
- List selected patches if applicable
validations:
required: true
- type: textarea
attributes:
label: Version of ReVanced Manager and version & name of application you tried to patch
validations:
required: true
- type: dropdown
attributes:
label: Installation type
options:
- Non-root
- Root
validations:
required: false
- type: textarea
attributes:
label: Device logs
description: Export logs in ReVanced Manager settings.
render: shell
validations:
required: true
- type: textarea
attributes:
label: Patcher logs
description: Export logs in "Patcher" screen.
render: shell
validations:
required: false
- type: checkboxes
attributes:
label: Acknowledgements
description: Your issue will be closed if you don't follow the checklist below!
options:
- label: This request is not a duplicate of an existing issue.
required: true
- label: I have chosen an appropriate title.
required: true
- label: All requested information has been provided properly.
required: true
- label: The issue is solely related to the ReVanced Manager
required: true

View File

@ -1,52 +1,42 @@
name: ⭐ Feature request
description: Create a detailed feature request.
title: 'feat: <title>'
labels: [feature-request]
body:
- type: dropdown
attributes:
label: Type
options:
- Functionality
- Cosmetic
- Other
validations:
required: true
- type: textarea
attributes:
label: Issue
description: What is the current problem. Why does it require a feature request?
validations:
required: true
- type: textarea
attributes:
label: Feature
description: Describe your feature in detail. How does it solve the issue?
validations:
required: true
- type: textarea
attributes:
label: Motivation
description: Why should your feature should be considered?
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add additional context here.
validations:
required: false
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Your issue will be closed if you haven't done these steps.
options:
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
required: true
- label: I have written a short but informative title.
required: true
- label: I filled out all of the requested information in this issue properly.
required: true
- label: The issue is related solely to the ReVanced Manager
required: true
name: ⭐ Feature request
description: Create a new feature request.
title: 'feat: <title>'
labels: [feature request]
body:
- type: markdown
attributes:
value: |
# ReVanced Manager feature request
Please check for existing feature requests [here](https://github.com/revanced/revanced-manager/labels/bug) before creating a new one.
- type: textarea
attributes:
label: Feature description
description: Describe your feature in detail.
validations:
required: true
- type: textarea
attributes:
label: Motivation
description: Explain why the lack of it is a problem.
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: In case there is something else you want to add.
validations:
required: false
- type: checkboxes
attributes:
label: Acknowledgements
description: Your issue will be closed if you don't follow the checklist below!
options:
- label: This request is not a duplicate of an existing issue.
required: true
- label: I have chosen an appropriate title.
required: true
- label: All requested information has been provided properly.
required: true
- label: The issue is solely related to the ReVanced Manager
required: true

2
.github/config.yaml vendored Normal file
View File

@ -0,0 +1,2 @@
firstPRMergeComment: >
❤️ Thank you for contributing to ReVanced Manager. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.

View File

@ -1,39 +0,0 @@
name: "Debug Build"
on:
push:
branches:
- "flutter-disabled"
tags-ignore:
- "*"
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up JDK 12
uses: actions/setup-java@v1
with:
java-version: '12.x'
- uses: subosito/flutter-action@v1
with:
channel: 'stable'
- name: Set up Flutter
run: flutter pub get
- name: Generate files with Builder
run: flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Build with Flutter
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: flutter build apk --debug
- name: Add version to APK
run: mv build/app/outputs/flutter-apk/app-debug.apk revanced-manager-latest.apk
- name: Publish debug APK
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: true
title: "Development Build"
files: revanced-manager-latest.apk

44
.github/workflows/pr-build.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: Build pull request
on:
pull_request:
paths:
- ".github/workflows/pr-build.yml"
- "app/**"
- "gradle/**"
- "*.properties"
- ".kts"
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew assembleRelease --no-daemon -PnoProguard -PsignAsDebug
- name: Set env
run: echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Add hash to APK
run: mv app/build/outputs/apk/release/app-release.apk revanced-manager-${{ env.COMMIT_HASH }}.apk
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: revanced-manager
path: revanced-manager-${{ env.COMMIT_HASH }}.apk

View File

@ -1,4 +1,4 @@
name: "Release Build"
name: Release Build
on:
push:
@ -6,41 +6,49 @@ on:
- "v*"
jobs:
release:
build:
name: Build
runs-on: ubuntu-latest
permissions:
id-token: write
attestations: write
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up JDK 12
uses: actions/setup-java@v1
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '12.x'
- uses: subosito/flutter-action@v1
with:
channel: 'stable'
- name: Set up Flutter
run: flutter pub get
- name: Generate files with Builder
run: flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Build with Flutter
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }}
run: flutter build apk
run: ./gradlew assembleRelease --no-daemon
- name: Sign APK
id: sign_apk
uses: r0adkll/sign-android-release@v1
uses: ilharp/sign-android-release@v1
with:
releaseDirectory: build/app/outputs/apk/release
signingKeyBase64: ${{ secrets.SIGNING_KEYSTORE }}
releaseDir: ./app/build/outputs/apk/release/
signingKey: ${{ secrets.SIGNING_KEYSTORE }}
keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }}
alias: ${{ secrets.SIGNING_KEY_ALIAS }}
keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }}
keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}
- name: Add version to APK
run: mv ${{steps.sign_apk.outputs.signedReleaseFile}} revanced-manager-${{ env.RELEASE_VERSION }}.apk
run: mv ${{ steps.sign_apk.outputs.signedFile }} revanced-manager-${{ env.RELEASE_VERSION }}.apk
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-path: revanced-manager-${{ env.RELEASE_VERSION }}.apk
- name: Publish release APK
uses: "marvinpinto/action-automatic-releases@latest"
with:

View File

@ -0,0 +1,19 @@
name: Update documentation
on:
push:
paths:
- docs/**
jobs:
trigger:
runs-on: ubuntu-latest
name: Dispatch event to documentation repository
if: github.ref == 'refs/heads/main'
steps:
- uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
repository: revanced/revanced-documentation
event-type: update-documentation
client-payload: '{"repo": "${{ github.event.repository.name }}", "ref": "${{ github.ref }}"}'

146
.gitignore vendored
View File

@ -1,138 +1,12 @@
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
# Visual Studio Code related
.classpath
.project
.settings/
# Flutter repo-specific
/bin/cache/
/bin/mingit/
/dev/benchmarks/mega_gallery/
/dev/bots/.recipe_deps
/dev/bots/android_tools/
/dev/docs/doc/
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related
**/doc/api/
**/*.g.dart
**/*.locator.dart
**/*.router.dart
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-cache/
.pub/
build/
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# macOS related
**/macos/Flutter/GeneratedPluginRegistrant.swift
**/macos/Flutter/Flutter-Debug.xcconfig
**/macos/Flutter/Flutter-Release.xcconfig
**/macos/Flutter/Flutter-Profile.xcconfig
# Windows related
**/windows/flutter/ephemeral/
**/windows/**/*.suo
**/windows/**/*.user
**/windows/**/*.userosscache
**/windows/**/*.sln.docstates
**/windows/x64/
**/windows/x86/
**/windows/**/*.[Cc]ache
**/windows/**/!*.[Cc]ache/
# Web related
lib/generated_plugin_registrant.dart
# Coverage
coverage/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
Firebase related
.firebase
.kotlin/

View File

@ -1,45 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 85684f9300908116a78138ea4c6036c35c9a1236
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
- platform: android
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
- platform: ios
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
- platform: linux
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
- platform: macos
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
- platform: web
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
- platform: windows
create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -1,6 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
<method v="2" />
</configuration>
</component>

91
.vscode/tasks.json vendored
View File

@ -1,91 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Generate (Builder)",
"type": "shell",
"command": "flutter packages pub run build_runner build --delete-conflicting-outputs",
"problemMatcher": []
},
{
"label": "Build (Android)",
"type": "shell",
"command": "flutter build apk",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Install (Android)",
"type": "shell",
"command": "adb install build\\app\\outputs\\flutter-apk\\app-release.apk",
"problemMatcher": []
},
{
"label": "Clean (Flutter)",
"type": "shell",
"command": "flutter clean && flutter pub get",
"problemMatcher": []
},
{
"label": "Clean (Builder)",
"type": "shell",
"command": "flutter packages pub run build_runner clean",
"problemMatcher": []
},
{
"label": "Build all (Android)",
"dependsOrder": "sequence",
"dependsOn": [
"Generate (Builder)",
"Build (Android)"
],
"problemMatcher": []
},
{
"label": "Clean all",
"dependsOrder": "sequence",
"dependsOn": [
"Clean (Flutter)",
"Clean (Builder)"
],
"problemMatcher": []
},
{
"label": "Clean all & Build all (Android)",
"dependsOrder": "sequence",
"dependsOn": [
"Clean all",
"Build all (Android)"
],
"problemMatcher": []
},
{
"label": "Clean all & Install (Android)",
"dependsOrder": "sequence",
"dependsOn": [
"Clean all",
"Build all (Android)",
"Install (Android)",
],
"problemMatcher": []
},
{
"label": "Build & Install (Android)",
"dependsOrder": "sequence",
"dependsOn": [
"Build (Android)",
"Install (Android)"
],
"problemMatcher": []
},
{
"label": "Validate translations",
"type": "shell",
"command": "flutter pub run flutter_i18n diff en.json pt.json",
"problemMatcher": []
}
]
}

View File

@ -1,30 +1,55 @@
# 💊 ReVanced Manager
# ReVanced Manager (Compose Rewrite)
The official ReVanced Manager based on Flutter.
[![GitHub license](https://img.shields.io/github/license/revanced/revanced-manager)](../../blob/main/LICENSE)
[![GitHub last commit](https://img.shields.io/github/last-commit/revanced/revanced-manager/compose-dev)](https://github.com/ReVanced/revanced-manager/commits/compose-dev)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/w/revanced/revanced-manager/compose-dev)](https://github.com/ReVanced/revanced-manager/commits/compose-dev)
_(Yet another)_ rewrite of the ReVanced Manager using Kotlin and Jetpack Compose.
## Design system
In this rewrite, we are adopting the latest Material Design principles and guidelines by using Material 3 and Material You.
Material Design is a design system developed by Google that provides a unified visual language for building beautiful and consistent user interfaces across all platforms and devices. Material You is an extension of Material Design that provides even more customization options for users, making it possible for them to personalize their device and create a unique look and feel.
### Why Material 3?
* **Consistent design language**
* **Improved accessibility**
* **Better user experience**
By using Material 3 and Material You, we are ensuring that the app's user interface is consistent, customizable, accessible, and engaging for our users. This will help to improve the overall user experience and increase user satisfaction with the the manager.
## Technology stack
* Kotlin: Kotlin is a modern and concise programming language that is fully interoperable with Java and provides improved safety, readability, and maintainability compared to Java.
* Jetpack Compose: Jetpack Compose is a modern UI toolkit for Android development that allows developers to build beautiful and performant user interfaces using declarative programming. It provides a unified and efficient way of building UI that is well-integrated with the Android framework.
## Why Kotlin and Compose?
* **Improved safety:** Kotlin provides improved safety compared to Java, which reduces the likelihood of common programming mistakes that can cause security vulnerabilities or crashes.
* **Concise and readable code:** Kotlin's concise syntax and expressive type system make the code more readable, which makes it easier for developers to understand and maintain the codebase.
* **Better performance:** Jetpack Compose uses the power of the Android framework to provide smooth and fast performance, which enhances the user experience.
* **Modern and efficient UI development:** Jetpack Compose provides a modern and efficient way of building UI, which makes it easier for developers to create beautiful and performant user interfaces.
## 🔽 Download
To download the Alpha version of Manager, go [here](https://github.com/revanced/revanced-manager/releases/latest) and install the provided APK file.
You can obtain ReVanced Manager by downloading it from either [revanced.app/download](https://revanced.app/download) or [GitHub Releases](https://github.com/ReVanced/revanced-manager/releases)
## 📝 Prerequisites
1. Android 8 or higher
2. Does not work on some armv7 devices
3. [Vanced MicroG](https://github.com/TeamVanced/VancedMicroG/releases) required for YouTube and YouTube Music (Only for non-root)
## ⚠️ Disclaimer
*Please note that even though we're releasing the Manager, it is an ALPHA version. There's a big chance that the Manager might not work at all for you.*
For a list of prerequisites, refer to [docs/0_prerequisites.md](docs/0_prerequisites.md)
## 🔴 Issues
For suggestions and bug reports, open an issue [here](https://github.com/revanced/revanced-manager/issues/new/choose).
## 💭 Discussion
If you wish to discuss the Manager, a thread has been made under the [#development](https://discord.com/channels/952946952348270622/1002922226443632761) channel in the Discord server, please note that this thread may be temporary and may be removed in the future.
## 🌐 Translation
[![Crowdin](https://badges.crowdin.net/revanced/localized.svg)](https://crowdin.com/project/revanced)
We're accepting translations on [Crowdin](https://translate.revanced.app)
## 🛠️ Building Manager from source
1. Setup flutter environment for your [platform](https://docs.flutter.dev/get-started/install)
2. Clone the repository locally
3. Add your github token in gradle.properties like [this](https://github.com/revanced/revanced-documentation/wiki/Building-from-source)
4. Open the project in terminal
5. Run `flutter pub get` in terminal
6. Then `flutter packages pub run build_runner build --delete-conflicting-outputs` (Must be done on each git pull)
7. To build release apk run `flutter build apk`
For instructions on how to build ReVanced Manager from source, refer to [docs/4_building.md](docs/4_building.md)

77
SECURITY.md Normal file
View File

@ -0,0 +1,77 @@
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# 🔒 Security Policy
This document describes how to report security vulnerabilities for ReVanced Manager.
## 🚨 Reporting a Vulnerability
Please open an issue in our [advisory tracker](https://github.com/ReVanced/revanced-manager/security/advisories/new) or reach out privately to us on [Discord](https://discord.gg/revanced).
If a vulnerability is confirmed and accepted, you can join our [Discord](https://discord.gg/revanced) server to receive a special contributor role.
### ⏳ Supported Versions
| Version | Branch | Supported |
| --------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------ |
| ![Latest stable release](https://img.shields.io/github/v/release/ReVanced/revanced-manager?style=for-the-badge "Latest stable release") | main | :white_check_mark: |
| ![Latest version](https://img.shields.io/badge/version-latest-brightgreen?style=for-the-badge "Latest version") | dev | :white_check_mark: |
| ![Latest version](https://img.shields.io/badge/version-latest-brightgreen?style=for-the-badge "Latest version") | compose-dev | :white_check_mark: |

View File

@ -1,33 +0,0 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- lib/utils/env_class.g.dart
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

13
android/.gitignore vendored
View File

@ -1,13 +0,0 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View File

@ -1,84 +0,0 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 33
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
applicationId "app.revanced.manager.flutter"
minSdkVersion 26
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
shrinkResources false
minifyEnabled false
signingConfig signingConfigs.debug
}
}
packagingOptions {
exclude '/prebuilt/**'
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// ReVanced
implementation "app.revanced:revanced-patcher:6.0.0"
// Signing & aligning
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
implementation("com.android.tools.build:apksig:7.2.2")
// MicroG cronet
implementation("org.microg:cronet-common:$cronetVersion")
implementation("org.microg:cronet-native:$cronetVersion")
}

View File

@ -1,4 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.revanced.manager.flutter">
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -1,51 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.revanced.manager.flutter">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:label="ReVanced Manager"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:largeHeap="true"
android:extractNativeLibs="true">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

View File

@ -1,320 +0,0 @@
package app.revanced.manager.flutter
import android.os.Build
import android.os.Handler
import android.os.Looper
import androidx.annotation.NonNull
import app.revanced.manager.flutter.utils.Aapt
import app.revanced.manager.flutter.utils.aligning.ZipAligner
import app.revanced.manager.flutter.utils.signing.Signer
import app.revanced.manager.flutter.utils.zip.ZipFile
import app.revanced.manager.flutter.utils.zip.structures.ZipEntry
import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.logging.Logger
import app.revanced.patcher.util.patch.PatchBundle
import dalvik.system.DexClassLoader
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
private const val PATCHER_CHANNEL = "app.revanced.manager.flutter/patcher"
private const val INSTALLER_CHANNEL = "app.revanced.manager.flutter/installer"
class MainActivity : FlutterActivity() {
private val handler = Handler(Looper.getMainLooper())
private lateinit var installerChannel: MethodChannel
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val mainChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, PATCHER_CHANNEL)
installerChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, INSTALLER_CHANNEL)
mainChannel.setMethodCallHandler { call, result ->
when (call.method) {
"runPatcher" -> {
val patchBundleFilePath = call.argument<String>("patchBundleFilePath")
val originalFilePath = call.argument<String>("originalFilePath")
val inputFilePath = call.argument<String>("inputFilePath")
val patchedFilePath = call.argument<String>("patchedFilePath")
val outFilePath = call.argument<String>("outFilePath")
val integrationsPath = call.argument<String>("integrationsPath")
val selectedPatches = call.argument<List<String>>("selectedPatches")
val cacheDirPath = call.argument<String>("cacheDirPath")
val mergeIntegrations = call.argument<Boolean>("mergeIntegrations")
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
if (patchBundleFilePath != null &&
originalFilePath != null &&
inputFilePath != null &&
patchedFilePath != null &&
outFilePath != null &&
integrationsPath != null &&
selectedPatches != null &&
cacheDirPath != null &&
mergeIntegrations != null &&
keyStoreFilePath != null
) {
runPatcher(
result,
patchBundleFilePath,
originalFilePath,
inputFilePath,
patchedFilePath,
outFilePath,
integrationsPath,
selectedPatches,
cacheDirPath,
mergeIntegrations,
keyStoreFilePath
)
} else {
result.notImplemented()
}
}
else -> result.notImplemented()
}
}
}
private fun runPatcher(
result: MethodChannel.Result,
patchBundleFilePath: String,
originalFilePath: String,
inputFilePath: String,
patchedFilePath: String,
outFilePath: String,
integrationsPath: String,
selectedPatches: List<String>,
cacheDirPath: String,
mergeIntegrations: Boolean,
keyStoreFilePath: String
) {
val originalFile = File(originalFilePath)
val inputFile = File(inputFilePath)
val patchedFile = File(patchedFilePath)
val outFile = File(outFilePath)
val integrations = File(integrationsPath)
val keyStoreFile = File(keyStoreFilePath)
Thread {
try {
val patches = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
PatchBundle.Dex(
patchBundleFilePath,
DexClassLoader(
patchBundleFilePath,
cacheDirPath,
null,
javaClass.classLoader
)
).loadPatches().filter { patch -> selectedPatches.any { it == patch.patchName } }
} else {
TODO("VERSION.SDK_INT < CUPCAKE")
}
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 0.1,
"header" to "",
"log" to "Copying original apk"
)
)
}
originalFile.copyTo(inputFile, true)
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 0.2,
"header" to "Unpacking apk...",
"log" to "Unpacking input apk"
)
)
}
val patcher =
Patcher(
PatcherOptions(
inputFile,
cacheDirPath,
Aapt.binary(applicationContext).absolutePath,
cacheDirPath,
logger = ManagerLogger()
)
)
handler.post {
installerChannel.invokeMethod(
"update",
mapOf("progress" to 0.3, "header" to "", "log" to "")
)
}
if (mergeIntegrations) {
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 0.4,
"header" to "Merging integrations...",
"log" to "Merging integrations"
)
)
}
patcher.addFiles(listOf(integrations)) {}
}
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 0.5,
"header" to "Applying patches...",
"log" to ""
)
)
}
patcher.addPatches(patches)
patcher.executePatches().forEach { (patch, res) ->
if (res.isSuccess) {
val msg = "Applied $patch"
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 0.5,
"header" to "",
"log" to msg
)
)
}
return@forEach
}
val msg = "$patch failed.\nError:\n" + res.exceptionOrNull()!!.printStackTrace()
handler.post {
installerChannel.invokeMethod(
"update",
mapOf("progress" to 0.5, "header" to "", "log" to msg)
)
}
}
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 0.7,
"header" to "Repacking apk...",
"log" to "Repacking patched apk"
)
)
}
val res = patcher.save()
ZipFile(patchedFile).use { file ->
res.dexFiles.forEach {
file.addEntryCompressData(
ZipEntry.createWithName(it.name),
it.stream.readBytes()
)
}
res.resourceFile?.let {
file.copyEntriesFromFileAligned(
ZipFile(it),
ZipAligner::getEntryAlignment
)
}
file.copyEntriesFromFileAligned(
ZipFile(inputFile),
ZipAligner::getEntryAlignment
)
}
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 0.9,
"header" to "Signing apk...",
"log" to ""
)
)
}
// Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)
try {
Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)
} catch (e: Exception) {
//log to console
print("Error signing apk: ${e.message}")
e.printStackTrace()
}
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to 1.0,
"header" to "Finished!",
"log" to "Finished!"
)
)
}
} catch (ex: Throwable) {
val stack = ex.stackTraceToString()
handler.post {
installerChannel.invokeMethod(
"update",
mapOf(
"progress" to -100.0,
"header" to "Aborting...",
"log" to "An error occurred! Aborting\nError:\n$stack"
)
)
}
}
handler.post { result.success(null) }
}.start()
}
inner class ManagerLogger : Logger {
override fun error(msg: String) {
handler.post {
installerChannel
.invokeMethod(
"update",
mapOf("progress" to -1.0, "header" to "", "log" to msg)
)
}
}
override fun warn(msg: String) {
handler.post {
installerChannel.invokeMethod(
"update",
mapOf("progress" to -1.0, "header" to "", "log" to msg)
)
}
}
override fun info(msg: String) {
handler.post {
installerChannel.invokeMethod(
"update",
mapOf("progress" to -1.0, "header" to "", "log" to msg)
)
}
}
override fun trace(msg: String) {
handler.post {
installerChannel.invokeMethod(
"update",
mapOf("progress" to -1.0, "header" to "", "log" to msg)
)
}
}
}
}

View File

@ -1,12 +0,0 @@
package app.revanced.manager.flutter.utils
import android.content.Context
import java.io.File
object Aapt {
fun binary(context: Context): File {
return File(context.applicationInfo.nativeLibraryDir).resolveAapt()
}
}
private fun File.resolveAapt() = resolve(list { _, f -> !File(f).isDirectory && f.contains("aapt") }!!.first())

View File

@ -1,11 +0,0 @@
package app.revanced.manager.flutter.utils.aligning
import app.revanced.manager.flutter.utils.zip.structures.ZipEntry
internal object ZipAligner {
private const val DEFAULT_ALIGNMENT = 4
private const val LIBRARY_ALIGNMENT = 4096
fun getEntryAlignment(entry: ZipEntry): Int? =
if (entry.compression.toUInt() != 0u) null else if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT else DEFAULT_ALIGNMENT
}

View File

@ -1,74 +0,0 @@
package app.revanced.manager.flutter.utils.signing
import com.android.apksig.ApkSigner
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.cert.X509v3CertificateBuilder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.operator.ContentSigner
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.math.BigInteger
import java.security.*
import java.security.cert.X509Certificate
import java.util.*
internal class Signer(
private val cn: String, password: String
) {
private val passwordCharArray = password.toCharArray()
private fun newKeystore(out: File) {
val (publicKey, privateKey) = createKey()
val privateKS = KeyStore.getInstance("BKS", "BC")
privateKS.load(null, passwordCharArray)
privateKS.setKeyEntry("alias", privateKey, passwordCharArray, arrayOf(publicKey))
privateKS.store(FileOutputStream(out), passwordCharArray)
}
private fun createKey(): Pair<X509Certificate, PrivateKey> {
val gen = KeyPairGenerator.getInstance("RSA")
gen.initialize(2048)
val pair = gen.generateKeyPair()
var serialNumber: BigInteger
do serialNumber =
BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
val x500Name = X500Name("CN=$cn")
val builder = X509v3CertificateBuilder(
x500Name,
serialNumber,
Date(System.currentTimeMillis() - 1000L * 60L * 60L * 24L * 30L),
Date(System.currentTimeMillis() + 1000L * 60L * 60L * 24L * 366L * 30L),
Locale.ENGLISH,
x500Name,
SubjectPublicKeyInfo.getInstance(pair.public.encoded)
)
val signer: ContentSigner = JcaContentSignerBuilder("SHA256withRSA").build(pair.private)
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
}
fun signApk(input: File, output: File, ks: File) {
Security.addProvider(BouncyCastleProvider())
if (!ks.exists()) newKeystore(ks)
val keyStore = KeyStore.getInstance("BKS", "BC")
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
val alias = keyStore.aliases().nextElement()
val config = ApkSigner.SignerConfig.Builder(
cn,
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
listOf(keyStore.getCertificate(alias) as X509Certificate)
).build()
val signer = ApkSigner.Builder(listOf(config))
signer.setCreatedBy(cn)
signer.setInputApk(input)
signer.setOutputApk(output)
signer.build().sign()
}
}

View File

@ -1,35 +0,0 @@
@file:Suppress("unused")
package app.revanced.manager.flutter.utils.zip
import java.io.DataInput
import java.io.DataOutput
import java.nio.ByteBuffer
fun UInt.toLittleEndian() =
(((this.toInt() and 0xff000000.toInt()) shr 24) or ((this.toInt() and 0x00ff0000) shr 8) or ((this.toInt() and 0x0000ff00) shl 8) or (this.toInt() shl 24)).toUInt()
fun UShort.toLittleEndian() = (this.toUInt() shl 16).toLittleEndian().toUShort()
fun UInt.toBigEndian() = (((this.toInt() and 0xff) shl 24) or ((this.toInt() and 0xff00) shl 8)
or ((this.toInt() and 0x00ff0000) ushr 8) or (this.toInt() ushr 24)).toUInt()
fun UShort.toBigEndian() = (this.toUInt() shl 16).toBigEndian().toUShort()
fun ByteBuffer.getUShort() = this.short.toUShort()
fun ByteBuffer.getUInt() = this.int.toUInt()
fun ByteBuffer.putUShort(ushort: UShort): ByteBuffer = this.putShort(ushort.toShort())
fun ByteBuffer.putUInt(uint: UInt): ByteBuffer = this.putInt(uint.toInt())
fun DataInput.readUShort() = this.readShort().toUShort()
fun DataInput.readUInt() = this.readInt().toUInt()
fun DataOutput.writeUShort(ushort: UShort) = this.writeShort(ushort.toInt())
fun DataOutput.writeUInt(uint: UInt) = this.writeInt(uint.toInt())
fun DataInput.readUShortLE() = this.readUShort().toBigEndian()
fun DataInput.readUIntLE() = this.readUInt().toBigEndian()
fun DataOutput.writeUShortLE(ushort: UShort) = this.writeUShort(ushort.toLittleEndian())
fun DataOutput.writeUIntLE(uint: UInt) = this.writeUInt(uint.toLittleEndian())

View File

@ -1,176 +0,0 @@
package app.revanced.manager.flutter.utils.zip
import app.revanced.manager.flutter.utils.zip.structures.ZipEndRecord
import app.revanced.manager.flutter.utils.zip.structures.ZipEntry
import java.io.Closeable
import java.io.File
import java.io.RandomAccessFile
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import java.util.zip.CRC32
import java.util.zip.Deflater
class ZipFile(file: File) : Closeable {
var entries: MutableList<ZipEntry> = mutableListOf()
private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw")
private var CDNeedsRewrite = false
private val compressionLevel = 5
init {
//if file isn't empty try to load entries
if (file.length() > 0) {
val endRecord = findEndRecord()
if (endRecord.diskNumber > 0u || endRecord.totalEntries != endRecord.diskEntries)
throw IllegalArgumentException("Multi-file archives are not supported")
entries = readEntries(endRecord).toMutableList()
}
//seek back to start for writing
filePointer.seek(0)
}
private fun findEndRecord(): ZipEndRecord {
//look from end to start since end record is at the end
for (i in filePointer.length() - 1 downTo 0) {
filePointer.seek(i)
//possible beginning of signature
if (filePointer.readByte() == 0x50.toByte()) {
//seek back to get the full int
filePointer.seek(i)
val possibleSignature = filePointer.readUIntLE()
if (possibleSignature == ZipEndRecord.ECD_SIGNATURE) {
filePointer.seek(i)
return ZipEndRecord.fromECD(filePointer)
}
}
}
throw Exception("Couldn't find end record")
}
private fun readEntries(endRecord: ZipEndRecord): List<ZipEntry> {
filePointer.seek(endRecord.centralDirectoryStartOffset.toLong())
val numberOfEntries = endRecord.diskEntries.toInt()
return buildList(numberOfEntries) {
for (i in 1..numberOfEntries) {
add(
ZipEntry.fromCDE(filePointer).also
{
//for some reason the local extra field can be different from the central one
it.readLocalExtra(
filePointer.channel.map(
FileChannel.MapMode.READ_ONLY,
it.localHeaderOffset.toLong() + 28,
2
)
)
})
}
}
}
private fun writeCD() {
val CDStart = filePointer.channel.position().toUInt()
entries.forEach {
filePointer.channel.write(it.toCDE())
}
val entriesCount = entries.size.toUShort()
val endRecord = ZipEndRecord(
0u,
0u,
entriesCount,
entriesCount,
filePointer.channel.position().toUInt() - CDStart,
CDStart,
""
)
filePointer.channel.write(endRecord.toECD())
}
private fun addEntry(entry: ZipEntry, data: ByteBuffer) {
CDNeedsRewrite = true
entry.localHeaderOffset = filePointer.channel.position().toUInt()
filePointer.channel.write(entry.toLFH())
filePointer.channel.write(data)
entries.add(entry)
}
fun addEntryCompressData(entry: ZipEntry, data: ByteArray) {
val compressor = Deflater(compressionLevel, true)
compressor.setInput(data)
compressor.finish()
val uncompressedSize = data.size
val compressedData =
ByteArray(uncompressedSize) //i'm guessing compression won't make the data bigger
val compressedDataLength = compressor.deflate(compressedData)
val compressedBuffer =
ByteBuffer.wrap(compressedData.take(compressedDataLength).toByteArray())
compressor.end()
val crc = CRC32()
crc.update(data)
entry.compression = 8u //deflate compression
entry.uncompressedSize = uncompressedSize.toUInt()
entry.compressedSize = compressedDataLength.toUInt()
entry.crc32 = crc.value.toUInt()
addEntry(entry, compressedBuffer)
}
private fun addEntryCopyData(entry: ZipEntry, data: ByteBuffer, alignment: Int? = null) {
alignment?.let {
//calculate where data would end up
val dataOffset = filePointer.filePointer + entry.LFHSize
val mod = dataOffset % alignment
//wrong alignment
if (mod != 0L) {
//add padding at end of extra field
entry.localExtraField =
entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
}
}
addEntry(entry, data)
}
fun getDataForEntry(entry: ZipEntry): ByteBuffer {
return filePointer.channel.map(
FileChannel.MapMode.READ_ONLY,
entry.dataOffset.toLong(),
entry.compressedSize.toLong()
)
}
fun copyEntriesFromFileAligned(file: ZipFile, entryAlignment: (entry: ZipEntry) -> Int?) {
for (entry in file.entries) {
if (entries.any { it.fileName == entry.fileName }) continue //don't add duplicates
val data = file.getDataForEntry(entry)
addEntryCopyData(entry, data, entryAlignment(entry))
}
}
override fun close() {
if (CDNeedsRewrite) writeCD()
filePointer.close()
}
}

View File

@ -1,78 +0,0 @@
package app.revanced.manager.flutter.utils.zip.structures
import app.revanced.manager.flutter.utils.zip.putUInt
import app.revanced.manager.flutter.utils.zip.putUShort
import app.revanced.manager.flutter.utils.zip.readUIntLE
import app.revanced.manager.flutter.utils.zip.readUShortLE
import java.io.DataInput
import java.nio.ByteBuffer
import java.nio.ByteOrder
data class ZipEndRecord(
val diskNumber: UShort,
val startingDiskNumber: UShort,
val diskEntries: UShort,
val totalEntries: UShort,
val centralDirectorySize: UInt,
val centralDirectoryStartOffset: UInt,
val fileComment: String,
) {
companion object {
const val ECD_HEADER_SIZE = 22
const val ECD_SIGNATURE = 0x06054b50u
fun fromECD(input: DataInput): ZipEndRecord {
val signature = input.readUIntLE()
if (signature != ECD_SIGNATURE)
throw IllegalArgumentException("Input doesn't start with end record signature")
val diskNumber = input.readUShortLE()
val startingDiskNumber = input.readUShortLE()
val diskEntries = input.readUShortLE()
val totalEntries = input.readUShortLE()
val centralDirectorySize = input.readUIntLE()
val centralDirectoryStartOffset = input.readUIntLE()
val fileCommentLength = input.readUShortLE()
var fileComment = ""
if (fileCommentLength > 0u) {
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
input.readFully(fileCommentBytes)
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
}
return ZipEndRecord(
diskNumber,
startingDiskNumber,
diskEntries,
totalEntries,
centralDirectorySize,
centralDirectoryStartOffset,
fileComment
)
}
}
fun toECD(): ByteBuffer {
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
val buffer = ByteBuffer.allocate(ECD_HEADER_SIZE + commentBytes.size)
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(ECD_SIGNATURE)
buffer.putUShort(diskNumber)
buffer.putUShort(startingDiskNumber)
buffer.putUShort(diskEntries)
buffer.putUShort(totalEntries)
buffer.putUInt(centralDirectorySize)
buffer.putUInt(centralDirectoryStartOffset)
buffer.putUShort(commentBytes.size.toUShort())
buffer.put(commentBytes)
buffer.flip()
return buffer
}
}

View File

@ -1,190 +0,0 @@
package app.revanced.manager.flutter.utils.zip.structures
import app.revanced.manager.flutter.utils.zip.*
import java.io.DataInput
import java.nio.ByteBuffer
import java.nio.ByteOrder
data class ZipEntry(
val version: UShort,
val versionNeeded: UShort,
val flags: UShort,
var compression: UShort,
val modificationTime: UShort,
val modificationDate: UShort,
var crc32: UInt,
var compressedSize: UInt,
var uncompressedSize: UInt,
val diskNumber: UShort,
val internalAttributes: UShort,
val externalAttributes: UInt,
var localHeaderOffset: UInt,
val fileName: String,
val extraField: ByteArray,
val fileComment: String,
var localExtraField: ByteArray = ByteArray(0), //separate for alignment
) {
val LFHSize: Int
get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size
val dataOffset: UInt
get() = localHeaderOffset + LFHSize.toUInt()
companion object {
const val CDE_HEADER_SIZE = 46
const val CDE_SIGNATURE = 0x02014b50u
const val LFH_HEADER_SIZE = 30
const val LFH_SIGNATURE = 0x04034b50u
fun createWithName(fileName: String): ZipEntry {
return ZipEntry(
0x1403u, //made by unix, version 20
0u,
0u,
0u,
0x0821u, //seems to be static time google uses, no idea
0x0221u, //same as above
0u,
0u,
0u,
0u,
0u,
0u,
0u,
fileName,
ByteArray(0),
""
)
}
fun fromCDE(input: DataInput): ZipEntry {
val signature = input.readUIntLE()
if (signature != CDE_SIGNATURE)
throw IllegalArgumentException("Input doesn't start with central directory entry signature")
val version = input.readUShortLE()
val versionNeeded = input.readUShortLE()
var flags = input.readUShortLE()
val compression = input.readUShortLE()
val modificationTime = input.readUShortLE()
val modificationDate = input.readUShortLE()
val crc32 = input.readUIntLE()
val compressedSize = input.readUIntLE()
val uncompressedSize = input.readUIntLE()
val fileNameLength = input.readUShortLE()
var fileName = ""
val extraFieldLength = input.readUShortLE()
val extraField = ByteArray(extraFieldLength.toInt())
val fileCommentLength = input.readUShortLE()
var fileComment = ""
val diskNumber = input.readUShortLE()
val internalAttributes = input.readUShortLE()
val externalAttributes = input.readUIntLE()
val localHeaderOffset = input.readUIntLE()
val variableFieldsLength =
fileNameLength.toInt() + extraFieldLength.toInt() + fileCommentLength.toInt()
if (variableFieldsLength > 0) {
val fileNameBytes = ByteArray(fileNameLength.toInt())
input.readFully(fileNameBytes)
fileName = fileNameBytes.toString(Charsets.UTF_8)
input.readFully(extraField)
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
input.readFully(fileCommentBytes)
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
}
flags = (flags and 0b1000u.inv()
.toUShort()) //disable data descriptor flag as they are not used
return ZipEntry(
version,
versionNeeded,
flags,
compression,
modificationTime,
modificationDate,
crc32,
compressedSize,
uncompressedSize,
diskNumber,
internalAttributes,
externalAttributes,
localHeaderOffset,
fileName,
extraField,
fileComment,
)
}
}
fun readLocalExtra(buffer: ByteBuffer) {
buffer.order(ByteOrder.LITTLE_ENDIAN)
localExtraField = ByteArray(buffer.getUShort().toInt())
}
fun toLFH(): ByteBuffer {
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
val buffer = ByteBuffer.allocate(LFH_HEADER_SIZE + nameBytes.size + localExtraField.size)
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(LFH_SIGNATURE)
buffer.putUShort(versionNeeded)
buffer.putUShort(flags)
buffer.putUShort(compression)
buffer.putUShort(modificationTime)
buffer.putUShort(modificationDate)
buffer.putUInt(crc32)
buffer.putUInt(compressedSize)
buffer.putUInt(uncompressedSize)
buffer.putUShort(nameBytes.size.toUShort())
buffer.putUShort(localExtraField.size.toUShort())
buffer.put(nameBytes)
buffer.put(localExtraField)
buffer.flip()
return buffer
}
fun toCDE(): ByteBuffer {
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
val buffer =
ByteBuffer.allocate(CDE_HEADER_SIZE + nameBytes.size + extraField.size + commentBytes.size)
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(CDE_SIGNATURE)
buffer.putUShort(version)
buffer.putUShort(versionNeeded)
buffer.putUShort(flags)
buffer.putUShort(compression)
buffer.putUShort(modificationTime)
buffer.putUShort(modificationDate)
buffer.putUInt(crc32)
buffer.putUInt(compressedSize)
buffer.putUInt(uncompressedSize)
buffer.putUShort(nameBytes.size.toUShort())
buffer.putUShort(extraField.size.toUShort())
buffer.putUShort(commentBytes.size.toUShort())
buffer.putUShort(diskNumber)
buffer.putUShort(internalAttributes)
buffer.putUInt(externalAttributes)
buffer.putUInt(localHeaderOffset)
buffer.put(nameBytes)
buffer.put(extraField)
buffer.put(commentBytes)
buffer.flip()
return buffer
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 814 B

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_launcher_round</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_launcher_round</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#1B1B1B</color>
</resources>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="." />
</paths>

View File

@ -1,4 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.revanced.manager.flutter">
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -1,37 +0,0 @@
buildscript {
ext.cronetVersion = '102.5005.125'
ext.kotlin_version = '1.7.20'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
credentials {
username = (project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")) as String
password = (project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")) as String
}
}
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -1,3 +0,0 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@ -1,11 +0,0 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

222
app/build.gradle.kts Normal file
View File

@ -0,0 +1,222 @@
import kotlin.random.Random
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.devtools)
alias(libs.plugins.about.libraries)
}
android {
namespace = "app.revanced.manager"
compileSdk = 35
buildToolsVersion = "35.0.1"
defaultConfig {
applicationId = "app.revanced.manager"
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "0.0.1"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
debug {
applicationIdSuffix = ".debug"
resValue("string", "app_name", "ReVanced Manager (Debug)")
isPseudoLocalesEnabled = true
buildConfigField("long", "BUILD_ID", "${Random.nextLong()}L")
}
release {
if (!project.hasProperty("noProguard")) {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
val keystoreFile = file("keystore.jks")
if (project.hasProperty("signAsDebug") || !keystoreFile.exists()) {
applicationIdSuffix = ".debug_signed"
resValue("string", "app_name", "ReVanced Manager (Debug signed)")
signingConfig = signingConfigs.getByName("debug")
isPseudoLocalesEnabled = true
} else {
signingConfig = signingConfigs.create("release") {
storeFile = keystoreFile
storePassword = System.getenv("KEYSTORE_PASSWORD")
keyAlias = System.getenv("KEYSTORE_ENTRY_ALIAS")
keyPassword = System.getenv("KEYSTORE_ENTRY_PASSWORD")
}
}
buildConfigField("long", "BUILD_ID", "0L")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
packaging {
resources.excludes.addAll(
listOf(
"/prebuilt/**",
"META-INF/DEPENDENCIES",
"META-INF/**.version",
"DebugProbesKt.bin",
"kotlin-tooling-metadata.json",
"org/bouncycastle/pqc/**.properties",
"org/bouncycastle/x509/**.properties",
)
)
jniLibs {
useLegacyPackaging = true
}
}
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
aidl = true
buildConfig = true
}
android {
androidResources {
generateLocaleConfig = true
}
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
}
kotlin {
jvmToolchain(17)
}
dependencies {
// AndroidX Core
implementation(libs.androidx.ktx)
implementation(libs.runtime.ktx)
implementation(libs.runtime.compose)
implementation(libs.splash.screen)
implementation(libs.activity.compose)
implementation(libs.work.runtime.ktx)
implementation(libs.preferences.datastore)
implementation(libs.appcompat)
// Compose
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui)
implementation(libs.compose.ui.preview)
implementation(libs.compose.ui.tooling)
implementation(libs.compose.livedata)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.material3)
implementation(libs.navigation.compose)
// Accompanist
implementation(libs.accompanist.drawablepainter)
// Placeholder
implementation(libs.placeholder.material3)
// HTML Scraper
implementation(libs.skrapeit.dsl)
implementation(libs.skrapeit.parser)
// Coil (async image loading, network image)
implementation(libs.coil.compose)
implementation(libs.coil.appiconloader)
// KotlinX
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.collection.immutable)
implementation(libs.kotlinx.datetime)
// Room
implementation(libs.room.runtime)
implementation(libs.room.ktx)
annotationProcessor(libs.room.compiler)
ksp(libs.room.compiler)
// ReVanced
implementation(libs.revanced.patcher)
implementation(libs.revanced.library)
// Downloader plugins
implementation(libs.plugin.api)
// Native processes
implementation(libs.kotlin.process)
// HiddenAPI
compileOnly(libs.hidden.api.stub)
// LibSU
implementation(libs.libsu.core)
implementation(libs.libsu.service)
implementation(libs.libsu.nio)
// Koin
implementation(libs.koin.android)
implementation(libs.koin.compose)
implementation(libs.koin.compose.navigation)
implementation(libs.koin.workmanager)
// Licenses
implementation(libs.about.libraries)
// Ktor
implementation(libs.ktor.core)
implementation(libs.ktor.logging)
implementation(libs.ktor.okhttp)
implementation(libs.ktor.content.negotiation)
implementation(libs.ktor.serialization)
// Markdown
implementation(libs.markdown.renderer)
// Fading Edges
implementation(libs.fading.edges)
// Scrollbars
implementation(libs.scrollbars)
// EnumUtil
implementation(libs.enumutil)
ksp(libs.enumutil.ksp)
// Reorderable lists
implementation(libs.reorderable)
// Compose Icons
implementation(libs.compose.icons.fontawesome)
}

63
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,63 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
-dontobfuscate
# Required for serialization to work properly
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}
# This required for the process runtime.
-keep class app.revanced.manager.patcher.runtime.process.* {
*;
}
# Required for the patcher to function correctly
-keep class app.revanced.patcher.** {
*;
}
-keep class brut.** {
*;
}
-keep class org.xmlpull.** {
*;
}
-keep class kotlin.** {
*;
}
-keep class org.jf.** {
*;
}
-keep class com.android.** {
*;
}
-keep class app.revanced.manager.plugin.** {
*;
}
-dontwarn com.google.auto.value.**
-dontwarn java.awt.**
-dontwarn javax.**
-dontwarn org.slf4j.**
-dontwarn it.skrape.fetcher.*
-dontwarn com.google.j2objc.annotations.*
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

View File

@ -0,0 +1,429 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "d0119047505da435972c5247181de675",
"entities": [
{
"tableName": "patch_bundles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `version` TEXT, `source` TEXT NOT NULL, `auto_update` INTEGER NOT NULL, PRIMARY KEY(`uid`))",
"fields": [
{
"fieldPath": "uid",
"columnName": "uid",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "autoUpdate",
"columnName": "auto_update",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"uid"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "patch_selections",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `patch_bundle` INTEGER NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`uid`), FOREIGN KEY(`patch_bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "uid",
"columnName": "uid",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "patchBundle",
"columnName": "patch_bundle",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"uid"
]
},
"indices": [
{
"name": "index_patch_selections_patch_bundle_package_name",
"unique": true,
"columnNames": [
"patch_bundle",
"package_name"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_patch_selections_patch_bundle_package_name` ON `${TABLE_NAME}` (`patch_bundle`, `package_name`)"
}
],
"foreignKeys": [
{
"table": "patch_bundles",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"patch_bundle"
],
"referencedColumns": [
"uid"
]
}
]
},
{
"tableName": "selected_patches",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`selection` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, PRIMARY KEY(`selection`, `patch_name`), FOREIGN KEY(`selection`) REFERENCES `patch_selections`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "selection",
"columnName": "selection",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "patchName",
"columnName": "patch_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"selection",
"patch_name"
]
},
"indices": [],
"foreignKeys": [
{
"table": "patch_selections",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"selection"
],
"referencedColumns": [
"uid"
]
}
]
},
{
"tableName": "downloaded_app",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `directory` TEXT NOT NULL, `last_used` INTEGER NOT NULL, PRIMARY KEY(`package_name`, `version`))",
"fields": [
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "directory",
"columnName": "directory",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUsed",
"columnName": "last_used",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"package_name",
"version"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "installed_app",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`current_package_name` TEXT NOT NULL, `original_package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `install_type` TEXT NOT NULL, PRIMARY KEY(`current_package_name`))",
"fields": [
{
"fieldPath": "currentPackageName",
"columnName": "current_package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "originalPackageName",
"columnName": "original_package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "installType",
"columnName": "install_type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"current_package_name"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "applied_patch",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `bundle` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, PRIMARY KEY(`package_name`, `bundle`, `patch_name`), FOREIGN KEY(`package_name`) REFERENCES `installed_app`(`current_package_name`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bundle",
"columnName": "bundle",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "patchName",
"columnName": "patch_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"package_name",
"bundle",
"patch_name"
]
},
"indices": [
{
"name": "index_applied_patch_bundle",
"unique": false,
"columnNames": [
"bundle"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_applied_patch_bundle` ON `${TABLE_NAME}` (`bundle`)"
}
],
"foreignKeys": [
{
"table": "installed_app",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"package_name"
],
"referencedColumns": [
"current_package_name"
]
},
{
"table": "patch_bundles",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"bundle"
],
"referencedColumns": [
"uid"
]
}
]
},
{
"tableName": "option_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `patch_bundle` INTEGER NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`uid`), FOREIGN KEY(`patch_bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "uid",
"columnName": "uid",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "patchBundle",
"columnName": "patch_bundle",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"uid"
]
},
"indices": [
{
"name": "index_option_groups_patch_bundle_package_name",
"unique": true,
"columnNames": [
"patch_bundle",
"package_name"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_option_groups_patch_bundle_package_name` ON `${TABLE_NAME}` (`patch_bundle`, `package_name`)"
}
],
"foreignKeys": [
{
"table": "patch_bundles",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"patch_bundle"
],
"referencedColumns": [
"uid"
]
}
]
},
{
"tableName": "options",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`group` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, `key` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`group`, `patch_name`, `key`), FOREIGN KEY(`group`) REFERENCES `option_groups`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "group",
"columnName": "group",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "patchName",
"columnName": "patch_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"group",
"patch_name",
"key"
]
},
"indices": [],
"foreignKeys": [
{
"table": "option_groups",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"group"
],
"referencedColumns": [
"uid"
]
}
]
},
{
"tableName": "trusted_downloader_plugins",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `signature` BLOB NOT NULL, PRIMARY KEY(`package_name`))",
"fields": [
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "signature",
"columnName": "signature",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"package_name"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd0119047505da435972c5247181de675')"
]
}
}

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<permission
android:name="app.revanced.manager.permission.PLUGIN_HOST"
android:protectionLevel="signature"
android:label="@string/plugin_host_permission_label"
android:description="@string/plugin_host_permission_description"
/>
<uses-permission android:name="app.revanced.manager.permission.PLUGIN_HOST" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".ManagerApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:largeHeap="true"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.ReVancedManager"
android:enableOnBackInvokedCallback="true"
tools:targetApi="34">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.ReVancedManager">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".plugin.downloader.webview.WebViewActivity" android:exported="false" android:theme="@style/Theme.WebViewActivity" />
<service android:name=".service.InstallService" />
<service android:name=".service.UninstallService" />
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="specialUse"
android:exported="false"
tools:node="merge">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="patching"
/>
</service>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
</application>
</manifest>

View File

@ -0,0 +1,8 @@
// IRootService.aidl
package app.revanced.manager;
// Declare any non-default types here with import statements
interface IRootSystemService {
IBinder getFileSystemService();
}

View File

@ -0,0 +1,11 @@
// IPatcherEvents.aidl
package app.revanced.manager.patcher.runtime.process;
// Interface for sending events back to the main app process.
oneway interface IPatcherEvents {
void log(String level, String msg);
void patchSucceeded();
void progress(String name, String state, String msg);
// The patching process has ended. The exceptionStackTrace is null if it finished successfully.
void finished(String exceptionStackTrace);
}

View File

@ -0,0 +1,14 @@
// IPatcherProcess.aidl
package app.revanced.manager.patcher.runtime.process;
import app.revanced.manager.patcher.runtime.process.Parameters;
import app.revanced.manager.patcher.runtime.process.IPatcherEvents;
interface IPatcherProcess {
// Returns BuildConfig.BUILD_ID, which is used to ensure the main app and runner process are running the same code.
long buildId();
// Makes the patcher process exit with code 0
oneway void exit();
// Starts patching.
oneway void start(in Parameters parameters, IPatcherEvents events);
}

View File

@ -0,0 +1,4 @@
// Parameters.aidl
package app.revanced.manager.patcher.runtime.process;
parcelable Parameters;

View File

@ -0,0 +1,6 @@
id=__PKG_NAME__-ReVanced
name=__LABEL__ ReVanced
version=__VERSION__
versionCode=0
author=ReVanced
description=Mounts the patched APK on top of the original one

View File

@ -0,0 +1,40 @@
#!/system/bin/sh
DIR=${0%/*}
package_name="__PKG_NAME__"
version="__VERSION__"
rm "$DIR/log"
{
until [ "$(getprop sys.boot_completed)" = 1 ]; do sleep 5; done
sleep 5
base_path="$DIR/$package_name.apk"
stock_path="$(pm path "$package_name" | grep base | sed 's/package://g')"
stock_version="$(dumpsys package "$package_name" | grep versionName | cut -d "=" -f2)"
echo "base_path: $base_path"
echo "stock_path: $stock_path"
echo "base_version: $version"
echo "stock_version: $stock_version"
if mount | grep -q "$stock_path" ; then
echo "Not mounting as stock path is already mounted"
exit 1
fi
if [ "$version" != "$stock_version" ]; then
echo "Not mounting as versions don't match"
exit 1
fi
if [ -z "$stock_path" ]; then
echo "Not mounting as app info could not be loaded"
exit 1
fi
mount -o bind "$base_path" "$stock_path"
} >> "$DIR/log"

View File

@ -0,0 +1,38 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("prop_override")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
prop_override.cpp)
# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
android
log)

View File

@ -0,0 +1,62 @@
// Library for overriding Android system properties via environment variables.
//
// Usage: LD_PRELOAD=prop_override.so PROP_dalvik.vm.heapsize=123M getprop dalvik.vm.heapsize
// Output: 123M
#include <string>
#include <cstring>
#include <cstdlib>
#include <dlfcn.h>
// Source: https://android.googlesource.com/platform/system/core/+/100b08a848d018eeb1caa5d5e7c7c2aaac65da15/libcutils/include/cutils/properties.h
#define PROP_VALUE_MAX 92
// This is the mangled name of "android::base::GetProperty".
#define GET_PROPERTY_MANGLED_NAME "_ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_"
extern "C" typedef int (*property_get_ptr)(const char *, char *, const char *);
typedef std::string (*GetProperty_ptr)(const std::string &, const std::string &);
char *GetPropOverride(const std::string &key) {
auto envKey = "PROP_" + key;
return getenv(envKey.c_str());
}
// See: https://android.googlesource.com/platform/system/core/+/100b08a848d018eeb1caa5d5e7c7c2aaac65da15/libcutils/properties.cpp
extern "C" int property_get(const char *key, char *value, const char *default_value) {
auto replacement = GetPropOverride(std::string(key));
if (replacement) {
int len = strnlen(replacement, PROP_VALUE_MAX);
strncpy(value, replacement, len);
return len;
}
static property_get_ptr original = NULL;
if (!original) {
// Get the address of the original function.
original = reinterpret_cast<property_get_ptr>(dlsym(RTLD_NEXT, "property_get"));
}
return original(key, value, default_value);
}
// Defining android::base::GetProperty ourselves won't work because std::string has a slightly different "path" in the NDK version of the C++ standard library.
// We can get around this by forcing the function to adopt a specific name using the asm keyword.
std::string GetProperty(const std::string &, const std::string &) asm(GET_PROPERTY_MANGLED_NAME);
// See: https://android.googlesource.com/platform/system/libbase/+/1a34bb67c4f3ba0a1ea6f4f20ac9fe117ba4fe64/properties.cpp
// This isn't used for the properties we want to override, but property_get is deprecated so that could change in the future.
std::string GetProperty(const std::string &key, const std::string &default_value) {
auto replacement = GetPropOverride(key);
if (replacement) {
return std::string(replacement);
}
static GetProperty_ptr original = NULL;
if (!original) {
original = reinterpret_cast<GetProperty_ptr>(dlsym(RTLD_NEXT, GET_PROPERTY_MANGLED_NAME));
}
return original(key, default_value);
}

View File

@ -0,0 +1,336 @@
package app.revanced.manager
import android.content.ActivityNotFoundException
import android.os.Bundle
import android.os.Parcelable
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import app.revanced.manager.ui.model.navigation.AppSelector
import app.revanced.manager.ui.model.navigation.ComplexParameter
import app.revanced.manager.ui.model.navigation.Dashboard
import app.revanced.manager.ui.model.navigation.InstalledApplicationInfo
import app.revanced.manager.ui.model.navigation.Patcher
import app.revanced.manager.ui.model.navigation.SelectedApplicationInfo
import app.revanced.manager.ui.model.navigation.Settings
import app.revanced.manager.ui.model.navigation.Update
import app.revanced.manager.ui.screen.AppSelectorScreen
import app.revanced.manager.ui.screen.DashboardScreen
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
import app.revanced.manager.ui.screen.PatcherScreen
import app.revanced.manager.ui.screen.PatchesSelectorScreen
import app.revanced.manager.ui.screen.RequiredOptionsScreen
import app.revanced.manager.ui.screen.SelectedAppInfoScreen
import app.revanced.manager.ui.screen.SettingsScreen
import app.revanced.manager.ui.screen.UpdateScreen
import app.revanced.manager.ui.screen.settings.AboutSettingsScreen
import app.revanced.manager.ui.screen.settings.AdvancedSettingsScreen
import app.revanced.manager.ui.screen.settings.ContributorScreen
import app.revanced.manager.ui.screen.settings.DeveloperOptionsScreen
import app.revanced.manager.ui.screen.settings.DownloadsSettingsScreen
import app.revanced.manager.ui.screen.settings.GeneralSettingsScreen
import app.revanced.manager.ui.screen.settings.ImportExportSettingsScreen
import app.revanced.manager.ui.screen.settings.LicensesScreen
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
import app.revanced.manager.ui.theme.ReVancedManagerTheme
import app.revanced.manager.ui.theme.Theme
import app.revanced.manager.ui.viewmodel.MainViewModel
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
import app.revanced.manager.util.EventEffect
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import org.koin.androidx.compose.navigation.koinNavViewModel
import org.koin.core.parameter.parametersOf
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel
class MainActivity : ComponentActivity() {
@ExperimentalAnimationApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
enableEdgeToEdge()
installSplashScreen()
val vm: MainViewModel = getActivityViewModel()
setContent {
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult(),
onResult = vm::applyLegacySettings
)
val theme by vm.prefs.theme.getAsState()
val dynamicColor by vm.prefs.dynamicColor.getAsState()
EventEffect(vm.legacyImportActivityFlow) {
try {
launcher.launch(it)
} catch (_: ActivityNotFoundException) {
}
}
ReVancedManagerTheme(
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
dynamicColor = dynamicColor
) {
ReVancedManager(vm)
}
}
}
}
@Composable
private fun ReVancedManager(vm: MainViewModel) {
val navController = rememberNavController()
EventEffect(vm.appSelectFlow) { app ->
navController.navigateComplex(
SelectedApplicationInfo,
SelectedApplicationInfo.ViewModelParams(app)
)
}
NavHost(
navController = navController,
startDestination = Dashboard,
enterTransition = { slideInHorizontally(initialOffsetX = { it }) },
exitTransition = { slideOutHorizontally(targetOffsetX = { -it / 3 }) },
popEnterTransition = { slideInHorizontally(initialOffsetX = { -it / 3 }) },
popExitTransition = { slideOutHorizontally(targetOffsetX = { it }) },
) {
composable<Dashboard> {
DashboardScreen(
onSettingsClick = { navController.navigate(Settings) },
onAppSelectorClick = {
navController.navigate(AppSelector)
},
onUpdateClick = {
navController.navigate(Update())
},
onDownloaderPluginClick = {
navController.navigate(Settings.Downloads)
},
onAppClick = { packageName ->
navController.navigate(InstalledApplicationInfo(packageName))
}
)
}
composable<InstalledApplicationInfo> {
val data = it.toRoute<InstalledApplicationInfo>()
InstalledAppInfoScreen(
onPatchClick = vm::selectApp,
onBackClick = navController::popBackStack,
viewModel = koinViewModel { parametersOf(data.packageName) }
)
}
composable<AppSelector> {
AppSelectorScreen(
onSelect = vm::selectApp,
onStorageSelect = vm::selectApp,
onBackClick = navController::popBackStack
)
}
composable<Patcher> {
PatcherScreen(
onBackClick = {
navController.navigate(route = Dashboard) {
launchSingleTop = true
popUpTo<Dashboard> {
inclusive = false
}
}
},
vm = koinViewModel { parametersOf(it.getComplexArg<Patcher.ViewModelParams>()) }
)
}
composable<Update> {
val data = it.toRoute<Update>()
UpdateScreen(
onBackClick = navController::popBackStack,
vm = koinViewModel { parametersOf(data.downloadOnScreenEntry) }
)
}
navigation<SelectedApplicationInfo>(startDestination = SelectedApplicationInfo.Main) {
composable<SelectedApplicationInfo.Main> {
val parentBackStackEntry = navController.navGraphEntry(it)
val data =
parentBackStackEntry.getComplexArg<SelectedApplicationInfo.ViewModelParams>()
val viewModel =
koinNavViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
parametersOf(data)
}
SelectedAppInfoScreen(
onBackClick = navController::popBackStack,
onPatchClick = {
it.lifecycleScope.launch {
navController.navigateComplex(
Patcher,
viewModel.getPatcherParams()
)
}
},
onPatchSelectorClick = { app, patches, options ->
navController.navigateComplex(
SelectedApplicationInfo.PatchesSelector,
SelectedApplicationInfo.PatchesSelector.ViewModelParams(
app,
patches,
options
)
)
},
onRequiredOptions = { app, patches, options ->
navController.navigateComplex(
SelectedApplicationInfo.RequiredOptions,
SelectedApplicationInfo.PatchesSelector.ViewModelParams(
app,
patches,
options
)
)
},
vm = viewModel
)
}
composable<SelectedApplicationInfo.PatchesSelector> {
val data =
it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>()
val selectedAppInfoVm = koinNavViewModel<SelectedAppInfoViewModel>(
viewModelStoreOwner = navController.navGraphEntry(it)
)
PatchesSelectorScreen(
onBackClick = navController::popBackStack,
onSave = { patches, options ->
selectedAppInfoVm.updateConfiguration(patches, options)
navController.popBackStack()
},
vm = koinViewModel { parametersOf(data) }
)
}
composable<SelectedApplicationInfo.RequiredOptions> {
val data =
it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>()
val selectedAppInfoVm = koinNavViewModel<SelectedAppInfoViewModel>(
viewModelStoreOwner = navController.navGraphEntry(it)
)
RequiredOptionsScreen(
onBackClick = navController::popBackStack,
onContinue = { patches, options ->
selectedAppInfoVm.updateConfiguration(patches, options)
it.lifecycleScope.launch {
navController.navigateComplex(
Patcher,
selectedAppInfoVm.getPatcherParams()
)
}
},
vm = koinViewModel { parametersOf(data) }
)
}
}
navigation<Settings>(startDestination = Settings.Main) {
composable<Settings.Main> {
SettingsScreen(
onBackClick = navController::popBackStack,
navigate = navController::navigate
)
}
composable<Settings.General> {
GeneralSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.Advanced> {
AdvancedSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.DeveloperOptions> {
DeveloperOptionsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.Updates> {
UpdatesSettingsScreen(
onBackClick = navController::popBackStack,
onChangelogClick = { navController.navigate(Settings.Changelogs) },
onUpdateClick = { navController.navigate(Update()) }
)
}
composable<Settings.Downloads> {
DownloadsSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.ImportExport> {
ImportExportSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.About> {
AboutSettingsScreen(
onBackClick = navController::popBackStack,
navigate = navController::navigate
)
}
composable<Settings.Changelogs> {
ChangelogsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.Contributors> {
ContributorScreen(onBackClick = navController::popBackStack)
}
composable<Settings.Licenses> {
LicensesScreen(onBackClick = navController::popBackStack)
}
}
}
}
@Composable
private fun NavController.navGraphEntry(entry: NavBackStackEntry) =
remember(entry) { getBackStackEntry(entry.destination.parent!!.id) }
// Androidx Navigation does not support storing complex types in route objects, so we have to store them inside the saved state handle of the back stack entry instead.
private fun <T : Parcelable, R : ComplexParameter<T>> NavController.navigateComplex(
route: R,
data: T
) {
navigate(route)
getBackStackEntry(route).savedStateHandle["args"] = data
}
private fun <T : Parcelable> NavBackStackEntry.getComplexArg() = savedStateHandle.get<T>("args")!!

View File

@ -0,0 +1,110 @@
package app.revanced.manager
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.util.Log
import app.revanced.manager.data.platform.Filesystem
import app.revanced.manager.di.*
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.domain.repository.DownloaderPluginRepository
import app.revanced.manager.domain.repository.PatchBundleRepository
import app.revanced.manager.util.tag
import kotlinx.coroutines.Dispatchers
import coil.Coil
import coil.ImageLoader
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.internal.BuilderImpl
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.androidx.workmanager.koin.workManagerFactory
import org.koin.core.context.startKoin
class ManagerApplication : Application() {
private val scope = MainScope()
private val prefs: PreferencesManager by inject()
private val patchBundleRepository: PatchBundleRepository by inject()
private val downloaderPluginRepository: DownloaderPluginRepository by inject()
private val fs: Filesystem by inject()
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@ManagerApplication)
androidLogger()
workManagerFactory()
modules(
httpModule,
preferencesModule,
repositoryModule,
serviceModule,
managerModule,
workerModule,
viewModelModule,
databaseModule,
rootModule
)
}
val pixels = 512
Coil.setImageLoader(
ImageLoader.Builder(this)
.components {
add(AppIconKeyer())
add(AppIconFetcher.Factory(pixels, true, this@ManagerApplication))
}
.build()
)
val shellBuilder = BuilderImpl.create().setFlags(Shell.FLAG_MOUNT_MASTER)
Shell.setDefaultBuilder(shellBuilder)
scope.launch {
prefs.preload()
}
scope.launch(Dispatchers.Default) {
downloaderPluginRepository.reload()
}
scope.launch(Dispatchers.Default) {
with(patchBundleRepository) {
reload()
updateCheck()
}
}
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
private var firstActivityCreated = false
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
if (firstActivityCreated) return
firstActivityCreated = true
// We do not want to call onFreshProcessStart() if there is state to restore.
// This can happen on system-initiated process death.
if (savedInstanceState == null) {
Log.d(tag, "Fresh process created")
onFreshProcessStart()
} else Log.d(tag, "System-initiated process death detected")
}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
})
}
private fun onFreshProcessStart() {
fs.uiTempDir.apply {
deleteRecursively()
mkdirs()
}
}
}

View File

@ -0,0 +1,51 @@
package app.revanced.manager.data.platform
import android.Manifest
import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import app.revanced.manager.util.RequestManageStorageContract
import java.io.File
import java.nio.file.Path
class Filesystem(private val app: Application) {
val contentResolver = app.contentResolver // TODO: move Content Resolver operations to here.
/**
* A directory that gets cleared when the app restarts.
* Do not store paths to this directory in a parcel.
*/
val tempDir: File = app.getDir("ephemeral", Context.MODE_PRIVATE).apply {
deleteRecursively()
mkdirs()
}
/**
* A directory for storing temporary files related to UI.
* This is the same as [tempDir], but does not get cleared on system-initiated process death.
* Paths to this directory can be safely stored in parcels.
*/
val uiTempDir: File = app.getDir("ui_ephemeral", Context.MODE_PRIVATE)
fun externalFilesDir(): Path = Environment.getExternalStorageDirectory().toPath()
private fun usesManagePermission() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
private val storagePermissionName =
if (usesManagePermission()) Manifest.permission.MANAGE_EXTERNAL_STORAGE else Manifest.permission.READ_EXTERNAL_STORAGE
fun permissionContract(): Pair<ActivityResultContract<String, Boolean>, String> {
val contract =
if (usesManagePermission()) RequestManageStorageContract() else ActivityResultContracts.RequestPermission()
return contract to storagePermissionName
}
fun hasStoragePermission() =
if (usesManagePermission()) Environment.isExternalStorageManager() else app.checkSelfPermission(
storagePermissionName
) == PackageManager.PERMISSION_GRANTED
}

View File

@ -0,0 +1,19 @@
package app.revanced.manager.data.platform
import android.app.Application
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import androidx.core.content.getSystemService
class NetworkInfo(app: Application) {
private val connectivityManager = app.getSystemService<ConnectivityManager>()!!
private fun getCapabilities() = connectivityManager.activeNetwork?.let { connectivityManager.getNetworkCapabilities(it) }
fun isConnected() = connectivityManager.activeNetwork != null
fun isUnmetered() = getCapabilities()?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) ?: true
/**
* Returns true if it is safe to download large files.
*/
fun isSafe() = isConnected() && isUnmetered()
}

View File

@ -0,0 +1,39 @@
package app.revanced.manager.data.room
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import app.revanced.manager.data.room.apps.downloaded.DownloadedAppDao
import app.revanced.manager.data.room.apps.downloaded.DownloadedApp
import app.revanced.manager.data.room.apps.installed.AppliedPatch
import app.revanced.manager.data.room.apps.installed.InstalledApp
import app.revanced.manager.data.room.apps.installed.InstalledAppDao
import app.revanced.manager.data.room.selection.PatchSelection
import app.revanced.manager.data.room.selection.SelectedPatch
import app.revanced.manager.data.room.selection.SelectionDao
import app.revanced.manager.data.room.bundles.PatchBundleDao
import app.revanced.manager.data.room.bundles.PatchBundleEntity
import app.revanced.manager.data.room.options.Option
import app.revanced.manager.data.room.options.OptionDao
import app.revanced.manager.data.room.options.OptionGroup
import app.revanced.manager.data.room.plugins.TrustedDownloaderPlugin
import app.revanced.manager.data.room.plugins.TrustedDownloaderPluginDao
import kotlin.random.Random
@Database(
entities = [PatchBundleEntity::class, PatchSelection::class, SelectedPatch::class, DownloadedApp::class, InstalledApp::class, AppliedPatch::class, OptionGroup::class, Option::class, TrustedDownloaderPlugin::class],
version = 1
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun patchBundleDao(): PatchBundleDao
abstract fun selectionDao(): SelectionDao
abstract fun downloadedAppDao(): DownloadedAppDao
abstract fun installedAppDao(): InstalledAppDao
abstract fun optionDao(): OptionDao
abstract fun trustedDownloaderPluginDao(): TrustedDownloaderPluginDao
companion object {
fun generateUid() = Random.Default.nextInt()
}
}

View File

@ -0,0 +1,26 @@
package app.revanced.manager.data.room
import androidx.room.TypeConverter
import app.revanced.manager.data.room.bundles.Source
import app.revanced.manager.data.room.options.Option.SerializedValue
import java.io.File
class Converters {
@TypeConverter
fun sourceFromString(value: String) = Source.from(value)
@TypeConverter
fun sourceToString(value: Source) = value.toString()
@TypeConverter
fun fileFromString(value: String) = File(value)
@TypeConverter
fun fileToString(file: File): String = file.path
@TypeConverter
fun serializedOptionFromString(value: String) = SerializedValue.fromJsonString(value)
@TypeConverter
fun serializedOptionToString(value: SerializedValue) = value.toJsonString()
}

View File

@ -0,0 +1,16 @@
package app.revanced.manager.data.room.apps.downloaded
import androidx.room.ColumnInfo
import androidx.room.Entity
import java.io.File
@Entity(
tableName = "downloaded_app",
primaryKeys = ["package_name", "version"]
)
data class DownloadedApp(
@ColumnInfo(name = "package_name") val packageName: String,
@ColumnInfo(name = "version") val version: String,
@ColumnInfo(name = "directory") val directory: File,
@ColumnInfo(name = "last_used") val lastUsed: Long = System.currentTimeMillis()
)

View File

@ -0,0 +1,26 @@
package app.revanced.manager.data.room.apps.downloaded
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface DownloadedAppDao {
@Query("SELECT * FROM downloaded_app")
fun getAllApps(): Flow<List<DownloadedApp>>
@Query("SELECT * FROM downloaded_app WHERE package_name = :packageName AND version = :version")
suspend fun get(packageName: String, version: String): DownloadedApp?
@Upsert
suspend fun upsert(downloadedApp: DownloadedApp)
@Query("UPDATE downloaded_app SET last_used = :newValue WHERE package_name = :packageName AND version = :version")
suspend fun markUsed(packageName: String, version: String, newValue: Long = System.currentTimeMillis())
@Delete
suspend fun delete(downloadedApps: Collection<DownloadedApp>)
}

View File

@ -0,0 +1,35 @@
package app.revanced.manager.data.room.apps.installed
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import app.revanced.manager.data.room.bundles.PatchBundleEntity
import kotlinx.parcelize.Parcelize
@Parcelize
@Entity(
tableName = "applied_patch",
primaryKeys = ["package_name", "bundle", "patch_name"],
foreignKeys = [
ForeignKey(
InstalledApp::class,
parentColumns = ["current_package_name"],
childColumns = ["package_name"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
PatchBundleEntity::class,
parentColumns = ["uid"],
childColumns = ["bundle"],
onDelete = ForeignKey.CASCADE
)
],
indices = [Index(value = ["bundle"], unique = false)]
)
data class AppliedPatch(
@ColumnInfo(name = "package_name") val packageName: String,
@ColumnInfo(name = "bundle") val bundle: Int,
@ColumnInfo(name = "patch_name") val patchName: String
) : Parcelable

View File

@ -0,0 +1,20 @@
package app.revanced.manager.data.room.apps.installed
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import app.revanced.manager.R
enum class InstallType(val stringResource: Int) {
DEFAULT(R.string.default_install),
MOUNT(R.string.mount_install)
}
@Entity(tableName = "installed_app")
data class InstalledApp(
@PrimaryKey
@ColumnInfo(name = "current_package_name") val currentPackageName: String,
@ColumnInfo(name = "original_package_name") val originalPackageName: String,
@ColumnInfo(name = "version") val version: String,
@ColumnInfo(name = "install_type") val installType: InstallType
)

View File

@ -0,0 +1,46 @@
package app.revanced.manager.data.room.apps.installed
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.MapColumn
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface InstalledAppDao {
@Query("SELECT * FROM installed_app")
fun getAll(): Flow<List<InstalledApp>>
@Query("SELECT * FROM installed_app WHERE current_package_name = :packageName")
suspend fun get(packageName: String): InstalledApp?
@Query(
"SELECT bundle, patch_name FROM applied_patch" +
" WHERE package_name = :packageName"
)
suspend fun getPatchesSelection(packageName: String): Map<@MapColumn("bundle") Int, List<@MapColumn(
"patch_name"
) String>>
@Transaction
suspend fun upsertApp(installedApp: InstalledApp, appliedPatches: List<AppliedPatch>) {
upsertApp(installedApp)
deleteAppliedPatches(installedApp.currentPackageName)
insertAppliedPatches(appliedPatches)
}
@Upsert
suspend fun upsertApp(installedApp: InstalledApp)
@Insert
suspend fun insertAppliedPatches(appliedPatches: List<AppliedPatch>)
@Query("DELETE FROM applied_patch WHERE package_name = :packageName")
suspend fun deleteAppliedPatches(packageName: String)
@Delete
suspend fun delete(installedApp: InstalledApp)
}

View File

@ -0,0 +1,37 @@
package app.revanced.manager.data.room.bundles
import androidx.room.*
import kotlinx.coroutines.flow.Flow
@Dao
interface PatchBundleDao {
@Query("SELECT * FROM patch_bundles")
suspend fun all(): List<PatchBundleEntity>
@Query("SELECT version, auto_update FROM patch_bundles WHERE uid = :uid")
fun getPropsById(uid: Int): Flow<BundleProperties?>
@Query("UPDATE patch_bundles SET version = :patches WHERE uid = :uid")
suspend fun updateVersion(uid: Int, patches: String?)
@Query("UPDATE patch_bundles SET auto_update = :value WHERE uid = :uid")
suspend fun setAutoUpdate(uid: Int, value: Boolean)
@Query("UPDATE patch_bundles SET name = :value WHERE uid = :uid")
suspend fun setName(uid: Int, value: String)
@Query("DELETE FROM patch_bundles WHERE uid != 0")
suspend fun purgeCustomBundles()
@Transaction
suspend fun reset() {
purgeCustomBundles()
updateVersion(0, null) // Reset the main source
}
@Query("DELETE FROM patch_bundles WHERE uid = :uid")
suspend fun remove(uid: Int)
@Insert
suspend fun add(source: PatchBundleEntity)
}

View File

@ -0,0 +1,44 @@
package app.revanced.manager.data.room.bundles
import androidx.room.*
import io.ktor.http.*
sealed class Source {
object Local : Source() {
const val SENTINEL = "local"
override fun toString() = SENTINEL
}
object API : Source() {
const val SENTINEL = "api"
override fun toString() = SENTINEL
}
data class Remote(val url: Url) : Source() {
override fun toString() = url.toString()
}
companion object {
fun from(value: String) = when (value) {
Local.SENTINEL -> Local
API.SENTINEL -> API
else -> Remote(Url(value))
}
}
}
@Entity(tableName = "patch_bundles")
data class PatchBundleEntity(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "name") val name: String,
@ColumnInfo(name = "version") val version: String? = null,
@ColumnInfo(name = "source") val source: Source,
@ColumnInfo(name = "auto_update") val autoUpdate: Boolean
)
data class BundleProperties(
@ColumnInfo(name = "version") val version: String? = null,
@ColumnInfo(name = "auto_update") val autoUpdate: Boolean
)

View File

@ -0,0 +1,116 @@
package app.revanced.manager.data.room.options
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import app.revanced.manager.patcher.patch.Option
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.add
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.float
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.typeOf
@Entity(
tableName = "options",
primaryKeys = ["group", "patch_name", "key"],
foreignKeys = [ForeignKey(
OptionGroup::class,
parentColumns = ["uid"],
childColumns = ["group"],
onDelete = ForeignKey.CASCADE
)]
)
data class Option(
@ColumnInfo(name = "group") val group: Int,
@ColumnInfo(name = "patch_name") val patchName: String,
@ColumnInfo(name = "key") val key: String,
// Encoded as Json.
@ColumnInfo(name = "value") val value: SerializedValue,
) {
@Serializable
data class SerializedValue(val raw: JsonElement) {
fun toJsonString() = json.encodeToString(raw)
fun deserializeFor(option: Option<*>): Any? {
if (raw is JsonNull) return null
val errorMessage = "Cannot deserialize value as ${option.type}"
try {
if (option.type.classifier == List::class) {
val elementType = option.type.arguments.first().type!!
return raw.jsonArray.map { deserializeBasicType(elementType, it.jsonPrimitive) }
}
return deserializeBasicType(option.type, raw.jsonPrimitive)
} catch (e: IllegalArgumentException) {
throw SerializationException(errorMessage, e)
} catch (e: IllegalStateException) {
throw SerializationException(errorMessage, e)
} catch (e: kotlinx.serialization.SerializationException) {
throw SerializationException(errorMessage, e)
}
}
companion object {
private val json = Json {
// Patcher does not forbid the use of these values, so we should support them.
allowSpecialFloatingPointValues = true
}
private fun deserializeBasicType(type: KType, value: JsonPrimitive) = when (type) {
typeOf<Boolean>() -> value.boolean
typeOf<Int>() -> value.int
typeOf<Long>() -> value.long
typeOf<Float>() -> value.float
typeOf<String>() -> value.content.also {
if (!value.isString) throw SerializationException(
"Expected value to be a string: $value"
)
}
else -> throw SerializationException("Unknown type: $type")
}
fun fromJsonString(value: String) = SerializedValue(json.decodeFromString(value))
fun fromValue(value: Any?) = SerializedValue(when (value) {
null -> JsonNull
is Number -> JsonPrimitive(value)
is Boolean -> JsonPrimitive(value)
is String -> JsonPrimitive(value)
is List<*> -> buildJsonArray {
var elementClass: KClass<out Any>? = null
value.forEach {
when (it) {
null -> throw SerializationException("List elements must not be null")
is Number -> add(it)
is Boolean -> add(it)
is String -> add(it)
else -> throw SerializationException("Unknown element type: ${it::class.simpleName}")
}
if (elementClass == null) elementClass = it::class
else if (elementClass != it::class) throw SerializationException("List elements must have the same type")
}
}
else -> throw SerializationException("Unknown type: ${value::class.simpleName}")
})
}
}
class SerializationException(message: String, cause: Throwable? = null) :
Exception(message, cause)
}

View File

@ -0,0 +1,50 @@
package app.revanced.manager.data.room.options
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.MapColumn
import androidx.room.Query
import androidx.room.Transaction
import kotlinx.coroutines.flow.Flow
@Dao
abstract class OptionDao {
@Transaction
@Query(
"SELECT patch_bundle, `group`, patch_name, `key`, value FROM option_groups" +
" LEFT JOIN options ON uid = options.`group`" +
" WHERE package_name = :packageName"
)
abstract suspend fun getOptions(packageName: String): Map<@MapColumn("patch_bundle") Int, List<Option>>
@Query("SELECT uid FROM option_groups WHERE patch_bundle = :bundleUid AND package_name = :packageName")
abstract suspend fun getGroupId(bundleUid: Int, packageName: String): Int?
@Query("SELECT package_name FROM option_groups")
abstract fun getPackagesWithOptions(): Flow<List<String>>
@Insert
abstract suspend fun createOptionGroup(group: OptionGroup)
@Query("DELETE FROM option_groups WHERE patch_bundle = :uid")
abstract suspend fun clearForPatchBundle(uid: Int)
@Query("DELETE FROM option_groups WHERE package_name = :packageName")
abstract suspend fun clearForPackage(packageName: String)
@Query("DELETE FROM option_groups")
abstract suspend fun reset()
@Insert
protected abstract suspend fun insertOptions(patches: List<Option>)
@Query("DELETE FROM options WHERE `group` = :groupId")
protected abstract suspend fun clearGroup(groupId: Int)
@Transaction
open suspend fun updateOptions(options: Map<Int, List<Option>>) =
options.forEach { (groupId, options) ->
clearGroup(groupId)
insertOptions(options)
}
}

View File

@ -0,0 +1,24 @@
package app.revanced.manager.data.room.options
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import app.revanced.manager.data.room.bundles.PatchBundleEntity
@Entity(
tableName = "option_groups",
foreignKeys = [ForeignKey(
PatchBundleEntity::class,
parentColumns = ["uid"],
childColumns = ["patch_bundle"],
onDelete = ForeignKey.CASCADE
)],
indices = [Index(value = ["patch_bundle", "package_name"], unique = true)]
)
data class OptionGroup(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "patch_bundle") val patchBundle: Int,
@ColumnInfo(name = "package_name") val packageName: String
)

View File

@ -0,0 +1,11 @@
package app.revanced.manager.data.room.plugins
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "trusted_downloader_plugins")
class TrustedDownloaderPlugin(
@PrimaryKey @ColumnInfo(name = "package_name") val packageName: String,
@ColumnInfo(name = "signature") val signature: ByteArray
)

View File

@ -0,0 +1,22 @@
package app.revanced.manager.data.room.plugins
import androidx.room.Dao
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Upsert
@Dao
interface TrustedDownloaderPluginDao {
@Query("SELECT signature FROM trusted_downloader_plugins WHERE package_name = :packageName")
suspend fun getTrustedSignature(packageName: String): ByteArray?
@Upsert
suspend fun upsertTrust(plugin: TrustedDownloaderPlugin)
@Query("DELETE FROM trusted_downloader_plugins WHERE package_name = :packageName")
suspend fun remove(packageName: String)
@Transaction
@Query("DELETE FROM trusted_downloader_plugins WHERE package_name IN (:packageNames)")
suspend fun removeAll(packageNames: Set<String>)
}

View File

@ -0,0 +1,24 @@
package app.revanced.manager.data.room.selection
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import app.revanced.manager.data.room.bundles.PatchBundleEntity
@Entity(
tableName = "patch_selections",
foreignKeys = [ForeignKey(
PatchBundleEntity::class,
parentColumns = ["uid"],
childColumns = ["patch_bundle"],
onDelete = ForeignKey.CASCADE
)],
indices = [Index(value = ["patch_bundle", "package_name"], unique = true)]
)
data class PatchSelection(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "patch_bundle") val patchBundle: Int,
@ColumnInfo(name = "package_name") val packageName: String
)

View File

@ -0,0 +1,20 @@
package app.revanced.manager.data.room.selection
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
@Entity(
tableName = "selected_patches",
primaryKeys = ["selection", "patch_name"],
foreignKeys = [ForeignKey(
PatchSelection::class,
parentColumns = ["uid"],
childColumns = ["selection"],
onDelete = ForeignKey.CASCADE
)]
)
data class SelectedPatch(
@ColumnInfo(name = "selection") val selection: Int,
@ColumnInfo(name = "patch_name") val patchName: String
)

View File

@ -0,0 +1,58 @@
package app.revanced.manager.data.room.selection
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.MapColumn
import androidx.room.Query
import androidx.room.Transaction
@Dao
abstract class SelectionDao {
@Transaction
@Query(
"SELECT patch_bundle, patch_name FROM patch_selections" +
" LEFT JOIN selected_patches ON uid = selected_patches.selection" +
" WHERE package_name = :packageName"
)
abstract suspend fun getSelectedPatches(packageName: String): Map<@MapColumn("patch_bundle") Int, List<@MapColumn(
"patch_name"
) String>>
@Transaction
@Query(
"SELECT package_name, patch_name FROM patch_selections" +
" LEFT JOIN selected_patches ON uid = selected_patches.selection" +
" WHERE patch_bundle = :bundleUid"
)
abstract suspend fun exportSelection(bundleUid: Int): Map<@MapColumn("package_name") String, List<@MapColumn(
"patch_name"
) String>>
@Query("SELECT uid FROM patch_selections WHERE patch_bundle = :bundleUid AND package_name = :packageName")
abstract suspend fun getSelectionId(bundleUid: Int, packageName: String): Int?
@Insert
abstract suspend fun createSelection(selection: PatchSelection)
@Query("DELETE FROM patch_selections WHERE patch_bundle = :uid")
abstract suspend fun clearForPatchBundle(uid: Int)
@Query("DELETE FROM patch_selections WHERE package_name = :packageName")
abstract suspend fun clearForPackage(packageName: String)
@Query("DELETE FROM patch_selections")
abstract suspend fun reset()
@Insert
protected abstract suspend fun selectPatches(patches: List<SelectedPatch>)
@Query("DELETE FROM selected_patches WHERE selection = :selectionId")
protected abstract suspend fun clearSelection(selectionId: Int)
@Transaction
open suspend fun updateSelections(selections: Map<Int, Set<String>>) =
selections.forEach { (selectionUid, patches) ->
clearSelection(selectionUid)
selectPatches(patches.map { SelectedPatch(selectionUid, it) })
}
}

View File

@ -0,0 +1,15 @@
package app.revanced.manager.di
import android.content.Context
import androidx.room.Room
import app.revanced.manager.data.room.AppDatabase
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val databaseModule = module {
fun provideAppDatabase(context: Context) = Room.databaseBuilder(context, AppDatabase::class.java, "manager").build()
single {
provideAppDatabase(androidContext())
}
}

View File

@ -0,0 +1,60 @@
package app.revanced.manager.di
import android.content.Context
import app.revanced.manager.BuildConfig
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.UserAgent
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
import okhttp3.Cache
import okhttp3.Dns
import org.koin.android.ext.koin.androidContext
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
import java.net.Inet4Address
import java.net.InetAddress
val httpModule = module {
fun provideHttpClient(context: Context, json: Json) = HttpClient(OkHttp) {
engine {
config {
dns(object : Dns {
override fun lookup(hostname: String): List<InetAddress> {
val addresses = Dns.SYSTEM.lookup(hostname)
return if (hostname == "raw.githubusercontent.com") {
addresses.filterIsInstance<Inet4Address>()
} else {
addresses
}
}
})
cache(Cache(context.cacheDir.resolve("cache").also { it.mkdirs() }, 1024 * 1024 * 100))
followRedirects(true)
followSslRedirects(true)
}
}
install(ContentNegotiation) {
json(json)
}
install(HttpTimeout) {
socketTimeoutMillis = 10000
}
install(UserAgent) {
agent = "ReVanced-Manager/${BuildConfig.VERSION_CODE}"
}
}
fun provideJson() = Json {
encodeDefaults = true
isLenient = true
ignoreUnknownKeys = true
}
single {
provideHttpClient(androidContext(), get())
}
singleOf(::provideJson)
}

View File

@ -0,0 +1,11 @@
package app.revanced.manager.di
import app.revanced.manager.domain.manager.KeystoreManager
import app.revanced.manager.util.PM
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val managerModule = module {
singleOf(::KeystoreManager)
singleOf(::PM)
}

View File

@ -0,0 +1,9 @@
package app.revanced.manager.di
import app.revanced.manager.domain.manager.PreferencesManager
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val preferencesModule = module {
singleOf(::PreferencesManager)
}

View File

@ -0,0 +1,29 @@
package app.revanced.manager.di
import app.revanced.manager.data.platform.Filesystem
import app.revanced.manager.data.platform.NetworkInfo
import app.revanced.manager.domain.repository.*
import app.revanced.manager.domain.worker.WorkerRepository
import app.revanced.manager.network.api.ReVancedAPI
import org.koin.core.module.dsl.createdAtStart
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val repositoryModule = module {
singleOf(::ReVancedAPI)
singleOf(::Filesystem) {
createdAtStart()
}
singleOf(::NetworkInfo)
singleOf(::PatchBundlePersistenceRepository)
singleOf(::PatchSelectionRepository)
singleOf(::PatchOptionsRepository)
singleOf(::PatchBundleRepository) {
// It is best to load patch bundles ASAP
createdAtStart()
}
singleOf(::DownloaderPluginRepository)
singleOf(::WorkerRepository)
singleOf(::DownloadedAppRepository)
singleOf(::InstalledAppRepository)
}

View File

@ -0,0 +1,9 @@
package app.revanced.manager.di
import app.revanced.manager.domain.installer.RootInstaller
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val rootModule = module {
singleOf(::RootInstaller)
}

View File

@ -0,0 +1,9 @@
package app.revanced.manager.di
import app.revanced.manager.network.service.HttpService
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val serviceModule = module {
singleOf(::HttpService)
}

View File

@ -0,0 +1,26 @@
package app.revanced.manager.di
import app.revanced.manager.ui.viewmodel.*
import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.dsl.module
val viewModelModule = module {
viewModelOf(::MainViewModel)
viewModelOf(::DashboardViewModel)
viewModelOf(::SelectedAppInfoViewModel)
viewModelOf(::PatchesSelectorViewModel)
viewModelOf(::GeneralSettingsViewModel)
viewModelOf(::AdvancedSettingsViewModel)
viewModelOf(::AppSelectorViewModel)
viewModelOf(::PatcherViewModel)
viewModelOf(::UpdateViewModel)
viewModelOf(::ChangelogsViewModel)
viewModelOf(::ImportExportViewModel)
viewModelOf(::AboutViewModel)
viewModelOf(::DeveloperOptionsViewModel)
viewModelOf(::ContributorViewModel)
viewModelOf(::DownloadsViewModel)
viewModelOf(::InstalledAppsViewModel)
viewModelOf(::InstalledAppInfoViewModel)
viewModelOf(::UpdatesSettingsViewModel)
}

View File

@ -0,0 +1,9 @@
package app.revanced.manager.di
import app.revanced.manager.patcher.worker.PatcherWorker
import org.koin.androidx.workmanager.dsl.workerOf
import org.koin.dsl.module
val workerModule = module {
workerOf(::PatcherWorker)
}

View File

@ -0,0 +1,21 @@
package app.revanced.manager.domain.bundles
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.InputStream
class LocalPatchBundle(name: String, id: Int, directory: File) :
PatchBundleSource(name, id, directory) {
suspend fun replace(patches: InputStream) {
withContext(Dispatchers.IO) {
patchBundleOutputStream().use { outputStream ->
patches.copyTo(outputStream)
}
}
reload()?.also {
saveVersion(it.readManifestAttribute("Version"))
}
}
}

View File

@ -0,0 +1,114 @@
package app.revanced.manager.domain.bundles
import android.app.Application
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.revanced.manager.R
import app.revanced.manager.domain.repository.PatchBundlePersistenceRepository
import app.revanced.manager.patcher.patch.PatchBundle
import app.revanced.manager.util.tag
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.File
import java.io.OutputStream
/**
* A [PatchBundle] source.
*/
@Stable
sealed class PatchBundleSource(initialName: String, val uid: Int, directory: File) : KoinComponent {
protected val configRepository: PatchBundlePersistenceRepository by inject()
private val app: Application by inject()
protected val patchesFile = directory.resolve("patches.jar")
private val _state = MutableStateFlow(load())
val state = _state.asStateFlow()
private val _nameFlow = MutableStateFlow(initialName)
val nameFlow =
_nameFlow.map { it.ifEmpty { app.getString(if (isDefault) R.string.bundle_name_default else R.string.bundle_name_fallback) } }
suspend fun getName() = nameFlow.first()
/**
* Returns true if the bundle has been downloaded to local storage.
*/
fun hasInstalled() = patchesFile.exists()
protected fun patchBundleOutputStream(): OutputStream = with(patchesFile) {
// Android 14+ requires dex containers to be readonly.
try {
setWritable(true, true)
outputStream()
} finally {
setReadOnly()
}
}
private fun load(): State {
if (!hasInstalled()) return State.Missing
return try {
State.Loaded(PatchBundle(patchesFile))
} catch (t: Throwable) {
Log.e(tag, "Failed to load patch bundle with UID $uid", t)
State.Failed(t)
}
}
suspend fun reload(): PatchBundle? {
val newState = load()
_state.value = newState
val bundle = newState.patchBundleOrNull()
// Try to read the name from the patch bundle manifest if the bundle does not have a name.
if (bundle != null && _nameFlow.value.isEmpty()) {
bundle.readManifestAttribute("Name")?.let { setName(it) }
}
return bundle
}
/**
* Create a flow that emits the [app.revanced.manager.data.room.bundles.BundleProperties] of this [PatchBundleSource].
* The flow will emit null if the associated [PatchBundleSource] is deleted.
*/
fun propsFlow() = configRepository.getProps(uid).flowOn(Dispatchers.Default)
suspend fun getProps() = propsFlow().first()!!
suspend fun currentVersion() = getProps().version
protected suspend fun saveVersion(version: String?) =
configRepository.updateVersion(uid, version)
suspend fun setName(name: String) {
configRepository.setName(uid, name)
_nameFlow.value = name
}
sealed interface State {
fun patchBundleOrNull(): PatchBundle? = null
data object Missing : State
data class Failed(val throwable: Throwable) : State
data class Loaded(val bundle: PatchBundle) : State {
override fun patchBundleOrNull() = bundle
}
}
companion object Extensions {
val PatchBundleSource.isDefault inline get() = uid == 0
val PatchBundleSource.asRemoteOrNull inline get() = this as? RemotePatchBundle
val PatchBundleSource.nameState
@Composable inline get() = nameFlow.collectAsStateWithLifecycle(
""
)
}
}

View File

@ -0,0 +1,71 @@
package app.revanced.manager.domain.bundles
import androidx.compose.runtime.Stable
import app.revanced.manager.network.api.ReVancedAPI
import app.revanced.manager.network.dto.ReVancedAsset
import app.revanced.manager.network.service.HttpService
import app.revanced.manager.network.utils.getOrThrow
import io.ktor.client.request.url
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.component.inject
import java.io.File
@Stable
sealed class RemotePatchBundle(name: String, id: Int, directory: File, val endpoint: String) :
PatchBundleSource(name, id, directory) {
protected val http: HttpService by inject()
protected abstract suspend fun getLatestInfo(): ReVancedAsset
private suspend fun download(info: ReVancedAsset) = withContext(Dispatchers.IO) {
patchBundleOutputStream().use {
http.streamTo(it) {
url(info.downloadUrl)
}
}
saveVersion(info.version)
reload()
}
suspend fun downloadLatest() {
download(getLatestInfo())
}
suspend fun update(): Boolean = withContext(Dispatchers.IO) {
val info = getLatestInfo()
if (hasInstalled() && info.version == currentVersion())
return@withContext false
download(info)
true
}
suspend fun deleteLocalFiles() = withContext(Dispatchers.Default) {
patchesFile.delete()
reload()
}
suspend fun setAutoUpdate(value: Boolean) = configRepository.setAutoUpdate(uid, value)
companion object {
const val updateFailMsg = "Failed to update patch bundle(s)"
}
}
class JsonPatchBundle(name: String, id: Int, directory: File, endpoint: String) :
RemotePatchBundle(name, id, directory, endpoint) {
override suspend fun getLatestInfo() = withContext(Dispatchers.IO) {
http.request<ReVancedAsset> {
url(endpoint)
}.getOrThrow()
}
}
class APIPatchBundle(name: String, id: Int, directory: File, endpoint: String) :
RemotePatchBundle(name, id, directory, endpoint) {
private val api: ReVancedAPI by inject()
override suspend fun getLatestInfo() = api.getPatchesUpdate().getOrThrow()
}

View File

@ -0,0 +1,181 @@
package app.revanced.manager.domain.installer
import android.app.Application
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import app.revanced.manager.IRootSystemService
import app.revanced.manager.service.ManagerRootService
import app.revanced.manager.util.PM
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ipc.RootService
import com.topjohnwu.superuser.nio.FileSystemManager
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.time.withTimeoutOrNull
import kotlinx.coroutines.withContext
import java.io.File
import java.time.Duration
class RootInstaller(
private val app: Application,
private val pm: PM
) : ServiceConnection {
private var remoteFS = CompletableDeferred<FileSystemManager>()
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val ipc = IRootSystemService.Stub.asInterface(service)
val binder = ipc.fileSystemService
remoteFS.complete(FileSystemManager.getRemote(binder))
}
override fun onServiceDisconnected(name: ComponentName?) {
remoteFS = CompletableDeferred()
}
private suspend fun awaitRemoteFS(): FileSystemManager {
if (remoteFS.isActive) {
withContext(Dispatchers.Main) {
val intent = Intent(app, ManagerRootService::class.java)
RootService.bind(intent, this@RootInstaller)
}
}
return withTimeoutOrNull(Duration.ofSeconds(20L)) {
remoteFS.await()
} ?: throw RootServiceException()
}
private suspend fun getShell() = with(CompletableDeferred<Shell>()) {
Shell.getShell(::complete)
await()
}
suspend fun execute(vararg commands: String) = getShell().newJob().add(*commands).exec()
fun hasRootAccess() = Shell.isAppGrantedRoot() ?: false
fun isDeviceRooted() = System.getenv("PATH")?.split(":")?.any { path ->
File(path, "su").canExecute()
} ?: false
suspend fun isAppInstalled(packageName: String) =
awaitRemoteFS().getFile("$modulesPath/$packageName-revanced").exists()
suspend fun isAppMounted(packageName: String) = withContext(Dispatchers.IO) {
pm.getPackageInfo(packageName)?.applicationInfo?.sourceDir?.let {
execute("mount | grep \"$it\"").isSuccess
} ?: false
}
suspend fun mount(packageName: String) {
if (isAppMounted(packageName)) return
withContext(Dispatchers.IO) {
val stockAPK = pm.getPackageInfo(packageName)?.applicationInfo?.sourceDir
?: throw Exception("Failed to load application info")
val patchedAPK = "$modulesPath/$packageName-revanced/$packageName.apk"
execute("mount -o bind \"$patchedAPK\" \"$stockAPK\"").assertSuccess("Failed to mount APK")
}
}
suspend fun unmount(packageName: String) {
if (!isAppMounted(packageName)) return
withContext(Dispatchers.IO) {
val stockAPK = pm.getPackageInfo(packageName)?.applicationInfo?.sourceDir
?: throw Exception("Failed to load application info")
execute("umount -l \"$stockAPK\"").assertSuccess("Failed to unmount APK")
}
}
suspend fun install(
patchedAPK: File,
stockAPK: File?,
packageName: String,
version: String,
label: String
) = withContext(Dispatchers.IO) {
val remoteFS = awaitRemoteFS()
val assets = app.assets
val modulePath = "$modulesPath/$packageName-revanced"
unmount(packageName)
stockAPK?.let { stockApp ->
pm.getPackageInfo(packageName)?.let { packageInfo ->
// TODO: get user id programmatically
if (pm.getVersionCode(packageInfo) <= pm.getVersionCode(
pm.getPackageInfo(patchedAPK)
?: error("Failed to get package info for patched app")
)
)
execute("pm uninstall -k --user 0 $packageName").assertSuccess("Failed to uninstall stock app")
}
execute("pm install \"${stockApp.absolutePath}\"").assertSuccess("Failed to install stock app")
}
remoteFS.getFile(modulePath).mkdir()
listOf(
"service.sh",
"module.prop",
).forEach { file ->
assets.open("root/$file").use { inputStream ->
remoteFS.getFile("$modulePath/$file").newOutputStream()
.use { outputStream ->
val content = String(inputStream.readBytes())
.replace("__PKG_NAME__", packageName)
.replace("__VERSION__", version)
.replace("__LABEL__", label)
.toByteArray()
outputStream.write(content)
}
}
}
"$modulePath/$packageName.apk".let { apkPath ->
remoteFS.getFile(patchedAPK.absolutePath)
.also { if (!it.exists()) throw Exception("File doesn't exist") }
.newInputStream().use { inputStream ->
remoteFS.getFile(apkPath).newOutputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
execute(
"chmod 644 $apkPath",
"chown system:system $apkPath",
"chcon u:object_r:apk_data_file:s0 $apkPath",
"chmod +x $modulePath/service.sh"
).assertSuccess("Failed to set file permissions")
}
}
suspend fun uninstall(packageName: String) {
val remoteFS = awaitRemoteFS()
if (isAppMounted(packageName))
unmount(packageName)
remoteFS.getFile("$modulesPath/$packageName-revanced").deleteRecursively()
.also { if (!it) throw Exception("Failed to delete files") }
}
companion object {
const val modulesPath = "/data/adb/modules"
private fun Shell.Result.assertSuccess(errorMessage: String) {
if (!isSuccess) throw Exception(errorMessage)
}
}
}
class RootServiceException : Exception("Root not available")

View File

@ -0,0 +1,95 @@
package app.revanced.manager.domain.manager
import android.app.Application
import android.content.Context
import app.revanced.library.ApkSigner
import app.revanced.library.ApkUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayInputStream
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Files
import java.security.UnrecoverableKeyException
import java.util.Date
import kotlin.time.Duration.Companion.days
class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
companion object Constants {
/**
* Default alias and password for the keystore.
*/
const val DEFAULT = "ReVanced"
private val eightYearsFromNow get() = Date(System.currentTimeMillis() + (365.days * 8).inWholeMilliseconds * 24)
}
private val keystorePath =
app.getDir("signing", Context.MODE_PRIVATE).resolve("manager.keystore")
private suspend fun updatePrefs(cn: String, pass: String) = prefs.edit {
prefs.keystoreCommonName.value = cn
prefs.keystorePass.value = pass
}
private suspend fun signingDetails(path: File = keystorePath) = ApkUtils.KeyStoreDetails(
keyStore = path,
keyStorePassword = null,
alias = prefs.keystoreCommonName.get(),
password = prefs.keystorePass.get()
)
suspend fun sign(input: File, output: File) = withContext(Dispatchers.Default) {
ApkUtils.signApk(input, output, prefs.keystoreCommonName.get(), signingDetails())
}
suspend fun regenerate() = withContext(Dispatchers.Default) {
val keyCertPair = ApkSigner.newPrivateKeyCertificatePair(
prefs.keystoreCommonName.get(),
eightYearsFromNow
)
val ks = ApkSigner.newKeyStore(
setOf(
ApkSigner.KeyStoreEntry(
DEFAULT, DEFAULT, keyCertPair
)
)
)
withContext(Dispatchers.IO) {
keystorePath.outputStream().use {
ks.store(it, null)
}
}
updatePrefs(DEFAULT, DEFAULT)
}
suspend fun import(cn: String, pass: String, keystore: InputStream): Boolean {
val keystoreData = withContext(Dispatchers.IO) { keystore.readBytes() }
try {
val ks = ApkSigner.readKeyStore(ByteArrayInputStream(keystoreData), null)
ApkSigner.readPrivateKeyCertificatePair(ks, cn, pass)
} catch (_: UnrecoverableKeyException) {
return false
} catch (_: IllegalArgumentException) {
return false
}
withContext(Dispatchers.IO) {
Files.write(keystorePath.toPath(), keystoreData)
}
updatePrefs(cn, pass)
return true
}
fun hasKeystore() = keystorePath.exists()
suspend fun export(target: OutputStream) {
withContext(Dispatchers.IO) {
Files.copy(keystorePath.toPath(), target)
}
}
}

Some files were not shown because too many files have changed in this diff Show More