1
mirror of https://github.com/topjohnwu/Magisk synced 2025-11-16 12:13:28 +01:00

Compare commits

...

136 Commits

Author SHA1 Message Date
topjohnwu
78f7fa348e Release Magisk v30.1
[skip ci]
2025-07-03 03:10:10 -07:00
pndwal
d8c448b99d Update faq.md
Add information on restoring Magisk App functionality when stub and full apps conflict.
2025-07-03 02:51:01 -07:00
topjohnwu
d4b83b6a44 Fix app compilation 2025-07-03 02:42:08 -07:00
vvb2060
e5d36d1d24 app: support config restrict policy 2025-07-03 02:42:08 -07:00
vvb2060
ff18cb8e70 su: support drop capabilities 2025-07-03 02:42:08 -07:00
topjohnwu
37a9724a54 Apply clippy fix 2025-07-02 21:20:14 -07:00
topjohnwu
d660401063 Treat magisk symlinks differently 2025-07-02 21:20:14 -07:00
topjohnwu
88541d6f49 Fix file attribute copy in module mounting logic
Due to various reasons, we cannot directly mount module files in /data
into the real paths. Instead we bind mount the module root directory and
remount this mirror with specific mount-point flags. Relevant to this
bug, the module mount is mounted as read-only, which means the file
attribute copy operation could fail in certain configurations.

The fix here is to always copy file attributes into writable locations,
so either in the tmpfs worker directory, or in the module directory
under /data.

A new test case is added to make sure this regression will no longer
happen again in the future.

Fix #9139
2025-07-02 19:23:46 -07:00
topjohnwu
ecd6129fe5 Add systemless hosts test 2025-07-02 19:23:46 -07:00
topjohnwu
6dfe9df9e2 Run cargo fmt 2025-07-02 19:23:46 -07:00
topjohnwu
e81de7ec36 Release Magisk v30.0
[skip ci]
2025-07-01 10:14:43 -07:00
topjohnwu
c78da1ce24 Update v30.0 changelog 2025-07-01 10:00:38 -07:00
Wang Han
7b2d40987c Refactor magisk bins injection logic
Magisk binary mounting logic is not very clear now. In this commit, it
is rewritten in a more robust way. Now it has following cases in mind:
1) Device has a su binary, magisk need to overlay it
2) Choose the PATH with least files to reduce bind mount
3) Filter path which is not suitable
2025-07-01 02:16:16 -07:00
Wang Han
3a37e8c9c5 Don't clone attributes for magisk symlinks
This avoids use existing attributes for su, which will obviously break
magisk functions.
2025-07-01 02:16:16 -07:00
igor
58b405bce1 Update portuguese/english translation 2025-07-01 01:32:36 -07:00
Wang Han
810174ef73 Ignore set_context() error if magisktmp is /sbin
recreate_sbin() will bind mount original files in /sbin to tmpfs /sbin,
so we have no choice but just log here to let the loop continue.
2025-06-30 17:39:54 -07:00
topjohnwu
690a5ac033 Update to ONDK r28.5 2025-06-30 12:15:43 -07:00
topjohnwu
89aad31f7e Update gradle dependencies 2025-06-26 09:35:18 -07:00
topjohnwu
7124db98e3 Stop involving JSON in release script 2025-06-20 00:59:38 -07:00
topjohnwu
0860e859f7 Stop updating README for each release 2025-06-20 00:32:11 -07:00
topjohnwu
04008949b8 Deprecate canary channel 2025-06-20 00:22:17 -07:00
Wang Han
39f2940bd1 Skip symlink in restore_tmpcon()
If magisktmp is /sbin, there may exist files which is symlink to files in
root dir. As root is RO, setcontext will fail and break iterating loop.
2025-06-16 11:09:33 -07:00
topjohnwu
1460317ebd Cleanup C++ headers 2025-06-16 02:25:38 -07:00
topjohnwu
12340c9bd5 Update gradle version 2025-06-16 02:17:29 -07:00
topjohnwu
c4590fe2ba Reorganize buildSrc 2025-06-10 16:51:31 -07:00
topjohnwu
b36066bbcd Run clippy fix 2025-06-10 16:34:38 -07:00
topjohnwu
65d1c5827c Update dependencies 2025-06-10 16:33:06 -07:00
AshiVered
1d675c8b2e Update strings.xml
Improve Hebrew translations
2025-06-06 17:08:51 -07:00
Wang Han
0b494ed7df Avoid throwing error if chmod a symlink
It is possible that we want to clone attributes of a regular file to a
symlink. In this case, we don't need to error out if chmod fails. Just
skip it.
2025-06-06 17:08:34 -07:00
topjohnwu
48d9fc24eb Update release.sh 2025-06-05 11:00:27 -07:00
topjohnwu
83426f7f36 Update check update logic 2025-06-04 12:50:19 -07:00
Wang Han
0e86d4dbcb Fix file pointer leak on error path 2025-06-03 00:34:52 -07:00
Wang Han
5e050d7456 Check binary existence before injecting zygisk bins 2025-06-03 00:33:40 -07:00
topjohnwu
898580bf90 Update dependencies 2025-06-02 19:51:12 -07:00
topjohnwu
86621a4c46 Fix gradle cache 2025-05-30 11:11:53 -07:00
topjohnwu
a834e72b71 Use sccache for clippy 2025-05-30 11:11:45 -07:00
topjohnwu
d8cf42af16 Reduce unstable feature usage 2025-05-30 11:11:36 -07:00
topjohnwu
8c79d66b7b Update ONDK r28.4 2025-05-30 01:53:26 -07:00
Wang Han
fada8b148a Ensure manager can always bypass su access policy
This fixes https://github.com/topjohnwu/Magisk/issues/9050.
2025-05-21 16:14:46 -07:00
topjohnwu
dc0acea47c Remove C++ I/O streams 2025-05-20 03:26:00 -07:00
topjohnwu
78d1200608 Migrate all compression code to Rust 2025-05-20 03:26:00 -07:00
topjohnwu
527bbc0368 Migrate module mounting to Rust 2025-05-20 03:24:43 -07:00
topjohnwu
4c89c7e2b3 Directly use MagiskJson 2025-05-19 10:40:58 -07:00
topjohnwu
adbea7e313 Improve update check code 2025-05-19 10:23:15 -07:00
vvb2060
76962f965e app: use github api to check updates 2025-05-19 10:23:15 -07:00
topjohnwu
a4b8c5e46b Update app dependencies 2025-05-18 22:24:44 -07:00
The Ali Dev
83c707439c Add Urdu Translation 2025-05-18 22:21:05 -07:00
Radoš Milićev
25dd6121f4 Add author for Serbian translations to .xml files 2025-05-18 21:54:24 -07:00
Radoš Milićev
67f35ad027 Add Serbian latin (values-b+sr+Latn) 2025-05-18 21:54:24 -07:00
topjohnwu
0c4b8afbc5 Simplify JNI hooking code 2025-05-17 02:05:21 -07:00
topjohnwu
34b30d7ce1 Promite Magisk v29.0 to stable 2025-05-16 12:17:53 -07:00
topjohnwu
2215088973 Remove unused files in native directory 2025-05-15 13:48:12 -07:00
topjohnwu
8b7fb6cdde Improve scripts 2025-05-15 02:56:36 -07:00
topjohnwu
94c7dbedf2 Fix signing config
[skip ci]
2025-05-14 15:14:34 -07:00
topjohnwu
b1dc47a047 Release new canary build
[skip ci]
2025-05-14 01:41:38 -07:00
topjohnwu
62b1310d97 Release Magisk v29.0
[skip ci]
2025-05-14 01:26:05 -07:00
topjohnwu
0a86916d3a Fix cleanup 2025-05-14 01:23:54 -07:00
topjohnwu
9907ce57aa Add Magisk r29.0 release notes 2025-05-14 01:13:37 -07:00
topjohnwu
b92626cacc Use sudo instead of su 2025-05-13 17:26:09 -07:00
topjohnwu
5a762f0a8e Move all gradle files into folder app
Decouple java and native projects
2025-05-13 17:04:41 -07:00
topjohnwu
5dd7a7d804 Better ABI support for build.py 2025-05-13 14:35:57 -07:00
topjohnwu
7831f40691 Make tests more granular 2025-05-13 14:08:54 -07:00
topjohnwu
4f4b1ff885 Add sepolicy.rule patching tests 2025-05-13 14:08:54 -07:00
topjohnwu
97901979dd Test replace functionality 2025-05-13 14:08:54 -07:00
topjohnwu
287316842c Fix Android M sepolicy reading 2025-05-13 12:28:10 -07:00
topjohnwu
608786e8f3 Print verbose logs in avd_test.sh and cuttlefish.sh 2025-05-11 02:18:36 -07:00
topjohnwu
9684a35cab Use rust::String::c_str to ensure nil termination 2025-05-09 16:02:46 -07:00
Radoš Milićev
e3e4202954 Update translation of core strings 2025-05-09 11:06:03 -07:00
Radoš Milićev
23c2054d46 Translate stub strings 2025-05-09 11:06:03 -07:00
Wang Han
a20a2a8fa0 Recognize Samsung custom policy version path 2025-05-09 11:03:58 -07:00
topjohnwu
a2896be4a6 Cargo fmt
[skip ci]
2025-05-08 23:29:27 -07:00
LoveSy
e9220a28d9 Use splice to pump tty to avoid userspace copying 2025-05-08 23:20:46 -07:00
vvb2060
cf12087e21 app: disable multiArch 2025-05-08 21:02:26 -07:00
topjohnwu
00c1b36837 Support generating files for C++ IDE 2025-05-07 22:18:44 -07:00
topjohnwu
03e034795d Implement Ord and PartialOrd for Utf8CStr familiy 2025-05-05 11:33:33 -07:00
LoveSy
79c0fafe43 Fix cleanup pre-init mount 2025-05-05 11:00:39 -07:00
vvb2060
d499819ba0 app: ignore androidx meta prop file 2025-05-02 16:40:37 -07:00
vvb2060
86da917174 core: fix mkdirs 2025-05-02 16:39:46 -07:00
ZGX089
30bd7d6555 feat: Improve Arabic translation (stub)
Fixed typos, Grammatical errors and changed/fixed other strings.
2025-05-01 13:47:46 -07:00
ZGX089
e5a12f0f5f feat: Improve Arabic translation
Fix Grammatical errors, typos, and changed/fixed other strings.
2025-05-01 13:47:46 -07:00
topjohnwu
c85a8434c6 Update dependencies 2025-04-29 16:22:45 -07:00
topjohnwu
427a1ca4e5 Release new canary build
[skip ci]
2025-04-29 11:54:17 -07:00
topjohnwu
22884e173a Implement reboot in Rust 2025-04-28 17:22:14 -07:00
topjohnwu
d1829308e9 Move more daemon_start code into Rust 2025-04-28 17:22:14 -07:00
topjohnwu
73840f8721 Migrate selinux.cpp to selinux.rs 2025-04-28 17:22:14 -07:00
topjohnwu
c7d1af9805 Stop using PathBuf in package.rs 2025-04-28 17:22:14 -07:00
topjohnwu
4ad26d3dfb Better path methods 2025-04-28 17:22:14 -07:00
topjohnwu
0c70b7670c Cleanup dir implementations 2025-04-28 17:22:14 -07:00
topjohnwu
f44d044095 Remove Utf8CStrBuffer 2025-04-28 17:22:14 -07:00
topjohnwu
5c1cb13472 Remove AsUtf8CStr trait 2025-04-28 17:22:14 -07:00
topjohnwu
3327fc668e Remove FsPath and FsPathMnt trait
Directly use Utf8CStr
2025-04-28 17:22:14 -07:00
topjohnwu
610945ac54 Remove open_fd macro 2025-04-28 17:22:14 -07:00
Howard Wu
ddf5474917 apt-get update before install to fix ci 2025-04-28 11:16:14 -07:00
Howard Wu
6ba1685ade Fix some seopt log 2025-04-22 03:25:21 -07:00
topjohnwu
e02b5f7868 Rename cstr_buf to cstr::buf 2025-04-22 03:21:00 -07:00
topjohnwu
ab2e5d1e7e Make FsPathBuf a trait and rename to FsPathBuilder 2025-04-22 03:21:00 -07:00
topjohnwu
f3fef7bfe4 Make FsPath a trait 2025-04-22 03:21:00 -07:00
topjohnwu
c34c7838bb Cleanup cstr implementation 2025-04-22 03:21:00 -07:00
topjohnwu
c8a16b0e0c Remove unused code 2025-04-16 17:13:03 -07:00
topjohnwu
14f9ed91a1 Remove unused methods 2025-04-15 11:35:31 -07:00
topjohnwu
7a207d4ccf Only accept UTF-8 directory entries 2025-04-15 10:26:22 -07:00
topjohnwu
92a42d901f Move most implementation into Directory 2025-04-15 10:26:22 -07:00
topjohnwu
084d89fcce Create Utf8CStrBuffer type 2025-04-15 10:26:22 -07:00
topjohnwu
55b036c071 Introduce BorrowedDirectory 2025-04-15 10:26:22 -07:00
topjohnwu
30e79310ab Make pointers NonNull after error check 2025-04-15 00:18:48 -07:00
topjohnwu
f063fa5054 Cleanup xwrap implementation 2025-04-15 00:18:48 -07:00
topjohnwu
7bd901273c Provide richer error messages
Make sure most syscall/libc calls results are mapped to OsResult
that can produce more detailed error messages.
2025-04-15 00:18:48 -07:00
topjohnwu
c1e061603b Specify ADB_SERIAL for emulator 2025-04-13 21:43:11 -07:00
topjohnwu
cb08504fe5 Update cargo dependencies 2025-04-11 14:48:16 -07:00
topjohnwu
c0a1fb77be Code cleanup 2025-04-11 14:48:01 -07:00
LoveSy
4864c1112a no pty for -c by default, and add -i to force pty 2025-04-11 13:21:10 -07:00
LoveSy
9ddeab034b Fix wrong tty pump
See #1463
2025-04-11 13:21:10 -07:00
LoveSy
c4847ed288 Move pts to rust, and avoid using thread 2025-04-11 13:21:10 -07:00
topjohnwu
b8f1523fb2 Minor code reorg
[skip ci]
2025-04-08 17:20:22 -07:00
topjohnwu
fb7fa8a6b3 Update to ONDK r29.1 2025-04-08 12:11:59 -07:00
topjohnwu
9c7d359093 Optimize and format imports
[skip ci]
2025-04-08 09:57:09 -07:00
topjohnwu
eb54bc1fd7 Cleanup unused code 2025-04-08 02:33:52 -07:00
topjohnwu
d4a0286e13 Migrate magiskinit selinux.cpp to Rust 2025-04-08 02:33:52 -07:00
Steven Xu
83e66767ff refactor: use empty navOptions 2025-04-02 09:13:32 -07:00
Steven Xu
7dc010749b feat: remove animation settings button transition 2025-04-02 09:13:32 -07:00
Steven Xu
8e8d013b1b feat: remove log overscroll 2025-04-02 09:13:32 -07:00
Steven Xu
bba0373808 feat: remove navigation transition when clicking buttons on the bottom bar 2025-04-02 09:13:32 -07:00
topjohnwu
1fa318dc8c Use Rust elf-cleaner implementation 2025-04-01 18:32:54 -07:00
cheesetosti
6edc5e2037 Update install.md
fixed grammar n stuff
2025-04-01 12:13:29 -07:00
topjohnwu
1523ed9f78 Always go through rustup proxies 2025-04-01 10:01:35 -07:00
topjohnwu
8e604d2ab8 Update cuttlefish CI 2025-03-28 00:12:31 -07:00
topjohnwu
2aba7247a9 Skip stub APK install on emulator
Reduce test flakiness
2025-03-26 13:15:12 -07:00
topjohnwu
e66fe8533e API 36 does not support wait_for_bootanim 2025-03-26 13:15:12 -07:00
vvb2060
b03fbb3917 avd_test: upgrade to android16 beta3 2025-03-26 13:15:12 -07:00
vvb2060
c2ece62e4c native: delete global 16k option
NDK 28 enable 16 KiB page size compatibility option by default, delete the global option to restore 4k alignment for 32-bit arch.
2025-03-26 13:15:12 -07:00
vvb2060
8c972dcf34 app: target sdk 36 2025-03-26 13:15:12 -07:00
topjohnwu
50af14f2a3 Move all MagiskInit entrypoints into init.rs 2025-03-24 17:26:03 -07:00
topjohnwu
e0a356b319 Introduce mount helper methods 2025-03-24 17:26:03 -07:00
topjohnwu
c09a792958 Reorganize magiskinit code 2025-03-24 17:26:03 -07:00
topjohnwu
0bbfe7f44d Fix 2SI on legacy SAR devices 2025-03-24 17:26:03 -07:00
topjohnwu
a396abf565 Minor changes
[skip ci]
2025-03-22 01:16:51 -07:00
217 changed files with 7889 additions and 6661 deletions

3
.gitattributes vendored
View File

@@ -12,7 +12,8 @@
# Denote all files that are truly binary and should not be modified.
tools/** binary
tools/rustup_wrapper/** -binary
tools/rustup-wrapper/** -binary
tools/elf-cleaner/** -binary
*.jar binary
*.exe binary
*.apk binary

View File

@@ -70,7 +70,7 @@ runs:
.gradle/caches
.gradle/wrapper
!.gradle/caches/build-cache-*
key: gradle-cache-${{ hashFiles('gradle/**') }}
key: gradle-cache-${{ hashFiles('app/gradle/**') }}
restore-keys: gradle-cache-
- name: Restore Gradle dependencies

View File

@@ -6,10 +6,7 @@ on:
paths:
- "app/**"
- "native/**"
- "buildSrc/**"
- "build.py"
- "gradle.properties"
- "gradle/libs.versions.toml"
- ".github/workflows/build.yml"
pull_request:
branches: [master]
@@ -39,7 +36,7 @@ jobs:
run: ./build.py -v all
- name: Stop gradle daemon
run: ./gradlew --stop
run: ./app/gradlew --stop
- name: Upload build artifact
uses: actions/upload-artifact@v4
@@ -75,7 +72,7 @@ jobs:
run: python build.py -v -c .github/ci.prop all
- name: Stop gradle daemon
run: ./gradlew --stop
run: ./app/gradlew --stop
avd-test:
name: Test API ${{ matrix.version }} (x86_64)
@@ -88,9 +85,9 @@ jobs:
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
type: [""]
include:
- version: "Baklava"
- version: 36
type: "google_apis"
- version: "Baklava"
- version: 36
type: "google_apis_ps16k"
steps:
@@ -113,7 +110,7 @@ jobs:
timeout-minutes: 10
env:
AVD_TEST_LOG: 1
run: scripts/avd_test.sh ${{ matrix.version }} ${{ matrix.type }}
run: scripts/avd.sh test ${{ matrix.version }} ${{ matrix.type }}
- name: Upload logs on error
if: ${{ failure() }}
@@ -155,7 +152,7 @@ jobs:
env:
FORCE_32_BIT: 1
AVD_TEST_LOG: 1
run: scripts/avd_test.sh ${{ matrix.version }}
run: scripts/avd.sh test ${{ matrix.version }}
- name: Upload logs on error
if: ${{ failure() }}
@@ -177,8 +174,8 @@ jobs:
fail-fast: false
matrix:
include:
- branch: "aosp-main"
device: "aosp_cf_x86_64_phone"
- branch: "aosp-android-latest-release"
device: "aosp_cf_x86_64_only_phone"
steps:
- name: Check out
@@ -197,7 +194,7 @@ jobs:
- name: Run Cuttlefish test
timeout-minutes: 10
run: su $USER -c 'scripts/cuttlefish.sh test'
run: sudo -E -u $USER scripts/cuttlefish.sh test
- name: Upload logs on error
if: ${{ failure() }}

10
.gitignore vendored
View File

@@ -2,19 +2,13 @@ out
*.zip
*.jks
*.apk
*.log
/config.prop
/notes.md
/update.sh
/app/dict.txt
# Built binaries
native/out
# Android Studio / Gradle
# Android Studio
*.iml
.gradle
.idea
.kotlin
/local.properties
/build
/captures

6
.gitmodules vendored
View File

@@ -10,9 +10,6 @@
[submodule "libcxx"]
path = native/src/external/libcxx
url = https://github.com/topjohnwu/libcxx.git
[submodule "zopfli"]
path = native/src/external/zopfli
url = https://github.com/google/zopfli.git
[submodule "cxx-rs"]
path = native/src/external/cxx-rs
url = https://github.com/topjohnwu/cxx.git
@@ -25,6 +22,3 @@
[submodule "crt0"]
path = native/src/external/crt0
url = https://github.com/topjohnwu/crt0.git
[submodule "termux-elf-cleaner"]
path = tools/termux-elf-cleaner
url = https://github.com/termux/termux-elf-cleaner.git

View File

@@ -16,13 +16,7 @@ Some highlight features:
## Downloads
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
Click the icon below to download Magisk apk.
[![](https://img.shields.io/badge/Magisk-v28.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
[![](https://img.shields.io/badge/Magisk%20Beta-v28.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://github.com/topjohnwu/Magisk/releases/tag/canary-28103)
[Github](https://github.com/topjohnwu/Magisk/releases) is the only source where you can get official Magisk information and downloads.
## Useful Links

7
app/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
/dict.txt
# Gradle
.gradle
.kotlin
/local.properties
/build

View File

@@ -35,7 +35,7 @@ android {
}
dependencies {
implementation(project(":app:core"))
implementation(project(":core"))
coreLibraryDesugaring(libs.jdk.libs)
implementation(libs.indeterminate.checkbox)

View File

@@ -1,10 +1,13 @@
package com.topjohnwu.magisk.arch
import android.content.ContentResolver
import android.view.KeyEvent
import androidx.databinding.ViewDataBinding
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navOptions
import com.topjohnwu.magisk.utils.AccessibilityUtils
abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Binding>() {
@@ -31,7 +34,17 @@ abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Bindin
}
}
companion object {
fun navigate(directions: NavDirections, navigation: NavController, cr: ContentResolver) {
if (AccessibilityUtils.isAnimationEnabled(cr)) {
navigation.navigate(directions)
} else {
navigation.navigate(directions, navOptions {})
}
}
}
fun NavDirections.navigate() {
navigation.navigate(this)
navigate(this, navigation, contentResolver)
}
}

View File

@@ -24,6 +24,7 @@ import androidx.core.widget.ImageViewCompat
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import androidx.databinding.InverseMethod
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
@@ -33,9 +34,11 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip
import com.google.android.material.slider.Slider
import com.google.android.material.textfield.TextInputLayout
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.utils.TextHolder
import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.widget.IndeterminateCheckBox
@@ -306,3 +309,38 @@ fun TextView.setText(text: TextHolder) {
fun Spinner.setAdapter(items: Array<Any>, layoutRes: Int) {
adapter = ArrayAdapter(context, layoutRes, items)
}
@BindingAdapter("labelFormatter")
fun Slider.setLabelFormatter(formatter: (Float) -> Int) {
setLabelFormatter { value -> resources.getString(formatter(value)) }
}
@InverseBindingAdapter(attribute = "android:value")
fun Slider.getValueBinding() = value
@BindingAdapter("android:valueAttrChanged")
fun Slider.setListener(attrChange: InverseBindingListener) {
addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: Slider) = Unit
override fun onStopTrackingTouch(slider: Slider) = attrChange.onChange()
})
}
@InverseMethod("sliderValueToPolicy")
fun policyToSliderValue(policy: Int): Float {
return when (policy) {
SuPolicy.DENY -> 1f
SuPolicy.RESTRICT -> 2f
SuPolicy.ALLOW -> 3f
else -> 1f
}
}
fun sliderValueToPolicy(value: Float): Int {
return when (value) {
1f -> SuPolicy.DENY
2f -> SuPolicy.RESTRICT
3f -> SuPolicy.ALLOW
else -> SuPolicy.DENY
}
}

View File

@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.core.AppContext
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.R
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.view.MagiskDialog
@@ -11,15 +10,10 @@ import java.io.File
class ManagerInstallDialog : MarkDownDialog() {
private val svc get() = ServiceLocator.networkService
override suspend fun getMarkdownText(): String {
val text = svc.fetchString(Info.remote.magisk.note)
val text = Info.update.note
// Cache the changelog
AppContext.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach {
it.delete()
}
File(AppContext.cacheDir, "${Info.remote.magisk.versionCode}.md").writeText(text)
File(AppContext.cacheDir, "${Info.update.versionCode}.md").writeText(text)
return text
}

View File

@@ -17,6 +17,8 @@ import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
import com.topjohnwu.magisk.core.R as CoreR
import androidx.navigation.findNavController
import com.topjohnwu.magisk.arch.NavigationActivity
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
@@ -68,7 +70,13 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
override fun onMenuItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_settings ->
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
activity?.let {
NavigationActivity.navigate(
HomeFragmentDirections.actionHomeFragmentToSettingsFragment(),
it.findNavController(R.id.main_nav_host),
it.contentResolver,
)
}
R.id.action_reboot -> activity?.let { RebootMenu.inflate(it).show() }
else -> return super.onOptionsItemSelected(item)
}

View File

@@ -91,16 +91,15 @@ class HomeViewModel(
override suspend fun doLoadWork() {
appState = State.LOADING
Info.getRemote(svc)?.apply {
Info.fetchUpdate(svc)?.apply {
appState = when {
BuildConfig.APP_VERSION_CODE < magisk.versionCode -> State.OUTDATED
BuildConfig.APP_VERSION_CODE < versionCode -> State.OUTDATED
else -> State.UP_TO_DATE
}
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
managerRemoteVersion =
("${magisk.version} (${magisk.versionCode})" +
if (isDebug) " (D)" else "").asText()
("$version (${versionCode})" + if (isDebug) " (D)" else "").asText()
} ?: run {
appState = State.INVALID
managerRemoteVersion = CoreR.string.not_available.asText()

View File

@@ -14,9 +14,8 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.core.AppContext
import com.topjohnwu.magisk.core.BuildConfig
import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.ContentResultCallback
import com.topjohnwu.magisk.core.ktx.toast
@@ -70,17 +69,16 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
init {
viewModelScope.launch(Dispatchers.IO) {
try {
val file = File(AppContext.cacheDir, "${BuildConfig.APP_VERSION_CODE}.md")
val text = when {
file.exists() -> file.readText()
Const.Url.CHANGELOG_URL.isEmpty() -> ""
val noteFile = File(AppContext.cacheDir, "${APP_VERSION_CODE}.md")
val noteText = when {
noteFile.exists() -> noteFile.readText()
else -> {
val str = svc.fetchString(Const.Url.CHANGELOG_URL)
file.writeText(str)
str
val note = svc.fetchUpdate(APP_VERSION_CODE).note
noteFile.writeText(note)
note
}
}
val spanned = markwon.toMarkdown(text)
val spanned = markwon.toMarkdown(noteText)
withContext(Dispatchers.Main) {
notes = spanned
}
@@ -100,13 +98,15 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
}
override fun onSaveState(state: Bundle) {
state.putParcelable(INSTALL_STATE_KEY, InstallState(
methodId,
step,
Config.keepVerity,
Config.keepEnc,
Config.recovery
))
state.putParcelable(
INSTALL_STATE_KEY, InstallState(
methodId,
step,
Config.keepVerity,
Config.keepEnc,
Config.recovery
)
)
}
override fun onRestoreState(state: Bundle) {
@@ -124,6 +124,7 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
override fun onActivityLaunch() {
AppContext.toast(CoreR.string.patch_file_msg, Toast.LENGTH_LONG)
}
override fun onActivityResult(result: Uri) {
uri.value = result
}

View File

@@ -5,6 +5,7 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.widget.HorizontalScrollView
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import com.topjohnwu.magisk.R
@@ -12,6 +13,7 @@ import com.topjohnwu.magisk.arch.BaseFragment
import com.topjohnwu.magisk.arch.viewModel
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.utils.AccessibilityUtils
import com.topjohnwu.magisk.utils.MotionRevealHelper
import rikka.recyclerview.addEdgeSpacing
import rikka.recyclerview.addItemSpacing
@@ -56,6 +58,11 @@ class LogFragment : BaseFragment<FragmentLogMd2Binding>(), MenuProvider {
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
fixEdgeEffect()
}
if (!AccessibilityUtils.isAnimationEnabled(requireContext().contentResolver)) {
val scrollView = view.findViewById<HorizontalScrollView>(R.id.log_scroll_magisk)
scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER)
}
}

View File

@@ -147,19 +147,11 @@ object UpdateChannel : BaseSettingsItem.Selector() {
get() = Config.updateChannel
set(value) {
Config.updateChannel = value
Info.remote = Info.EMPTY_REMOTE
Info.resetUpdate()
}
override val title = CoreR.string.settings_update_channel_title.asText()
override val entryRes = CoreR.array.update_channel
override fun entries(res: Resources): Array<String> {
return super.entries(res).let {
if (!Const.APP_IS_CANARY && !BuildConfig.DEBUG)
it.copyOfRange(0, Config.Value.CANARY_CHANNEL)
else it
}
}
}
object UpdateChannelUrl : BaseSettingsItem.Input() {
@@ -169,7 +161,7 @@ object UpdateChannelUrl : BaseSettingsItem.Input() {
get() = Config.customChannelUrl
set(value) {
Config.customChannelUrl = value
Info.remote = Info.EMPTY_REMOTE
Info.resetUpdate()
notifyPropertyChanged(BR.description)
}
@@ -330,6 +322,12 @@ object Reauthenticate : BaseSettingsItem.Toggle() {
override var value by Config::suReAuth
override fun refresh() {
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Info.showSuperUser
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
}
}
object Restrict : BaseSettingsItem.Toggle() {
override val title = CoreR.string.settings_su_restrict_title.asText()
override val description = CoreR.string.settings_su_restrict_summary.asText()
override var value by Config::suRestrict
}

View File

@@ -83,6 +83,9 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
// Can hide overlay windows on 12.0+
list.remove(Tapjack)
}
if (Const.Version.atLeast_30_1()) {
list.add(Restrict)
}
}
return list

View File

@@ -4,11 +4,13 @@ import android.graphics.drawable.Drawable
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.databinding.DiffItem
import com.topjohnwu.magisk.databinding.ItemWrapper
import com.topjohnwu.magisk.databinding.ObservableRvItem
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.core.R as CoreR
class PolicyRvItem(
private val viewModel: SuperuserViewModel,
@@ -33,14 +35,34 @@ class PolicyRvItem(
var isExpanded = false
set(value) = set(value, field, { field = it }, BR.expanded)
val showSlider = Config.suRestrict || item.policy == SuPolicy.RESTRICT
@get:Bindable
var isEnabled
get() = item.policy == SuPolicy.ALLOW
get() = item.policy >= SuPolicy.ALLOW
set(value) = setImpl(value, isEnabled) {
notifyPropertyChanged(BR.enabled)
viewModel.togglePolicy(this, value)
viewModel.updatePolicy(this, if (it) SuPolicy.ALLOW else SuPolicy.DENY)
}
@get:Bindable
var sliderValue
get() = item.policy
set(value) = setImpl(value, sliderValue) {
notifyPropertyChanged(BR.sliderValue)
notifyPropertyChanged(BR.enabled)
viewModel.updatePolicy(this, it)
}
val sliderValueToPolicyString: (Float) -> Int = { value ->
when (value.toInt()) {
1 -> CoreR.string.deny
2 -> CoreR.string.restrict
3 -> CoreR.string.grant
else -> CoreR.string.deny
}
}
@get:Bindable
var shouldNotify
get() = item.notification

View File

@@ -156,15 +156,16 @@ class SuperuserViewModel(
}
}
fun togglePolicy(item: PolicyRvItem, enable: Boolean) {
fun updatePolicy(item: PolicyRvItem, policy: Int) {
val items = itemsPolicies.filter { it.item.uid == item.item.uid }
fun updateState() {
viewModelScope.launch {
val res = if (enable) R.string.su_snack_grant else R.string.su_snack_deny
item.item.policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY
val res = if (policy >= SuPolicy.ALLOW) R.string.su_snack_grant else R.string.su_snack_deny
item.item.policy = policy
db.update(item.item)
items.forEach {
it.notifyPropertyChanged(BR.enabled)
it.notifyPropertyChanged(BR.sliderValue)
}
SnackbarEvent(res.asText(item.appName)).publish()
}

View File

@@ -0,0 +1,14 @@
package com.topjohnwu.magisk.utils
import android.content.ContentResolver
import android.provider.Settings
class AccessibilityUtils {
companion object {
fun isAnimationEnabled(cr: ContentResolver): Boolean {
return !(Settings.Global.getFloat(cr, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f
&& Settings.Global.getFloat(cr, Settings.Global.TRANSITION_ANIMATION_SCALE, 1.0f) == 0.0f
&& Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE, 1.0f) == 0.0f)
}
}
}

View File

@@ -16,6 +16,7 @@
android:layout_height="match_parent">
<HorizontalScrollView
android:id="@+id/log_scroll_magisk"
gone="@{viewModel.loading}"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@@ -25,7 +25,7 @@
<include
android:id="@+id/log_track_container"
bullet="@{item.log.action == 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
bullet="@{item.log.action >= 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
isBottom="@{item.isBottom}"
isSelected="@{item.log.action != 2}"
isTop="@{item.isTop}"

View File

@@ -5,6 +5,8 @@
<data>
<import type="com.topjohnwu.magisk.databinding.DataBindingAdaptersKt" />
<variable
name="item"
type="com.topjohnwu.magisk.ui.superuser.PolicyRvItem" />
@@ -85,16 +87,32 @@
app:layout_constraintVertical_bias="0"
tools:text="com.topjohnwu.magisk" />
<com.google.android.material.switchmaterial.SwitchMaterial
<FrameLayout
android:id="@+id/policy_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/l1"
android:checked="@={item.enabled}"
android:nextFocusLeft="@id/policy"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.switchmaterial.SwitchMaterial
gone="@{item.showSlider}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={item.enabled}" />
<com.google.android.material.slider.Slider
goneUnless="@{item.showSlider}"
labelFormatter="@{item.sliderValueToPolicyString}"
android:layout_width="96dp"
android:layout_height="wrap_content"
android:stepSize="1"
android:value="@={DataBindingAdaptersKt.policyToSliderValue(item.sliderValue)}"
android:valueFrom="1"
android:valueTo="3" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,5 +1,11 @@
tasks.register("clean") {
plugins {
id("MagiskPlugin")
}
tasks.register("clean", Delete::class) {
delete(rootProject.layout.buildDirectory)
subprojects.forEach {
dependsOn(":app:${it.name}:clean")
dependsOn(":${it.name}:clean")
}
}

View File

@@ -30,5 +30,6 @@ dependencies {
implementation(libs.ksp.plugin)
implementation(libs.navigation.safe.args.plugin)
implementation(libs.lsparanoid.plugin)
implementation(libs.moshi.plugin)
implementation(libs.jgit)
}

View File

@@ -0,0 +1,77 @@
import com.android.build.api.artifact.ArtifactTransformationRequest
import com.android.build.api.dsl.ApkSigningConfig
import com.android.builder.internal.packaging.IncrementalPackager
import com.android.tools.build.apkzlib.sign.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zfile.ZFiles
import com.android.tools.build.apkzlib.zip.ZFileOptions
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.util.jar.JarFile
abstract class AddCommentTask: DefaultTask() {
@get:Input
abstract val comment: Property<String>
@get:Input
abstract val signingConfig: Property<ApkSigningConfig>
@get:InputFiles
abstract val apkFolder: DirectoryProperty
@get:OutputDirectory
abstract val outFolder: DirectoryProperty
@get:Internal
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
@TaskAction
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
val inFile = File(artifact.outputFile)
val outFile = outFolder.file(inFile.name).get().asFile
val privateKey = signingConfig.get().getPrivateKey()
val signingOptions = SigningOptions.builder()
.setMinSdkVersion(0)
.setV1SigningEnabled(true)
.setV2SigningEnabled(true)
.setKey(privateKey.privateKey)
.setCertificates(privateKey.certificate as X509Certificate)
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
.build()
val options = ZFileOptions().apply {
noTimestamps = true
autoSortFiles = true
}
outFile.parentFile?.mkdirs()
inFile.copyTo(outFile, overwrite = true)
ZFiles.apk(outFile, options).use {
SigningExtension(signingOptions).register(it)
it.eocdComment = comment.get().toByteArray()
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
it.get(JarFile.MANIFEST_NAME)?.delete()
}
outFile
}
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
storeFile!!.inputStream().use {
keyStore.load(it, storePassword!!.toCharArray())
}
val keyPwdArray = keyPassword!!.toCharArray()
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
return entry as KeyStore.PrivateKeyEntry
}
}

View File

@@ -5,6 +5,12 @@ import org.gradle.api.Project
import org.gradle.kotlin.dsl.provideDelegate
import java.io.File
import java.util.Properties
import java.util.Random
// Set non-zero value here to fix the random seed for reproducible builds
// CI builds are always reproducible
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
lateinit var RANDOM: Random
private val props = Properties()
private var commitHash = ""
@@ -14,7 +20,7 @@ private val defaultAbis = setOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
object Config {
operator fun get(key: String): String? {
val v = props[key] as? String ?: return null
return if (v.isBlank()) null else v
return v.ifBlank { null }
}
fun contains(key: String) = get(key) != null
@@ -28,19 +34,25 @@ object Config {
}
}
fun Project.rootFile(path: String): File {
val file = File(path)
return if (file.isAbsolute) file
else File(rootProject.file(".."), path)
}
class MagiskPlugin : Plugin<Project> {
override fun apply(project: Project) = project.applyPlugin()
private fun Project.applyPlugin() {
initRandom(rootProject.file("app/dict.txt"))
initRandom(rootProject.file("dict.txt"))
props.clear()
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
val configPath: String? by this
val config = configPath?.let { File(it) } ?: rootProject.file("config.prop")
val config = rootFile(configPath ?: "config.prop")
if (config.exists())
config.inputStream().use { props.load(it) }
val repo = FileRepository(rootProject.file(".git"))
val repo = FileRepository(rootFile(".git"))
val refId = repo.refDatabase.exactRef("HEAD").objectId
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
}

View File

@@ -1,8 +1,11 @@
import com.android.build.api.artifact.SingleArtifact
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
@@ -10,22 +13,25 @@ import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.named
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import java.security.SecureRandom
import java.util.Random
import java.util.zip.Deflater
import java.util.zip.DeflaterOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.random.asKotlinRandom
// Set non-zero value here to fix the random seed for reproducible builds
// CI builds are always reproducible
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
private lateinit var RANDOM: Random
private val kRANDOM get() = RANDOM.asKotlinRandom()
private val c1 = mutableListOf<String>()
@@ -72,7 +78,7 @@ private fun PrintStream.byteField(name: String, bytes: ByteArray) {
}
@CacheableTask
abstract class ManifestUpdater: DefaultTask() {
private abstract class ManifestUpdater: DefaultTask() {
@get:Input
abstract val applicationId: Property<String>
@@ -182,9 +188,7 @@ abstract class ManifestUpdater: DefaultTask() {
}
fun genStubClasses(factoryOutDir: File, appOutDir: File) {
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
val classNameGenerator = sequence {
fun notJavaKeyword(name: String) = when (name) {
"do", "if", "for", "int", "new", "try" -> false
@@ -228,7 +232,7 @@ fun genStubClasses(factoryOutDir: File, appOutDir: File) {
genClass("StubApplication", appOutDir)
}
fun genEncryptedResources(res: ByteArray, outDir: File) {
private fun genEncryptedResources(res: ByteArray, outDir: File) {
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
mainPkgDir.mkdirs()
@@ -259,3 +263,86 @@ fun genEncryptedResources(res: ByteArray, outDir: File) {
it.println("}")
}
}
fun Project.setupStubApk() {
setupAppCommon()
androidComponents.onVariants { variant ->
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
val manifestUpdater =
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
dependsOn("generate${variantCapped}ObfuscatedClass")
applicationId = variant.applicationId
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
ManifestUpdater::mergedManifest,
ManifestUpdater::outputManifest)
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
androidApp.applicationVariants.all {
val variantCapped = name.replaceFirstChar { it.uppercase() }
val variantLowered = name.lowercase()
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
inputs.property("seed", RAND_SEED)
outputs.dirs(outFactoryClassDir, outAppClassDir)
doLast {
outFactoryClassDir.mkdirs()
outAppClassDir.mkdirs()
genStubClasses(outFactoryClassDir, outAppClassDir)
}
}
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
outputs.dir(outResDir)
doLast {
val apkTmp = File("${apk}.tmp")
exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->
ZipOutputStream(apk.outputStream()).use {
it.setLevel(Deflater.BEST_COMPRESSION)
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
it.closeEntry()
}
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
}
}
apkTmp.delete()
genEncryptedResources(bos.toByteArray(), outResDir)
}
}
registerJavaGeneratingTask(processResourcesTask, outResDir)
}
// Override optimizeReleaseResources task
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
afterEvaluate {
tasks.named("optimizeReleaseResources") {
doLast { apk.copyTo(optRes, true) }
}
}
tasks.named<Delete>("clean") {
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
}
}

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