Compare commits

...

187 Commits

Author SHA1 Message Date
topjohnwu
483dbcdc40 Release v22.0 2021-02-23 04:09:26 -08:00
topjohnwu
a1096b5bf0 Do not run pm install on main thread 2021-02-23 04:09:13 -08:00
Chris Renshaw
5ac0e64edb Update guides.md for system_ext 2021-02-23 03:27:36 -08:00
Lishoo
60b2624607 Update polish translations
Add missing strings
2021-02-23 03:26:47 -08:00
topjohnwu
d2e2847b03 Fix stub 2021-02-23 03:24:51 -08:00
topjohnwu
b9669f54f7 Update docs 2021-02-23 03:06:00 -08:00
topjohnwu
8c7bd77d33 Do not wrap twice 2021-02-23 01:49:15 -08:00
Shaka Huang
ba1ce16b8b Fix error in pure 64-bit environment
In Android S preview, there’s no 32-bit libraries in x86_64 system image for emulator.

Signed-off-by: Shaka Huang <shakalaca@gmail.com>
2021-02-22 03:28:54 -08:00
topjohnwu
68090943f4 Several changes
- Change error message strings
- Move non-root stub error to SplashActivity
- Skip shell init in non-root stub
2021-02-22 03:28:19 -08:00
vvb2060
a4fb1297b0 Fix crash in pure 64-bit devices 2021-02-22 03:08:51 -08:00
vvb2060
860a05abf2 Simplify UpdateChannel 2021-02-22 03:08:51 -08:00
vvb2060
8bb2f356c0 Allow offline restore app 2021-02-22 03:08:51 -08:00
vvb2060
4950020635 Prevent dot in the first position again 2021-02-22 03:08:51 -08:00
vvb2060
0a6140c6eb Try install with root first 2021-02-22 03:08:51 -08:00
vvb2060
bba2ac8817 Add unsupport env check 2021-02-22 03:08:51 -08:00
topjohnwu
331b1f542f Use standard Android APIs for install and launch 2021-02-20 20:12:35 -08:00
topjohnwu
ccb55205e6 Fix pre 21 support 2021-02-20 03:38:39 -08:00
topjohnwu
9cc91b30b3 Fix #3871 2021-02-20 02:49:43 -08:00
grmasa
e836caf31e Update Greek translation 2021-02-20 01:51:39 -08:00
Lishoo
beaa1e5be2 Add missing strings and small updates. 2021-02-20 01:51:02 -08:00
Lishoo
ea545bae26 Update polish translations 2021-02-20 01:50:44 -08:00
topjohnwu
1c9ec2df45 Publish new canary build 2021-02-14 13:43:24 -08:00
vvb2060
b76c80e2ce Fix apex path 2021-02-14 13:37:38 -08:00
topjohnwu
236990f4a3 Fix stub app crashing 2021-02-14 13:37:13 -08:00
topjohnwu
1ed32df20d Publish new canary build 2021-02-13 17:26:53 -08:00
topjohnwu
8476eb9f4b Avoid patching vendor_boot.img 2021-02-13 17:15:04 -08:00
JoanVC100
735af7843b Add new ca-strings 2021-02-13 17:09:46 -08:00
MC Naveen
ded73e958b Added Tamil Translation 2021-02-13 17:09:28 -08:00
Ooggle
6dcb84d4f4 French translation of newest commit 2021-02-13 17:08:53 -08:00
topjohnwu
501bc9f438 Restore init from backup rather than symlink
Because of course Samsung don't follow AOSP norms.
I mean, why would they?
2021-02-13 16:43:06 -08:00
topjohnwu
f88e812b63 Move behavior to XML 2021-02-13 15:26:32 -08:00
Tornike Khintibidze
be6386c410 Updated Georgian translation 2021-02-12 03:59:35 -08:00
Didgeridoohan
2af4fd17c4 Minor fixes and changes to Swedish transaltions 2021-02-12 03:59:07 -08:00
Mikael Bjurström
f870418bd0 Update Swedish translation 2021-02-12 00:07:40 -08:00
vvb2060
00659e4795 Hide OTA option on virtual A/B devices 2021-02-12 00:07:15 -08:00
Jose Manuel Estrada-Nora Muñoz
cdda10207e Spanish strings 2021-02-11 23:32:24 -08:00
Ilya Kushnir
701700279f Update RU strings 2021-02-11 23:32:04 -08:00
alex26052005
a9d804724a Update strings.xml
Updated German language
2021-02-11 23:31:04 -08:00
DanGLES3
e033a9ab47 Update Portugues Brazilian translation 2021-02-11 23:30:35 -08:00
kubalav
059e5fb8aa Update Slovak translation 2021-02-11 23:28:49 -08:00
vvb2060
a78f255928 Update zh-rCN translation 2021-02-11 23:25:24 -08:00
AndroPlus
1d10e69288 Update Japanese translation 2021-02-11 23:23:36 -08:00
topjohnwu
63590d379c Update hide icon strategy 2021-02-11 22:38:41 -08:00
topjohnwu
5f63e88984 Hide icons when things don't fit 2021-02-11 05:08:40 -08:00
topjohnwu
75584e2b19 App string resources overhaul 2021-02-11 02:34:27 -08:00
vvb2060
1426ee2ebd Fix Android build version sdk in script 2021-02-10 22:22:50 -08:00
topjohnwu
b6643b7bfc Publish new canary build 2021-02-07 21:39:00 -08:00
Hen Ry
721dfdf553 Added translation of new strings 2021-02-07 17:42:33 -08:00
topjohnwu
2963747d14 Fix LZ4_LG format decompression
Fix #3802, fix #3722, fix #3770, fix #3635, fix #3787, close #3812
2021-02-07 17:40:59 -08:00
topjohnwu
e7350d5041 Fix unable to patch images when app is hidden 2021-02-07 06:42:06 -08:00
topjohnwu
f37e8f4ca8 Fix boot image patching 2021-02-07 01:54:08 -08:00
topjohnwu
594c2accc0 Update dependencies 2021-02-05 04:41:01 -08:00
topjohnwu
7acfac6a91 Publish new canary build 2021-01-30 12:54:51 -08:00
Laz M
0646f48e14 [README] Warn users that the official website is github
Google puts a number of cheeky looking websites in the results for Magisk.

I only found out they were unofficial is though issue #3435. This deserves to be shown more prominently.
2021-01-30 11:59:39 -08:00
tzagim
37565fd067 Fix TYPOs 2021-01-30 11:58:43 -08:00
vvb2060
26b2e7dc5d Care version code changes 2021-01-30 11:58:10 -08:00
vvb2060
c3313623e4 Fix release build 2021-01-30 11:58:10 -08:00
topjohnwu
2089223690 Fix #3785 2021-01-30 11:51:15 -08:00
topjohnwu
52e1b84d41 Symlink pre API 21 2021-01-30 01:12:49 -08:00
topjohnwu
8794141b7f Support super old emulators 2021-01-30 00:56:16 -08:00
topjohnwu
f6126dd20e Support Shortcuts pre API 26
Close #3778
2021-01-29 23:16:09 -08:00
topjohnwu
18acfda99b Remove Windows NDK symlink
https://github.com/actions/virtual-environments/pull/2343
2021-01-29 05:43:41 -08:00
topjohnwu
bec5edca84 Avoiding using shell I/O 2021-01-29 05:15:22 -08:00
topjohnwu
6fb20b3ee5 Proper proguard rules 2021-01-27 04:56:39 -08:00
topjohnwu
eaf4d8064b Also download to external storage 2021-01-27 04:09:07 -08:00
topjohnwu
2a5f5b1bba Workaround zip extraction bug on older devices 2021-01-27 03:00:09 -08:00
topjohnwu
c538a77937 Tweak build configs and scripts 2021-01-27 02:36:32 -08:00
sominn
aa9e7b1ed1 Update strings.xml
CS string update
2021-01-27 01:00:10 -08:00
Arbri çoçka
a3066eddab Fix string in values-sq 2021-01-27 00:59:49 -08:00
Arbri çoçka
d1729fa787 Fix string in values-sq 2021-01-27 00:59:49 -08:00
vvb2060
93961dde2c Fix version on continuous build 2021-01-27 00:54:11 -08:00
topjohnwu
1024e68eb6 Remove class mapping in full APK 2021-01-26 07:27:35 -08:00
topjohnwu
6ae2c9387d Use stub APK hiding method for Android 5.0+
At the same time, disable app hiding on devices lower than 5.0
to simplify the logic in the app. By doing so, a hidden app always
implies running as stub.
2021-01-26 07:27:35 -08:00
topjohnwu
fba83e2330 Support stub APK loading down to Android 5.0 2021-01-26 07:27:35 -08:00
topjohnwu
f1295cb7d6 Fix root on Android 7.0 and lower 2021-01-26 02:16:11 -08:00
topjohnwu
dc61dfbde6 Cache update check results 2021-01-25 04:13:08 -08:00
topjohnwu
21466426da Some code cleanup 2021-01-25 03:44:38 -08:00
topjohnwu
3f0136362b Move nand flash handling into boot_patch.sh 2021-01-25 03:37:41 -08:00
topjohnwu
e92d77bbec Some optimizations 2021-01-25 03:02:43 -08:00
topjohnwu
07bd36c94b Fix patching files
Fix #3765
2021-01-25 02:24:12 -08:00
topjohnwu
b1dbbdef12 Remove unneeded busybox redirection 2021-01-25 00:23:42 -08:00
topjohnwu
3e479726ec Fix legacy rootfs devices 2021-01-25 00:19:10 -08:00
vvb2060
4cc41eccb3 Skip download notes when loading notes url 2021-01-24 21:02:51 -08:00
vvb2060
8f08ae59ac Fix permission 2021-01-24 21:02:43 -08:00
vvb2060
e8d4e492d6 Fix CHANGELOG_URL 2021-01-24 21:02:37 -08:00
topjohnwu
b8090a8e18 Ensure cwd is writable in module scripts
Close #3763
2021-01-24 20:58:30 -08:00
topjohnwu
c609a01e55 Proper shortcut name 2021-01-24 08:00:17 -08:00
Wagg13
c97fb385cd New update values-pt-rBR
update brazilian strings.xml
2021-01-24 07:36:07 -08:00
LLZN
da6c57750e correction czech translat
change and fix some strings after trying a new version of the application (v8.0.6)
2021-01-24 07:35:49 -08:00
topjohnwu
6951d926f7 Rename app name to just Magisk 2021-01-24 07:35:00 -08:00
topjohnwu
6623195bd5 Cleanup scripts 2021-01-24 07:24:13 -08:00
topjohnwu
ec31bb9a82 Rename scripts 2021-01-24 07:18:14 -08:00
vvb2060
8618cc383a Fix install modules
Fix #3759
2021-01-24 07:03:19 -08:00
vvb2060
4b01e3a3c7 Cleanup more kotlin stuffs 2021-01-24 07:03:06 -08:00
vvb2060
7f748c23c1 Use Java debugger 2021-01-24 07:02:44 -08:00
vvb2060
963d248cc7 Rename apk to be uninstaller 2021-01-24 07:02:36 -08:00
topjohnwu
657056e636 Cache changelog files 2021-01-24 06:55:43 -08:00
topjohnwu
9d5efea66e Remove ManagerJson
Everything is now Magisk
2021-01-24 05:14:46 -08:00
topjohnwu
658d74e026 Update home fragment 2021-01-24 00:02:49 -08:00
vvb2060
5113f6d375 Fix stop magiskhide 2021-01-23 18:13:15 -08:00
vvb2060
96405c26d0 writeTo has closed InputStream 2021-01-23 18:12:19 -08:00
vvb2060
4ea5f34bf3 Remove unused action 2021-01-23 18:11:08 -08:00
vvb2060
dbd13a2019 Clean code 2021-01-23 18:10:26 -08:00
vvb2060
06773235da Fix Windows build 2021-01-23 18:06:01 -08:00
vvb2060
e57556a8af Use ro.kernel.qemu to check emulator 2021-01-23 18:05:38 -08:00
vvb2060
b54b78c29d Fix prevent dot in the first position 2021-01-23 17:31:18 -08:00
vvb2060
317336f771 Add isolated processes log 2021-01-23 17:31:11 -08:00
topjohnwu
b4e52f6135 Better development workflow 2021-01-23 16:50:55 -08:00
topjohnwu
f2ca042915 Fix script for handling .apex files 2021-01-23 16:09:30 -08:00
topjohnwu
1060dd2906 Random refactoring 2021-01-23 13:26:28 -08:00
topjohnwu
2e0f7a82fa More complete stub sources 2021-01-22 20:45:37 -08:00
topjohnwu
5798536559 Remove unnecessary hacks 2021-01-22 20:25:37 -08:00
topjohnwu
ab9a83c82f Bump target SDK to 30 2021-01-22 05:03:33 -08:00
topjohnwu
c87fdbea0f Fix erroneous stream close 2021-01-22 03:07:39 -08:00
topjohnwu
ec8fffe61c Merge Magisk install zip into Magisk Manager
Distribute Magisk directly with Magisk Manager APK. The APK will
contain all required binaries and scripts for installation and
uninstallation. App versions will now align with Magisk releases.

Extra effort is spent to make the APK itself also a flashable zip that
can be used in custom recoveries, so those still prefer to install
Magisk with recoveries will not be affected with this change.

As a bonus, this makes the whole installation and uninstallation
process 100% offline. The existing Magisk Manager was not really
functional without an Internet connection, as the installation process
was highly tied to zips hosted on the server.

An additional bonus: since all binaries are now shipped as "native
libraries" of the APK, we can finally bump the target SDK version
higher than 28. The target SDK version was stuck at 28 for a long time
because newer SELinux restricts running executables from internal
storage. More details can be found here: https://github.com/termux/termux-app/issues/1072
The target SDK bump will be addressed in a future commit.

Co-authored with @vvb2060
2021-01-22 02:29:54 -08:00
topjohnwu
61d52991f1 Update BusyBox 2021-01-21 00:35:22 -08:00
topjohnwu
9100186dce Make emulator direct install env fix 2021-01-18 13:32:10 -08:00
topjohnwu
d2bc2cfcf8 Install both 32 and 64 bit binaries 2021-01-18 12:37:08 -08:00
topjohnwu
5a71998b4e Stop embedding magisk in magiskinit 2021-01-18 04:25:26 -08:00
topjohnwu
42278f12ff Fix typo in init daemon 2021-01-18 04:13:54 -08:00
topjohnwu
f5593e051c Update README 2021-01-17 06:19:56 -08:00
topjohnwu
a27e30cf54 Update release notes 2021-01-17 06:08:15 -08:00
topjohnwu
79140c7636 Proper xxread and xwrite implementation 2021-01-17 01:42:45 -08:00
topjohnwu
1f4c595cd3 Revert to old su -c behavior 2021-01-16 23:59:31 -08:00
topjohnwu
b5b62e03af Fix copySepolicyRules logic 2021-01-16 21:45:45 -08:00
topjohnwu
67e2a4720e Fix xxread false negatives
Fix #3710
2021-01-16 21:43:53 -08:00
topjohnwu
f5c2d72429 Also log pid and tid 2021-01-16 16:10:47 -08:00
topjohnwu
2f5331ab48 Update README 2021-01-16 05:02:39 -08:00
topjohnwu
7f8257152f Add v21.3 release notes 2021-01-16 04:55:44 -08:00
topjohnwu
0cd80f2556 Update app changelog 2021-01-16 04:42:14 -08:00
rydwhelchel
1717387876 Grammatical changes to the install docs 2021-01-15 21:32:29 -08:00
Mspy1
109363ebf6 Fixed typo 2021-01-15 21:31:58 -08:00
LLZN
716c4fa386 new update values-cs
update czech strings.xml
2021-01-15 21:31:17 -08:00
Arbri çoçka
9a09b4eb20 fix strings-sq 2021-01-15 21:29:53 -08:00
Rikka
95a5b57265 Remove "Flashing" overlay
Fix #3579, fix #3250
2021-01-15 21:28:59 -08:00
topjohnwu
13fbf397d1 Isolated processes might still be hide-able 2021-01-15 20:22:49 -08:00
vvb2060
20be99ec8a Restore mistakenly deleted codes 2021-01-15 19:59:55 -08:00
topjohnwu
04c53c3578 Legacy SAR: use a simpler method to detect is_two_stage 2021-01-15 02:44:40 -08:00
topjohnwu
51bc27a869 Avoid F2FS like a plague 2021-01-15 02:24:11 -08:00
topjohnwu
71b083794c Maintain global mount list 2021-01-14 21:14:54 -08:00
topjohnwu
b100d0c503 Revert DTB fstab changes 2021-01-14 19:48:00 -08:00
topjohnwu
76061296c9 Let MagiskBoot handle dtb fstab patching 2021-01-14 06:20:12 -08:00
topjohnwu
bb303d2da1 Remove old unused code 2021-01-14 05:59:53 -08:00
topjohnwu
c91c070343 Re-enable DTB table rebuilding 2021-01-14 05:45:05 -08:00
topjohnwu
aec06a6f61 Get proper total image size 2021-01-14 03:55:27 -08:00
topjohnwu
e8ba671fc2 Guard all injection features behind a global flag 2021-01-13 20:07:23 -08:00
topjohnwu
1860e5d133 Dynamically find libselinux.so path 2021-01-13 19:41:57 -08:00
topjohnwu
f2cb3c38fe Update mmap implementation
Always map memory as writable, but private when read-only
2021-01-12 22:50:55 -08:00
topjohnwu
9a28dd4f6e Implement MagiskHide through code injection 2021-01-12 03:28:00 -08:00
topjohnwu
d2acd59ea8 Minor code refactoring 2021-01-12 00:07:48 -08:00
topjohnwu
79dfdb29e7 Minor tweaks for patching tar files 2021-01-11 19:47:36 -08:00
topjohnwu
eb21c8b42e Code cleanups 2021-01-11 02:19:10 -08:00
topjohnwu
541bb53553 Update links in README 2021-01-10 20:36:58 -08:00
Hen Ry
fe8997efae Fix 2021-01-10 20:17:20 -08:00
Arbri çoçka
23455c722c fix in Values-sq 2021-01-10 20:16:57 -08:00
topjohnwu
5ce29c30d2 Fix sepolicy copying 2021-01-10 20:16:02 -08:00
topjohnwu
70d67728fd Add global toggle for ptrace monitor 2021-01-10 19:27:54 -08:00
topjohnwu
e546884b08 Remove isolated process handling in ptrace
Impossible to achieve only through ptrace
2021-01-10 17:18:42 -08:00
topjohnwu
b36e6d987d Reorganize MagiskHide code
Prepare for zygote injection hiding
2021-01-10 17:11:00 -08:00
topjohnwu
53c3dd5e8b Auto track JNI method hooks 2021-01-10 05:07:17 -08:00
topjohnwu
da723b207a Allow 3rd party code to load pre-specializing
Magisk's policy is to never allow 3rd party code to be loaded in the
zygote daemon process so we have 100% control over injection and hiding.
However, this makes it impossible for 3rd party modules to run anything
before process specialization, which includes the ability to modify the
arguments being sent to these original nativeForkAndXXX methods.

The trick here is to fork before calling the original nativeForkAndXXX
methods, and hook `fork` in libandroid_runtime.so to skip the next
invocation; basically, we're moving the responsibility of process
forking to our own hands.
2021-01-10 01:25:30 -08:00
topjohnwu
e050f77198 Don't hook SystemProperties#set
Doesn't seem necessary
2021-01-09 20:39:59 -08:00
topjohnwu
540b4b7ea9 Update pre/post hooks implementation 2021-01-09 17:41:25 -08:00
topjohnwu
bbef22daf7 More macro magic to automate more code 2021-01-09 04:28:26 -08:00
topjohnwu
9ed110c91b Add JNI hooks to critical methods 2021-01-08 05:25:44 -08:00
topjohnwu
a30d510eb1 Use xHook to hook functions in PLT 2021-01-08 00:53:24 -08:00
topjohnwu
ef98eaed8f Proper injection entry and unloading 2021-01-06 23:59:05 -08:00
topjohnwu
2a257f327c Sanitize /proc/PID/environ 2021-01-06 23:41:37 -08:00
topjohnwu
4060c2107c Add preliminary zygote code injection support
Prototyping the injection setup and a clean "self unloading" mechanism.
2021-01-06 22:21:17 -08:00
topjohnwu
cd23d27048 Fix remote_write implementation 2021-01-06 21:56:29 -08:00
topjohnwu
18b86e4fd2 Update Android.mk for test binary
Make Android Studio happy
2021-01-05 00:01:02 -08:00
topjohnwu
5f2e22a259 Support remote function call with ptrace
End up not used for anything, but keep it for good
2021-01-02 21:29:45 -08:00
topjohnwu
4e97b18977 Move libsystemproperties to external 2020-12-31 15:06:19 -08:00
topjohnwu
f9bde347bc Convert indentation to spaces
The tab war is lost
2020-12-30 22:11:24 -08:00
Billy Laws
947a7d6a2f Support rootwait cmdline parameter on legacy SAR
On devices where the primary storage is slow to probe it makes sense to
wait forever for the system partition to mount, this emulates the
kernel's behaviour when waiting for rootfs on SAR if the rootwait
parameter is supplied.

This issue was encountered with some SD cards on the Nintendo Switch.
2020-12-30 16:43:28 -08:00
Björn Engel
872ab2e99b Change translation for next
Nächste sounds a little bit strange.
2020-12-30 16:41:22 -08:00
kubalav
90b8813bb7 Fixed typo 2020-12-30 16:41:01 -08:00
Arbri çoçka
88d0f63294 Fix text in strings_sq 2020-12-30 16:40:47 -08:00
topjohnwu
79fa0d3a90 Hide selection improvements 2020-12-30 16:40:22 -08:00
topjohnwu
8e61080a4a Preparation for hiding isolated processes 2020-12-30 15:55:53 -08:00
topjohnwu
3f9a64417b Disable gradle daemon on Windows CI 2020-12-29 02:46:57 -08:00
topjohnwu
eb959379e8 Prevent resource ID clash 2020-12-29 02:39:47 -08:00
topjohnwu
41a644afb9 Open source stub APK loader
Close #3537
2020-12-29 01:44:02 -08:00
topjohnwu
6b42db943d Better bug report details 2020-12-28 17:03:20 -08:00
topjohnwu
1c325459eb Only run CI when it matters 2020-12-28 16:38:25 -08:00
John Wu
6d88d8ad95 Add issue templates 2020-12-28 16:26:10 -08:00
topjohnwu
246997f273 Update links 2020-12-28 15:58:53 -08:00
289 changed files with 16228 additions and 14662 deletions

28
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
## READ BEFORE OPENING ISSUES
All bug reports require you to **USE CANARY BUILDS**. Please include the version name and version code in the bug report.
If you experience a bootloop, attach a `dmesg` (kernel logs) when the device refuse to boot. This may very likely require a custom kernel on some devices as `last_kmsg` or `pstore ramoops` are usually not enabled by default. In addition, please also upload the result of `cat /proc/mounts` when your device is working correctly **WITHOUT ROOT**.
If you experience issues during installation, in recovery, upload the recovery logs, or in Magisk Manager, upload the install logs. Please also upload the `boot.img` or `recovery.img` that you are using for patching.
If you experience a crash of Magisk Manager, dump the full `logcat` **when the crash happens**. **DO NOT** upload `magisk.log`.
If you experience other issues related to Magisk, upload `magisk.log`, and preferably also include a boot `logcat` (start dumping `logcat` when the device boots up)
**DO NOT** open issues regarding root detection.
**DO NOT** ask for instructions.
**DO NOT** report issues if you have any modules installed.
Without following the rules above, your issue will be closed without explanation.

View File

@@ -3,6 +3,13 @@ name: Magisk Build
on:
push:
branches: [ master ]
paths:
- 'app/**'
- 'native/**'
- 'stub/**'
- 'buildSrc/**'
- 'build.py'
- 'gradle.properties'
pull_request:
branches: [ master ]
workflow_dispatch:
@@ -36,13 +43,10 @@ jobs:
- name: Set up GitHub env (Windows)
if: runner.os == 'Windows'
run: |
$oldAndroidPath = $env:ANDROID_SDK_ROOT
$sdk_root = "C:\Android"
New-Item -Path $sdk_root -ItemType SymbolicLink -Value $oldAndroidPath
$ndk_ver = Select-String -Path "gradle.properties" -Pattern "^magisk.fullNdkVersion=" | % { $_ -replace ".*=" }
echo "ANDROID_SDK_ROOT=$sdk_root" >> $env:GITHUB_ENV
echo "ANDROID_HOME=$sdk_root" >> $env:GITHUB_ENV
echo "ANDROID_SDK_ROOT=$env:ANDROID_SDK_ROOT" >> $env:GITHUB_ENV
echo "MAGISK_NDK_VERSION=$ndk_ver" >> $env:GITHUB_ENV
echo "GRADLE_OPTS=-Dorg.gradle.daemon=false" >> $env:GITHUB_ENV
- name: Set up GitHub env (Unix)
if: runner.os != 'Windows'
@@ -57,7 +61,6 @@ jobs:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/**/*.lock
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle-

3
.gitmodules vendored
View File

@@ -25,6 +25,9 @@
[submodule "pcre"]
path = native/jni/external/pcre
url = https://android.googlesource.com/platform/external/pcre
[submodule "xhook"]
path = native/jni/external/xhook
url = https://github.com/iqiyi/xHook.git
[submodule "termux-elf-cleaner"]
path = tools/termux-elf-cleaner
url = https://github.com/termux/termux-elf-cleaner.git

View File

@@ -15,11 +15,13 @@ Here are some feature highlights:
## Downloads
[![](https://img.shields.io/badge/Magisk%20Manager-v8.0.3-green)](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.3/MagiskManager-v8.0.3.apk)
Please note that the only source of official Magisk information and downloads is [Github](https://github.com/topjohnwu/Magisk/). Beware of any other websites.
[![](https://img.shields.io/badge/Magisk%20Manager-v8.0.7-green)](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.7/MagiskManager-v8.0.7.apk)
[![](https://img.shields.io/badge/Magisk%20Manager-Canary-red)](https://raw.githubusercontent.com/topjohnwu/magisk_files/canary/app-debug.apk)
<br>
[![](https://img.shields.io/badge/Magisk-v20.4-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v20.4)
[![](https://img.shields.io/badge/Magisk%20Beta-v21.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v21.1)
[![](https://img.shields.io/badge/Magisk-v21.4-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v21.4)
[![](https://img.shields.io/badge/Magisk%20Beta-v21.4-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v21.4)
## Useful Links
@@ -60,7 +62,7 @@ For Magisk Manager crashes, record and upload the logcat when the crash occurs.
- Run `./build.py ndk` to let the script download and install NDK for you
- To start building, run `build.py` to see your options. \
For each action, use `-h` to access help (e.g. `./build.py all -h`)
- To start development, open the project in Android Studio. Both app (Kotlin/Java) and native (C++/C) source code can be properly developed using the IDE, but *always* use `build.py` for building.
- To start development, open the project with Android Studio. The IDE can be used for both app (Kotlin/Java) and native (C++/C) sources.
- Optionally, set custom configs with `config.prop`. A sample `config.prop.sample` is provided.
- To sign APKs and zips with your own private keys, set signing configs in `config.prop`. For more info, check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key).

5
app/.gitignore vendored
View File

@@ -6,6 +6,7 @@
app/release
*.hprof
.externalNativeBuild/
public.certificate.x509.pem
private.key.pk8
*.apk
src/main/assets
src/main/jniLibs
src/main/resources

View File

@@ -1,3 +1,4 @@
import org.apache.tools.ant.filters.FixCrLfFilter
import java.io.PrintStream
plugins {
@@ -22,8 +23,9 @@ android {
applicationId = "com.topjohnwu.magisk"
vectorDrawables.useSupportLibrary = true
multiDexEnabled = true
versionName = Config.appVersion
versionCode = Config.appVersionCode
versionName = Config.version
versionCode = Config.versionCode
ndk.abiFilters("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
javaCompileOptions.annotationProcessorOptions.arguments(
mapOf("room.incremental" to "true")
@@ -51,13 +53,14 @@ android {
}
packagingOptions {
exclude("/META-INF/**")
exclude("/META-INF/*")
exclude("/org/bouncycastle/**")
exclude("/kotlin/**")
exclude("/kotlinx/**")
exclude("/okhttp3/**")
exclude("/*.txt")
exclude("/*.bin")
doNotStrip("**/*.so")
}
kotlinOptions {
@@ -65,10 +68,83 @@ android {
}
}
tasks["preBuild"]?.dependsOn(tasks.register("copyUtils", Copy::class) {
from(rootProject.file("scripts/util_functions.sh"))
into("src/main/res/raw")
})
val syncLibs by tasks.registering(Sync::class) {
into("src/main/jniLibs")
into("armeabi-v7a") {
from(rootProject.file("native/out/armeabi-v7a")) {
include("busybox", "magiskboot", "magiskinit", "magisk")
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
}
from(rootProject.file("native/out/arm64-v8a")) {
include("magisk")
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
}
}
into("x86") {
from(rootProject.file("native/out/x86")) {
include("busybox", "magiskboot", "magiskinit", "magisk")
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
}
from(rootProject.file("native/out/x86_64")) {
include("magisk")
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
}
}
onlyIf {
if (inputs.sourceFiles.files.size != 10)
throw StopExecutionException("Please build binaries first! (./build.py binary)")
true
}
}
val createStubLibs by tasks.registering {
dependsOn(syncLibs)
doLast {
val arm64 = project.file("src/main/jniLibs/arm64-v8a/libstub.so")
arm64.parentFile.mkdirs()
arm64.createNewFile()
val x64 = project.file("src/main/jniLibs/x86_64/libstub.so")
x64.parentFile.mkdirs()
x64.createNewFile()
}
}
val syncAssets by tasks.registering(Sync::class) {
dependsOn(createStubLibs)
inputs.property("version", Config.version)
inputs.property("versionCode", Config.versionCode)
into("src/main/assets")
from(rootProject.file("scripts")) {
include("util_functions.sh", "boot_patch.sh", "uninstaller.sh", "addon.d.sh")
}
into("chromeos") {
from(rootProject.file("tools/futility"))
from(rootProject.file("tools/keys")) {
include("kernel_data_key.vbprivk", "kernel.keyblock")
}
}
filesMatching("**/util_functions.sh") {
filter {
it.replace("#MAGISK_VERSION_STUB",
"MAGISK_VER='${Config.version}'\n" +
"MAGISK_VER_CODE=${Config.versionCode}")
}
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
}
}
val syncResources by tasks.registering(Sync::class) {
dependsOn(syncAssets)
into("src/main/resources/META-INF/com/google/android")
from(rootProject.file("scripts/update_binary.sh")) {
rename { "update-binary" }
}
from(rootProject.file("scripts/flash_script.sh")) {
rename { "updater-script" }
}
}
tasks["preBuild"]?.dependsOn(syncResources)
android.applicationVariants.all {
val keysDir = rootProject.file("tools/keys")
@@ -125,13 +201,13 @@ dependencies {
implementation("${bindingAdapter}:${vBAdapt}")
implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
val vMarkwon = "4.6.0"
val vMarkwon = "4.6.1"
implementation("io.noties.markwon:core:${vMarkwon}")
implementation("io.noties.markwon:html:${vMarkwon}")
implementation("io.noties.markwon:image:${vMarkwon}")
implementation("com.caverock:androidsvg:1.4")
val vLibsu = "3.0.2"
val vLibsu = "3.1.1"
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
implementation("com.github.topjohnwu.libsu:io:${vLibsu}")
@@ -158,7 +234,7 @@ dependencies {
implementation("com.squareup.moshi:moshi:${vMoshi}")
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
val vRoom = "2.3.0-alpha04"
val vRoom = "2.3.0-beta01"
implementation("androidx.room:room-runtime:${vRoom}")
implementation("androidx.room:room-ktx:${vRoom}")
kapt("androidx.room:room-compiler:${vRoom}")
@@ -167,17 +243,16 @@ dependencies {
implementation("androidx.navigation:navigation-fragment-ktx:${vNav}")
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
implementation("androidx.biometric:biometric:1.0.1")
implementation("androidx.biometric:biometric:1.1.0")
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.browser:browser:1.3.0")
implementation("androidx.preference:preference:1.1.1")
implementation("androidx.recyclerview:recyclerview:1.1.0")
implementation("androidx.fragment:fragment-ktx:1.2.5")
implementation("androidx.work:work-runtime-ktx:2.4.0")
implementation("androidx.transition:transition:1.3.1")
implementation("androidx.work:work-runtime-ktx:2.5.0")
implementation("androidx.transition:transition:1.4.0")
implementation("androidx.multidex:multidex:2.0.1")
implementation("androidx.core:core-ktx:1.3.2")
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
implementation("com.google.android.material:material:1.2.1")
implementation("com.google.android.material:material:1.3.0")
}

View File

@@ -18,16 +18,10 @@
# Kotlin
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
public static void checkExpressionValueIsNotNull(...);
public static void checkNotNullExpressionValue(...);
public static void checkReturnedValueIsNotNull(...);
public static void checkFieldIsNotNull(...);
public static void checkParameterIsNotNull(...);
public static void check*(...);
public static void throw*(...);
}
# Stubs
-keep class a.* { *; }
# Snet
-keepclassmembers class com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper { *; }
-keep,allowobfuscation interface com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper$Callback
@@ -35,14 +29,17 @@
void onResponse(org.json.JSONObject);
}
# Stub
-keep class com.topjohnwu.magisk.core.App { <init>(java.lang.Object); }
# Strip Timber verbose and debug logging
-assumenosideeffects class timber.log.Timber.Tree {
-assumenosideeffects class timber.log.Timber$Tree {
public void v(**);
public void d(**);
}
# Excessive obfuscation
-repackageclasses
-repackageclasses 'a'
-allowaccessmodification
# QOL

View File

@@ -1,17 +1,27 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.shared">
package="com.topjohnwu.shared"
android:installLocation="internalOnly">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<application
android:label="Magisk Manager"
android:installLocation="internalOnly"
android:usesCleartextTraffic="true"
android:allowBackup="false"
android:label="Magisk"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
tools:ignore="UnusedAttribute">
</application>
android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute" />
</manifest>

View File

@@ -1,7 +1,10 @@
package com.topjohnwu.magisk.utils;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
@@ -10,20 +13,36 @@ import com.topjohnwu.magisk.FileProvider;
import java.io.File;
public class APKInstall {
public static Intent installIntent(Context c, File apk) {
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 24) {
intent.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk));
} else {
//noinspection ResultOfMethodCallIgnored SetWorldReadable
apk.setReadable(true, false);
intent.setData(Uri.fromFile(apk));
}
return intent;
}
public static void install(Context c, File apk) {
c.startActivity(installIntent(c, apk));
}
public static Intent installIntent(Context c, File apk) {
Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
install.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk));
} else {
apk.setReadable(true, false);
install.setData(Uri.fromFile(apk));
}
return install;
public static void registerInstallReceiver(Context c, BroadcastReceiver r) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
c.getApplicationContext().registerReceiver(r, filter);
}
public static void installHideResult(Activity c, File apk) {
Intent intent = installIntent(c, apk);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
c.startActivityForResult(intent, 0); // Ignore result, use install receiver
}
}

View File

@@ -3,21 +3,18 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.magisk">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<application
android:name=".core.App"
android:extractNativeLibs="true"
android:icon="@drawable/ic_launcher"
android:name="a.e"
android:allowBackup="false"
android:multiArch="true"
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
<!-- Splash -->
<activity
android:name="a.c"
android:name=".core.SplashActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -30,27 +27,27 @@
</activity>
<!-- Main -->
<activity android:name="a.b" />
<activity android:name=".ui.MainActivity" />
<!-- Superuser -->
<activity
android:name="a.m"
android:name=".ui.surequest.SuRequestActivity"
android:directBootAware="true"
android:excludeFromRecents="true"
android:exported="false"
tools:ignore="AppLinkUrlError">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- Receiver -->
<receiver
android:name="a.h"
android:directBootAware="true">
android:name=".core.Receiver"
android:directBootAware="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.REBOOT" />
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
<intent-filter>
@@ -62,16 +59,15 @@
</receiver>
<!-- DownloadService -->
<service android:name="a.j" />
<service android:name=".core.download.DownloadService" />
<!-- FileProvider -->
<provider
android:name="a.p"
android:name=".core.Provider"
android:authorities="${applicationId}.provider"
android:directBootAware="true"
android:exported="false"
android:grantUriPermissions="true">
</provider>
android:grantUriPermissions="true" />
<!-- Hardcode GMS version -->
<meta-data
@@ -82,16 +78,12 @@
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
tools:node="remove"
tools:ignore="ExportedContentProvider" />
<!-- We don't invalidate Room -->
<service
android:name="androidx.room.MultiInstanceInvalidationService"
tools:node="remove"/>
<!-- We don't use Device Credentials -->
<activity
android:name="androidx.biometric.DeviceCredentialHandlerActivity"
tools:node="remove" />
</application>

View File

@@ -1,32 +0,0 @@
@file:JvmName("a")
package a
import com.topjohnwu.magisk.core.App
import com.topjohnwu.magisk.core.Provider
import com.topjohnwu.magisk.core.Receiver
import com.topjohnwu.magisk.core.SplashActivity
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.signing.SignBoot
fun main(args: Array<String>) {
SignBoot.main(args)
}
class b : MainActivity()
class c : SplashActivity()
class e : App {
constructor() : super()
constructor(o: Any) : super(o)
}
class h : Receiver()
class j : DownloadService()
class m : SuRequestActivity()
class p : Provider()

View File

@@ -41,6 +41,11 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
return binding.root
}
override fun onStart() {
super.onStart()
activity.supportActionBar?.subtitle = null
}
override fun onEventDispatched(event: ViewEvent) = when(event) {
is ContextExecutor -> event(requireContext())
is ActivityExecutor -> event(activity)

View File

@@ -10,8 +10,9 @@ import androidx.multidex.MultiDex
import androidx.work.WorkManager
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.utils.AppShellInit
import com.topjohnwu.magisk.core.utils.BusyBoxInit
import com.topjohnwu.magisk.core.utils.IODispatcherExecutor
import com.topjohnwu.magisk.core.utils.RootInit
import com.topjohnwu.magisk.core.utils.updateConfig
import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.magisk.ktx.unwrap
@@ -19,6 +20,7 @@ import com.topjohnwu.superuser.Shell
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import timber.log.Timber
import java.io.File
import kotlin.system.exitProcess
open class App() : Application() {
@@ -31,7 +33,7 @@ open class App() : Application() {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
Shell.setDefaultBuilder(Shell.Builder.create()
.setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(RootInit::class.java)
.setInitializers(BusyBoxInit::class.java, AppShellInit::class.java)
.setTimeout(2))
Shell.EXECUTOR = IODispatcherExecutor()
@@ -61,12 +63,18 @@ open class App() : Application() {
val wrapped = impl.wrap()
super.attachBaseContext(wrapped)
val info = base.applicationInfo
val libDir = runCatching {
info.javaClass.getDeclaredField("secondaryNativeLibraryDir").get(info) as String?
}.getOrNull() ?: info.nativeLibraryDir
Const.NATIVE_LIB_DIR = File(libDir)
// Normal startup
startKoin {
androidContext(wrapped)
modules(koinModules)
}
ResMgr.init(impl)
AssetHack.init(impl)
app.registerActivityLifecycleCallbacks(ForegroundTracker)
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
}

View File

@@ -1,13 +1,31 @@
package com.topjohnwu.magisk.core
import android.os.Build
import android.os.Process
import com.topjohnwu.magisk.BuildConfig
import java.io.File
@Suppress("DEPRECATION")
object Const {
val CPU_ABI: String
val CPU_ABI_32: String
init {
if (Build.VERSION.SDK_INT >= 21) {
CPU_ABI = Build.SUPPORTED_ABIS[0]
CPU_ABI_32 = Build.SUPPORTED_32_BIT_ABIS.firstOrNull() ?: CPU_ABI
} else {
CPU_ABI = Build.CPU_ABI
CPU_ABI_32 = CPU_ABI
}
}
// Paths
lateinit var MAGISKTMP: String
lateinit var NATIVE_LIB_DIR: File
val MAGISK_PATH get() = "$MAGISKTMP/modules"
const val TMP_FOLDER_PATH = "/dev/tmp"
const val TMPDIR = "/dev/tmp"
const val MAGISK_LOG = "/cache/magisk.log"
// Versions
@@ -19,24 +37,17 @@ object Const {
val USER_ID = Process.myUid() / 100000
object Version {
const val MIN_VERSION = "v19.0"
const val MIN_VERCODE = 19000
const val MIN_VERSION = "v20.4"
const val MIN_VERCODE = 20400
fun atLeast_20_2() = Info.env.magiskVersionCode >= 20200 || isCanary()
fun atLeast_20_4() = Info.env.magiskVersionCode >= 20400 || isCanary()
fun atLeast_21_0() = Info.env.magiskVersionCode >= 21000 || isCanary()
fun atLeast_21_2() = Info.env.magiskVersionCode >= 21200 || isCanary()
fun isCanary() = Info.env.magiskVersionCode % 100 != 0
}
object ID {
const val FETCH_ZIP = 2
const val SELECT_FILE = 3
const val MAX_ACTIVITY_RESULT = 10
// notifications
const val MAGISK_UPDATE_NOTIFICATION_ID = 4
const val APK_UPDATE_NOTIFICATION_ID = 5
const val HIDE_MANAGER_NOTIFICATION_ID = 8
const val UPDATE_NOTIFICATION_CHANNEL = "update"
const val PROGRESS_NOTIFICATION_CHANNEL = "progress"
const val CHECK_MAGISK_UPDATE_WORKER_ID = "magisk_update"
@@ -46,6 +57,9 @@ object Const {
const val PATREON_URL = "https://www.patreon.com/topjohnwu"
const val SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk"
val CHANGELOG_URL = if (BuildConfig.VERSION_CODE % 100 != 0) Info.remote.magisk.note
else "https://topjohnwu.github.io/Magisk/releases/${BuildConfig.VERSION_CODE}.md"
const val GITHUB_RAW_URL = "https://raw.githubusercontent.com/"
const val GITHUB_API_URL = "https://api.github.com/"
const val GITHUB_PAGE_URL = "https://topjohnwu.github.io/magisk_files/"

View File

@@ -14,44 +14,38 @@ import android.content.Intent
import android.content.res.AssetManager
import android.content.res.Configuration
import android.content.res.Resources
import android.util.DisplayMetrics
import androidx.annotation.RequiresApi
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.utils.refreshLocale
import com.topjohnwu.magisk.core.utils.updateConfig
import com.topjohnwu.magisk.ktx.forceGetDeclaredField
import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
fun AssetManager.addAssetPath(path: String) {
DynAPK.addAssetPath(this, path)
}
fun Context.wrap(global: Boolean = true): Context =
if (global) GlobalResContext(this) else ResContext(this)
fun Context.wrap(inject: Boolean = false): Context =
if (inject) ReInjectedContext(this) else InjectedContext(this)
fun Context.wrapJob(): Context = object : GlobalResContext(this) {
fun Context.wrapJob(): Context = object : InjectedContext(this) {
override fun getApplicationContext(): Context {
return this
}
override fun getApplicationContext() = this
@SuppressLint("NewApi")
override fun getSystemService(name: String): Any? {
return if (!isRunningAsStub) super.getSystemService(name) else
when (name) {
Context.JOB_SCHEDULER_SERVICE ->
JobSchedulerWrapper(super.getSystemService(name) as JobScheduler)
else -> super.getSystemService(name)
return super.getSystemService(name).let {
when {
!isRunningAsStub -> it
name == JOB_SCHEDULER_SERVICE -> JobSchedulerWrapper(it as JobScheduler)
else -> it
}
}
}
}
fun Class<*>.cmp(pkg: String): ComponentName {
val name = ClassMap[this].name
return ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
}
fun Class<*>.cmp(pkg: String) =
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
inline fun <reified T> Activity.redirect() = Intent(intent)
.setComponent(T::class.java.cmp(packageName))
@@ -59,34 +53,27 @@ inline fun <reified T> Activity.redirect() = Intent(intent)
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
private open class GlobalResContext(base: Context) : ContextWrapper(base) {
open val mRes: Resources get() = ResMgr.resource
override fun getResources(): Resources {
return mRes
}
override fun getClassLoader(): ClassLoader {
return javaClass.classLoader!!
}
private open class InjectedContext(base: Context) : ContextWrapper(base) {
open val res: Resources get() = AssetHack.resource
override fun getAssets(): AssetManager = res.assets
override fun getResources() = res
override fun getClassLoader() = javaClass.classLoader!!
override fun createConfigurationContext(config: Configuration): Context {
return ResContext(super.createConfigurationContext(config))
return super.createConfigurationContext(config).wrap(true)
}
}
private class ResContext(base: Context) : GlobalResContext(base) {
override val mRes by lazy { base.resources.patch() }
private class ReInjectedContext(base: Context) : InjectedContext(base) {
override val res by lazy { base.resources.patch() }
private fun Resources.patch(): Resources {
updateConfig()
if (isRunningAsStub)
assets.addAssetPath(ResMgr.apk)
assets.addAssetPath(AssetHack.apk)
return this
}
}
object ResMgr {
object AssetHack {
lateinit var resource: Resources
lateinit var apk: String
@@ -101,62 +88,39 @@ object ResMgr {
apk = context.packageResourcePath
}
}
fun newResource(): Resources {
val asset = AssetManager::class.java.newInstance()
asset.addAssetPath(apk)
val config = Configuration(resource.configuration)
val metrics = DisplayMetrics()
metrics.setTo(resource.displayMetrics)
return Resources(asset, metrics, config)
}
}
@RequiresApi(28)
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
override fun schedule(job: JobInfo): Int {
return base.schedule(job.patch())
}
override fun enqueue(job: JobInfo, work: JobWorkItem): Int {
return base.enqueue(job.patch(), work)
}
override fun cancel(jobId: Int) {
base.cancel(jobId)
}
override fun cancelAll() {
base.cancelAll()
}
override fun getAllPendingJobs(): List<JobInfo> {
return base.allPendingJobs
}
override fun getPendingJob(jobId: Int): JobInfo? {
return base.getPendingJob(jobId)
}
override fun schedule(job: JobInfo) = base.schedule(job.patch())
override fun enqueue(job: JobInfo, work: JobWorkItem) = base.enqueue(job.patch(), work)
override fun cancel(jobId: Int) = base.cancel(jobId)
override fun cancelAll() = base.cancelAll()
override fun getAllPendingJobs(): List<JobInfo> = base.allPendingJobs
override fun getPendingJob(jobId: Int) = base.getPendingJob(jobId)
private fun JobInfo.patch(): JobInfo {
// We need to swap out the service of JobInfo
val name = service.className
val component = ComponentName(
service.packageName,
Info.stubChk.classToComponent[name] ?: name
)
// Swap out the service of JobInfo
val component = service.run {
ComponentName(packageName,
Info.stub?.classToComponent?.get(className) ?: className)
}
javaClass.getDeclaredField("service").apply {
isAccessible = true
}.set(this, component)
javaClass.forceGetDeclaredField("service")?.set(this, component)
return this
}
}
private object ClassMap {
private val map = mapOf(
App::class.java to a.e::class.java,
MainActivity::class.java to a.b::class.java,
SplashActivity::class.java to a.c::class.java,
Receiver::class.java to a.h::class.java,
DownloadService::class.java to a.j::class.java,
SuRequestActivity::class.java to a.m::class.java
)
operator fun get(c: Class<*>) = map.getOrElse(c) { c }
}
// Keep a reference to these resources to prevent it from
// being removed when running "remove unused resources"
val shouldKeepResources = listOf(

View File

@@ -1,41 +1,45 @@
package com.topjohnwu.magisk.core
import android.os.Build
import androidx.databinding.ObservableBoolean
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.model.UpdateInfo
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.ktx.get
import com.topjohnwu.magisk.utils.CachedValue
import com.topjohnwu.magisk.ktx.getProperty
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils.fastCmd
import com.topjohnwu.superuser.internal.UiThreadHandler
import java.io.FileInputStream
import java.io.File
import java.io.IOException
import java.util.*
val isRunningAsStub get() = Info.stub != null
object Info {
val envRef = CachedValue { loadState() }
@JvmStatic val env by envRef
var stub: DynAPK.Data? = null
val stubChk: DynAPK.Data
get() = stub as DynAPK.Data
var remote = UpdateInfo()
val EMPTY_REMOTE = UpdateInfo()
var remote = EMPTY_REMOTE
suspend fun getRemote(svc: NetworkService): UpdateInfo? {
return if (remote === EMPTY_REMOTE) {
svc.fetchUpdate()?.apply { remote = this }
} else remote
}
// Device state
var crypto = ""
@JvmStatic var isSAR = false
@JvmStatic var isAB = false
@JvmStatic val env by lazy { loadState() }
@JvmField var isSAR = false
@JvmField var isAB = false
@JvmField val isVirtualAB = getProperty("ro.virtual_ab.enabled", "false") == "true"
@JvmStatic val isFDE get() = crypto == "block"
@JvmStatic var ramdisk = false
@JvmStatic var hasGMS = true
@JvmStatic var isPixel = false
@JvmStatic val cryptoText get() = crypto.capitalize(Locale.US)
@JvmField var ramdisk = false
@JvmField var hasGMS = true
@JvmField val isPixel = Build.BRAND == "google"
@JvmField val isEmulator = getProperty("ro.kernel.qemu", "0") == "1"
var crypto = ""
var noDataExec = false
val isConnected by lazy {
ObservableBoolean(false).also { field ->
@@ -47,14 +51,12 @@ object Info {
val isNewReboot by lazy {
try {
FileInputStream("/proc/sys/kernel/random/boot_id").bufferedReader().use {
val id = it.readLine()
if (id != Config.bootId) {
Config.bootId = id
true
} else {
false
}
val id = File("/proc/sys/kernel/random/boot_id").readText()
if (id != Config.bootId) {
Config.bootId = id
true
} else {
false
}
} catch (e: IOException) {
false
@@ -73,8 +75,8 @@ object Info {
hide: Boolean = false
) {
val magiskHide get() = Config.magiskHide
val magiskVersionCode = when (code) {
in Int.MIN_VALUE..Const.Version.MIN_VERCODE -> -1
val magiskVersionCode = when {
code < Const.Version.MIN_VERCODE -> -1
else -> if (Shell.rootAccess()) code else -1
}
val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE

View File

@@ -1,32 +1,30 @@
package com.topjohnwu.magisk.core
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.ktx.get
import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.util.concurrent.CountDownLatch
open class SplashActivity : Activity() {
open class SplashActivity : BaseActivity() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base.wrap())
}
private val latch = CountDownLatch(1)
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.SplashTheme)
super.onCreate(savedInstanceState)
GlobalScope.launch(Dispatchers.IO) {
initAndStart()
}
// Pre-initialize root shell
Shell.getShell(null) { initAndStart() }
}
private fun handleRepackage(pkg: String?) {
@@ -40,13 +38,26 @@ open class SplashActivity : Activity() {
if (Config.suManager.isNotEmpty())
Config.suManager = ""
pkg ?: return
Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec()
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess)
uninstallApp(pkg)
}
}
private fun initAndStart() {
// Pre-initialize root shell
Shell.getShell()
if (isRunningAsStub && !Shell.rootAccess()) {
runOnUiThread {
MagiskDialog(this)
.applyTitle(R.string.unsupport_nonroot_stub_title)
.applyMessage(R.string.unsupport_nonroot_stub_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.install
onClick { HideAPK.restore(this@SplashActivity) }
}
.cancellable(false)
.reveal()
}
return
}
val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)
@@ -60,11 +71,21 @@ open class SplashActivity : Activity() {
get<NetworkService>()
DONE = true
redirect<MainActivity>().also { startActivity(it) }
startActivity(redirect<MainActivity>())
finish()
}
@Suppress("DEPRECATION")
private fun uninstallApp(pkg: String) {
val uri = Uri.Builder().scheme("package").opaquePart(pkg).build()
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, uri)
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
startActivityForResult(intent) { _, _ ->
latch.countDown()
}
latch.await()
}
companion object {
var DONE = false
}

View File

@@ -1,13 +1,11 @@
package com.topjohnwu.magisk.core
import android.annotation.SuppressLint
import android.content.Context
import androidx.work.*
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.KoinComponent
import org.koin.core.inject
import java.util.concurrent.TimeUnit
@@ -18,20 +16,15 @@ class UpdateCheckService(context: Context, workerParams: WorkerParameters)
private val svc: NetworkService by inject()
override suspend fun doWork(): Result {
// Make sure shell initializer was ran
withContext(Dispatchers.IO) {
Shell.getShell()
}
return svc.fetchUpdate()?.let {
if (BuildConfig.VERSION_CODE < it.app.versionCode)
return svc.fetchUpdate()?.run {
if (Info.env.isActive && BuildConfig.VERSION_CODE < magisk.versionCode)
Notifications.managerUpdate(applicationContext)
else if (Info.env.isActive && Info.env.magiskVersionCode < it.magisk.versionCode)
Notifications.magiskUpdate(applicationContext)
Result.success()
} ?: Result.failure()
}
companion object {
@SuppressLint("NewApi")
fun schedule(context: Context) {
if (Config.checkUpdate) {
val constraints = Constraints.Builder()

View File

@@ -14,7 +14,6 @@ import androidx.collection.SparseArrayCompat
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.core.wrap
import com.topjohnwu.magisk.ktx.set
@@ -26,6 +25,13 @@ typealias ActivityResultCallback = BaseActivity.(Int, Intent?) -> Unit
abstract class BaseActivity : AppCompatActivity() {
private val resultCallbacks by lazy { SparseArrayCompat<ActivityResultCallback>() }
private val newRequestCode: Int get() {
var requestCode: Int
do {
requestCode = Random.nextInt(0, 1 shl 15)
} while (resultCallbacks.containsKey(requestCode))
return requestCode
}
override fun applyOverrideConfiguration(config: Configuration?) {
// Force applying our preferred local
@@ -34,7 +40,7 @@ abstract class BaseActivity : AppCompatActivity() {
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base.wrap(false))
super.attachBaseContext(base.wrap(true))
}
fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) {
@@ -49,10 +55,7 @@ abstract class BaseActivity : AppCompatActivity() {
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
request.onSuccess()
} else {
var requestCode: Int
do {
requestCode = Random.nextInt(Const.ID.MAX_ACTIVITY_RESULT + 1, 1 shl 15)
} while (resultCallbacks.containsKey(requestCode))
val requestCode = newRequestCode
resultCallbacks[requestCode] = { result, _ ->
if (result > 0)
request.onSuccess()
@@ -92,7 +95,8 @@ abstract class BaseActivity : AppCompatActivity() {
}
}
fun startActivityForResult(intent: Intent, requestCode: Int, callback: ActivityResultCallback) {
fun startActivityForResult(intent: Intent, callback: ActivityResultCallback) {
val requestCode = newRequestCode
resultCallbacks[requestCode] = callback
try {
startActivityForResult(intent, requestCode)

View File

@@ -1,31 +0,0 @@
package com.topjohnwu.magisk.core.download
import android.net.Uri
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
sealed class Action : Parcelable {
sealed class Flash : Action() {
@Parcelize
object Primary : Flash()
@Parcelize
object Secondary : Flash()
}
@Parcelize
object Download : Action()
@Parcelize
object Uninstall : Action()
@Parcelize
object EnvFix : Action()
@Parcelize
data class Patch(val fileUri: Uri) : Action()
}

View File

@@ -8,11 +8,8 @@ import androidx.lifecycle.MutableLiveData
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.ForegroundTracker
import com.topjohnwu.magisk.core.base.BaseService
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.checkSum
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.core.utils.ProgressInputStream
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.ktx.withStreams
import com.topjohnwu.magisk.view.Notifications
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -69,18 +66,11 @@ abstract class BaseDownloader : BaseService(), KoinComponent {
// -- Download logic
private suspend fun Subject.startDownload() {
val skip = this is Subject.Magisk && file.checkSum("MD5", magisk.md5)
if (!skip) {
val stream = service.fetchFile(url).toProgressStream(this)
when (this) {
is Subject.Module -> // Download and process on-the-fly
stream.toModule(file, service.fetchInstaller().byteStream())
else -> {
withStreams(stream, file.outputStream()) { it, out -> it.copyTo(out) }
if (this is Subject.Manager)
handleAPK(this)
}
}
val stream = service.fetchFile(url).toProgressStream(this)
when (this) {
is Subject.Module -> // Download and process on-the-fly
stream.toModule(file, service.fetchInstaller().byteStream())
is Subject.Manager -> handleAPK(this, stream)
}
val newId = notifyFinish(this)
if (ForegroundTracker.hasForeground)

View File

@@ -7,11 +7,10 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.net.toFile
import com.topjohnwu.magisk.core.download.Action.*
import com.topjohnwu.magisk.core.download.Action.Flash.Secondary
import com.topjohnwu.magisk.core.download.Subject.*
import com.topjohnwu.magisk.core.download.Action.Flash
import com.topjohnwu.magisk.core.download.Subject.Manager
import com.topjohnwu.magisk.core.download.Subject.Module
import com.topjohnwu.magisk.core.intent
import com.topjohnwu.magisk.core.tasks.EnvFixTask
import com.topjohnwu.magisk.ui.flash.FlashFragment
import com.topjohnwu.magisk.utils.APKInstall
import kotlin.random.Random.Default.nextInt
@@ -22,25 +21,12 @@ open class DownloadService : BaseDownloader() {
private val context get() = this
override suspend fun onFinish(subject: Subject, id: Int) = when (subject) {
is Magisk -> subject.onFinish(id)
is Module -> subject.onFinish(id)
is Manager -> subject.onFinish(id)
}
private suspend fun Magisk.onFinish(id: Int) = when (val action = action) {
Uninstall -> FlashFragment.uninstall(file, id)
EnvFix -> {
remove(id)
EnvFixTask(file).exec()
Unit
}
is Patch -> FlashFragment.patch(file, action.fileUri, id)
is Flash -> FlashFragment.flash(file, action is Secondary, id)
else -> Unit
}
private fun Module.onFinish(id: Int) = when (action) {
is Flash -> FlashFragment.install(file, id)
Flash -> FlashFragment.install(file, id)
else -> Unit
}
@@ -53,22 +39,13 @@ open class DownloadService : BaseDownloader() {
override fun Notification.Builder.setIntent(subject: Subject)
= when (subject) {
is Magisk -> setIntent(subject)
is Module -> setIntent(subject)
is Manager -> setIntent(subject)
}
private fun Notification.Builder.setIntent(subject: Magisk)
= when (val action = subject.action) {
Uninstall -> setContentIntent(FlashFragment.uninstallIntent(context, subject.file))
is Flash -> setContentIntent(FlashFragment.flashIntent(context, subject.file, action is Secondary))
is Patch -> setContentIntent(FlashFragment.patchIntent(context, subject.file, action.fileUri))
else -> setContentIntent(Intent())
}
private fun Notification.Builder.setIntent(subject: Module)
= when (subject.action) {
is Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file))
Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file))
else -> setContentIntent(Intent())
}

View File

@@ -2,19 +2,22 @@ package com.topjohnwu.magisk.core.download
import android.content.Context
import androidx.core.net.toFile
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.ktx.relaunchApp
import com.topjohnwu.magisk.ktx.withStreams
import com.topjohnwu.magisk.ktx.writeTo
import java.io.File
import java.io.InputStream
import java.io.OutputStream
private fun Context.patch(apk: File) {
val patched = File(apk.parent, "patched.apk")
HideAPK.patch(this, apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel)
HideAPK.patch(this, apk, patched, packageName, applicationInfo.nonLocalizedLabel)
apk.delete()
patched.renameTo(apk)
}
@@ -22,30 +25,51 @@ private fun Context.patch(apk: File) {
private fun BaseDownloader.notifyHide(id: Int) {
update(id) {
it.setProgress(0, 0, true)
.setContentTitle(getString(R.string.hide_manager_title))
.setContentTitle(getString(R.string.hide_app_title))
.setContentText("")
}
}
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) {
val apk = subject.file.toFile()
val id = subject.notifyID()
private class DupOutputStream(
private val o1: OutputStream,
private val o2: OutputStream
) : OutputStream() {
override fun write(b: Int) {
o1.write(b)
o2.write(b)
}
override fun write(b: ByteArray?, off: Int, len: Int) {
o1.write(b, off, len)
o2.write(b, off, len)
}
override fun close() {
o1.close()
o2.close()
}
}
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager, stream: InputStream) {
fun write(output: OutputStream) {
val ext = subject.externalFile.outputStream()
val o = DupOutputStream(ext, output)
withStreams(stream, o) { src, out -> src.copyTo(out) }
}
if (isRunningAsStub) {
// Move to upgrade location
apk.copyTo(DynAPK.update(this), overwrite = true)
apk.delete()
if (Info.stubChk.version < subject.stub.versionCode) {
notifyHide(id)
val apk = subject.file.toFile()
val id = subject.notifyID()
write(DynAPK.update(this).outputStream())
if (Info.stub!!.version < subject.stub.versionCode) {
// Also upgrade stub
service.fetchFile(subject.stub.link).byteStream().use { it.writeTo(apk) }
notifyHide(id)
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
patch(apk)
} else {
// Simply relaunch the app
stopSelf()
relaunchApp(this)
}
} else if (packageName != BuildConfig.APPLICATION_ID) {
notifyHide(id)
patch(apk)
} else {
write(subject.file.outputStream())
}
}

View File

@@ -1,8 +1,9 @@
package com.topjohnwu.magisk.core.download
import android.net.Uri
import com.topjohnwu.magisk.ktx.withStreams
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.ktx.forEach
import com.topjohnwu.magisk.ktx.withStreams
import java.io.InputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@@ -25,8 +26,7 @@ fun InputStream.toModule(file: Uri, installer: InputStream) {
zout.write("#MAGISK\n".toByteArray(charset("UTF-8")))
var off = -1
var entry: ZipEntry? = zin.nextEntry
while (entry != null) {
zin.forEach { entry ->
if (off < 0) {
off = entry.name.indexOf('/') + 1
}
@@ -38,8 +38,6 @@ fun InputStream.toModule(file: Uri, installer: InputStream) {
zin.copyTo(zout)
}
}
entry = zin.nextEntry
}
}
}

View File

@@ -6,7 +6,6 @@ import android.os.Parcelable
import androidx.core.net.toUri
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.model.MagiskJson
import com.topjohnwu.magisk.core.model.ManagerJson
import com.topjohnwu.magisk.core.model.StubJson
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
@@ -40,70 +39,26 @@ sealed class Subject : Parcelable {
@Parcelize
class Manager(
private val app: ManagerJson = Info.remote.app,
private val json: MagiskJson = Info.remote.magisk,
val stub: StubJson = Info.remote.stub
) : Subject() {
override val action get() = Action.Download
override val title: String get() = "MagiskManager-${app.version}(${app.versionCode})"
override val url: String get() = app.link
override val title: String get() = "Magisk-${json.version}(${json.versionCode})"
override val url: String get() = json.link
@IgnoredOnParcel
override val file by lazy {
cachedFile("manager.apk")
}
val externalFile get() = MediaStoreUtils.getFile("$title.apk").uri
}
abstract class Magisk : Subject() {
val magisk: MagiskJson = Info.remote.magisk
@Parcelize
private class Internal(
override val action: Action
) : Magisk() {
override val url: String get() = magisk.link
override val title: String get() = "Magisk-${magisk.version}(${magisk.versionCode})"
@IgnoredOnParcel
override val file by lazy {
cachedFile("magisk.zip")
}
}
@Parcelize
private class Uninstall : Magisk() {
override val action get() = Action.Uninstall
override val url: String get() = Info.remote.uninstaller.link
override val title: String get() = "uninstall.zip"
@IgnoredOnParcel
override val file by lazy {
cachedFile(title)
}
}
@Parcelize
private class Download : Magisk() {
override val action get() = Action.Download
override val url: String get() = magisk.link
override val title: String get() = "Magisk-${magisk.version}(${magisk.versionCode}).zip"
@IgnoredOnParcel
override val file by lazy {
MediaStoreUtils.getFile(title).uri
}
}
companion object {
operator fun invoke(config: Action) = when (config) {
Action.Download -> Download()
Action.Uninstall -> Uninstall()
Action.EnvFix, is Action.Flash, is Action.Patch -> Internal(config)
}
}
}
}
sealed class Action : Parcelable {
@Parcelize
object Flash : Action()
@Parcelize
object Download : Action()
}

View File

@@ -6,17 +6,11 @@ import kotlinx.parcelize.Parcelize
@JsonClass(generateAdapter = true)
data class UpdateInfo(
val app: ManagerJson = ManagerJson(),
val uninstaller: UninstallerJson = UninstallerJson(),
val magisk: MagiskJson = MagiskJson(),
val stub: StubJson = StubJson()
)
@JsonClass(generateAdapter = true)
data class UninstallerJson(
val link: String = ""
)
@Parcelize
@JsonClass(generateAdapter = true)
data class MagiskJson(
val version: String = "",
@@ -24,15 +18,6 @@ data class MagiskJson(
val link: String = "",
val note: String = "",
val md5: String = ""
)
@Parcelize
@JsonClass(generateAdapter = true)
data class ManagerJson(
val version: String = "",
val versionCode: Int = -1,
val link: String = "",
val note: String = ""
) : Parcelable
@Parcelize

View File

@@ -27,13 +27,13 @@ class LocalModule(path: String) : Module() {
val dir = "$PERSIST/$id"
if (enable) {
disableFile.delete()
if (Const.Version.isCanary())
if (Const.Version.atLeast_21_2())
Shell.su("copy_sepolicy_rules").submit()
else
Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit()
} else {
!disableFile.createNewFile()
if (Const.Version.isCanary())
if (Const.Version.atLeast_21_2())
Shell.su("copy_sepolicy_rules").submit()
else
Shell.su("rm -rf $dir").submit()
@@ -45,13 +45,13 @@ class LocalModule(path: String) : Module() {
set(remove) {
if (remove) {
removeFile.createNewFile()
if (Const.Version.isCanary())
if (Const.Version.atLeast_21_2())
Shell.su("copy_sepolicy_rules").submit()
else
Shell.su("rm -rf $PERSIST/$id").submit()
} else {
!removeFile.delete()
if (Const.Version.isCanary())
if (Const.Version.atLeast_21_2())
Shell.su("copy_sepolicy_rules").submit()
else
Shell.su("cp -af $ruleFile $PERSIST/$id").submit()

View File

@@ -1,11 +1,13 @@
@file:SuppressLint("InlinedApi")
package com.topjohnwu.magisk.core.model.su
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.INTERACTIVE
import com.topjohnwu.magisk.ktx.getLabel
data class SuPolicy(
var uid: Int,
val packageName: String,
@@ -38,7 +40,7 @@ fun SuPolicy.toMap() = mapOf(
fun Map<String, String>.toPolicy(pm: PackageManager): SuPolicy {
val uid = get("uid")?.toIntOrNull() ?: -1
val packageName = get("package_name").orEmpty()
val info = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES)
val info = pm.getApplicationInfo(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES)
if (info.uid != uid)
throw PackageManager.NameNotFoundException()
@@ -59,7 +61,7 @@ fun Map<String, String>.toPolicy(pm: PackageManager): SuPolicy {
fun Int.toPolicy(pm: PackageManager, policy: Int = INTERACTIVE): SuPolicy {
val pkg = pm.getPackagesForUid(this)?.firstOrNull()
?: throw PackageManager.NameNotFoundException()
val info = pm.getApplicationInfo(pkg, PackageManager.GET_UNINSTALLED_PACKAGES)
val info = pm.getApplicationInfo(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES)
return SuPolicy(
uid = info.uid,
packageName = pkg,

View File

@@ -2,14 +2,13 @@ package com.topjohnwu.magisk.core.tasks
import android.content.Context
import android.net.Uri
import androidx.core.os.postDelayed
import androidx.core.net.toFile
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
import com.topjohnwu.magisk.core.utils.unzip
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
import com.topjohnwu.magisk.core.utils.unzip
import com.topjohnwu.magisk.ktx.writeTo
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.internal.UiThreadHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.KoinComponent
@@ -25,61 +24,50 @@ open class FlashZip(
private val logs: MutableList<String>
): KoinComponent {
val context: Context by inject()
private val installFolder = File(context.cacheDir, "flash").apply {
if (!exists()) mkdirs()
}
private val tmpFile: File = File(installFolder, "install.zip")
@Throws(IOException::class)
private fun unzipAndCheck(): Boolean {
val parentFile = tmpFile.parentFile ?: return false
tmpFile.unzip(parentFile, "META-INF/com/google/android", true)
val updaterScript = File(parentFile, "updater-script")
return Shell
.su("grep -q '#MAGISK' $updaterScript")
.exec()
.isSuccess
}
private val context: Context by inject()
private val installDir = File(context.cacheDir, "flash")
private lateinit var zipFile: File
@Throws(IOException::class)
private fun flash(): Boolean {
console.add("- Copying zip to temp directory")
installDir.deleteRecursively()
installDir.mkdirs()
runCatching {
mUri.inputStream().writeTo(tmpFile)
}.getOrElse {
when (it) {
is FileNotFoundException -> console.add("! Invalid Uri")
is IOException -> console.add("! Cannot copy to cache")
zipFile = if (mUri.scheme == "file") {
mUri.toFile()
} else {
File(installDir, "install.zip").also {
console.add("- Copying zip to temp directory")
try {
mUri.inputStream().writeTo(it)
} catch (e: IOException) {
when (e) {
is FileNotFoundException -> console.add("! Invalid Uri")
else -> console.add("! Cannot copy to cache")
}
throw e
}
}
throw it
}
val isMagiskModule = runCatching {
unzipAndCheck()
val isValid = runCatching {
zipFile.unzip(installDir, "META-INF/com/google/android", true)
val script = File(installDir, "updater-script")
script.readText().contains("#MAGISK")
}.getOrElse {
console.add("! Unzip error")
throw it
}
if (!isMagiskModule) {
console.add("! This zip is not a Magisk Module!")
if (!isValid) {
console.add("! This zip is not a Magisk module!")
return false
}
console.add("- Installing ${mUri.displayName}")
val parentFile = tmpFile.parent ?: return false
return Shell
.su(
"cd $parentFile",
"BOOTMODE=true sh update-binary dummy 1 $tmpFile"
)
.to(console, logs)
.exec().isSuccess
return Shell.su("sh $installDir/update-binary dummy 1 \"$zipFile\"")
.to(console, logs).exec().isSuccess
}
open suspend fun exec() = withContext(Dispatchers.IO) {
@@ -94,25 +82,7 @@ open class FlashZip(
Timber.e(e)
false
} finally {
Shell.su("cd /", "rm -rf ${tmpFile.parent} ${Const.TMP_FOLDER_PATH}").submit()
Shell.su("cd /", "rm -rf $installDir ${Const.TMPDIR}").submit()
}
}
class Uninstall(
uri: Uri,
console: MutableList<String>,
log: MutableList<String>
) : FlashZip(uri, console, log) {
override suspend fun exec(): Boolean {
val success = super.exec()
if (success) {
UiThreadHandler.handler.postDelayed(3000) {
Shell.su("pm uninstall " + context.packageName).exec()
}
}
return success
}
}
}

View File

@@ -1,38 +1,41 @@
package com.topjohnwu.magisk.core.tasks
import android.app.ProgressDialog
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build.VERSION.SDK_INT
import android.widget.Toast
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.Provider
import com.topjohnwu.magisk.core.utils.AXML
import com.topjohnwu.magisk.core.utils.Keygen
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.ktx.inject
import com.topjohnwu.magisk.ktx.writeTo
import com.topjohnwu.magisk.utils.APKInstall
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.signing.JarMap
import com.topjohnwu.signing.SignApk
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.lang.ref.WeakReference
import java.security.SecureRandom
object HideAPK {
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
private const val ALPHADOTS = "$ALPHA....."
private const val APP_NAME = "Magisk Manager"
private const val APP_NAME = "Magisk"
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
// Some arbitrary limit
@@ -49,7 +52,7 @@ object HideAPK {
var next: Char
var prev = 0.toChar()
for (i in 0 until len) {
next = if (prev == '.' || prev == 0.toChar() || i == len - 1) {
next = if (prev == '.' || i == 0 || i == len - 1) {
ALPHA[random.nextInt(ALPHA.length)]
} else {
ALPHADOTS[random.nextInt(ALPHADOTS.length)]
@@ -59,19 +62,19 @@ object HideAPK {
}
if (!builder.contains('.')) {
// Pick a random index and set it as dot
val idx = random.nextInt(len - 1)
builder[idx] = '.'
val idx = random.nextInt(len - 2)
builder[idx + 1] = '.'
}
return builder.toString()
}
fun patch(
context: Context,
apk: String, out: String,
apk: File, out: File,
pkg: String, label: CharSequence
): Boolean {
try {
val jar = JarMap.open(apk)
val jar = JarMap.open(apk, true)
val je = jar.getJarEntry(ANDROID_MANIFEST)
val xml = AXML(jar.getRawData(je))
@@ -90,103 +93,76 @@ object HideAPK {
return true
}
private suspend fun patchAndHide(context: Context, label: String): Boolean {
val dlStub = !isRunningAsStub && SDK_INT >= 28 && Const.Version.atLeast_20_2()
val src = if (dlStub) {
val stub = File(context.cacheDir, "stub.apk")
try {
svc.fetchFile(Info.remote.stub.link).byteStream().use {
it.writeTo(stub)
}
} catch (e: IOException) {
Timber.e(e)
return false
}
stub.path
} else {
context.packageCodePath
}
private class WaitPackageReceiver(
private val pkg: String,
activity: Activity
) : BroadcastReceiver() {
// Generate a new random package name and signature
val repack = File(context.cacheDir, "patched.apk")
val pkg = genPackageName()
Config.keyStoreRaw = ""
private val activity = WeakReference(activity)
if (!patch(context, src, repack.path, pkg, label))
return false
// Install the application
if (!Shell.su("adb_pm_install $repack").exec().isSuccess)
return false
context.apply {
val intent = packageManager.getLaunchIntentForPackage(pkg) ?: return false
Config.suManager = pkg
private fun launchApp(): Unit = activity.get()?.run {
val intent = packageManager.getLaunchIntentForPackage(pkg) ?: return
Config.suManager = if (pkg == APPLICATION_ID) "" else pkg
grantUriPermission(pkg, APK_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
grantUriPermission(pkg, PREFS_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.putExtra(Const.Key.PREV_PKG, packageName)
startActivity(intent)
}
finish()
} ?: Unit
return true
}
@Suppress("DEPRECATION")
fun hide(context: Context, label: String) {
val dialog = ProgressDialog.show(context, context.getString(R.string.hide_manager_title), "", true)
GlobalScope.launch {
val result = withContext(Dispatchers.IO) {
patchAndHide(context, label)
}
if (!result) {
Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG)
dialog.dismiss()
}
}
}
private suspend fun downloadAndRestore(context: Context): Boolean {
val apk = if (isRunningAsStub) {
DynAPK.current(context)
} else {
File(context.cacheDir, "manager.apk").also { apk ->
try {
svc.fetchFile(Info.remote.app.link).byteStream().use {
it.writeTo(apk)
override fun onReceive(context: Context, intent: Intent) {
when (intent.action ?: return) {
Intent.ACTION_PACKAGE_REPLACED, Intent.ACTION_PACKAGE_ADDED -> {
val newPkg = intent.data?.encodedSchemeSpecificPart.orEmpty()
if (newPkg == pkg) {
context.unregisterReceiver(this)
launchApp()
}
} catch (e: IOException) {
Timber.e(e)
return false
}
}
}
if (!Shell.su("adb_pm_install $apk").exec().isSuccess)
return false
}
context.apply {
val intent = packageManager.getLaunchIntentForPackage(APPLICATION_ID) ?: return false
Config.suManager = ""
grantUriPermission(APPLICATION_ID, APK_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
grantUriPermission(APPLICATION_ID, PREFS_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.putExtra(Const.Key.PREV_PKG, packageName)
startActivity(intent)
private suspend fun patchAndHide(activity: Activity, label: String): Boolean {
val stub = File(activity.cacheDir, "stub.apk")
try {
svc.fetchFile(Info.remote.stub.link).byteStream().writeTo(stub)
} catch (e: IOException) {
Timber.e(e)
return false
}
// Generate a new random package name and signature
val repack = File(activity.cacheDir, "patched.apk")
val pkg = genPackageName()
Config.keyStoreRaw = ""
if (!patch(activity, stub, repack, pkg, label))
return false
// Install and auto launch app
APKInstall.registerInstallReceiver(activity, WaitPackageReceiver(pkg, activity))
if (!Shell.su("adb_pm_install $repack").exec().isSuccess)
APKInstall.installHideResult(activity, repack)
return true
}
@Suppress("DEPRECATION")
fun restore(context: Context) {
val dialog = ProgressDialog.show(context, context.getString(R.string.restore_img_msg), "", true)
GlobalScope.launch {
val result = withContext(Dispatchers.IO) {
downloadAndRestore(context)
}
if (!result) {
Utils.toast(R.string.restore_manager_fail_toast, Toast.LENGTH_LONG)
dialog.dismiss()
}
suspend fun hide(activity: Activity, label: String) {
val result = withContext(Dispatchers.IO) {
patchAndHide(activity, label)
}
if (!result) {
Utils.toast(R.string.failure, Toast.LENGTH_LONG)
}
}
fun restore(activity: Activity) {
val apk = DynAPK.current(activity)
APKInstall.registerInstallReceiver(activity, WaitPackageReceiver(APPLICATION_ID, activity))
Shell.su("adb_pm_install $apk").submit {
if (!it.isSuccess)
APKInstall.installHideResult(activity, apk)
}
}
}

View File

@@ -1,25 +1,23 @@
package com.topjohnwu.magisk.core.tasks
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.widget.Toast
import androidx.annotation.WorkerThread
import androidx.core.os.postDelayed
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.di.Protected
import com.topjohnwu.magisk.events.dialog.EnvFixDialog
import com.topjohnwu.magisk.ktx.reboot
import com.topjohnwu.magisk.ktx.symlink
import com.topjohnwu.magisk.ktx.withStreams
import com.topjohnwu.magisk.ktx.writeTo
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.signing.SignBoot
import com.topjohnwu.superuser.Shell
@@ -37,164 +35,171 @@ import org.kamranzafar.jtar.TarHeader
import org.kamranzafar.jtar.TarInputStream
import org.kamranzafar.jtar.TarOutputStream
import org.koin.core.KoinComponent
import org.koin.core.get
import org.koin.core.inject
import timber.log.Timber
import java.io.*
import java.nio.ByteBuffer
import java.security.SecureRandom
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipFile
abstract class MagiskInstallImpl : KoinComponent {
abstract class MagiskInstallImpl protected constructor(
protected val console: MutableList<String> = NOPList.getInstance(),
private val logs: MutableList<String> = NOPList.getInstance()
) : KoinComponent {
protected lateinit var installDir: File
private lateinit var srcBoot: String
private lateinit var zipUri: Uri
protected val console: MutableList<String>
private val logs: MutableList<String>
private var tarOut: TarOutputStream? = null
protected var installDir = File("xxx")
private lateinit var srcBoot: File
private val shell = Shell.getShell()
private val service: NetworkService by inject()
protected val context: Context by inject()
protected constructor() {
console = NOPList.getInstance()
logs = NOPList.getInstance()
}
protected constructor(zip: Uri, out: MutableList<String>, err: MutableList<String>) {
console = out
logs = err
zipUri = zip
installDir = File(get<Context>(Protected).filesDir.parent, "install")
"rm -rf $installDir".sh()
installDir.mkdirs()
}
protected val context: Context by inject(Protected)
private val useRootDir = shell.isRoot && Info.noDataExec
private fun findImage(): Boolean {
srcBoot = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
if (srcBoot.isEmpty()) {
val bootPath = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
if (bootPath.isEmpty()) {
console.add("! Unable to detect target image")
return false
}
console.add("- Target image: $srcBoot")
srcBoot = SuFile(bootPath)
console.add("- Target image: $bootPath")
return true
}
private fun findSecondaryImage(): Boolean {
private fun findSecondary(): Boolean {
val slot = "echo \$SLOT".fsh()
val target = if (slot == "_a") "_b" else "_a"
console.add("- Target slot: $target")
srcBoot = arrayOf(
val bootPath = arrayOf(
"SLOT=$target",
"find_boot_image",
"SLOT=$slot",
"echo \"\$BOOTIMAGE\"").fsh()
if (srcBoot.isEmpty()) {
if (bootPath.isEmpty()) {
console.add("! Unable to detect target image")
return false
}
console.add("- Target image: $srcBoot")
srcBoot = SuFile(bootPath)
console.add("- Target image: $bootPath")
return true
}
@Suppress("DEPRECATION")
private fun extractZip(): Boolean {
val arch = if (Build.VERSION.SDK_INT >= 21) {
val abis = listOf(*Build.SUPPORTED_ABIS)
if (abis.contains("x86")) "x86" else "arm"
} else {
if (Build.CPU_ABI == "x86") "x86" else "arm"
}
private fun extractFiles(): Boolean {
console.add("- Device platform: ${Const.CPU_ABI}")
console.add("- Installing: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
console.add("- Device platform: " + Build.CPU_ABI)
console.add("- Magisk Manager: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
console.add("- Install target: ${Info.remote.magisk.version} (${Info.remote.magisk.versionCode})")
installDir = File(context.filesDir.parent, "install")
installDir.deleteRecursively()
installDir.mkdirs()
try {
ZipInputStream(zipUri.inputStream().buffered()).use { zi ->
lateinit var ze: ZipEntry
while (zi.nextEntry?.let { ze = it } != null) {
if (ze.isDirectory)
continue
var name: String? = null
val names = arrayOf("$arch/", "common/", "META-INF/com/google/android/update-binary")
for (n in names) {
ze.name.run {
if (startsWith(n)) {
name = substring(lastIndexOf('/') + 1)
}
}
name ?: continue
break
}
if (name == null && ze.name.startsWith("chromeos/"))
name = ze.name
name?.also {
val dest = if (installDir is SuFile)
SuFile(installDir, it)
else
File(installDir, it)
dest.parentFile!!.mkdirs()
SuFileOutputStream(dest).use { s -> zi.copyTo(s) }
} ?: continue
// Extract binaries
if (isRunningAsStub) {
val zf = ZipFile(DynAPK.current(context))
zf.entries().asSequence().filter {
!it.isDirectory && it.name.startsWith("lib/${Const.CPU_ABI_32}/")
}.forEach {
val n = it.name.substring(it.name.lastIndexOf('/') + 1)
val name = n.substring(3, n.length - 3)
val dest = File(installDir, name)
zf.getInputStream(it).writeTo(dest)
}
} else {
val libs = Const.NATIVE_LIB_DIR.listFiles { _, name ->
name.startsWith("lib") && name.endsWith(".so")
} ?: emptyArray()
for (lib in libs) {
val name = lib.name.substring(3, lib.name.length - 3)
symlink(lib.path, "$installDir/$name")
}
}
} catch (e: IOException) {
console.add("! Cannot unzip zip")
// Extract scripts
for (script in listOf("util_functions.sh", "boot_patch.sh", "addon.d.sh")) {
val dest = File(installDir, script)
context.assets.open(script).writeTo(dest)
}
// Extract chromeos tools
File(installDir, "chromeos").mkdir()
for (file in listOf("futility", "kernel_data_key.vbprivk", "kernel.keyblock")) {
val name = "chromeos/$file"
val dest = File(installDir, name)
context.assets.open(name).writeTo(dest)
}
} catch (e: Exception) {
console.add("! Unable to extract files")
Timber.e(e)
return false
}
val init64 = SuFile.open(installDir, "magiskinit64")
if (Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_64_BIT_ABIS.isNotEmpty()) {
init64.renameTo(SuFile.open(installDir, "magiskinit"))
} else {
init64.delete()
if (useRootDir) {
// Move everything to tmpfs to workaround Samsung bullshit
SuFile(Const.TMPDIR).also {
arrayOf(
"rm -rf $it",
"mkdir -p $it",
"cp_readlink $installDir $it",
"rm -rf $installDir"
).sh()
installDir = it
}
}
"cd $installDir; chmod 755 *".sh()
return true
}
private fun newEntry(name: String, size: Long): TarEntry {
// Optimization for SuFile I/O streams to skip an internal trial and error
private fun installDirFile(name: String): File {
return if (useRootDir)
SuFile(installDir, name)
else
File(installDir, name)
}
private fun InputStream.cleanPump(out: OutputStream) = withStreams(this, out) { src, _ ->
src.copyTo(out)
}
private fun newTarEntry(name: String, size: Long): TarEntry {
console.add("-- Writing: $name")
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
}
@Throws(IOException::class)
private fun handleTar(input: InputStream, output: OutputStream): OutputStream {
private fun processTar(input: InputStream, output: OutputStream): OutputStream {
console.add("- Processing tar file")
val tarOut = TarOutputStream(output)
TarInputStream(input).use { tarIn ->
lateinit var entry: TarEntry
fun decompressedStream() =
if (entry.name.contains(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
fun decompressedStream(): InputStream {
val src = if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
return object : FilterInputStream(src) {
override fun available() = 0 /* Workaround bug in LZ4FrameInputStream */
override fun close() { /* Never close src stream */ }
}
}
while (tarIn.nextEntry?.let { entry = it } != null) {
if (entry.name.contains("boot.img") ||
if (entry.name.startsWith("boot.img") ||
(Config.recovery && entry.name.contains("recovery.img"))) {
val name = entry.name.replace(".lz4", "")
console.add("-- Extracting: $name")
val extract = File(installDir, name)
FileOutputStream(extract).use { decompressedStream().copyTo(it) }
val extract = installDirFile(name)
decompressedStream().cleanPump(SuFileOutputStream.open(extract))
} else if (entry.name.contains("vbmeta.img")) {
val rawData = ByteArrayOutputStream().let {
decompressedStream().copyTo(it)
it.toByteArray()
}
val rawData = decompressedStream().readBytes()
// Valid vbmeta.img should be at least 256 bytes
if (rawData.size < 256)
continue
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED |
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
console.add("-- Patching: vbmeta.img")
ByteBuffer.wrap(rawData).putInt(120, 2)
tarOut.putNextEntry(newEntry("vbmeta.img", rawData.size.toLong()))
ByteBuffer.wrap(rawData).putInt(120, 3)
tarOut.putNextEntry(newTarEntry("vbmeta.img", rawData.size.toLong()))
tarOut.write(rawData)
} else {
console.add("-- Copying: ${entry.name}")
@@ -202,29 +207,32 @@ abstract class MagiskInstallImpl : KoinComponent {
tarIn.copyTo(tarOut, bufferSize = 1024 * 1024)
}
}
val boot = SuFile.open(installDir, "boot.img")
val recovery = SuFile.open(installDir, "recovery.img")
if (recovery.exists() && boot.exists()) {
// Install Magisk to recovery
srcBoot = recovery.path
// Repack boot image to prevent restore
arrayOf(
"./magiskboot unpack boot.img",
"./magiskboot repack boot.img",
"./magiskboot cleanup",
"mv new-boot.img boot.img").sh()
SuFileInputStream(boot).use {
tarOut.putNextEntry(newEntry("boot.img", boot.length()))
it.copyTo(tarOut)
}
boot.delete()
} else {
if (!boot.exists()) {
console.add("! No boot image found")
throw IOException()
}
srcBoot = boot.path
}
val boot = installDirFile("boot.img")
val recovery = installDirFile("recovery.img")
if (Config.recovery && recovery.exists() && boot.exists()) {
// Install to recovery
srcBoot = recovery
// Repack boot image to prevent auto restore
arrayOf(
"cd $installDir",
"./magiskboot unpack boot.img",
"./magiskboot repack boot.img",
"cat new-boot.img > boot.img",
"./magiskboot cleanup",
"rm -f new-boot.img",
"cd /").sh()
SuFileInputStream.open(boot).use {
tarOut.putNextEntry(newTarEntry("boot.img", boot.length()))
it.copyTo(tarOut)
}
boot.delete()
} else {
if (!boot.exists()) {
console.add("! No boot image found")
throw IOException()
}
srcBoot = boot
}
return tarOut
}
@@ -247,20 +255,22 @@ abstract class MagiskInstallImpl : KoinComponent {
val alpha = "abcdefghijklmnopqrstuvwxyz"
val alphaNum = "$alpha${alpha.toUpperCase(Locale.ROOT)}0123456789"
val random = SecureRandom()
val suffix = StringBuilder()
for (i in 1..5) {
suffix.append(alphaNum[random.nextInt(alphaNum.length)])
val filename = StringBuilder("magisk_patched_").run {
for (i in 1..5) {
append(alphaNum[random.nextInt(alphaNum.length)])
}
toString()
}
val filename = "magisk_patched_$suffix"
outStream = if (magic.contentEquals("ustar".toByteArray())) {
// tar file
outFile = MediaStoreUtils.getFile("$filename.tar", true)
handleTar(src, outFile!!.uri.outputStream())
processTar(src, outFile!!.uri.outputStream())
} else {
// Raw image
srcBoot = File(installDir, "boot.img").path
// raw image
srcBoot = installDirFile("boot.img")
console.add("- Copying image to cache")
FileOutputStream(srcBoot).use { src.copyTo(it) }
src.cleanPump(SuFileOutputStream.open(srcBoot))
outFile = MediaStoreUtils.getFile("$filename.img", true)
outFile!!.uri.outputStream()
}
@@ -280,13 +290,13 @@ abstract class MagiskInstallImpl : KoinComponent {
// Output file
try {
val patched = SuFile.open(installDir, "new-boot.img")
val newBoot = installDirFile("new-boot.img")
if (outStream is TarOutputStream) {
val name = if (srcBoot.contains("recovery")) "recovery.img" else "boot.img"
outStream.putNextEntry(newEntry(name, patched.length()))
val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img"
outStream.putNextEntry(newTarEntry(name, newBoot.length()))
}
withStreams(SuFileInputStream(patched), outStream) { src, out -> src.copyTo(out) }
patched.delete()
SuFileInputStream.open(newBoot).cleanPump(outStream)
newBoot.delete()
console.add("")
console.add("****************************")
@@ -300,118 +310,107 @@ abstract class MagiskInstallImpl : KoinComponent {
return false
}
// Fix up binaries
srcBoot.delete()
if (shell.isRoot) {
"fix_env $installDir".sh()
} else {
"cp_readlink $installDir".sh()
}
return true
}
private fun patchBoot(): Boolean {
var srcNand = ""
if ("[ -c $srcBoot ] && nanddump -f boot.img $srcBoot".sh().isSuccess) {
srcNand = srcBoot
srcBoot = File(installDir, "boot.img").path
}
var isSigned: Boolean
try {
SuFileInputStream(srcBoot).use {
isSigned = SignBoot.verifySignature(it, null)
if (isSigned) {
console.add("- Boot image is signed with AVB 1.0")
var isSigned = false
if (srcBoot.let { it !is SuFile || !it.isCharacter }) {
try {
SuFileInputStream.open(srcBoot).use {
if (SignBoot.verifySignature(it, null)) {
isSigned = true
console.add("- Boot image is signed with AVB 1.0")
}
}
} catch (e: IOException) {
console.add("! Unable to check signature")
Timber.e(e)
return false
}
} catch (e: IOException) {
console.add("! Unable to check signature")
}
val newBoot = installDirFile("new-boot.img")
if (!useRootDir) {
// Create output files before hand
newBoot.createNewFile()
File(installDir, "stock_boot.img").createNewFile()
}
val cmds = arrayOf(
"cd $installDir",
"KEEPFORCEENCRYPT=${Config.keepEnc} " +
"KEEPVERITY=${Config.keepVerity} " +
"RECOVERYMODE=${Config.recovery} " +
"sh boot_patch.sh $srcBoot")
if (!cmds.sh().isSuccess)
return false
}
if (!("KEEPFORCEENCRYPT=${Config.keepEnc} KEEPVERITY=${Config.keepVerity} " +
"RECOVERYMODE=${Config.recovery} sh update-binary " +
"sh boot_patch.sh $srcBoot").sh().isSuccess) {
return false
}
val job = shell.newJob().add("./magiskboot cleanup", "cd /")
if (srcNand.isNotEmpty()) {
srcBoot = srcNand
}
val job = Shell.sh(
"./magiskboot cleanup",
"mv bin/busybox busybox",
"rm -rf magisk.apk bin boot.img update-binary",
"cd /")
val patched = File(installDir, "new-boot.img")
if (isSigned) {
console.add("- Signing boot image with verity keys")
val signed = File(installDir, "signed.img")
val signed = File.createTempFile("signed", ".img", context.cacheDir)
try {
withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) {
input, out -> SignBoot.doSignature(null, null, input, out, "/boot")
val src = SuFileInputStream.open(newBoot).buffered()
val out = signed.outputStream().buffered()
withStreams(src, out) { _, _ ->
SignBoot.doSignature(null, null, src, out, "/boot")
}
} catch (e: IOException) {
console.add("! Unable to sign image")
Timber.e(e)
return false
}
job.add("mv -f $signed $patched")
job.add("cat $signed > $newBoot", "rm -f $signed")
}
job.exec()
return true
}
private fun copySepolicyRules(): Boolean {
if (Info.remote.magisk.versionCode >= 21100) return true
// Copy existing rules for migration
"copy_sepolicy_rules".sh()
return true
}
private fun flashBoot(): Boolean {
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
return false
"run_migrations".sh()
return true
}
private fun flashBoot() = "direct_install $installDir $srcBoot".sh().isSuccess
private suspend fun postOTA(): Boolean {
val bootctl = SuFile("/data/adb/bootctl")
try {
withStreams(service.fetchBootctl().byteStream(), SuFileOutputStream(bootctl)) {
it, out -> it.copyTo(out)
}
val bootctl = File.createTempFile("bootctl", null, context.cacheDir)
service.fetchBootctl().byteStream().writeTo(bootctl)
"post_ota $bootctl".sh()
} catch (e: IOException) {
console.add("! Unable to download bootctl")
Timber.e(e)
return false
}
"post_ota ${bootctl.parent}".sh()
console.add("***************************************")
console.add(" Next reboot will boot to second slot!")
console.add("***************************************")
return true
}
private fun String.sh() = Shell.sh(this).to(console, logs).exec()
private fun Array<String>.sh() = Shell.sh(*this).to(console, logs).exec()
private fun String.fsh() = ShellUtils.fastCmd(this)
private fun Array<String>.fsh() = ShellUtils.fastCmd(*this)
private fun String.sh() = shell.newJob().add(this).to(console, logs).exec()
private fun Array<String>.sh() = shell.newJob().add(*this).to(console, logs).exec()
private fun String.fsh() = ShellUtils.fastCmd(shell, this)
private fun Array<String>.fsh() = ShellUtils.fastCmd(shell, *this)
protected fun doPatchFile(patchFile: Uri) = extractZip() && handleFile(patchFile)
protected fun doPatchFile(patchFile: Uri) = extractFiles() && handleFile(patchFile)
protected fun direct() = findImage() && extractZip() && patchBoot() &&
copySepolicyRules() && flashBoot()
protected fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot()
protected suspend fun secondSlot() = findSecondaryImage() && extractZip() &&
patchBoot() && copySepolicyRules() && flashBoot() && postOTA()
protected suspend fun secondSlot() =
findSecondary() && extractFiles() && patchBoot() && flashBoot() && postOTA()
protected fun fixEnv(zip: Uri): Boolean {
installDir = SuFile("/data/adb/magisk")
Shell.su("rm -rf /data/adb/magisk/*").exec()
zipUri = zip
return extractZip() && Shell.su("fix_env").exec().isSuccess
}
protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess
protected fun uninstall() = "run_uninstaller ${AssetHack.apk}".sh().isSuccess
@WorkerThread
protected abstract suspend fun operations(): Boolean
@@ -419,11 +418,10 @@ abstract class MagiskInstallImpl : KoinComponent {
open suspend fun exec() = withContext(Dispatchers.IO) { operations() }
}
sealed class MagiskInstaller(
file: Uri,
abstract class MagiskInstaller(
console: MutableList<String>,
logs: MutableList<String>
) : MagiskInstallImpl(file, console, logs) {
) : MagiskInstallImpl(console, logs) {
override suspend fun exec(): Boolean {
val success = super.exec()
@@ -437,46 +435,64 @@ sealed class MagiskInstaller(
}
class Patch(
file: Uri,
private val uri: Uri,
console: MutableList<String>,
logs: MutableList<String>
) : MagiskInstaller(file, console, logs) {
) : MagiskInstaller(console, logs) {
override suspend fun operations() = doPatchFile(uri)
}
class SecondSlot(
file: Uri,
console: MutableList<String>,
logs: MutableList<String>
) : MagiskInstaller(file, console, logs) {
) : MagiskInstaller(console, logs) {
override suspend fun operations() = secondSlot()
}
class Direct(
file: Uri,
console: MutableList<String>,
logs: MutableList<String>
) : MagiskInstaller(file, console, logs) {
) : MagiskInstaller(console, logs) {
override suspend fun operations() = direct()
}
}
class Emulator(
console: MutableList<String>,
logs: MutableList<String>
) : MagiskInstaller(console, logs) {
override suspend fun operations() = fixEnv()
}
class EnvFixTask(
private val zip: Uri
) : MagiskInstallImpl() {
override suspend fun operations() = fixEnv(zip)
class Uninstall(
console: MutableList<String>,
logs: MutableList<String>
) : MagiskInstallImpl(console, logs) {
override suspend fun operations() = uninstall()
override suspend fun exec(): Boolean {
val success = super.exec()
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(EnvFixDialog.DISMISS))
Utils.toast(
if (success) R.string.reboot_delay_toast else R.string.setup_fail,
Toast.LENGTH_LONG
)
if (success)
UiThreadHandler.handler.postDelayed(5000) { reboot() }
return success
override suspend fun exec(): Boolean {
val success = super.exec()
if (success) {
UiThreadHandler.handler.postDelayed(3000) {
Shell.su("pm uninstall ${context.packageName}").exec()
}
}
return success
}
}
class FixEnv(private val callback: () -> Unit) : MagiskInstallImpl() {
override suspend fun operations() = fixEnv()
override suspend fun exec(): Boolean {
val success = super.exec()
callback()
Utils.toast(
if (success) R.string.reboot_delay_toast else R.string.setup_fail,
Toast.LENGTH_LONG
)
if (success)
UiThreadHandler.handler.postDelayed(5000) { reboot() }
return success
}
}
}

View File

@@ -3,20 +3,16 @@
package com.topjohnwu.magisk.core.utils
import android.annotation.SuppressLint
import android.content.res.AssetManager
import android.content.res.Configuration
import android.content.res.Resources
import android.util.DisplayMetrics
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.ResMgr
import com.topjohnwu.magisk.core.addAssetPath
import com.topjohnwu.magisk.ktx.langTagToLocale
import com.topjohnwu.magisk.ktx.toLangTag
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.*
import kotlin.Comparator
import kotlin.collections.ArrayList
var currentLocale: Locale = Locale.getDefault()
@@ -30,11 +26,8 @@ suspend fun availableLocales() = cachedLocales ?:
withContext(Dispatchers.Default) {
val compareId = R.string.app_changelog
// Create a completely new resource to prevent cross talk over app's configs
val asset = AssetManager::class.java.newInstance().apply { addAssetPath(ResMgr.apk) }
val config = Configuration(ResMgr.resource.configuration)
val metrics = DisplayMetrics().apply { setTo(ResMgr.resource.displayMetrics) }
val res = Resources(asset, metrics, config)
// Create a completely new resource to prevent cross talk over active configs
val res = AssetHack.newResource()
val locales = ArrayList<String>().apply {
// Add default locale
@@ -45,19 +38,17 @@ withContext(Dispatchers.Default) {
add("pt-BR")
// Then add all supported locales
addAll(res.assets.locales)
addAll(Resources.getSystem().assets.locales)
}.map {
it.langTagToLocale()
}.distinctBy {
config.setLocale(it)
res.updateConfiguration(config, metrics)
res.updateLocale(it)
res.getString(compareId)
}.sortedWith(Comparator { a, b ->
}.sortedWith { a, b ->
a.getDisplayName(a).compareTo(b.getDisplayName(b), true)
})
}
config.setLocale(defaultLocale)
res.updateConfiguration(config, metrics)
res.updateLocale(defaultLocale)
val defName = res.getString(R.string.system_default)
val names = ArrayList<String>(locales.size + 1)
@@ -79,6 +70,11 @@ fun Resources.updateConfig(config: Configuration = configuration) {
updateConfiguration(config, displayMetrics)
}
fun Resources.updateLocale(locale: Locale) {
configuration.setLocale(locale)
updateConfiguration(configuration, displayMetrics)
}
fun refreshLocale() {
val localeConfig = Config.locale
currentLocale = when {
@@ -86,5 +82,5 @@ fun refreshLocale() {
else -> localeConfig.langTagToLocale()
}
Locale.setDefault(currentLocale)
ResMgr.resource.updateConfig()
AssetHack.resource.updateConfig()
}

View File

@@ -1,59 +0,0 @@
package com.topjohnwu.magisk.core.utils
import android.content.Context
import android.os.Build
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.wrap
import com.topjohnwu.magisk.ktx.rawResource
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
class RootInit : Shell.Initializer() {
override fun onInit(context: Context, shell: Shell): Boolean {
return init(context.wrap(), shell)
}
fun init(context: Context, shell: Shell): Boolean {
shell.newJob().apply {
add("export SDK_INT=${Build.VERSION.SDK_INT}")
if (Const.Version.atLeast_20_4()) {
add("export MAGISKTMP=\$(magisk --path)/.magisk")
} else {
add("export MAGISKTMP=/sbin/.magisk")
}
if (Const.Version.atLeast_21_0()) {
add("export ASH_STANDALONE=1")
add("[ -x /data/adb/magisk/busybox ] && exec /data/adb/magisk/busybox sh")
} else {
add("export PATH=\"\$MAGISKTMP/busybox:\$PATH\"")
}
add(context.rawResource(R.raw.manager))
if (shell.isRoot) {
add(context.rawResource(R.raw.util_functions))
}
add("mm_init")
}.exec()
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
fun getVar(name: String) = fastCmd("echo \$$name")
fun getBool(name: String) = getVar(name).toBoolean()
Const.MAGISKTMP = getVar("MAGISKTMP")
Info.isSAR = getBool("SYSTEM_ROOT")
Info.ramdisk = getBool("RAMDISKEXIST")
Info.isAB = getBool("ISAB")
Info.crypto = getVar("CRYPTOTYPE")
Info.isPixel = fastCmd("getprop ro.product.brand") == "google"
// Default presets
Config.recovery = getBool("RECOVERYMODE")
Config.keepVerity = getBool("KEEPVERITY")
Config.keepEnc = getBool("KEEPFORCEENCRYPT")
return true
}
}

View File

@@ -0,0 +1,102 @@
package com.topjohnwu.magisk.core.utils
import android.content.Context
import android.os.Build
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.ktx.cachedFile
import com.topjohnwu.magisk.ktx.deviceProtectedContext
import com.topjohnwu.magisk.ktx.rawResource
import com.topjohnwu.magisk.ktx.writeTo
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import java.io.File
import java.util.jar.JarFile
abstract class BaseShellInit : Shell.Initializer() {
final override fun onInit(context: Context, shell: Shell): Boolean {
return init(context.wrap(), shell)
}
abstract fun init(context: Context, shell: Shell): Boolean
}
class BusyBoxInit : BaseShellInit() {
override fun init(context: Context, shell: Shell): Boolean {
shell.newJob().apply {
add("export ASH_STANDALONE=1")
val localBB: File
if (isRunningAsStub) {
if (!shell.isRoot)
return true
val jar = JarFile(DynAPK.current(context))
val bb = jar.getJarEntry("lib/${Const.CPU_ABI_32}/libbusybox.so")
localBB = context.deviceProtectedContext.cachedFile("busybox")
localBB.delete()
jar.getInputStream(bb).writeTo(localBB)
localBB.setExecutable(true)
} else {
localBB = File(Const.NATIVE_LIB_DIR, "libbusybox.so")
}
if (shell.isRoot) {
add("export MAGISKTMP=\$(magisk --path)/.magisk")
// Test if we can properly execute stuff in /data
Info.noDataExec = !shell.newJob().add("$localBB true").exec().isSuccess
}
if (Info.noDataExec) {
// Copy it out of /data to workaround Samsung bullshit
add(
"if [ -x \$MAGISKTMP/busybox/busybox ]; then",
" cp -af $localBB \$MAGISKTMP/busybox/busybox",
" exec \$MAGISKTMP/busybox/busybox sh",
"else",
" cp -af $localBB /dev/.busybox",
" exec /dev/.busybox sh",
"fi"
)
} else {
// Directly execute the file
add("exec $localBB sh")
}
}.exec()
return true
}
}
class AppShellInit : BaseShellInit() {
override fun init(context: Context, shell: Shell): Boolean {
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
fun getVar(name: String) = fastCmd("echo \$$name")
fun getBool(name: String) = getVar(name).toBoolean()
shell.newJob().apply {
add("export API=${Build.VERSION.SDK_INT}")
add(context.rawResource(R.raw.manager))
if (shell.isRoot) {
add(context.assets.open("util_functions.sh"))
}
add("app_init")
}.exec()
Const.MAGISKTMP = getVar("MAGISKTMP")
Info.isSAR = getBool("SYSTEM_ROOT")
Info.ramdisk = getBool("RAMDISKEXIST")
Info.isAB = getBool("ISAB")
Info.crypto = getVar("CRYPTOTYPE")
// Default presets
Config.recovery = getBool("RECOVERYMODE")
Config.keepVerity = getBool("KEEPVERITY")
Config.keepEnc = getBool("KEEPFORCEENCRYPT")
return true
}
}

View File

@@ -36,7 +36,7 @@ fun InputStream.unzip(folder: File, path: String, junkPath: Boolean) {
dest = SuFile(folder, name)
dest.parentFile!!.mkdirs()
}
SuFileOutputStream(dest).use { out -> zin.copyTo(out) }
SuFileOutputStream.open(dest).use { out -> zin.copyTo(out) }
}
} catch (e: IOException) {
e.printStackTrace()

View File

@@ -8,7 +8,9 @@ import com.topjohnwu.magisk.core.Config.Value.DEFAULT_CHANNEL
import com.topjohnwu.magisk.core.Config.Value.STABLE_CHANNEL
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.model.*
import com.topjohnwu.magisk.core.model.MagiskJson
import com.topjohnwu.magisk.core.model.StubJson
import com.topjohnwu.magisk.core.model.UpdateInfo
import com.topjohnwu.magisk.data.network.*
import retrofit2.HttpException
import timber.log.Timber
@@ -34,7 +36,6 @@ class NetworkService(
Config.updateChannel = BETA_CHANNEL
info = fetchBetaUpdate()
}
Info.remote = info
info
}
@@ -47,16 +48,12 @@ class NetworkService(
val info = jsd.fetchCanaryUpdate(sha)
fun genCDNUrl(name: String) = "${Const.Url.JS_DELIVR_URL}${MAGISK_FILES}@${sha}/${name}"
fun ManagerJson.updateCopy() = copy(link = genCDNUrl(link), note = genCDNUrl(note))
fun MagiskJson.updateCopy() = copy(link = genCDNUrl(link), note = genCDNUrl(note))
fun StubJson.updateCopy() = copy(link = genCDNUrl(link))
fun UninstallerJson.updateCopy() = copy(link = genCDNUrl(link))
return info.copy(
app = info.app.updateCopy(),
magisk = info.magisk.updateCopy(),
stub = info.stub.updateCopy(),
uninstaller = info.uninstaller.updateCopy()
stub = info.stub.updateCopy()
)
}

View File

@@ -1,9 +1,9 @@
package com.topjohnwu.magisk.di
import android.content.Context
import android.os.Build
import androidx.preference.PreferenceManager
import com.topjohnwu.magisk.core.ResMgr
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.ktx.deviceProtectedContext
import org.koin.core.qualifier.named
import org.koin.dsl.module
@@ -11,15 +11,9 @@ val SUTimeout = named("su_timeout")
val Protected = named("protected")
val applicationModule = module {
factory { ResMgr.resource }
factory { AssetHack.resource }
factory { get<Context>().packageManager }
factory(Protected) { createDEContext(get()) }
factory(Protected) { get<Context>().deviceProtectedContext }
single(SUTimeout) { get<Context>(Protected).getSharedPreferences("su_timeout", 0) }
single { PreferenceManager.getDefaultSharedPreferences(get<Context>(Protected)) }
}
private fun createDEContext(context: Context): Context {
return if (Build.VERSION.SDK_INT >= 24)
context.createDeviceProtectedStorageContext()
else context
single { PreferenceManager.getDefaultSharedPreferences(get(Protected)) }
}

View File

@@ -29,6 +29,6 @@ val viewModelModules = module {
viewModel { MainViewModel() }
// Legacy
viewModel { (args: FlashFragmentArgs) -> FlashViewModel(args, get()) }
viewModel { (args: FlashFragmentArgs) -> FlashViewModel(args) }
viewModel { SuRequestViewModel(get(), get(), get(SUTimeout), get()) }
}

View File

@@ -14,20 +14,22 @@ import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.base.ActivityResultCallback
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.events.dialog.MarkDownDialog
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.MarkDownWindow
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.Shortcuts
import kotlinx.coroutines.launch
class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), ActivityExecutor {
override fun invoke(activity: BaseUIActivity<*, *>) = action(activity)
}
class OpenReadmeEvent(val item: OnlineModule) : ViewEventWithScope(), ContextExecutor {
override fun invoke(context: Context) {
scope.launch {
MarkDownWindow.show(context, null, item::notes)
}
class OpenReadmeEvent(private val item: OnlineModule) : MarkDownDialog() {
override suspend fun getMarkdownText() = item.notes()
override fun build(dialog: MagiskDialog) {
super.build(dialog)
dialog.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}.cancellable(true)
}
}
@@ -78,7 +80,7 @@ class MagiskInstallFileEvent(private val callback: ActivityResultCallback)
override fun invoke(activity: BaseUIActivity<*, *>) {
val intent = Intent(Intent.ACTION_GET_CONTENT).setType("*/*")
try {
activity.startActivityForResult(intent, Const.ID.SELECT_FILE, callback)
activity.startActivityForResult(intent, callback)
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG)
} catch (e: ActivityNotFoundException) {
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
@@ -107,10 +109,10 @@ class SelectModuleEvent : ViewEvent(), FragmentExecutor {
val intent = Intent(Intent.ACTION_GET_CONTENT).setType("application/zip")
try {
fragment.apply {
activity.startActivityForResult(intent, Const.ID.FETCH_ZIP) { code, intent ->
activity.startActivityForResult(intent) { code, intent ->
if (code == Activity.RESULT_OK && intent != null) {
intent.data?.also {
MainDirections.actionFlashFragment(it, Const.Value.FLASH_ZIP).navigate()
MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate()
}
}
}

View File

@@ -10,7 +10,9 @@ abstract class DialogEvent : ViewEvent(), ActivityExecutor {
protected lateinit var dialog: MagiskDialog
override fun invoke(activity: BaseUIActivity<*, *>) {
dialog = MagiskDialog(activity).apply(this::build).reveal()
dialog = MagiskDialog(activity)
.apply { setOwnerActivity(activity) }
.apply(this::build).reveal()
}
abstract fun build(dialog: MagiskDialog)

View File

@@ -1,15 +1,11 @@
package com.topjohnwu.magisk.events.dialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.download.Action.EnvFix
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject.Magisk
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
import com.topjohnwu.magisk.view.MagiskDialog
import kotlinx.coroutines.launch
class EnvFixDialog : DialogEvent() {
@@ -24,20 +20,17 @@ class EnvFixDialog : DialogEvent() {
.applyMessage(R.string.setup_msg)
.resetButtons()
.cancellable(false)
val lbm = LocalBroadcastManager.getInstance(dialog.context)
lbm.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
(dialog.ownerActivity as BaseActivity).lifecycleScope.launch {
MagiskInstaller.FixEnv {
dialog.dismiss()
lbm.unregisterReceiver(this)
}
}, IntentFilter(DISMISS))
DownloadService.start(dialog.context, Magisk(EnvFix))
}.exec()
}
}
}
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.let { Unit }
.let { }
companion object {
const val DISMISS = "com.topjohnwu.magisk.ENV_DONE"

View File

@@ -1,40 +1,41 @@
package com.topjohnwu.magisk.events.dialog
import android.content.Context
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.ktx.res
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.MarkDownWindow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.koin.core.get
import org.koin.core.inject
import java.io.File
class ManagerInstallDialog : DialogEvent() {
class ManagerInstallDialog : MarkDownDialog() {
private val svc: NetworkService by inject()
override suspend fun getMarkdownText(): String {
val text = svc.fetchString(Info.remote.magisk.note)
// Cache the changelog
val context = get<Context>()
context.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach {
it.delete()
}
File(context.cacheDir, "${Info.remote.magisk.versionCode}.md").writeText(text)
return text
}
override fun build(dialog: MagiskDialog) {
super.build(dialog)
with(dialog) {
val subject = Subject.Manager()
applyTitle(R.string.repo_install_title.res(R.string.app_name.res()))
applyMessage(R.string.repo_install_msg.res(subject.title))
setCancelable(true)
applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.install
onClick { DownloadService.start(context, subject) }
onClick { DownloadService.start(context, Subject.Manager()) }
}
if (Info.remote.app.note.isEmpty()) return
applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.app_changelog
onClick {
GlobalScope.launch(Dispatchers.Main.immediate) {
MarkDownWindow.show(context, null, Info.remote.app.note)
}
}
titleRes = android.R.string.cancel
}
}
}

View File

@@ -0,0 +1,45 @@
package com.topjohnwu.magisk.events.dialog
import android.view.LayoutInflater
import android.widget.TextView
import androidx.annotation.CallSuper
import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.view.MagiskDialog
import io.noties.markwon.Markwon
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.KoinComponent
import org.koin.core.inject
import timber.log.Timber
import kotlin.coroutines.cancellation.CancellationException
abstract class MarkDownDialog : DialogEvent(), KoinComponent {
private val markwon: Markwon by inject()
abstract suspend fun getMarkdownText(): String
@CallSuper
override fun build(dialog: MagiskDialog) {
with(dialog) {
val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null)
applyView(view)
(ownerActivity as BaseActivity).lifecycleScope.launch {
val tv = view.findViewById<TextView>(R.id.md_txt)
withContext(Dispatchers.IO) {
try {
markwon.setMarkdown(tv, getMarkdownText())
} catch (e: Exception) {
if (e is CancellationException)
throw e
Timber.e(e)
tv.post { tv.setText(R.string.download_file_error) }
}
}
}
}
}
}

View File

@@ -14,7 +14,7 @@ class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() {
with(dialog) {
fun download(install: Boolean) {
val config = if (install) Action.Flash.Primary else Action.Download
val config = if (install) Action.Flash else Action.Download
val subject = Subject.Module(item, config)
DownloadService.start(context, subject)
}

View File

@@ -3,10 +3,7 @@ package com.topjohnwu.magisk.events.dialog
import android.app.ProgressDialog
import android.widget.Toast
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.Action
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.ui.flash.FlashFragment
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.superuser.Shell
@@ -20,12 +17,10 @@ class UninstallDialog : DialogEvent() {
titleRes = R.string.restore_img
onClick { restore() }
}
if (Info.remote.uninstaller.link.isNotEmpty()) {
dialog.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.complete_uninstall
onClick { completeUninstall() }
}
}
}
@Suppress("DEPRECATION")
@@ -46,7 +41,7 @@ class UninstallDialog : DialogEvent() {
}
private fun completeUninstall() {
DownloadService.start(dialog.context, Subject.Magisk(Action.Uninstall))
FlashFragment.uninstall()
}
}

View File

@@ -7,9 +7,11 @@ import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.ComponentInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.*
import android.content.pm.ServiceInfo
import android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS
import android.content.pm.ServiceInfo.FLAG_USE_APP_ZYGOTE
import android.content.res.Configuration
import android.content.res.Resources
import android.database.Cursor
@@ -19,8 +21,8 @@ import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.system.Os
import android.text.PrecomputedText
import android.view.View
import android.view.ViewGroup
@@ -39,11 +41,13 @@ import androidx.core.widget.TextViewCompat
import androidx.databinding.BindingAdapter
import androidx.fragment.app.Fragment
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.lifecycle.lifecycleScope
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.ResMgr
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.utils.DynamicClassLoader
import com.topjohnwu.magisk.utils.Utils
@@ -53,37 +57,31 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
import java.lang.reflect.Method
import java.lang.reflect.Array as JArray
val packageName: String get() = get<Context>().packageName
val ApplicationInfo.processes: List<String> @SuppressLint("InlinedApi") get() {
val pm = get<PackageManager>()
val appProcessName = processName ?: packageName
val baseFlag = MATCH_DISABLED_COMPONENTS or MATCH_UNINSTALLED_PACKAGES
val packageInfo = try {
val request = GET_ACTIVITIES or GET_SERVICES or GET_RECEIVERS or GET_PROVIDERS
pm.getPackageInfo(packageName, baseFlag or request)
} catch (e: NameNotFoundException) { // EdXposed hooked, issue#3276
return listOf(appProcessName)
} catch (e: Exception) {
// Exceed binder data transfer limit, fetch each component type separately
pm.getPackageInfo(packageName, baseFlag).apply {
runCatching { activities = pm.getPackageInfo(packageName, GET_ACTIVITIES).activities }
runCatching { services = pm.getPackageInfo(packageName, GET_SERVICES).services }
runCatching { receivers = pm.getPackageInfo(packageName, GET_RECEIVERS).receivers }
runCatching { providers = pm.getPackageInfo(packageName, GET_PROVIDERS).providers }
private lateinit var osSymlink: Method
private lateinit var os: Any
fun symlink(oldPath: String, newPath: String) {
if (SDK_INT >= 21) {
Os.symlink(oldPath, newPath)
} else {
if (!::osSymlink.isInitialized) {
os = Class.forName("libcore.io.Libcore").getField("os").get(null)!!
osSymlink = os.javaClass.getMethod("symlink", String::class.java, String::class.java)
}
}
fun Array<out ComponentInfo>.processNames() = map { it.processName ?: appProcessName }
return with(packageInfo) {
activities?.processNames().orEmpty() +
services?.processNames().orEmpty() +
receivers?.processNames().orEmpty() +
providers?.processNames().orEmpty()
osSymlink.invoke(os, oldPath, newPath)
}
}
val ServiceInfo.isIsolated get() = (flags and FLAG_ISOLATED_PROCESS) != 0
@get:SuppressLint("InlinedApi")
val ServiceInfo.useAppZygote get() = (flags and FLAG_USE_APP_ZYGOTE) != 0
fun Context.rawResource(id: Int) = resources.openRawResource(id)
fun Context.getBitmap(id: Int): Bitmap {
@@ -103,6 +101,11 @@ fun Context.getBitmap(id: Int): Bitmap {
return bitmap
}
val Context.deviceProtectedContext: Context get() =
if (SDK_INT >= 24) {
createDeviceProtectedStorageContext()
} else { this }
fun Intent.startActivity(context: Context) = context.startActivity(this)
fun Intent.startActivityWithRoot() {
@@ -247,15 +250,13 @@ fun Context.colorStateListCompat(@ColorRes id: Int) = try {
null
}
fun Context.drawableCompat(@DrawableRes id: Int) = ContextCompat.getDrawable(this, id)
fun Context.drawableCompat(@DrawableRes id: Int) = AppCompatResources.getDrawable(this, id)
/**
* Pass [start] and [end] dimensions, function will return left and right
* with respect to RTL layout direction
*/
fun Context.startEndToLeftRight(start: Int, end: Int): Pair<Int, Int> {
if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 &&
resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
) {
if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
return end to start
}
return start to end
@@ -315,8 +316,21 @@ fun ViewGroup.startAnimations() {
)
}
val View.activity: Activity get() {
var context = context
while(true) {
if (context !is ContextWrapper)
error("View is not attached to activity")
if (context is Activity)
return context
context = context.baseContext
}
}
var View.coroutineScope: CoroutineScope
get() = getTag(R.id.coroutineScope) as? CoroutineScope ?: GlobalScope
get() = getTag(R.id.coroutineScope) as? CoroutineScope
?: (activity as? BaseActivity)?.lifecycleScope
?: GlobalScope
set(value) = setTag(R.id.coroutineScope, value)
@set:BindingAdapter("precomputedText")
@@ -366,6 +380,16 @@ var TextView.precomputedText: CharSequence
}
fun Int.dpInPx(): Int {
val scale = ResMgr.resource.displayMetrics.density
val scale = AssetHack.resource.displayMetrics.density
return (this * scale + 0.5).toInt()
}
@SuppressLint("PrivateApi")
fun getProperty(key: String, def: String): String {
runCatching {
val clazz = Class.forName("android.os.SystemProperties")
val get = clazz.getMethod("get", String::class.java, String::class.java)
return get.invoke(clazz, key, def) as String
}
return def
}

View File

@@ -6,14 +6,12 @@ import timber.log.Timber
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
inline fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
var entry: ZipEntry? = nextEntry
while (entry != null) {
callback(entry)
@@ -53,7 +51,7 @@ fun String.langTagToLocale(): Locale {
if (Build.VERSION.SDK_INT >= 21) {
return Locale.forLanguageTag(this)
} else {
val tok = split("-".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val tok = split("[-_]".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (tok.isEmpty()) {
return Locale("")
}
@@ -110,32 +108,3 @@ fun Locale.toLangTag(): String {
fun SimpleDateFormat.parseOrNull(date: String) =
runCatching { parse(date) }.onFailure { Timber.e(it) }.getOrNull()
// Reflection hacks
private val loadClass = ClassLoader::class.java.getMethod("loadClass", String::class.java)
private val getDeclaredMethod = Class::class.java.getMethod("getDeclaredMethod",
String::class.java, arrayOf<Class<*>>()::class.java)
private val getDeclaredField = Class::class.java.getMethod("getDeclaredField", String::class.java)
fun ClassLoader.forceLoadClass(name: String) =
runCatching { loadClass.invoke(this, name) }.getOrNull() as Class<*>?
fun Class<*>.forceGetDeclaredMethod(name: String, vararg types: Class<*>) =
(runCatching { getDeclaredMethod.invoke(this, name, types) }.getOrNull() as Method?)?.also {
it.isAccessible = true
}
fun Class<*>.forceGetDeclaredField(name: String) =
(runCatching { getDeclaredField.invoke(this, name) }.getOrNull() as Field?)?.also {
it.isAccessible = true
}
inline fun <reified T> T.forceGetClass(name: String) =
T::class.java.classLoader?.forceLoadClass(name)
fun Class<*>.forceGetField(name: String): Field? =
forceGetDeclaredField(name) ?: superclass?.forceGetField(name)
fun Class<*>.forceGetMethod(name: String, vararg types: Class<*>): Method? =
forceGetDeclaredMethod(name, *types) ?: superclass?.forceGetMethod(name, *types)

View File

@@ -1,6 +1,7 @@
package com.topjohnwu.magisk.ui
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.os.Build
import android.os.Bundle
import android.view.MenuItem
@@ -10,9 +11,7 @@ import android.view.WindowManager
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.view.forEach
import androidx.core.view.updateLayoutParams
import androidx.navigation.NavDirections
import com.google.android.material.card.MaterialCardView
import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIActivity
@@ -22,13 +21,12 @@ import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.ui.home.HomeFragmentDirections
import com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior
import com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior
import com.topjohnwu.magisk.utils.HideableBehavior
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.Shortcuts
import org.koin.androidx.viewmodel.ext.android.viewModel
import java.io.File
class MainViewModel : BaseViewModel()
@@ -77,12 +75,6 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
setSupportActionBar(binding.mainToolbar)
binding.mainToolbarWrapper.updateLayoutParams<CoordinatorLayout.LayoutParams> {
behavior = HideTopViewOnScrollBehavior<MaterialCardView>()
}
binding.mainBottomBar.updateLayoutParams<CoordinatorLayout.LayoutParams> {
behavior = HideBottomViewOnScrollBehavior<MaterialCardView>()
}
binding.mainNavigation.setOnNavigationItemSelectedListener {
getScreen(it.itemId)?.navigate()
true
@@ -91,7 +83,7 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
(currentFragment as? ReselectionTarget)?.onReselected()
}
val section = if (intent.action == ACTION_APPLICATION_PREFERENCES) Const.Nav.SETTINGS
val section = if (intent.action == Intent.ACTION_APPLICATION_PREFERENCES) Const.Nav.SETTINGS
else intent.getStringExtra(Const.Key.OPEN_SECTION)
getScreen(section)?.navigate()
@@ -198,7 +190,37 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
.applyTitle(R.string.unsupport_magisk_title)
.applyMessage(R.string.unsupport_magisk_msg, Const.Version.MIN_VERSION)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
.cancellable(true)
.cancellable(false)
.reveal()
}
if (!Info.isEmulator && Info.env.isActive && System.getenv("PATH")
?.split(':')
?.filterNot { File("$it/magisk").exists() }
?.any { File("$it/su").exists() } == true) {
MagiskDialog(this)
.applyTitle(R.string.unsupport_general_title)
.applyMessage(R.string.unsupport_other_su_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
.cancellable(false)
.reveal()
}
if (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) {
MagiskDialog(this)
.applyTitle(R.string.unsupport_general_title)
.applyMessage(R.string.unsupport_system_app_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
.cancellable(false)
.reveal()
}
if (applicationInfo.flags and ApplicationInfo.FLAG_EXTERNAL_STORAGE != 0) {
MagiskDialog(this)
.applyTitle(R.string.unsupport_general_title)
.applyMessage(R.string.unsupport_external_storage_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
.cancellable(false)
.reveal()
}
}
@@ -222,11 +244,4 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
.reveal()
}
}
companion object {
private val ACTION_APPLICATION_PREFERENCES get() =
if (Build.VERSION.SDK_INT >= 24) Intent.ACTION_APPLICATION_PREFERENCES
else "???"
}
}

View File

@@ -17,13 +17,12 @@ import com.topjohnwu.magisk.ui.MainActivity
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import com.topjohnwu.magisk.MainDirections.Companion.actionFlashFragment as toFlash
import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs as args
class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>() {
override val layoutRes = R.layout.fragment_flash_md2
override val viewModel by viewModel<FlashViewModel> {
parametersOf(args.fromBundle(requireArguments()))
parametersOf(FlashFragmentArgs.fromBundle(requireArguments()))
}
private var defaultOrientation = -1
@@ -32,6 +31,10 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
super.onStart()
setHasOptionsMenu(true)
activity.setTitle(R.string.flash_screen_title)
viewModel.subtitle.observe(this) {
activity.supportActionBar?.setSubtitle(it)
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -74,7 +77,7 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
companion object {
private fun createIntent(context: Context, args: args) =
private fun createIntent(context: Context, args: FlashFragmentArgs) =
NavDeepLinkBuilder(context)
.setGraph(R.navigation.main)
.setComponentName(MainActivity::class.java.cmp(context.packageName))
@@ -87,59 +90,34 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
/* Flashing is understood as installing / flashing magisk itself */
fun flashIntent(context: Context, file: Uri, isSecondSlot: Boolean, id: Int = -1) = args(
installer = file,
action = flashType(isSecondSlot),
dismissId = id
).let { createIntent(context, it) }
fun flash(file: Uri, isSecondSlot: Boolean, id: Int) = toFlash(
installer = file,
action = flashType(isSecondSlot),
dismissId = id
fun flash(isSecondSlot: Boolean) = toFlash(
action = flashType(isSecondSlot)
).let { BaseUIActivity.postDirections(it) }
/* Patching is understood as injecting img files with magisk */
fun patchIntent(context: Context, file: Uri, uri: Uri, id: Int = -1) = args(
installer = file,
fun patch(uri: Uri) = toFlash(
action = Const.Value.PATCH_FILE,
additionalData = uri,
dismissId = id
).let { createIntent(context, it) }
fun patch(file: Uri, uri: Uri, id: Int) = toFlash(
installer = file,
action = Const.Value.PATCH_FILE,
additionalData = uri,
dismissId = id
additionalData = uri
).let { BaseUIActivity.postDirections(it) }
/* Uninstalling is understood as removing magisk entirely */
fun uninstallIntent(context: Context, file: Uri, id: Int = -1) = args(
installer = file,
action = Const.Value.UNINSTALL,
dismissId = id
).let { createIntent(context, it) }
fun uninstall(file: Uri, id: Int) = toFlash(
installer = file,
action = Const.Value.UNINSTALL,
dismissId = id
fun uninstall() = toFlash(
action = Const.Value.UNINSTALL
).let { BaseUIActivity.postDirections(it) }
/* Installing is understood as flashing modules / zips */
fun installIntent(context: Context, file: Uri, id: Int = -1) = args(
installer = file,
fun installIntent(context: Context, file: Uri, id: Int = -1) = FlashFragmentArgs(
action = Const.Value.FLASH_ZIP,
additionalData = file,
dismissId = id
).let { createIntent(context, it) }
fun install(file: Uri, id: Int) = toFlash(
installer = file,
action = Const.Value.FLASH_ZIP,
additionalData = file,
dismissId = id
).let { BaseUIActivity.postDirections(it) }
}

View File

@@ -1,9 +1,10 @@
package com.topjohnwu.magisk.ui.flash
import android.content.res.Resources
import android.net.Uri
import android.view.MenuItem
import androidx.databinding.Bindable
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
@@ -11,6 +12,7 @@ import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.arch.diffListOf
import com.topjohnwu.magisk.arch.itemBindingOf
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.tasks.FlashZip
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
@@ -25,18 +27,14 @@ import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class FlashViewModel(
args: FlashFragmentArgs,
private val resources: Resources
) : BaseViewModel() {
class FlashViewModel(args: FlashFragmentArgs) : BaseViewModel() {
@get:Bindable
var showReboot = Shell.rootAccess()
set(value) = set(value, field, { field = it }, BR.showReboot)
@get:Bindable
var behaviorText = resources.getString(R.string.flashing)
set(value) = set(value, field, { field = it }, BR.behaviorText)
private val _subtitle = MutableLiveData(R.string.flashing)
val subtitle get() = _subtitle as LiveData<Int>
val adapter = RvBindingAdapter<ConsoleItem>()
val items = diffListOf<ConsoleItem>()
@@ -52,33 +50,35 @@ class FlashViewModel(
}
init {
args.dismissId.takeIf { it != -1 }?.also {
Notifications.mgr.cancel(it)
}
val (installer, action, uri) = args
startFlashing(installer, uri, action)
val (action, uri, id) = args
if (id != -1)
Notifications.mgr.cancel(id)
startFlashing(action, uri)
}
private fun startFlashing(installer: Uri, uri: Uri?, action: String) {
private fun startFlashing(action: String, uri: Uri?) {
viewModelScope.launch {
val result = when (action) {
Const.Value.FLASH_ZIP -> {
FlashZip(installer, outItems, logItems).exec()
FlashZip(uri!!, outItems, logItems).exec()
}
Const.Value.UNINSTALL -> {
showReboot = false
FlashZip.Uninstall(installer, outItems, logItems).exec()
MagiskInstaller.Uninstall(outItems, logItems).exec()
}
Const.Value.FLASH_MAGISK -> {
MagiskInstaller.Direct(installer, outItems, logItems).exec()
if (Info.isEmulator)
MagiskInstaller.Emulator(outItems, logItems).exec()
else
MagiskInstaller.Direct(outItems, logItems).exec()
}
Const.Value.FLASH_INACTIVE_SLOT -> {
MagiskInstaller.SecondSlot(installer, outItems, logItems).exec()
MagiskInstaller.SecondSlot(outItems, logItems).exec()
}
Const.Value.PATCH_FILE -> {
uri ?: return@launch
showReboot = false
MagiskInstaller.Patch(installer, uri, outItems, logItems).exec()
MagiskInstaller.Patch(uri, outItems, logItems).exec()
}
else -> {
back()
@@ -91,9 +91,9 @@ class FlashViewModel(
private fun onResult(success: Boolean) {
state = if (success) State.LOADED else State.LOADING_FAILED
behaviorText = when {
success -> resources.getString(R.string.done)
else -> resources.getString(R.string.failure)
when {
success -> _subtitle.postValue(R.string.done)
else -> _subtitle.postValue(R.string.failure)
}
}

View File

@@ -1,47 +0,0 @@
package com.topjohnwu.magisk.ui.hide
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.ktx.getLabel
class HideTarget(line: String) {
val packageName: String
val process: String
init {
val split = line.split(Regex("\\|"), 2)
packageName = split[0]
process = split.getOrElse(1) { packageName }
}
}
class HideAppInfo(info: ApplicationInfo, pm: PackageManager)
: ApplicationInfo(info), Comparable<HideAppInfo> {
val label = info.getLabel(pm)
val iconImage: Drawable = info.loadIcon(pm)
override fun compareTo(other: HideAppInfo) = comparator.compare(this, other)
companion object {
private val comparator = compareBy<HideAppInfo>(
{ it.label.toLowerCase(currentLocale) },
{ it.packageName }
)
}
}
data class HideProcessInfo(
val name: String,
val packageName: String,
val isHidden: Boolean
)
class HideAppTarget(
val info: HideAppInfo,
val processes: List<HideProcessInfo>
) : Comparable<HideAppTarget> {
override fun compareTo(other: HideAppTarget) = compareValuesBy(this, other) { it.info }
}

View File

@@ -0,0 +1,105 @@
package com.topjohnwu.magisk.ui.hide
import android.annotation.SuppressLint
import android.content.pm.ApplicationInfo
import android.content.pm.ComponentInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.*
import android.content.pm.ServiceInfo
import android.graphics.drawable.Drawable
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.ktx.getLabel
import com.topjohnwu.magisk.ktx.isIsolated
import com.topjohnwu.magisk.ktx.useAppZygote
class CmdlineHiddenItem(line: String) {
val packageName: String
val process: String
init {
val split = line.split(Regex("\\|"), 2)
packageName = split[0]
process = split.getOrElse(1) { packageName }
}
}
const val ISOLATED_MAGIC = "isolated"
@SuppressLint("InlinedApi")
class HideAppInfo(info: ApplicationInfo, pm: PackageManager, hideList: List<CmdlineHiddenItem>)
: ApplicationInfo(info), Comparable<HideAppInfo> {
val label = info.getLabel(pm)
val iconImage: Drawable = info.loadIcon(pm)
val processes = fetchProcesses(pm, hideList)
override fun compareTo(other: HideAppInfo) = comparator.compare(this, other)
private fun fetchProcesses(
pm: PackageManager,
hideList: List<CmdlineHiddenItem>
): List<HideProcessInfo> {
// Fetch full PackageInfo
val baseFlag = MATCH_DISABLED_COMPONENTS or MATCH_UNINSTALLED_PACKAGES
val packageInfo = try {
val request = GET_ACTIVITIES or GET_SERVICES or GET_RECEIVERS or GET_PROVIDERS
pm.getPackageInfo(packageName, baseFlag or request)
} catch (e: NameNotFoundException) {
// EdXposed hooked, issue#3276
return emptyList()
} catch (e: Exception) {
// Exceed binder data transfer limit, fetch each component type separately
pm.getPackageInfo(packageName, baseFlag).apply {
runCatching { activities = pm.getPackageInfo(packageName, baseFlag or GET_ACTIVITIES).activities }
runCatching { services = pm.getPackageInfo(packageName, baseFlag or GET_SERVICES).services }
runCatching { receivers = pm.getPackageInfo(packageName, baseFlag or GET_RECEIVERS).receivers }
runCatching { providers = pm.getPackageInfo(packageName, baseFlag or GET_PROVIDERS).providers }
}
}
val hidden = hideList.filter { it.packageName == packageName || it.packageName == ISOLATED_MAGIC }
fun createProcess(name: String, pkg: String = packageName): HideProcessInfo {
return HideProcessInfo(name, pkg, hidden.any { it.process == name })
}
var haveAppZygote = false
fun Array<out ComponentInfo>.processes() = map { createProcess(it.processName) }
fun Array<ServiceInfo>.processes() = map {
if (it.isIsolated) {
if (it.useAppZygote) {
haveAppZygote = true
// Using app zygote, don't need to track the process
null
} else {
createProcess("${it.processName}:${it.name}", ISOLATED_MAGIC)
}
} else {
createProcess(it.processName)
}
}
return with(packageInfo) {
activities?.processes().orEmpty() +
services?.processes().orEmpty() +
receivers?.processes().orEmpty() +
providers?.processes().orEmpty() +
listOf(if (haveAppZygote) createProcess("${processName}_zygote") else null)
}.filterNotNull().distinctBy { it.name }.sortedBy { it.name }
}
companion object {
private val comparator = compareBy<HideAppInfo>(
{ it.label.toLowerCase(currentLocale) },
{ it.packageName }
)
}
}
data class HideProcessInfo(
val name: String,
val packageName: String,
var isHidden: Boolean
) {
val isIsolated get() = name == ISOLATED_MAGIC
val isAppZygote get() = name.endsWith("_zygote")
}

View File

@@ -12,14 +12,13 @@ import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell
import kotlin.math.roundToInt
class HideItem(
app: HideAppTarget
) : ObservableItem<HideItem>(), Comparable<HideItem> {
class HideRvItem(
val info: HideAppInfo
) : ObservableItem<HideRvItem>(), Comparable<HideRvItem> {
override val layoutRes = R.layout.item_hide_md2
override val layoutRes get() = R.layout.item_hide_md2
val info = app.info
val processes = app.processes.map { HideProcessItem(it) }
val processes = info.processes.map { HideProcessRvItem(it) }
@get:Bindable
var isExpanded = false
@@ -41,11 +40,10 @@ class HideItem(
if (value == true) {
processes
.filterNot { it.isHidden }
.filter { isExpanded || it.process.name == it.process.packageName }
.filter { isExpanded || it.defaultSelection }
} else {
processes
.filter { it.isHidden }
.filter { isExpanded || it.process.name == it.process.packageName }
}.forEach { it.toggle() }
}
@@ -69,14 +67,19 @@ class HideItem(
else -> null
}
} else {
processes.find { it.isHidden && it.process.name == it.process.packageName } != null
val defaultProcesses = processes.filter { it.defaultSelection }
when (defaultProcesses.count { it.isHidden }) {
0 -> false
defaultProcesses.size -> true
else -> null
}
}
}
override fun compareTo(other: HideItem) = comparator.compare(this, other)
override fun compareTo(other: HideRvItem) = comparator.compare(this, other)
companion object {
private val comparator = compareBy<HideItem>(
private val comparator = compareBy<HideRvItem>(
{ it.itemsChecked == 0 },
{ it.info }
)
@@ -84,16 +87,17 @@ class HideItem(
}
class HideProcessItem(
class HideProcessRvItem(
val process: HideProcessInfo
) : ObservableItem<HideProcessItem>() {
) : ObservableItem<HideProcessRvItem>() {
override val layoutRes = R.layout.item_hide_process_md2
override val layoutRes get() = R.layout.item_hide_process_md2
@get:Bindable
var isHidden = process.isHidden
set(value) = set(value, field, { field = it }, BR.hidden) {
val arg = if (isHidden) "add" else "rm"
var isHidden
get() = process.isHidden
set(value) = set(value, process.isHidden, { process.isHidden = it }, BR.hidden) {
val arg = if (it) "add" else "rm"
val (name, pkg) = process
Shell.su("magiskhide --$arg $pkg $name").submit()
}
@@ -102,7 +106,10 @@ class HideProcessItem(
isHidden = !isHidden
}
override fun contentSameAs(other: HideProcessItem) = process == other.process
override fun itemSameAs(other: HideProcessItem) = process.name == other.process.name
val defaultSelection get() =
process.isIsolated || process.isAppZygote || process.name == process.packageName
override fun contentSameAs(other: HideProcessRvItem) = process == other.process
override fun itemSameAs(other: HideProcessRvItem) = process.name == other.process.name
}

View File

@@ -3,6 +3,7 @@ package com.topjohnwu.magisk.ui.hide
import android.annotation.SuppressLint
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import android.os.Process
import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope
@@ -14,7 +15,6 @@ import com.topjohnwu.magisk.arch.itemBindingOf
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.ktx.get
import com.topjohnwu.magisk.ktx.packageName
import com.topjohnwu.magisk.ktx.processes
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell
@@ -45,11 +45,11 @@ class HideViewModel : BaseViewModel(), Queryable {
submitQuery()
}
val items = filterableListOf<HideItem>()
val itemBinding = itemBindingOf<HideItem> {
val items = filterableListOf<HideRvItem>()
val itemBinding = itemBindingOf<HideRvItem> {
it.bindExtra(BR.viewModel, this)
}
val itemInternalBinding = itemBindingOf<HideProcessItem> {
val itemInternalBinding = itemBindingOf<HideProcessRvItem> {
it.bindExtra(BR.viewModel, this)
}
@@ -62,14 +62,13 @@ class HideViewModel : BaseViewModel(), Queryable {
state = State.LOADING
val (apps, diff) = withContext(Dispatchers.Default) {
val pm = get<PackageManager>()
val hides = Shell.su("magiskhide --ls").exec().out.map { HideTarget(it) }
val apps = pm.getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES)
val hideList = Shell.su("magiskhide --ls").exec().out.map { CmdlineHiddenItem(it) }
val apps = pm.getInstalledApplications(MATCH_UNINSTALLED_PACKAGES)
.asSequence()
.filter { it.enabled && !blacklist.contains(it.packageName) }
.map { HideAppInfo(it, pm) }
.map { createTarget(it, hides) }
.map { HideAppInfo(it, pm, hideList) }
.filter { it.processes.isNotEmpty() }
.map { HideItem(it) }
.map { HideRvItem(it) }
.toList()
.sorted()
apps to items.calculateDiff(apps)
@@ -80,18 +79,6 @@ class HideViewModel : BaseViewModel(), Queryable {
// ---
private fun createTarget(info: HideAppInfo, hideList: List<HideTarget>): HideAppTarget {
val pkg = info.packageName
val hidden = hideList.filter { it.packageName == pkg }
val processNames = info.processes.distinct()
val processes = processNames.map { name ->
HideProcessInfo(name, pkg, hidden.any { name == it.process })
}
return HideAppTarget(info, processes)
}
// ---
override fun query() {
items.filter {
fun showHidden() = it.itemsChecked != 0

View File

@@ -2,6 +2,8 @@ package com.topjohnwu.magisk.ui.home
import android.os.Bundle
import android.view.*
import android.widget.ImageView
import android.widget.TextView
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIFragment
import com.topjohnwu.magisk.core.download.BaseDownloader
@@ -22,20 +24,32 @@ class HomeFragment : BaseUIFragment<HomeViewModel, FragmentHomeMd2Binding>() {
BaseDownloader.observeProgress(this, viewModel::onProgressUpdate)
}
private fun checkTitle(text: TextView, icon: ImageView) {
text.post {
if (text.layout.getEllipsisCount(0) != 0) {
with (icon) {
layoutParams.width = 0
layoutParams.height = 0
requestLayout()
}
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
super.onCreateView(inflater, container, savedInstanceState)
// Set barrier reference IDs in code, since resource IDs will be stripped in release mode
binding.homeMagiskWrapper.homeMagiskTitleBarrier.referencedIds =
intArrayOf(R.id.home_magisk_button, R.id.home_magisk_title, R.id.home_magisk_icon)
binding.homeMagiskWrapper.homeMagiskBarrier.referencedIds =
intArrayOf(R.id.home_magisk_latest_version, R.id.home_magisk_installed_version)
binding.homeManagerWrapper.homeManagerTitleBarrier.referencedIds =
intArrayOf(R.id.home_manager_button, R.id.home_manager_title, R.id.home_manager_icon)
// If titles are squished, hide icons
with(binding.homeMagiskWrapper) {
checkTitle(homeMagiskTitle, homeMagiskIcon)
}
with(binding.homeManagerWrapper) {
checkTitle(homeManagerTitle, homeManagerIcon)
}
return binding.root
}
@@ -46,11 +60,14 @@ class HomeFragment : BaseUIFragment<HomeViewModel, FragmentHomeMd2Binding>() {
menu.removeItem(R.id.action_reboot)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_settings -> HomeFragmentDirections.actionHomeFragmentToSettingsFragment()
.navigate()
R.id.action_reboot -> RebootEvent.inflateMenu(activity).show()
else -> null
}?.let { true } ?: super.onOptionsItemSelected(item)
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_settings ->
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
R.id.action_reboot -> RebootEvent.inflateMenu(activity).show()
else -> return super.onOptionsItemSelected(item)
}
return true
}
}

View File

@@ -9,8 +9,6 @@ import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.core.download.Subject.Manager
import com.topjohnwu.magisk.core.model.MagiskJson
import com.topjohnwu.magisk.core.model.ManagerJson
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.events.OpenInappLinkEvent
import com.topjohnwu.magisk.events.SnackbarEvent
@@ -18,8 +16,7 @@ import com.topjohnwu.magisk.events.dialog.EnvFixDialog
import com.topjohnwu.magisk.events.dialog.ManagerInstallDialog
import com.topjohnwu.magisk.events.dialog.UninstallDialog
import com.topjohnwu.magisk.ktx.await
import com.topjohnwu.magisk.ktx.packageName
import com.topjohnwu.magisk.ktx.res
import com.topjohnwu.magisk.utils.asTransitive
import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
@@ -34,41 +31,46 @@ class HomeViewModel(
private val svc: NetworkService
) : BaseViewModel() {
val magiskTitleBarrierIds =
intArrayOf(R.id.home_magisk_icon, R.id.home_magisk_title, R.id.home_magisk_button)
val magiskDetailBarrierIds =
intArrayOf(R.id.home_magisk_installed_version, R.id.home_device_details_ramdisk)
val appTitleBarrierIds =
intArrayOf(R.id.home_manager_icon, R.id.home_manager_title, R.id.home_manager_button)
@get:Bindable
var isNoticeVisible = Config.safetyNotice
set(value) = set(value, field, { field = it }, BR.noticeVisible)
@get:Bindable
var stateMagisk = MagiskState.LOADING
set(value) = set(value, field, { field = it }, BR.stateMagisk, BR.showUninstall)
val stateMagisk = when {
!Info.env.isActive -> MagiskState.NOT_INSTALLED
Info.env.magiskVersionCode < BuildConfig.VERSION_CODE -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE
}
@get:Bindable
var stateManager = MagiskState.LOADING
set(value) = set(value, field, { field = it }, BR.stateManager)
@get:Bindable
var magiskRemoteVersion = R.string.loading.res()
set(value) = set(value, field, { field = it }, BR.magiskRemoteVersion)
val magiskInstalledVersion get() =
"${Info.env.magiskVersionString} (${Info.env.magiskVersionCode})"
val magiskInstalledVersion get() = Info.env.run {
if (isActive)
"$magiskVersionString ($magiskVersionCode)".asTransitive()
else
R.string.not_available.asTransitive()
}
@get:Bindable
var managerRemoteVersion = R.string.loading.res()
var managerRemoteVersion = R.string.loading.asTransitive()
set(value) = set(value, field, { field = it }, BR.managerRemoteVersion)
val managerInstalledVersion = Info.stub?.let {
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) (${it.version})"
} ?: "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
val statePackageName = packageName
@get:Bindable
var stateManagerProgress = 0
set(value) = set(value, field, { field = it }, BR.stateManagerProgress)
@get:Bindable
val showUninstall get() = Info.env.isActive && state != State.LOADING
@get:Bindable
val showSafetyNet get() = Info.hasGMS && isConnected.get()
@@ -80,30 +82,25 @@ class HomeViewModel(
override fun refresh() = viewModelScope.launch {
state = State.LOADING
svc.fetchUpdate()?.apply {
notifyPropertyChanged(BR.showSafetyNet)
Info.getRemote(svc)?.apply {
state = State.LOADED
stateMagisk = when {
!Info.env.isActive -> MagiskState.NOT_INSTALLED
magisk.isObsolete -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE
}
stateManager = when {
app.isObsolete -> MagiskState.OBSOLETE
BuildConfig.VERSION_CODE < magisk.versionCode -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE
}
magiskRemoteVersion =
"${magisk.version} (${magisk.versionCode})"
managerRemoteVersion =
"${app.version} (${app.versionCode}) (${stub.versionCode})"
"${magisk.version} (${magisk.versionCode}) (${stub.versionCode})".asTransitive()
launch {
ensureEnv()
}
} ?: apply { state = State.LOADING_FAILED }
notifyPropertyChanged(BR.showUninstall)
notifyPropertyChanged(BR.showSafetyNet)
} ?: {
state = State.LOADING_FAILED
managerRemoteVersion = R.string.not_available.asTransitive()
}()
}
val showTest = false
@@ -115,9 +112,8 @@ class HomeViewModel(
}.publish()
fun onProgressUpdate(progress: Float, subject: Subject) {
when (subject) {
is Manager -> stateManagerProgress = progress.times(100f).roundToInt()
}
if (subject is Manager)
stateManagerProgress = progress.times(100f).roundToInt()
}
fun onLinkPressed(link: String) = OpenInappLinkEvent(link).publish()
@@ -125,17 +121,13 @@ class HomeViewModel(
fun onDeletePressed() = UninstallDialog().publish()
fun onManagerPressed() = when (state) {
State.LOADED -> ManagerInstallDialog().publish()
State.LOADED -> withExternalRW { ManagerInstallDialog().publish() }
State.LOADING -> SnackbarEvent(R.string.loading).publish()
else -> SnackbarEvent(R.string.no_connection).publish()
}
fun onMagiskPressed() = when (state) {
State.LOADED -> withExternalRW {
HomeFragmentDirections.actionHomeFragmentToInstallFragment().publish()
}
State.LOADING -> SnackbarEvent(R.string.loading).publish()
else -> SnackbarEvent(R.string.no_connection).publish()
fun onMagiskPressed() = withExternalRW {
HomeFragmentDirections.actionHomeFragmentToInstallFragment().publish()
}
fun onSafetyNetPressed() =
@@ -160,9 +152,4 @@ class HomeViewModel(
}
}
private val MagiskJson.isObsolete
get() = Info.env.isActive && Info.env.magiskVersionCode < versionCode
private val ManagerJson.isObsolete
get() = BuildConfig.VERSION_CODE < versionCode
}

View File

@@ -7,7 +7,6 @@ import android.view.ViewGroup
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIFragment
import com.topjohnwu.magisk.core.download.BaseDownloader
import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
import com.topjohnwu.magisk.ktx.coroutineScope
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -23,7 +22,6 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
// Allow markwon to run in viewmodel scope
binding.releaseNotes.coroutineScope = viewModel.viewModelScope
BaseDownloader.observeProgress(this, viewModel::onProgressUpdate)
}
override fun onCreateView(

View File

@@ -1,33 +1,35 @@
package com.topjohnwu.magisk.ui.install
import android.app.Activity
import android.content.Context
import android.net.Uri
import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.Action
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.events.MagiskInstallFileEvent
import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
import com.topjohnwu.magisk.ui.flash.FlashFragment
import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
import org.koin.core.get
import timber.log.Timber
import java.io.File
import java.io.IOException
import kotlin.math.roundToInt
class InstallViewModel(
svc: NetworkService
) : BaseViewModel(State.LOADED) {
) : BaseViewModel() {
val isRooted = Shell.rootAccess()
val skipOptions = Info.ramdisk && !Info.isFDE && Info.isSAR
val skipOptions = Info.isEmulator || (Info.ramdisk && !Info.isFDE && Info.isSAR)
val noSecondSlot = !isRooted || Info.isPixel || Info.isVirtualAB || !Info.isAB || Info.isEmulator
@get:Bindable
var step = if (skipOptions) 1 else 0
@@ -52,10 +54,6 @@ class InstallViewModel(
}
}
@get:Bindable
var progress = 0
set(value) = set(value, field, { field = it }, BR.progress)
@get:Bindable
var data: Uri? = null
set(value) = set(value, field, { field = it }, BR.data)
@@ -67,41 +65,35 @@ class InstallViewModel(
init {
viewModelScope.launch {
try {
notes = svc.fetchString(Info.remote.magisk.note)
val context = get<Context>()
File(context.cacheDir, "${BuildConfig.VERSION_CODE}.md").run {
notes = when {
exists() -> readText()
Const.Url.CHANGELOG_URL.isEmpty() -> ""
else -> {
val text = svc.fetchString(Const.Url.CHANGELOG_URL)
writeText(text)
text
}
}
}
} catch (e: IOException) {
Timber.e(e)
}
}
}
fun onProgressUpdate(progress: Float, subject: Subject) {
if (subject !is Subject.Magisk) {
return
}
this.progress = progress.times(100).roundToInt()
if (this.progress >= 100) {
state = State.LOADED
} else if (this.progress < -150) {
state = State.LOADING_FAILED
}
}
fun step(nextStep: Int) {
step = nextStep
}
fun install() {
DownloadService.start(get(), Subject.Magisk(resolveAction()))
when (method) {
R.id.method_patch -> FlashFragment.patch(data!!)
R.id.method_direct -> FlashFragment.flash(false)
R.id.method_inactive_slot -> FlashFragment.flash(true)
else -> error("Unknown value")
}
state = State.LOADING
}
// ---
private fun resolveAction() = when (method) {
R.id.method_download -> Action.Download
R.id.method_patch -> Action.Patch(data!!)
R.id.method_direct -> Action.Flash.Primary
R.id.method_inactive_slot -> Action.Flash.Secondary
else -> error("Unknown value")
}
}

View File

@@ -313,10 +313,8 @@ class ModuleViewModel(
fun infoPressed(item: ModuleItem) {
item.repo?.also {
if (isConnected.get())
OpenReadmeEvent(it).publish()
else
SnackbarEvent(R.string.no_connection).publish()
if (isConnected.get()) OpenReadmeEvent(it).publish()
else SnackbarEvent(R.string.no_connection).publish()
} ?: return
}
}

View File

@@ -61,10 +61,10 @@ object Theme : BaseSettingsItem.Blank() {
override val title = R.string.section_theme.asTransitive()
}
// --- Manager
// --- App
object Manager : BaseSettingsItem.Section() {
override val title = R.string.manager.asTransitive()
object AppSettings : BaseSettingsItem.Section() {
override val title = R.string.home_app_title.asTransitive()
}
object ClearRepoCache : BaseSettingsItem.Blank() {
@@ -77,8 +77,8 @@ object ClearRepoCache : BaseSettingsItem.Blank() {
}
object Hide : BaseSettingsItem.Input() {
override val title = R.string.settings_hide_manager_title.asTransitive()
override val description = R.string.settings_hide_manager_summary.asTransitive()
override val title = R.string.settings_hide_app_title.asTransitive()
override val description = R.string.settings_hide_app_summary.asTransitive()
override var value = ""
set(value) = setV(value, field, { field = it })
@@ -106,11 +106,8 @@ object Hide : BaseSettingsItem.Input() {
}
object Restore : BaseSettingsItem.Blank() {
override val title = R.string.settings_restore_manager_title.asTransitive()
override val description = R.string.settings_restore_manager_summary.asTransitive()
override fun refresh() {
isEnabled = Info.remote.app.versionCode > 0
}
override val title = R.string.settings_restore_app_title.asTransitive()
override val description = R.string.settings_restore_app_summary.asTransitive()
}
object AddShortcut : BaseSettingsItem.Blank() {
@@ -141,21 +138,27 @@ object DownloadPath : BaseSettingsItem.Input() {
object UpdateChannel : BaseSettingsItem.Selector() {
override var value = Config.updateChannel
set(value) = setV(value, field, { field = it }) { Config.updateChannel = it }
set(value) = setV(value, field, { field = it }) {
Config.updateChannel = it
Info.remote = Info.EMPTY_REMOTE
}
override val title = R.string.settings_update_channel_title.asTransitive()
override val entries: Array<String> = resources.getStringArray(R.array.update_channel).let {
if (BuildConfig.DEBUG) it.toMutableList().apply { add("Canary") }.toTypedArray() else it
override val entries: Array<String> = resources.getStringArray(R.array.update_channel).apply {
if (BuildConfig.VERSION_CODE % 100 == 0)
toMutableList().apply { removeAt(Config.Value.CANARY_CHANNEL) }.toTypedArray()
}
override val description
get() = entries.getOrNull(value)?.asTransitive()
?: TransitiveText.String(if (value == -1) entries[0] else "Canary")
get() = entries.getOrNull(value)?.asTransitive() ?: TransitiveText.String(entries[0])
}
object UpdateChannelUrl : BaseSettingsItem.Input() {
override val title = R.string.settings_update_custom.asTransitive()
override var value = Config.customChannelUrl
set(value) = setV(value, field, { field = it }) { Config.customChannelUrl = it }
set(value) = setV(value, field, { field = it }) {
Config.customChannelUrl = it
Info.remote = Info.EMPTY_REMOTE
}
override val description get() = value.asTransitive()
override val inputResult get() = result
@@ -312,7 +315,7 @@ object RequestTimeout : BaseSettingsItem.Selector() {
override var value = selected
set(value) = setV(value, field, { field = it }) {
Config.suDefaultTimeout = it
Config.suDefaultTimeout = entryValues[it].toInt()
}
private val selected: Int

View File

@@ -21,6 +21,7 @@ import com.topjohnwu.magisk.data.database.RepoDao
import com.topjohnwu.magisk.events.AddHomeIconEvent
import com.topjohnwu.magisk.events.RecreateEvent
import com.topjohnwu.magisk.events.dialog.BiometricEvent
import com.topjohnwu.magisk.ktx.activity
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
@@ -54,13 +55,17 @@ class SettingsViewModel(
// Manager
list.addAll(listOf(
Manager,
AppSettings,
UpdateChannel, UpdateChannelUrl, DoHToggle, UpdateChecker, DownloadPath
))
if (Info.env.isActive) {
list.add(ClearRepoCache)
if (Const.USER_ID == 0 && Info.isConnected.get())
list.add(if (hidden) Restore else Hide)
if (Build.VERSION.SDK_INT >= 21 && Const.USER_ID == 0) {
if (hidden)
list.add(Restore)
else if (Info.isConnected.get())
list.add(Hide)
}
}
// Magisk
@@ -101,16 +106,18 @@ class SettingsViewModel(
is Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().publish()
is ClearRepoCache -> clearRepoCache()
is SystemlessHosts -> createHosts()
is Restore -> HideAPK.restore(view.context)
is Restore -> HideAPK.restore(view.activity)
is AddShortcut -> AddHomeIconEvent().publish()
else -> callback()
}
override fun onItemChanged(view: View, item: BaseSettingsItem) = when (item) {
is Language -> RecreateEvent().publish()
is UpdateChannel -> openUrlIfNecessary(view)
is Hide -> HideAPK.hide(view.context, item.value)
else -> Unit
override fun onItemChanged(view: View, item: BaseSettingsItem) {
when (item) {
is Language -> RecreateEvent().publish()
is UpdateChannel -> openUrlIfNecessary(view)
is Hide -> viewModelScope.launch { HideAPK.hide(view.activity, item.value) }
else -> Unit
}
}
private fun openUrlIfNecessary(view: View) {

View File

@@ -1,5 +1,7 @@
package com.topjohnwu.magisk.utils
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.coordinatorlayout.widget.CoordinatorLayout
@@ -13,8 +15,8 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import kotlin.math.roundToInt
class HideBottomViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(),
HideableBehavior<V> {
class HideBottomViewOnScrollBehavior<V : View>(context: Context, attrs: AttributeSet) :
HideBottomViewOnScrollBehavior<V>(), HideableBehavior<V> {
private var lockState: Boolean = false
private var isLaidOut = false
@@ -122,4 +124,4 @@ class HideBottomViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<
.setInterpolator(FastOutSlowInInterpolator())
.start()
}
}

View File

@@ -1,6 +1,8 @@
package com.topjohnwu.magisk.utils
import android.animation.TimeInterpolator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
@@ -8,9 +10,8 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import com.google.android.material.animation.AnimationUtils
class HideTopViewOnScrollBehavior<V : View> :
CoordinatorLayout.Behavior<V>(),
HideableBehavior<V> {
class HideTopViewOnScrollBehavior<V : View>(context: Context, attrs: AttributeSet) :
CoordinatorLayout.Behavior<V>(), HideableBehavior<V> {
companion object {
private const val STATE_SCROLLED_DOWN = 1
@@ -139,4 +140,4 @@ class HideTopViewOnScrollBehavior<V : View> :
.setDuration(duration)
.withEndAction { currentAnimator = null }
.let { currentAnimator = it }
}
}

View File

@@ -15,7 +15,7 @@ import android.widget.TextView
import androidx.annotation.WorkerThread
import com.caverock.androidsvg.SVG
import com.caverock.androidsvg.SVGParseException
import com.topjohnwu.magisk.core.ResMgr
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.superuser.internal.WaitRunnable
import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.MarkwonSpansFactory
@@ -216,7 +216,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
return PictureDrawable(picture)
}
val density: Float = ResMgr.resource.displayMetrics.density
val density: Float = AssetHack.resource.displayMetrics.density
val width = (w * density + .5f).toInt()
val height = (h * density + .5f).toInt()
@@ -226,7 +226,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
canvas.scale(density, density)
svg.renderToCanvas(canvas)
return BitmapDrawable(ResMgr.resource, bitmap)
return BitmapDrawable(AssetHack.resource, bitmap)
}
}

View File

@@ -4,7 +4,6 @@ import android.content.res.Resources
import android.widget.TextView
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import com.topjohnwu.magisk.ktx.get
sealed class TransitiveText {
@@ -42,13 +41,13 @@ sealed class TransitiveText {
}
fun Int.asTransitive(vararg params: Any) = TransitiveText.Res(this, *params)
fun CharSequence.asTransitive() = TransitiveText.String(this)
fun Int.asTransitive(vararg params: Any): TransitiveText = TransitiveText.Res(this, *params)
fun CharSequence.asTransitive(): TransitiveText = TransitiveText.String(this)
@BindingAdapter("android:text")
fun TextView.setText(text: TransitiveText) {
this.text = text.getText(get())
this.text = text.getText(context.resources)
}
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")

View File

@@ -47,7 +47,8 @@ class MagiskDialog(
super.onCreate(savedInstanceState)
super.setContentView(binding.root)
val surfaceColor = MaterialColors.getColor(context, R.attr.colorSurfaceSurfaceVariant, javaClass.canonicalName)
val default = MaterialColors.getColor(context, R.attr.colorSurface, javaClass.canonicalName)
val surfaceColor = MaterialColors.getColor(context, R.attr.colorSurfaceSurfaceVariant, default)
val materialShapeDrawable = MaterialShapeDrawable(context, null, R.attr.alertDialogStyle, R.style.MaterialAlertDialog_MaterialComponents)
materialShapeDrawable.initializeElevationOverlay(context)
materialShapeDrawable.fillColor = ColorStateList.valueOf(surfaceColor)

View File

@@ -1,54 +0,0 @@
package com.topjohnwu.magisk.view
import android.content.Context
import android.view.LayoutInflater
import android.widget.TextView
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.ktx.coroutineScope
import io.noties.markwon.Markwon
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.KoinComponent
import org.koin.core.inject
import timber.log.Timber
import kotlin.coroutines.coroutineContext
object MarkDownWindow : KoinComponent {
private val svc: NetworkService by inject()
private val markwon: Markwon by inject()
suspend fun show(activity: Context, title: String?, url: String) {
show(activity, title) {
svc.fetchString(url)
}
}
suspend fun show(activity: Context, title: String?, input: suspend () -> String) {
val view = LayoutInflater.from(activity).inflate(R.layout.markdown_window_md2, null)
MagiskDialog(activity)
.applyTitle(title ?: "")
.applyView(view)
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.reveal()
val tv = view.findViewById<TextView>(R.id.md_txt)
tv.coroutineScope = CoroutineScope(coroutineContext)
withContext(Dispatchers.IO) {
try {
markwon.setMarkdown(tv, input())
} catch (e: Exception) {
if (e is CancellationException)
throw e
Timber.e(e)
tv.post { tv.setText(R.string.download_file_error) }
}
}
}
}

View File

@@ -3,24 +3,20 @@ package com.topjohnwu.magisk.view
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.os.Build.VERSION.SDK_INT
import androidx.core.app.TaskStackBuilder
import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toIcon
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Const.ID.PROGRESS_NOTIFICATION_CHANNEL
import com.topjohnwu.magisk.core.Const.ID.UPDATE_NOTIFICATION_CHANNEL
import com.topjohnwu.magisk.core.SplashActivity
import com.topjohnwu.magisk.core.cmp
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.core.intent
import com.topjohnwu.magisk.ktx.get
import com.topjohnwu.magisk.ktx.getBitmap
@Suppress("DEPRECATION")
object Notifications {
val mgr by lazy { get<Context>().getSystemService<NotificationManager>()!! }
@@ -49,29 +45,11 @@ object Notifications {
}
}
fun magiskUpdate(context: Context) {
val intent = context.intent<SplashActivity>()
.putExtra(Const.Key.OPEN_SECTION, "magisk")
val stackBuilder = TaskStackBuilder.create(context)
stackBuilder.addParentStack(SplashActivity::class.java.cmp(context.packageName))
stackBuilder.addNextIntent(intent)
val pendingIntent = stackBuilder.getPendingIntent(
Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = updateBuilder(context)
.setContentTitle(context.getString(R.string.magisk_update_title))
.setContentText(context.getString(R.string.manager_download_install))
.setAutoCancel(true)
.setContentIntent(pendingIntent)
mgr.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build())
}
fun managerUpdate(context: Context) {
val intent = DownloadService.pendingIntent(context, Subject.Manager())
val builder = updateBuilder(context)
.setContentTitle(context.getString(R.string.manager_update_title))
.setContentTitle(context.getString(R.string.magisk_update_title))
.setContentText(context.getString(R.string.manager_download_install))
.setAutoCancel(true)
.setContentIntent(intent)

View File

@@ -1,6 +1,5 @@
package com.topjohnwu.magisk.view
import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
@@ -8,6 +7,8 @@ import android.content.pm.ShortcutManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
@@ -24,16 +25,14 @@ object Shortcuts {
}
}
@TargetApi(26)
fun addHomeIcon(context: Context) {
val manager = context.getSystemService<ShortcutManager>() ?: return
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) ?: return
val info = ShortcutInfo.Builder(context, Const.Nav.HOME)
.setShortLabel(context.getString(R.string.app_name))
val info = ShortcutInfoCompat.Builder(context, Const.Nav.HOME)
.setShortLabel(context.getString(R.string.magisk))
.setIntent(intent)
.setIcon(context.getIcon(R.drawable.ic_launcher))
.setIcon(context.getIconCompat(R.drawable.ic_launcher))
.build()
manager.requestPinShortcut(info, null)
ShortcutManagerCompat.requestPinShortcut(context, info, null)
}
private fun Context.getIconCompat(id: Int): IconCompat {

View File

@@ -19,18 +19,10 @@ public abstract class JarMap implements Closeable {
LinkedHashMap<String, JarEntry> entryMap;
public static JarMap open(String file) throws IOException {
return new FileMap(new File(file), true, ZipFile.OPEN_READ);
}
public static JarMap open(File file, boolean verify) throws IOException {
return new FileMap(file, verify, ZipFile.OPEN_READ);
}
public static JarMap open(String file, boolean verify) throws IOException {
return new FileMap(new File(file), verify, ZipFile.OPEN_READ);
}
public static JarMap open(InputStream is, boolean verify) throws IOException {
return new StreamMap(is, verify);
}

View File

@@ -1,5 +1,6 @@
package com.topjohnwu.signing;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -32,6 +33,7 @@ import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
@Keep
public class SignBoot {
private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632;
@@ -88,6 +90,21 @@ public class SignBoot {
}
}
private static int fullRead(InputStream in, byte[] b) throws IOException {
return fullRead(in, b, 0, b.length);
}
private static int fullRead(InputStream in, byte[] b, int off, int len) throws IOException {
int n = 0;
while (n < len) {
int count = in.read(b, off + n, len - n);
if (count <= 0)
break;
n += count;
}
return n;
}
public static boolean doSignature(
@Nullable X509Certificate cert, @Nullable PrivateKey key,
@NonNull InputStream imgIn, @NonNull OutputStream imgOut, @NonNull String target
@@ -96,7 +113,7 @@ public class SignBoot {
PushBackRWStream in = new PushBackRWStream(imgIn, imgOut);
byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
// First read the header
in.read(hdr);
fullRead(in, hdr);
int signableSize = getSignableImageSize(hdr);
// Unread header
in.unread(hdr);
@@ -126,7 +143,7 @@ public class SignBoot {
try {
// Read the header for size
byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
if (imgIn.read(hdr) != hdr.length) {
if (fullRead(imgIn, hdr) != hdr.length) {
System.err.println("Unable to read image header");
return false;
}
@@ -135,7 +152,7 @@ public class SignBoot {
// Read the rest of the image
byte[] rawImg = Arrays.copyOf(hdr, signableSize);
int remain = signableSize - hdr.length;
if (imgIn.read(rawImg, hdr.length, remain) != remain) {
if (fullRead(imgIn, rawImg, hdr.length, remain) != remain) {
System.err.println("Unable to read image");
return false;
}

View File

@@ -3,9 +3,9 @@
<item>
<shape android:shape="rectangle">
<solid android:color="?colorSecondary" />
<solid android:color="@android:color/black" />
<corners android:bottomLeftRadius="2dp" android:bottomRightRadius="2dp" />
</shape>
</item>
</selector>
</selector>

View File

@@ -3,9 +3,9 @@
<item>
<shape android:shape="rectangle">
<solid android:color="?colorSecondary" />
<solid android:color="@android:color/black" />
<corners android:topLeftRadius="2dp" android:topRightRadius="2dp" />
</shape>
</item>
</selector>
</selector>

View File

@@ -35,6 +35,7 @@
style="@style/WidgetFoundation.Appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior"
app:fitsSystemWindowsInsets="top">
<com.google.android.material.appbar.MaterialToolbar
@@ -76,6 +77,7 @@
android:layout_marginBottom="@dimen/l1"
android:fitsSystemWindows="false"
app:layout_fitsSystemWindowsInsets="bottom"
app:layout_behavior="com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior"
tools:layout_marginBottom="64dp">
<com.google.android.material.bottomnavigation.BottomNavigationView

View File

@@ -56,29 +56,6 @@
app:icon="@drawable/ic_restart"
app:iconTint="?colorOnPrimary" />
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card.Elevated"
goneUnless="@{viewModel.loading}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:contentPadding="@dimen/l1">
<TextView
movieBehavior="@{viewModel.loading}"
movieBehaviorText="@{viewModel.behaviorText}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="monospace"
android:gravity="center"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textColor="?colorOnSurface"
android:textStyle="bold"
tools:text="Flashing..." />
</com.google.android.material.card.MaterialCardView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

View File

@@ -108,7 +108,7 @@
app:layout_constraintTop_toBottomOf="@+id/home_magisk_wrapper" />
<Space
gone="@{!viewModel.showSafetyNet &amp;&amp; !viewModel.showUninstall}"
gone="@{!viewModel.showSafetyNet &amp;&amp; !Info.env.isActive}"
android:layout_width="match_parent"
android:layout_height="@dimen/l1" />
@@ -128,7 +128,7 @@
<Button
style="@style/WidgetFoundation.Button.Outlined.Error"
gone="@{!viewModel.showUninstall}"
goneUnless="@{Info.env.isActive}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"

View File

@@ -25,306 +25,218 @@
app:fitsSystemWindowsInsets="top|bottom"
tools:paddingTop="24dp">
<FrameLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingTop="@dimen/l_50">
<LinearLayout
goneUnless="@{viewModel.loaded &amp;&amp; viewModel.progress &lt;= 0}"
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
gone="@{viewModel.skipOptions}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingTop="@dimen/l_50">
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
gone="@{viewModel.skipOptions}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginEnd="@dimen/l1"
android:focusable="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
style="@style/WidgetFoundation.Icon"
isSelected="@{viewModel.step > 0}"
android:layout_marginStart="@dimen/l_25"
app:srcCompat="@drawable/ic_check_circle_md2" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/l1"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/install_options_title"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold" />
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.step != 0}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.step(1)}"
android:text="@string/install_next" />
</LinearLayout>
<LinearLayout
gone="@{viewModel.step != 0}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l_50"
android:layout_marginEnd="@dimen/l1"
android:layout_marginBottom="@dimen/l_50"
android:orientation="vertical"
android:paddingStart="3dp"
android:paddingEnd="3dp"
tools:layout_gravity="center">
<CheckBox
style="@style/WidgetFoundation.Checkbox"
gone="@{Info.isSAR}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@={Config.keepVerity}"
android:text="@string/keep_dm_verity"
tools:checked="true" />
<CheckBox
style="@style/WidgetFoundation.Checkbox"
goneUnless="@{Info.isFDE}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@={Config.keepEnc}"
android:text="@string/keep_force_encryption"
app:tint="?colorPrimary" />
<CheckBox
style="@style/WidgetFoundation.Checkbox"
gone="@{Info.ramdisk}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@={Config.recovery}"
android:text="@string/recovery_mode"
app:tint="?colorPrimary" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l1"
android:layout_marginEnd="@dimen/l1"
android:focusable="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
style="@style/WidgetFoundation.Icon"
isSelected="@{viewModel.step > 1}"
android:layout_marginStart="@dimen/l_25"
app:srcCompat="@drawable/ic_check_circle_md2" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/l1"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/install_method_title"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold" />
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.step != 1}"
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.install()}"
android:text="@string/install_start"
app:icon="@drawable/ic_forth_md2"
app:iconGravity="textEnd" />
</LinearLayout>
<RadioGroup
gone="@{viewModel.step != 1}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l_50"
android:layout_marginEnd="@dimen/l1"
android:layout_marginBottom="@dimen/l_50"
android:checkedButton="@={viewModel.method}"
android:paddingStart="3dp"
android:paddingEnd="3dp">
<RadioButton
android:id="@+id/method_download"
style="@style/WidgetFoundation.RadioButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/download_zip_only"
tools:checked="true" />
<RadioButton
android:id="@+id/method_patch"
style="@style/WidgetFoundation.RadioButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/select_patch_file" />
<RadioButton
android:id="@+id/method_direct"
style="@style/WidgetFoundation.RadioButton"
gone="@{!viewModel.rooted}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/direct_install" />
<RadioButton
android:id="@+id/method_inactive_slot"
style="@style/WidgetFoundation.RadioButton"
gone="@{!viewModel.isRooted || Info.isPixel || !Info.isAB}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/install_inactive_slot" />
</RadioGroup>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/l1"
android:layout_marginStart="@dimen/l1"
android:layout_marginEnd="@dimen/l1"
android:focusable="false">
<TextView
android:id="@+id/release_notes"
markdownText="@{viewModel.notes}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:textAppearance="@style/AppearanceFoundation.Caption"
android:visibility="gone"
tools:maxLines="5"
tools:text="@tools:sample/lorem/random"
tools:visibility="visible" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
goneUnless="@{viewModel.loading}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginEnd="@dimen/l1"
android:gravity="center"
android:orientation="vertical">
android:focusable="false">
<TextView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/loading"
android:textAppearance="@style/AppearanceFoundation.Title" />
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/l1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ProgressBar
style="@style/WidgetFoundation.ProgressBar"
invisible="@{viewModel.progress &lt;= 0}"
android:layout_width="100dp"
android:layout_gravity="center"
android:progress="@{viewModel.progress}" />
<ImageView
style="@style/WidgetFoundation.Icon"
isSelected="@{viewModel.step > 0}"
android:layout_marginStart="@dimen/l_25"
app:srcCompat="@drawable/ic_check_circle_md2" />
<ProgressBar
style="@style/WidgetFoundation.ProgressBar.Indeterminate"
invisibleUnless="@{viewModel.progress &lt;= 0}"
android:layout_width="100dp"
android:layout_gravity="center" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/l1"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/install_options_title"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold" />
</FrameLayout>
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.step != 0}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.step(1)}"
android:text="@string/install_next" />
</LinearLayout>
</LinearLayout>
<LinearLayout
gone="@{viewModel.step != 0}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l_50"
android:layout_marginEnd="@dimen/l1"
android:layout_marginBottom="@dimen/l_50"
android:orientation="vertical"
android:paddingStart="3dp"
android:paddingEnd="3dp"
tools:layout_gravity="center">
<LinearLayout
goneUnless="@{viewModel.loaded &amp;&amp; viewModel.progress >= 100}"
<CheckBox
style="@style/WidgetFoundation.Checkbox"
gone="@{Info.isSAR}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@={Config.keepVerity}"
android:text="@string/keep_dm_verity"
tools:checked="true" />
<CheckBox
style="@style/WidgetFoundation.Checkbox"
goneUnless="@{Info.isFDE}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@={Config.keepEnc}"
android:text="@string/keep_force_encryption"
app:tint="?colorPrimary" />
<CheckBox
style="@style/WidgetFoundation.Checkbox"
gone="@{Info.ramdisk}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@={Config.recovery}"
android:text="@string/recovery_mode"
app:tint="?colorPrimary" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l1"
android:layout_marginEnd="@dimen/l1"
android:focusable="false">
<TextView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/download_complete"
android:textAppearance="@style/AppearanceFoundation.Title" />
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
goneUnless="@{viewModel.loadFailed}"
<ImageView
style="@style/WidgetFoundation.Icon"
isSelected="@{viewModel.step > 1}"
android:layout_marginStart="@dimen/l_25"
app:srcCompat="@drawable/ic_check_circle_md2" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/l1"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/install_method_title"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold" />
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.step != 1}"
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.install()}"
android:text="@string/install_start"
app:icon="@drawable/ic_forth_md2"
app:iconGravity="textEnd" />
</LinearLayout>
<RadioGroup
gone="@{viewModel.step != 1}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l_50"
android:layout_marginEnd="@dimen/l1"
android:layout_marginBottom="@dimen/l_50"
android:checkedButton="@={viewModel.method}"
android:paddingStart="3dp"
android:paddingEnd="3dp">
<RadioButton
android:id="@+id/method_patch"
style="@style/WidgetFoundation.RadioButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/select_patch_file" />
<RadioButton
android:id="@+id/method_direct"
style="@style/WidgetFoundation.RadioButton"
gone="@{!viewModel.rooted}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/direct_install" />
<RadioButton
android:id="@+id/method_inactive_slot"
style="@style/WidgetFoundation.RadioButton"
gone="@{viewModel.noSecondSlot}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/install_inactive_slot" />
</RadioGroup>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
gone="@{viewModel.notes.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
android:layout_marginTop="@dimen/l1"
android:layout_marginStart="@dimen/l1"
android:layout_marginEnd="@dimen/l1"
android:focusable="false">
<TextView
android:id="@+id/release_notes"
markdownText="@{viewModel.notes}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/download_file_error"
android:textAppearance="@style/AppearanceFoundation.Title" />
android:layout_margin="15dp"
android:textAppearance="@style/AppearanceFoundation.Caption"
android:visibility="gone"
tools:maxLines="5"
tools:text="@tools:sample/lorem/random"
tools:visibility="visible" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -46,46 +46,52 @@
android:text="@string/magisk"
android:textAppearance="@style/AppearanceFoundation.Title"
android:textColor="?colorPrimary"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_icon"
app:layout_constraintEnd_toStartOf="@+id/home_magisk_button"
app:layout_constraintStart_toEndOf="@+id/home_magisk_icon"
app:layout_constraintTop_toTopOf="@+id/home_magisk_icon" />
<Button
<FrameLayout
android:id="@+id/home_magisk_button"
style="@style/WidgetFoundation.Button"
invisible="@{viewModel.stateMagisk != MagiskState.OBSOLETE}"
android:enabled="@{viewModel.stateManager != MagiskState.OBSOLETE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onMagiskPressed()}"
android:text="@string/update"
android:textAllCaps="false"
app:icon="@drawable/ic_update_md2"
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/home_magisk_title"/>
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_title"
app:layout_constraintTop_toTopOf="@+id/home_magisk_title">
<Button
style="@style/WidgetFoundation.Button.Text"
invisible="@{viewModel.stateMagisk == MagiskState.OBSOLETE}"
android:enabled="@{viewModel.stateManager != MagiskState.OBSOLETE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onMagiskPressed()}"
android:text="@string/install"
android:textAllCaps="false"
app:icon="@drawable/ic_install"
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/home_magisk_title"/>
<Button
style="@style/WidgetFoundation.Button"
gone="@{viewModel.stateMagisk != MagiskState.OBSOLETE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onMagiskPressed()}"
android:text="@string/update"
android:textAllCaps="false"
android:layout_gravity="end"
app:icon="@drawable/ic_update_md2" />
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.stateMagisk == MagiskState.OBSOLETE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:onClick="@{() -> viewModel.onMagiskPressed()}"
android:text="@string/install"
android:textAllCaps="false"
app:icon="@drawable/ic_install"/>
</FrameLayout>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/home_magisk_title_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
tools:constraint_referenced_ids="home_magisk_button,home_magisk_title,home_magisk_icon" />
app:referencedIds="@{viewModel.magiskTitleBarrierIds}"
tools:constraint_referenced_ids="home_magisk_icon,home_magisk_title,home_magisk_button" />
<HorizontalScrollView
android:layout_width="match_parent"
@@ -105,41 +111,24 @@
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/home_magisk_latest_version"
android:id="@+id/home_magisk_installed_version"
style="@style/W.Home.Item.Top"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
style="@style/W.Home.ItemContent"
android:text="@string/home_latest_version" />
<TextView
style="@style/W.Home.ItemContent.Right"
android:text="@{viewModel.loadFailed ? @string/not_available : viewModel.magiskRemoteVersion }"
tools:text="20.1 (12345)" />
</LinearLayout>
<LinearLayout
android:id="@+id/home_magisk_installed_version"
style="@style/W.Home.Item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/home_magisk_latest_version">
<TextView
style="@style/W.Home.ItemContent"
android:text="@string/home_installed_version" />
<TextView
style="@style/W.Home.ItemContent.Right"
android:text="@{Info.env.isActive ? viewModel.magiskInstalledVersion : @string/not_available}"
tools:text="20.1 (12345)" />
android:text="@{viewModel.magiskInstalledVersion}"
tools:text="22.0 (22000)" />
</LinearLayout>
<LinearLayout
android:id="@+id/home_device_details_recovery"
android:id="@+id/home_device_details_ramdisk"
style="@style/W.Home.Item.Bottom"
app:layout_constraintStart_toStartOf="@+id/home_magisk_installed_version"
app:layout_constraintTop_toBottomOf="@+id/home_magisk_installed_version">
@@ -160,7 +149,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
tools:constraint_referenced_ids="home_magisk_latest_version,home_magisk_installed_version"/>
app:referencedIds="@{viewModel.magiskDetailBarrierIds}"
tools:constraint_referenced_ids="home_magisk_installed_version,home_device_details_ramdisk"/>
<LinearLayout
android:id="@+id/home_device_details_ab"
@@ -181,7 +171,7 @@
<LinearLayout
android:id="@+id/home_device_details_sar"
style="@style/W.Home.Item"
style="@style/W.Home.Item.Bottom"
app:layout_constraintStart_toStartOf="@+id/home_device_details_ab"
app:layout_constraintTop_toBottomOf="@+id/home_device_details_ab">
@@ -196,23 +186,6 @@
</LinearLayout>
<LinearLayout
android:id="@+id/home_device_details_crypto"
style="@style/W.Home.Item.Bottom"
app:layout_constraintStart_toStartOf="@+id/home_device_details_sar"
app:layout_constraintTop_toBottomOf="@+id/home_device_details_sar">
<TextView
style="@style/W.Home.ItemContent"
android:text="Crypto" />
<TextView
style="@style/W.Home.ItemContent.Right"
android:text="@{Info.cryptoText}"
tools:text="N/A" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@@ -43,48 +43,56 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:text="@string/manager"
android:text="@string/home_app_title"
android:textAppearance="@style/AppearanceFoundation.Title"
android:textColor="?colorPrimary"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintBottom_toBottomOf="@+id/home_manager_icon"
app:layout_constraintEnd_toStartOf="@+id/home_manager_button"
app:layout_constraintStart_toEndOf="@+id/home_manager_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/manager" />
tools:text="@string/home_app_title" />
<Button
<FrameLayout
android:id="@+id/home_manager_button"
style="@style/WidgetFoundation.Button"
invisible="@{viewModel.stateManager != MagiskState.OBSOLETE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onManagerPressed()}"
android:text="@string/update"
android:textAllCaps="false"
app:icon="@drawable/ic_update_md2"
app:layout_constraintBottom_toBottomOf="@+id/home_manager_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/home_manager_title"/>
app:layout_constraintBottom_toBottomOf="@+id/home_manager_title"
app:layout_constraintTop_toTopOf="@+id/home_manager_title">
<Button
style="@style/WidgetFoundation.Button.Text"
invisible="@{viewModel.stateManager == MagiskState.OBSOLETE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onManagerPressed()}"
android:text="@string/install"
android:textAllCaps="false"
app:icon="@drawable/ic_install"
app:layout_constraintBottom_toBottomOf="@+id/home_manager_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/home_manager_title"/>
<Button
style="@style/WidgetFoundation.Button"
gone="@{viewModel.stateManager != MagiskState.OBSOLETE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onManagerPressed()}"
android:text="@string/update"
android:textAllCaps="false"
android:layout_gravity="end"
app:icon="@drawable/ic_update_md2" />
<Button
style="@style/WidgetFoundation.Button.Text"
gone="@{viewModel.stateManager != MagiskState.UP_TO_DATE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:onClick="@{() -> viewModel.onManagerPressed()}"
android:text="@string/install"
android:textAllCaps="false"
app:icon="@drawable/ic_install"/>
</FrameLayout>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/home_manager_title_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
tools:constraint_referenced_ids="home_manager_button,home_manager_title,home_manager_icon" />
app:referencedIds="@{viewModel.appTitleBarrierIds}"
tools:constraint_referenced_ids="home_manager_icon,home_manager_title,home_manager_button" />
<HorizontalScrollView
android:layout_width="match_parent"
@@ -115,8 +123,8 @@
<TextView
style="@style/W.Home.ItemContent.Right"
android:text="@{viewModel.loadFailed ? @string/not_available : viewModel.managerRemoteVersion}"
tools:text="8.0.0 (123) (10)" />
android:text="@{viewModel.managerRemoteVersion}"
tools:text="22.0 (22000) (16)" />
</LinearLayout>
@@ -133,7 +141,7 @@
<TextView
style="@style/W.Home.ItemContent.Right"
android:text="@{viewModel.managerInstalledVersion}"
tools:text="8.0.0 (123) (10)" />
tools:text="22.0 (22000) (16)" />
</LinearLayout>
@@ -150,7 +158,7 @@
<TextView
android:id="@+id/home_manager_extra_connection_value"
style="@style/W.Home.ItemContent.Right"
android:text="@{viewModel.statePackageName}"
android:text="@{context.packageName}"
tools:text="com.topjohnwu.magisk" />
</LinearLayout>

View File

@@ -9,7 +9,7 @@
<variable
name="item"
type="com.topjohnwu.magisk.ui.hide.HideItem" />
type="com.topjohnwu.magisk.ui.hide.HideRvItem" />
<variable
name="viewModel"
@@ -64,7 +64,7 @@
app:layout_constraintStart_toEndOf="@+id/hide_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@string/app_name" />
tools:text="@string/magisk" />
<TextView
android:id="@+id/hide_package"

View File

@@ -7,7 +7,7 @@
<variable
name="item"
type="com.topjohnwu.magisk.ui.hide.HideProcessItem" />
type="com.topjohnwu.magisk.ui.hide.HideProcessRvItem" />
<variable
name="viewModel"

View File

@@ -58,7 +58,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/app_name" />
tools:text="@string/magisk" />
<TextView
android:id="@+id/log_date"

View File

@@ -29,14 +29,15 @@
tools:layout_gravity="center"
tools:minHeight="?listPreferredItemHeightSmall">
<View
<ImageView
android:id="@+id/track_top"
invisible="@{isTop}"
android:layout_width="2dp"
android:layout_height="0dp"
android:layout_marginBottom="2dp"
android:alpha=".3"
android:background="@drawable/bg_line_bottom_rounded"
app:tint="?colorSecondary"
app:srcCompat="@drawable/bg_line_bottom_rounded"
app:layout_constraintBottom_toTopOf="@+id/track_bullet"
app:layout_constraintEnd_toEndOf="@+id/track_bullet"
app:layout_constraintStart_toStartOf="@+id/track_bullet"
@@ -54,14 +55,15 @@
app:tint="@color/color_primary_error_transient"
tools:srcCompat="@drawable/ic_check_md2" />
<View
<ImageView
android:id="@+id/track_bottom"
invisible="@{isBottom}"
android:layout_width="2dp"
android:layout_height="0dp"
android:layout_marginTop="2dp"
android:alpha=".3"
android:background="@drawable/bg_line_top_rounded"
app:tint="?colorSecondary"
app:srcCompat="@drawable/bg_line_top_rounded"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/track_bullet"
app:layout_constraintStart_toStartOf="@+id/track_bullet"

View File

@@ -61,7 +61,7 @@
app:layout_constraintEnd_toStartOf="@+id/policy_indicator"
app:layout_constraintStart_toEndOf="@+id/policy_app_icon"
app:layout_constraintTop_toTopOf="@+id/policy_app_icon"
tools:text="@string/app_name" />
tools:text="@string/magisk" />
<TextView
android:id="@+id/policy_package_name"

View File

@@ -49,10 +49,6 @@
android:label="FlashFragment"
tools:layout="@layout/fragment_flash_md2">
<argument
android:name="installer"
app:argType="android.net.Uri" />
<argument
android:name="action"
app:argType="string" />

View File

@@ -1 +0,0 @@
/util_functions.sh

View File

@@ -1,10 +1,8 @@
## v8.0.4
## v8.0.7
- A lot of stability changes and minor bug fixes
- Collect device properties, app logcat, and Magisk logs when saving logs in the logs menu
- Fix sepolicy rule migration when upgrading
## v8.0.3
## v8.0.6
- Switch to the new Magisk Module Repo setup in preparation to allow 3rd party repos
- Add tapjacking protection on Superuser request dialog
- Stability changes and bug fixes
- Minor UI changes
- Update internal scripts

View File

@@ -7,27 +7,41 @@ run_delay() {
}
env_check() {
for file in busybox magisk magiskboot magiskinit util_functions.sh boot_patch.sh; do
for file in busybox magiskboot magiskinit util_functions.sh boot_patch.sh; do
[ -f $MAGISKBIN/$file ] || return 1
done
return 0
}
fix_env() {
cd $MAGISKBIN
PATH=/system/bin /system/bin/sh update-binary -x
./busybox rm -f update-binary magisk.apk
./busybox chmod -R 755 .
./magiskinit -x magisk magisk
cp_readlink() {
if [ -z $2 ]; then
cd $1
else
cp -af $1/. $2
cd $2
fi
for file in *; do
if [ -L $file ]; then
local full=$(readlink -f $file)
rm $file
cp -af $full $file
fi
done
chmod -R 755 .
cd /
}
direct_install() {
rm -rf $MAGISKBIN/* 2>/dev/null
fix_env() {
# Cleanup and make dirs
rm -rf $MAGISKBIN/*
mkdir -p $MAGISKBIN 2>/dev/null
chmod 700 $NVBASE
cp -af $1/. $MAGISKBIN
rm -f $MAGISKBIN/new-boot.img
cp_readlink $1 $MAGISKBIN
rm -rf $1
chown -R 0:0 $MAGISKBIN
}
direct_install() {
echo "- Flashing new boot image"
flash_image $1/new-boot.img $2
case $? in
@@ -40,10 +54,22 @@ direct_install() {
return 2
;;
esac
rm -rf $1
rm -f $1/new-boot.img
fix_env $1
run_migrations
copy_sepolicy_rules
return 0
}
run_uninstaller() {
rm -rf /dev/tmp
mkdir -p /dev/tmp/install
unzip -o "$1" "assets/*" "lib/*" -d /dev/tmp/install
INSTALLER=/dev/tmp/install sh /dev/tmp/install/assets/uninstaller.sh dummy 1 "$1"
}
restore_imgs() {
[ -z $SHA1 ] && return 1
local BACKUPDIR=/data/magisk_backup_$SHA1
@@ -63,15 +89,17 @@ restore_imgs() {
}
post_ota() {
cd $1
cd /data/adb
cp -f $1 bootctl
rm -f $1
chmod 755 bootctl
./bootctl hal-info || return
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
./bootctl set-active-boot-slot $SLOT_NUM
cat << EOF > post-fs-data.d/post_ota.sh
${1}/bootctl mark-boot-successful
rm -f ${1}/bootctl
rm -f ${1}/post-fs-data.d/post_ota.sh
/data/adb/bootctl mark-boot-successful
rm -f /data/adb/bootctl
rm -f /data/adb/post-fs-data.d/post_ota.sh
EOF
chmod 755 post-fs-data.d/post_ota.sh
cd /
@@ -172,8 +200,7 @@ grep_prop() { return; }
# Initialize
#############
mm_init() {
export BOOTMODE=true
app_init() {
mount_partitions
get_flags
run_migrations
@@ -183,3 +210,5 @@ mm_init() {
# Make sure RECOVERYMODE has value
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
}
export BOOTMODE=true

View File

@@ -14,20 +14,15 @@
<!--Home-->
<string name="no_connection">لا يوجد إتصال</string>
<string name="app_changelog">تفاصيل التحديث</string>
<string name="manager">المدير</string>
<string name="loading">جارٍ التحميل...</string>
<string name="update">تحديث</string>
<string name="not_available">غير/متوفر</string>
<string name="hide">إخفاء</string>
<string name="status">الحالة</string>
<string name="home_package">الحزمة</string>
<string name="home_notice_content">تأكد دوما أنك تستعمل إصدار ماجسيك رسمي، التطبيقات المثبتة من الجهات الخارجية ستسبب لك أضراراً</string>
<string name="home_support_title">تبرع لنا</string>
<string name="home_item_source">الكود المصدري للتطبيق</string>
<string name="home_support_content">مـاجـيسك هي، وستظل دوماً، مجانيةً و مفتوحة المصدر، اظهر اهتمامك لنا لكي نبقيها هكذا بدعم مالي صغير</string>
<string name="home_status_normal"> الوضـع الطبيعي</string>
<string name="home_status_stub"> وضـع STUB</string>
<string name="home_installed_version">تم التثبيت</string>
<string name="home_latest_version">آخر إصدار</string>
<string name="invalid_update_channel">مصدر التحديث غير صالح</string>
@@ -43,7 +38,6 @@
<string name="install_next">التالي</string>
<string name="install_start">هيا، بنا</string>
<string name="manager_download_install">اضغط للتنزيل و التثبيت</string>
<string name="download_zip_only">تحميل Zip فقط</string>
<string name="direct_install">تثبيت مباشر (موصى بها)</string>
<string name="install_inactive_slot">التثبيت على المنطقة الغير نشطة (بعد OTA)</string>
<string name="install_inactive_slot_msg">سيُجبر جهازك للاقلاع على المنطقة الغير النشطة بعد إعادة التشغيل!\n استخدم هذا الخيار فقط بعد الانتهاء من OTA. استمرار؟</string>
@@ -133,10 +127,6 @@
<string name="settings_download_path_message">ستحمل الملفات إلى %1$s</string>
<string name="settings_clear_cache_title">حذف الذاكرة المؤقتة للإضافات</string>
<string name="settings_clear_cache_summary">حذف المعلومات المخزنة محليا، هذا سيجبر التطبيق على التحديث الإضافات عبر الانترنت</string>
<string name="settings_hide_manager_title">إخفاء مـاجـيسك Manager</string>
<string name="settings_hide_manager_summary">تغيير اسم حزمة التطبيق لتجنب الكشف</string>
<string name="settings_restore_manager_title">استعادة مـاجـيسك Manager</string>
<string name="settings_restore_manager_summary">استعادة مـاجـيسك Manager مع اسم الحزمة الأصلي</string>
<string name="language">اللغة</string>
<string name="system_default">(الأفتراضي)</string>
<string name="settings_check_update_title">تحقق من التحديثات</string>
@@ -195,10 +185,7 @@
<string name="progress_channel">إشعارات التقدم</string>
<string name="download_complete">اكتمل التنزيل</string>
<string name="download_file_error">فشل تنزيل الملف</string>
<string name="download_open_parent">أظهر المجلد الرئيسي</string>
<string name="download_open_self">أظهر الملف</string>
<string name="magisk_update_title">تحديث مـاجـيسك متوفر!</string>
<string name="manager_update_title">تحديث مـاجـيسك Manager متوفر!</string>
<!--Toasts, Dialogs-->
<string name="yes">نعم</string>
@@ -212,8 +199,6 @@
<string name="flashing">يتم الحرق...</string>
<string name="done">تم!</string>
<string name="failure">فشل!</string>
<string name="hide_manager_title">جار إخفاء مـاجـيسك Manager…</string>
<string name="hide_manager_fail_toast">فشل إخفاء مـاجـيسك Manager…</string>
<string name="open_link_failed_toast">لم يُعثر على تطبيق لفتح الرابط …</string>
<string name="complete_uninstall">إلغاء التثبيت بالكامل</string>
<string name="restore_img">استعادة الصور</string>
@@ -221,13 +206,10 @@
<string name="restore_done">تم الأستعادة</string>
<string name="restore_fail">النسخة الاحتياطية الأصلية غير موجودة!</string>
<string name="proprietary_title">تنزيل شفرة الملكية</string>
<string name="proprietary_notice">مـاجـيسك برنامج FOSS(السرعة والأمان)،ولا يحتوي على شفرة SafetyNet الخاصة بكوكل.\n\nهل تسمح لـمـاجـيسك بتحميل ملحق GoogleApiClient لفحص SafetyNet?</string>
<string name="setup_fail">فشل التضبيط</string>
<string name="env_fix_title"> الإعداد الأضافي مطلوب</string>
<string name="env_fix_msg">يحتاج جهازك إلى إعداد إضافي ليتسنى لـمـاجـيسك العمل بشكل صحيح. سيتم تنزيل ملف مضغوط لتثبيت مـاجـيسك هل تريد المتابعة؟</string>
<string name="setup_msg">جار تضبيت البيئة</string>
<string name="authenticate">تصديق</string>
<string name="unsupport_magisk_title">إصدار مـاجـيسك غير مدعوم</string>
<string name="unsupport_magisk_msg">إصدار الملف أصغر من %1$s.مـاجـيسك لا يدعمها..\n\nسيتصرف التطبيق كأنك لا تملك روت بجهازك.فضلاًً حدث المعجبين باسرع وقت.</string>
</resources>

View File

@@ -43,10 +43,7 @@
<string name="progress_channel">Nəticə Bildirişləri</string>
<string name="download_complete">Yükləmə bitdi</string>
<string name="download_file_error">Faylı yükləmək alınmadı</string>
<string name="download_open_parent">Valideyn qovluğunda göstər</string>
<string name="download_open_self">Faylı göstər</string>
<string name="magisk_update_title">Magisk Yeniləməsi Var!</string>
<string name="manager_update_title">Magisk Manager Yeniləməsi Var!</string>
<!--Toasts, Dialogs-->
<string name="repo_install_title">%1$s faylını yüklə</string>
@@ -57,10 +54,7 @@
<string name="repo_cache_cleared">Repo keşi silindi</string>
<string name="manager_download_install">Yükləyib quraşdırmaq üçün toxun.</string>
<string name="flashing">Qurulur</string>
<string name="hide_manager_title">Magisk Manager gizlədilir…</string>
<string name="hide_manager_fail_toast">Magisk Manager\'i gizlətmək alınmadı.</string>
<string name="open_link_failed_toast">Keçid açmağa heçbir tətbiq tapılmadı.</string>
<string name="download_zip_only">Yalnız Zip yüklə</string>
<string name="direct_install">Birdəfəlik Yüklə (Tövsiyə olunur)</string>
<string name="select_patch_file">Fayl Seç və Yamaqla</string>
<string name="install_inactive_slot">Fəal olmayan slota quraşdır (OTA\'dan sonra)</string>
@@ -71,20 +65,14 @@
<string name="restore_done">Geri qaytarma bitdi!</string>
<string name="restore_fail">Stock nüsxə mövcud deyil!</string>
<string name="proprietary_title">Özəl kodu yükləyin</string>
<string name="proprietary_notice">Magisk Manager açıq lisenziyalıdır və Google\'ın özəl SafetyNet API kodunu ehtiva etmir.\n\Magisk Managerə SafetyNet yoxlamaları üçün tərkibində GoogleApiClient olan əlavəni yükləməyə icazə verirsiniz?</string>
<string name="setup_fail">Quraşdırma alınmadı.</string>
<string name="env_fix_title">Əlavə Quraşdırma Lazımdır</string>
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün işləməsi üçün əlavə quraşdırmaya ehtiyacı var . Bu Magisk zip faylını endirəcək, davam etmək istəyirsiniz?</string>
<string name="setup_title">Əlavə quraşdırma</string>
<string name="setup_msg">Quraşdırma yerinə yetirilir…</string>
<!--Settings Activity -->
<string name="settings_clear_cache_title">Repo Keşini Təmizlə</string>
<string name="settings_clear_cache_summary">Onlayn repolar üçün keşlənmiş məlumatı silin. Bu tətbiqi onlayn şəkildə yenilənməyə məcbur edir.</string>
<string name="settings_hide_manager_title">Magisk Manager\'i Gizlə</string>
<string name="settings_hide_manager_summary">Magisk Manager\'i təsadüfi adla yenidən sıxışdır.</string>
<string name="settings_restore_manager_title">Magisk Manager\'i Geri Qaytar</string>
<string name="settings_restore_manager_summary">Magisk Manager\'i orjinal sıxışdırma ilə geri qaytar</string>
<string name="language">Dil</string>
<string name="system_default">(Sistem Dili)</string>
<string name="settings_check_update_title">Yeniləmələri Yoxla</string>

View File

@@ -13,19 +13,14 @@
<!--Home-->
<string name="no_connection">Злучэнне адсутнічае</string>
<string name="app_changelog">Спіс змен</string>
<string name="manager">Кіраўнік</string>
<string name="loading">Загрузка…</string>
<string name="update">Абнавіць</string>
<string name="not_available">Не</string>
<string name="hide">Схаваць</string>
<string name="status">Статус</string>
<string name="home_package">Пакунак</string>
<string name="home_notice_content">Заўсёды правярайце адкуль вы спампоўваеце Magisk Manager. Пакункі, спампаваныя з невядомых крыніц, могуць змяшчаць шкоднасны код.</string>
<string name="home_support_title">Падтрымайце нас</string>
<string name="home_item_source">Зыходны код</string>
<string name="home_support_content">Magisk ёсць і заўсёды будзе бясплатным праектам з адкрытым зыходным кодам. Але вы заўсёды можаце ахвяраваць нам на распрацоўку.</string>
<string name="home_status_normal">Звычайны</string>
<string name="home_status_stub">Хаванне</string>
<string name="home_installed_version">Усталявана</string>
<string name="home_latest_version">Апошні</string>
<string name="invalid_update_channel">Хібны канал абнаўлення</string>
@@ -41,7 +36,6 @@
<string name="install_next">Далей</string>
<string name="install_start">Усталяваць</string>
<string name="manager_download_install">Націсніце, каб спампаваць і ўсталяваць</string>
<string name="download_zip_only">Толькі спампаваць Zip з праграмай</string>
<string name="direct_install">Непасрэдная ўсталёўка (рэкамендуецца)</string>
<string name="install_inactive_slot">Усталяваць у неактыўны слот (пасля OTA)</string>
<string name="install_inactive_slot_msg">Ваша прылада ПРЫМУСОВА перазапусціцца ў неактыўны слот!\nВыкарыстоўвайце гэты параметр толькі пасля завяршэння OTA.\nПрацягнуць?</string>
@@ -124,10 +118,6 @@
<string name="settings_download_path_message">Файлы будуць спампоўвацца ў %1$s</string>
<string name="settings_clear_cache_title">Ачыстка кэшу рэпазіторыя</string>
<string name="settings_clear_cache_summary">Ачысціць кэш сеціўных рэпазіторыяў. Гэта прымусіць абнавіць кэш</string>
<string name="settings_hide_manager_title">Схаваць Magisk Manager</string>
<string name="settings_hide_manager_summary">Перапакваць Magisk Manager з выпадковай назвай і назвай пакунка</string>
<string name="settings_restore_manager_title">Аднавіць Magisk Manager</string>
<string name="settings_restore_manager_summary">Аднавіць Magisk Manager з арыгінальнай назвай і назвай пакунка</string>
<string name="language">Мова</string>
<string name="system_default">(Сістэмная)</string>
<string name="settings_check_update_title">Правяраць на абнаўленні</string>
@@ -186,10 +176,7 @@
<string name="progress_channel">Апавяшчэнні пра ход выканання</string>
<string name="download_complete">Спампоўка завершаная</string>
<string name="download_file_error">Не атрымалася спампаваць файл</string>
<string name="download_open_parent">Паказаць у каталозе</string>
<string name="download_open_self">Паказаць файл</string>
<string name="magisk_update_title">Даступна абнаўленне Magisk!</string>
<string name="manager_update_title">Даступна абнаўленне Magisk Manager!</string>
<!--Toasts, Dialogs-->
<string name="yes">Так</string>
<string name="no">Не</string>
@@ -202,8 +189,6 @@
<string name="flashing">Усталёўка…</string>
<string name="done">Завершана!</string>
<string name="failure">Не атрымалася</string>
<string name="hide_manager_title">Хаванне Magisk Manager…</string>
<string name="hide_manager_fail_toast">Не атрымалася схаваць Magisk Manager.</string>
<string name="open_link_failed_toast">Праграмы для адкрыцця спасылкі не знойдзена</string>
<string name="complete_uninstall">Поўнае выдаленне</string>
<string name="restore_img">Аднавіць раздзелы</string>
@@ -211,16 +196,12 @@
<string name="restore_done">Аднаўленне завершана!</string>
<string name="restore_fail">Рэзервовая копія адсутнічае!</string>
<string name="proprietary_title">Спампаваць прапрыетарны код</string>
<string name="proprietary_notice">Magisk Manager - праект з адкрытым зыходным кодам, які не змяшчае прапрыетарны код SafetyNet API.\n\nДазволіць Magisk Manager спампаваць пашырэнне SafetyNet(змяшчае GoogleApiClient)?</string>
<string name="setup_fail">Усталяваць не атрымалася</string>
<string name="env_fix_title">Патрабуецца дадатковая ўсталёўка</string>
<string name="env_fix_msg">Вашай прыладзе для карэктнай працы патрабуецца дадатковая ўсталёўка Magisk. Будзе спампаваны Zip Magisk. Працягнуць?</string>
<string name="setup_msg">Наладка асяроддзя…</string>
<string name="authenticate">Аўтэнтыфікацыя</string>
<string name="unsupport_magisk_title">Непадтрымліваемая версія Magisk</string>
<string name="unsupport_magisk_msg">Гэтая версія Magisk Manager не падтрымлівае Magisk версіі ніжэй за %1$s.\n\nПраграма будзе працаваць так, як быццам Magisk не ўсталяваны. Як мага хутчэй абнавіце Magisk.</string>
<string name="external_rw_permission_denied">Дайце доступ да сховішча, каб уключыць гэтую функцыю</string>
<string name="add_shortcut_title">Дадаць цэтлік на хатні экран</string>
<string name="add_shortcut_msg">Пасля хавання Magisk Manager яго значок і назву можа быць цяжка разглядзець. Хочаце дадаць цэтлік на хатні экран?</string>
<string name="app_not_found">Не знойдзена праграм для апрацоўкі гэтага дзеяння</string>
</resources>

View File

@@ -46,14 +46,10 @@
<string name="magisk_update_title">Налице е нова версия на Magisk.</string>
<string name="release_notes">Бележки</string>
<string name="repo_cache_cleared">Кешът на хранилището е изчистен.</string>
<string name="manager_update_title">Налице е нова версия на Magisk Manager.</string>
<string name="manager_download_install">Докоснете за изтегляне и инсталиране.</string>
<string name="update_channel">Актуализации на Magisk</string>
<string name="flashing">Инсталиране</string>
<string name="hide_manager_title">Скриване на Magisk Manager…</string>
<string name="hide_manager_fail_toast">Скриването на Magisk Manager бе неуспешно.</string>
<string name="open_link_failed_toast">Не бе намерено приложение за отваряне на линка.</string>
<string name="download_zip_only">Изтегляне само на архив</string>
<string name="direct_install">Директно инсталиране (Препоръчва се.)</string>
<string name="install_inactive_slot">Инсталиране на неактивен слот (След OTA)</string>
<string name="install_inactive_slot_msg">Вашето устройство НАЛОЖИТЕЛНО ще стартира текущия неактивен слот при следващото рестартиране.\nИзползвайте тази опция само след като приключи инсталирането на OTA.\nПродължаване?</string>
@@ -64,10 +60,8 @@
<string name="restore_done">Възстановяването е успешно!</string>
<string name="restore_fail">Не е налице архив на стоковия образ!</string>
<string name="proprietary_title">Изтегляне на патентования код</string>
<string name="proprietary_notice">Magisk Manager е FOSS и затова не включва патентования код за SafetyNet ППИ на Google.\n\nПозволявате ли на Magisk Manager да изтегли добавката (включва GoogleApiClient) за SafetyNet проверки?</string>
<string name="setup_fail">Първоначалната настройка е неуспешна.</string>
<string name="env_fix_title">Изисква допълнително настройване</string>
<string name="env_fix_msg">Вашето устройство се нуждае от допълнително надстройване на Magisk, за да работи нормално. Ще бъде изтеглен архивът за надстройка на Magisk. Желаете ли да продължите?</string>
<string name="setup_title">Допълнително надстройване</string>
<string name="setup_msg">Надстройването на средата е в ход…</string>
<string name="download_file_error">Грешка при изтеглянето на файла.</string>
@@ -75,10 +69,6 @@
<!--Settings Activity -->
<string name="settings_clear_cache_title">Изчистване кеша на хранилището</string>
<string name="settings_clear_cache_summary">Изчистване на кешираната информация на онлайн хранилището за принудителното му обновяване.</string>
<string name="settings_hide_manager_title">Скриване на Magisk Manager</string>
<string name="settings_hide_manager_summary">Смяна пакетното наименование на Magisk Manager със случайно наименование.</string>
<string name="settings_restore_manager_title">Възстановяване на Magisk Manager</string>
<string name="settings_restore_manager_summary">Възстановяване на оригиналното пакетно наименование на Magisk Manager.</string>
<string name="language">Език</string>
<string name="system_default">(Системен)</string>
<string name="settings_check_update_title">Проверка за актуализации</string>

View File

@@ -14,20 +14,17 @@
<!--Home-->
<string name="no_connection">Connexió no disponible</string>
<string name="app_changelog">Registre de canvis</string>
<string name="manager">Manager</string>
<string name="loading">Carregant…</string>
<string name="update">Actualització</string>
<string name="not_available">N/A</string>
<string name="hide">Amagar</string>
<string name="status">Estat</string>
<string name="home_package">Paquet</string>
<string name="home_app_title">App</string>
<string name="home_notice_content">Asseguris sempre que està utilitzant un Magisk Manager de codi lliure. Mànagers de fonts desconegudes podrien causar accions malicioses.</string>
<string name="home_support_title">Doni\'ns suport</string>
<string name="home_notice_content">Descarregui Magisk NOMÉS des de la pàgina oficial de GitHub. Fitxers d\'altres fonts desconegudes poden ser maliciosos!</string>
<string name="home_support_title">Doni suport</string>
<string name="home_item_source">Codi font</string>
<string name="home_support_content">Magisk és, i sempre serà, gratis i codi lliure. De totes maneres, pot mostrar el seu interès fent una petita donació.</string>
<string name="home_status_normal">Normal</string>
<string name="home_status_stub">Stub</string>
<string name="home_installed_version">Instal·lat</string>
<string name="home_latest_version">Última</string>
<string name="invalid_update_channel">Canal d\'actualització invàlid</string>
@@ -44,7 +41,6 @@
<string name="install_next">Següent</string>
<string name="install_start">Endavant</string>
<string name="manager_download_install">Premi per baixar i instal·lar</string>
<string name="download_zip_only">Únicament baixa el ZIP</string>
<string name="direct_install">Instal·lació directa (Recomanat)</string>
<string name="install_inactive_slot">Instal·la a la ranura inactiva (Després d\'una OTA)</string>
<string name="install_inactive_slot_msg">El teu dispositiu serà FORÇAT a arrancar en l\'actual ranura inactiva després del reinici!\nUtilitza aquesta opció NOMÉS quan l\'OTA s\'hagi fet.\nContinuar?</string>
@@ -56,6 +52,7 @@
<!--Superuser-->
<string name="su_request_title">Petició de superusuari</string>
<string name="touch_filtered_warning">Com que una aplicació està ofuscant la petició de superusuari, Magisk no pot verificar la seva resposta</string>
<string name="deny">Denegar</string>
<string name="prompt">Preguntar</string>
<string name="grant">Permetre</string>
@@ -85,7 +82,7 @@
<!--Logs-->
<string name="log_data_none">No hi ha cap registre. Provi d\'utilitzar aplicacions que requereixen permisos de superusuari.</string>
<string name="log_data_magisk_none">Els registre de Magisk estan buits. Això és estrany.</string>
<string name="log_data_magisk_none">Els registres de Magisk estan buits. Això és estrany.</string>
<string name="menuSaveLog">Desar registre</string>
<string name="menuClearLog">Netejar registre ara</string>
<string name="logs_cleared">Registre netejat correctament.</string>
@@ -97,18 +94,20 @@
<string name="safetynet_res_invalid">La resposta és invàlida</string>
<string name="safetynet_attest_success">Èxit!</string>
<string name="safetynet_attest_failure">Certificació fallida!</string>
<string name="safetynet_attest_loading">Només un segon…</string>
<string name="safetynet_attest_loading">Esperi uns segons</string>
<string name="safetynet_attest_restart">Intenta de nou</string>
<!-- MagiskHide -->
<string name="show_system_app">Mostra aplicacions del sistema</string>
<string name="show_os_app">Mostra aplicacions del SO</string>
<string name="hide_filter_hint">Filtra per nom</string>
<string name="hide_scroll_up">Vés amunt</string>
<string name="hide_filters">Filtres</string>
<string name="hide_search">Cerca</string>
<!--Module Fragment-->
<!--Module-->
<string name="no_info_provided">(No hi ha informació)</string>
<string name="reboot_userspace">Reinici suau</string>
<string name="reboot_recovery">Reiniciar en Mode Recuperació</string>
<string name="reboot_bootloader">Reiniciar en Mode Bootloader</string>
<string name="reboot_download">Reiniciar en Mode Download</string>
@@ -124,7 +123,7 @@
<string name="module_section_online">En línia</string>
<string name="sorting_order">Ordre de Classificació</string>
<!--Settings -->
<!--Settings-->
<string name="settings_dark_mode_title">Mode del tema</string>
<string name="settings_dark_mode_message">Seleccioni el mode que més s\'adeqüi al seu estil!</string>
<string name="settings_dark_mode_light">Sempre clar</string>
@@ -133,11 +132,11 @@
<string name="settings_download_path_title">Directori de baixades</string>
<string name="settings_download_path_message">Els arxius es desaran a %1$s</string>
<string name="settings_clear_cache_title">Netejar memòria cau del repositori</string>
<string name="settings_clear_cache_summary">Neteja la informació en memòria cau per als repositoris en línia. Força a l\'aplicació a actualitzar-se en línia.</string>
<string name="settings_hide_manager_title">Amagar Magisk Manager</string>
<string name="settings_hide_manager_summary">Torna a empaquetar Magisk Manager amb un nom de paquet a l\'atzar</string>
<string name="settings_restore_manager_title">Restaurar Magisk Manager</string>
<string name="settings_restore_manager_summary">Restaura Magisk Manager amb el nom de paquet original</string>
<string name="settings_clear_cache_summary">Neteja la informació en memòria cau per als repositoris en línia. Força a l\'aplicació a actualitzar-se en línia</string>
<string name="settings_hide_app_title">Amagar Magisk Manager</string>
<string name="settings_hide_app_summary">Torna a empaquetar Magisk Manager amb un nom de paquet a l\'atzar</string>
<string name="settings_restore_app_title">Restaurar Magisk Manager</string>
<string name="settings_restore_app_summary">Restaura Magisk Manager amb el nom de paquet original</string>
<string name="language">Idioma</string>
<string name="system_default">(Idioma del sistema)</string>
<string name="settings_check_update_title">Comprovar Actualitzacions</string>
@@ -170,6 +169,8 @@
<string name="superuser_notification">Notificació de superusuari</string>
<string name="settings_su_reauth_title">Demanar després d\'una actualització</string>
<string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada</string>
<string name="settings_su_tapjack_title">Activa la protecció contra \'TapJacking\'</string>
<string name="settings_su_tapjack_summary">El diàleg per donar permisos de superusuari no respondrà mentre estigui ofuscat per alguna altra finestra o superposició</string>
<string name="settings_su_biometric_title">Activar autenticació biomètrica</string>
<string name="settings_su_biometric_summary">Utilitza l\'autenticació biomètrica per permetre sol·licituds de superusuari</string>
<string name="no_biometric">El dispositiu no suporta o no té establerta configuració biomètrica</string>
@@ -177,7 +178,7 @@
<string name="setting_add_shortcut_summary">Afegeix una bonica drecera a la pantalla d\'inici en cas que el nom i la icona siguin difícils de reconèixer després d\'amagar l\'aplicació.</string>
<string name="settings_doh_title">DNS sobre HTTPS</string>
<string name="settings_doh_description">Solució per enverinament de DNS en algunes nacions</string>
<string name="multiuser_mode">Mode Multiusuari</string>
<string name="settings_owner_only">Només Administrador del Dispositiu</string>
<string name="settings_owner_manage">Administrador del Dispositiu</string>
@@ -199,10 +200,7 @@
<string name="progress_channel">Notificacions de progrés</string>
<string name="download_complete">Baixada completada</string>
<string name="download_file_error">Error en baixar l\'arxiu</string>
<string name="download_open_parent">Mostra\'l al directori principal</string>
<string name="download_open_self">Mostra l\'arxiu</string>
<string name="magisk_update_title">Actualització de Magisk disponible!</string>
<string name="manager_update_title">Actualització de Magisk Manager disponible!</string>
<!--Toasts, Dialogs-->
<string name="yes"></string>
@@ -216,8 +214,7 @@
<string name="flashing">Arranjament…</string>
<string name="done">Fet!</string>
<string name="failure">Fallit</string>
<string name="hide_manager_title">Amagant Magisk Manager…</string>
<string name="hide_manager_fail_toast">No s\'ha pogut amagar Magisk Manager.</string>
<string name="hide_app_title">Amagant Magisk Manager…</string>
<string name="open_link_failed_toast">No s\'ha trobat una aplicació per obrir l\'enllaç</string>
<string name="complete_uninstall">Desinstal·lació completa</string>
<string name="restore_img">Restaura imatges</string>
@@ -225,7 +222,7 @@
<string name="restore_done">Restauració feta!</string>
<string name="restore_fail">La còpia de seguretat de Estock no existeix!</string>
<string name="proprietary_title">Baixar codi propietari</string>
<string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nVol permetre que Magisk Manager baixi una extensió (que conté la GoogleApiClient) per poder fer la comprovació de SafetyNet?</string>
<string name="proprietary_notice">Magisk és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nVol permetre que Magisk Manager baixi una extensió per poder fer la comprovació de SafetyNet?</string>
<string name="setup_fail">Instal·lació fallida</string>
<string name="env_fix_title">Es requereix instal·lació addicional</string>
<string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk, vol procedir a la instal·lació ara?</string>

View File

@@ -1,169 +1,227 @@
<resources>
<!--Welcome Activity-->
<!--Sections-->
<string name="modules">Moduly</string>
<string name="superuser">Superuser</string>
<string name="logs">Log</string>
<string name="superuser">SuperUser</string>
<string name="logs">Protokol</string>
<string name="settings">Nastavení</string>
<string name="refresh">Obnovit lokální data</string>
<string name="install">Instalovat</string>
<string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string>
<string name="section_home">Domů</string>
<string name="section_theme">Téma</string>
<string name="safetynet">SafetyNet</string>
<!--Status Fragment-->
<string name="invalid_update_channel">Neplatný kanál aktualizace</string>
<string name="safetynet_api_error">Chyba SafetyNet API</string>
<string name="safetynet_res_invalid">Odpověď je neplatná.</string>
<string name="keep_force_encryption">Udržet "force encryption"</string>
<string name="keep_dm_verity">Udržet "AVB 2.0/dm-verity"</string>
<string name="uninstall_magisk_title">Odinstalovat Magisk</string>
<string name="uninstall_magisk_msg">Všechny moduly budou zakázány/odstraněny. Root bude odstraněn a pokud jsou data dešifrována, můžou být zašifrována.</string>
<string name="update">Aktualizovat</string>
<!--Module Fragment-->
<string name="no_info_provided">(Žádné informace)</string>
<string name="reboot_recovery">Restartovat do Recovery</string>
<string name="reboot_bootloader">Restartovat do Bootloaderu</string>
<string name="reboot_download">Restartovat do Download</string>
<!--Repo Fragment-->
<string name="update_available">Dostupná aktualizace</string>
<string name="home_installed_version">Nainstalováno</string>
<string name="sorting_order">Pořadí řazení</string>
<!--Log Fragment-->
<string name="menuSaveLog">Uložit log</string>
<string name="menuClearLog">Smazat log</string>
<string name="logs_cleared">Log byl smazán.</string>
<!--About Activity-->
<!--Home-->
<string name="no_connection">Žádné připojení</string>
<string name="app_changelog">Seznam změn</string>
<string name="loading">Načítání…</string>
<string name="update">Aktualizovat</string>
<string name="not_available">N/A</string>
<string name="hide">Skrýt</string>
<string name="home_package">Balíček</string>
<!-- System Components, Notifications -->
<string name="update_channel">Aktualizace Magisk</string>
<string name="progress_channel">Oznámení o průběhu</string>
<string name="download_complete">Stahování dokončeno</string>
<string name="download_file_error">Chyba při stahování souboru</string>
<string name="magisk_update_title">Aktualizace Magisk je dostupná!</string>
<string name="manager_update_title">Aktualizace Magisk Manager je dostupná!</string>
<string name="home_support_title">Podpořte nás</string>
<string name="home_item_source">Zdroj</string>
<string name="home_support_content">Magisk je a vždy bude svobodný s otevřeným kódem. Můžete nám však zaslat malý dar jako poděkování.</string>
<string name="home_installed_version">Nainstalovano</string>
<string name="home_latest_version">Poslední</string>
<string name="invalid_update_channel">Neplatný kanál aktualizace</string>
<string name="uninstall_magisk_title">Odinstalovat Magisk</string>
<string name="uninstall_magisk_msg">Všechny moduly budou zakázány/odstraněny!\nROOT bude odstraněn!\nPokud jsou data dešifrována, můžou být zašifrována!</string>
<string name="home_check_safetynet">Zkontrolovat SafetyNet</string>
<!-- Installation -->
<string name="manager_download_install">Stiskněte pro stažení a instalaci.</string>
<string name="download_zip_only">Stáhnout pouze zip</string>
<!--Install-->
<string name="keep_force_encryption">Ponechat Force Encryption</string>
<string name="keep_dm_verity">Ponechat AVB 2.0/DM-Verity</string>
<string name="recovery_mode">Režim Recovery</string>
<string name="install_options_title">Možnosti</string>
<string name="install_method_title">Způsob</string>
<string name="install_next">Další</string>
<string name="install_start">Spustit</string>
<string name="manager_download_install">Stiskněte pro stažení a instalaci</string>
<string name="direct_install">Přímá instalace (doporučeno)</string>
<string name="install_inactive_slot">Instalace do druhého slotu (po OTA)</string>
<string name="install_inactive_slot_msg">Vaše zařízení bude po restartu VYNUCENĚ spuštěno do aktuálního neaktivního slotu!\nTuto možnost použijte pouze po dokončení OTA.\nPokračovat?</string>
<string name="install_inactive_slot_msg">Vaše zařízení bude po restartu VYNUCENĚ spuštěno do aktuálního neaktivního slotu!\nTuto možnost použijte pouze po dokončení OTA.\nChcete pokračovat?</string>
<string name="setup_title">Další nastavení</string>
<string name="select_patch_file">Vybrat a opravit soubor</string>
<string name="patch_file_msg">Vyberte obraz raw (*.img) nebo soubor tar pro ODIN (*.tar)</string>
<string name="patch_file_msg">Vyberte soubor RAW (*.img) nebo soubor TAR pro ODIN (*.tar)</string>
<string name="reboot_delay_toast">Restartování za 5 sekund…</string>
<string name="flash_screen_title">Instalace</string>
<!--Toasts, Dialogs-->
<string name="repo_install_title">Instalovat %1$s</string>
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
<string name="download">Stáhnout</string>
<string name="reboot">Restartovat</string>
<string name="release_notes">Poznámky k vydání</string>
<string name="repo_cache_cleared">Mezipaměť smazána</string>
<!--Superuser-->
<string name="su_request_title">Požadavek SuperUser</string>
<string name="touch_filtered_warning">Protože aplikace zavírá požadavek SuperUser, Magisk nemůže ověřit Vaši odpověď</string>
<string name="deny">Zakázat</string>
<string name="prompt">Zeptat se</string>
<string name="grant">Povolit</string>
<string name="su_warning">Povolí plný přístup k Vašemu zařízení.\nZakažte, pokud si nejste jisti!</string>
<string name="forever">Trvale</string>
<string name="once">Jednou</string>
<string name="tenmin">10 minut</string>
<string name="twentymin">20 minut</string>
<string name="thirtymin">30 minut</string>
<string name="sixtymin">60 minut</string>
<string name="su_allow_toast">Pro %1$s bylo oprávnění SuperUser povoleno</string>
<string name="su_deny_toast">Pro %1$s bylo oprávnění SuperUser zakázáno</string>
<string name="su_snack_grant">SuperUser oprávnění pro %1$s je povoleno</string>
<string name="su_snack_deny">SuperUser oprávnění pro %1$s je zakázáno</string>
<string name="su_snack_notif_on">Oznámení pro %1$s jsou povolena</string>
<string name="su_snack_notif_off">Oznámení pro %1$s jsou zakázána</string>
<string name="su_snack_log_on">Protokolování %1$s je povoleno</string>
<string name="su_snack_log_off">Protokolování %1$s je zakázáno</string>
<string name="su_revoke_title">Smazat?</string>
<string name="su_revoke_msg">Chcete smazat protokol k oprávnění pro %1$s?</string>
<string name="toast">Text</string>
<string name="none">Žádné</string>
<string name="flashing">Instalová</string>
<string name="hide_manager_title">Skrytí Magisk Manageru…</string>
<string name="hide_manager_fail_toast">Skrytí Magisk Manageru selhalo.</string>
<string name="open_link_failed_toast">Nebyla nalezena žádná aplikace pro otevření odkazu.</string>
<string name="complete_uninstall">Dokončete odinstalaci</string>
<string name="restore_img">Obnovit obrazy</string>
<string name="restore_img_msg">Obnovování…</string>
<string name="restore_done">Obnovení bylo provedeno!</string>
<string name="restore_fail">Výchozí záloha neexistuje!</string>
<string name="proprietary_title">Stáhnout proprietární kód</string>
<string name="proprietary_notice">Magisk Manager je FOSS a neobsahuje proprietární kód SafetyNet API společnosti Google.\n\nChcete povolit aplikaci Magisk Manager stažení rozšíření (obsahuje GoogleApiClient) pro kontrolu SafetyNet?</string>
<string name="setup_fail">Nastavení selhalo.</string>
<string name="env_fix_title">Vyžaduje se další nastavení</string>
<string name="env_fix_msg">Vaše zařízení potřebuje další nastavení, aby Magisk fungoval správně. Stáhne se instalační zip Magisk, chcete pokračovat?</string>
<string name="setup_msg">Nastavení je spuštěno…</string>
<string name="superuser_toggle_notification">Oznáme</string>
<string name="superuser_toggle_revoke">Smazat</string>
<string name="superuser_policy_none">Žádná aplikace nepožádala o oprávnění SuperUser.</string>
<!--Settings Activity -->
<string name="settings_clear_cache_title">Smazat uchovanou mezipaměť</string>
<string name="settings_clear_cache_summary">Smaže informace online použití v mezipaměti, donutí aplikaci obnovit informace online.</string>
<string name="settings_hide_manager_title">Skrýt Magisk Manager</string>
<string name="settings_hide_manager_summary">Nahradí Magisk Manager náhodným názvem balíčku.</string>
<string name="settings_restore_manager_title">Obnovit Magisk Manager</string>
<string name="settings_restore_manager_summary">Obnoví Magisk Manager na původní název balíčku.</string>
<!--Logs-->
<string name="log_data_none">Žádné protokoly. Vyzkoušejte nějakou aplikaci vyžadující oprávnění SuperUser.</string>
<string name="log_data_magisk_none">Protokoly Magisk jsou prázdné. To je zvláštní!</string>
<string name="menuSaveLog">Uložit protokol</string>
<string name="menuClearLog">Smazat protokol</string>
<string name="logs_cleared">Protokol byl smazán</string>
<string name="pid">PID:%1$d</string>
<string name="target_uid">Cílové UID: %1$d</string>
<!--SafetyNet-->
<string name="safetynet_api_error">Chyba SafetyNet API</string>
<string name="safetynet_res_invalid">Odpověď je neplatná</string>
<string name="safetynet_attest_success">Úspěšné!</string>
<string name="safetynet_attest_failure">Chyba ověření!</string>
<string name="safetynet_attest_loading">Čekejte…</string>
<string name="safetynet_attest_restart">Zkusit znovu</string>
<!--MagiskHide-->
<string name="show_system_app">Zobrazit systémové aplikace</string>
<string name="show_os_app">Zobrazit OS aplikace</string>
<string name="hide_filter_hint">Filtrovat podle názvu</string>
<string name="hide_scroll_up">Posunout nahoru</string>
<string name="hide_filters">Filtry</string>
<string name="hide_search">Vyhledávání</string>
<!--Module-->
<string name="no_info_provided">(žádné informace)</string>
<string name="reboot_userspace">Restartovat do UserSpace</string>
<string name="reboot_recovery">Restartovat do Recovery</string>
<string name="reboot_bootloader">Restartovat do Bootloader</string>
<string name="reboot_download">Restartovat do Download</string>
<string name="reboot_edl">Restartovat do EDL</string>
<string name="module_version_author">%1$s od %2$s</string>
<string name="module_section_pending">Aktualizace</string>
<string name="module_section_pending_action">Aktualizovat vše</string>
<string name="module_state_remove">Odstranit</string>
<string name="module_state_restore">Obnovit</string>
<string name="module_action_install_external">Instalace z úložiště</string>
<string name="update_available">Dostupná aktualizace</string>
<string name="module_installed">@string/home_installed_version</string>
<string name="module_section_online">Online</string>
<string name="sorting_order">Seřadit</string>
<!--Settings-->
<string name="settings_dark_mode_title">Režim motivu</string>
<string name="settings_dark_mode_message">Vyberte režim, který nejlépe vyhovuje Vašemu stylu!</string>
<string name="settings_dark_mode_light">Světlý</string>
<string name="settings_dark_mode_system">Podle systému</string>
<string name="settings_dark_mode_dark">Tmavý</string>
<string name="settings_download_path_title">Složka pro stahování</string>
<string name="settings_download_path_message">Soubory budou uloženy do %1$s.</string>
<string name="settings_clear_cache_title">Smazat mezipaměť</string>
<string name="settings_clear_cache_summary">Smažete online informace o použití z mezipaměti, a tím na aplikaci vynutíte obnovení online informací.</string>
<string name="language">Jazyk</string>
<string name="system_default">(Výchozí systémový)</string>
<string name="system_default">(výchozí podle systému)</string>
<string name="settings_check_update_title">Zkontrolovat aktualizace</string>
<string name="settings_check_update_summary">Pravidelně kontrolujte aktualizace na pozadí.</string>
<string name="settings_check_update_summary">Povolíte pravidelnou kontrolu aktualizace na pozadí.</string>
<string name="settings_update_channel_title">Kanál aktualizace</string>
<string name="settings_update_stable">Stabilní</string>
<string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Vlastní</string>
<string name="settings_update_custom_msg">Vložte vlastní URL</string>
<string name="settings_magiskhide_summary">Skrýt Magisk z různých detekcí.</string>
<string name="settings_hosts_title">Nesystémoví hostitelé</string>
<string name="settings_hosts_summary">Podpora nesystémových hostitelů pro aplikace Adblock.</string>
<string name="settings_magiskhide_summary">Skryjete Magisk před různými detekcemi.</string>
<string name="settings_hosts_title">Nesystémový hostitel</string>
<string name="settings_hosts_summary">Přidáte modul pro podporu nesystémových hostitelů v aplikaci AdBlock.</string>
<string name="settings_hosts_toast">Přidán systémový modul hostitelů</string>
<string name="settings_app_name_hint">Nový název</string>
<string name="settings_app_name_helper">Název aplikace bude nahrazen tímto názvem.</string>
<string name="settings_app_name_error">Neplatný formát</string>
<string name="settings_su_app_adb">Aplikace a ADB</string>
<string name="settings_su_app">Pouze aplikace</string>
<string name="settings_su_adb">Pouze ADB</string>
<string name="settings_su_disable">Zakázáno</string>
<string name="settings_su_disable">Zakázat</string>
<string name="settings_su_request_10">10 sekund</string>
<string name="settings_su_request_15">15 sekund</string>
<string name="settings_su_request_20">20 sekund</string>
<string name="settings_su_request_30">30 sekund</string>
<string name="settings_su_request_45">45 sekund</string>
<string name="settings_su_request_60">60 sekund</string>
<string name="superuser_access">Přístup Superuser</string>
<string name="superuser_access">Přístup SuperUser</string>
<string name="auto_response">Automatická odezva</string>
<string name="request_timeout">Časový limit požadavku</string>
<string name="superuser_notification">Oznámení Superuser</string>
<string name="superuser_notification">Oznámení SuperUser</string>
<string name="settings_su_reauth_title">Opětovné ověření po aktualizaci</string>
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění Superuser po aktualizaci aplikace</string>
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění SuperUser po aktualizaci aplikace.</string>
<string name="settings_su_tapjack_title">Povolit ochranu před TapJack</string>
<string name="settings_su_tapjack_summary">Okno dialogu SuperUser nebude reagovat na kliknutí v případě, že je zavřené nebo překryté jiným oknem.</string>
<string name="settings_su_biometric_title">Povolit biometrické ověření</string>
<string name="settings_su_biometric_summary">Použijte biometrické ověření pro povolení požadavků SuperUser.</string>
<string name="no_biometric">Nepodporované zařízení nebo není biometrické ověření povolené</string>
<string name="settings_customization">Přizpůsobit</string>
<string name="setting_add_shortcut_summary">Přidejte odkaz na domovskou obrazovku v případě, že se po skrytí aplikace její název a ikona těžko rozpoznávají.</string>
<string name="settings_doh_title">DNS nebo HTTPS</string>
<string name="settings_doh_description">Řešení pro opravy DNS v některých zemích.</string>
<string name="multiuser_mode">Režim více uživatelů</string>
<string name="settings_owner_only">Pouze vlastník zařízení</string>
<string name="settings_owner_manage">Spravováno vlastníkem zařízení</string>
<string name="settings_user_independent">Nezávislý uživatel</string>
<string name="owner_only_summary">Pouze vlastník má root přístup.</string>
<string name="owner_manage_summary">Root přístup spravuje pouze vlastník a přijímá požadavky k přístupu.</string>
<string name="user_indepenent_summary">Každý uživatel má svá vlastní pravidla root.</string>
<string name="settings_owner_only">Vlastník zařízení</string>
<string name="settings_owner_manage">Správce zařízení</string>
<string name="settings_user_independent">Všichni uživatelé</string>
<string name="owner_only_summary">Pouze vlastník má přístup ROOT.</string>
<string name="owner_manage_summary">Přístup ROOT má správce zařízení, který přijímá požadavky k přístupu.</string>
<string name="user_indepenent_summary">Každý uživatel má svá vlastní pravidla přístupu ROOT.</string>
<string name="mount_namespace_mode">Režim připojení jmenného prostoru</string>
<string name="settings_ns_global">Globální jmenný prostor</string>
<string name="settings_ns_requester">Zděděný jmenný prostor</string>
<string name="settings_ns_requester">Odvozený jmenný prostor</string>
<string name="settings_ns_isolate">Izolovaný jmenný prostor</string>
<string name="global_summary">Všechny relace root používají globální připojení jmenného prostoru.</string>
<string name="requester_summary">Kořenové relace dědí jmenný prostor žadatele.</string>
<string name="isolate_summary">Každá relace root bude mít svůj vlastní izolovaný jmenný prostor.</string>
<string name="global_summary">Všechny relace ROOT používají globální jmenný prostor.</string>
<string name="requester_summary">Relace ROOT je odvozena od jmenného prostoru žadatele.</string>
<string name="isolate_summary">Každá relace ROOT má svůj vlastní izolovaný jmenný prostor.</string>
<!--Superuser-->
<string name="su_request_title">Požadavek Superuser</string>
<string name="deny">Zamítnout</string>
<string name="prompt">Dotaz</string>
<string name="grant">Povolit</string>
<string name="su_warning">Povolí plný přístup k vašemu zařízení.\nZamítněte, pokud si nejste jisti!</string>
<string name="forever">Navždy</string>
<string name="once">Jednou</string>
<string name="tenmin">10 minut</string>
<string name="twentymin">20 minut</string>
<string name="thirtymin">30 minut</string>
<string name="sixtymin">60 minut</string>
<string name="su_allow_toast">Pro %1$s bylo oprávnění Superuser povoleno</string>
<string name="su_deny_toast">Pro %1$s bylo oprávnění Superuser zamítnuto</string>
<string name="su_snack_grant">Superuser oprávnění pro %1$s je povoleno</string>
<string name="su_snack_deny">Superuser oprávnění pro %1$s je zamítnuto</string>
<string name="su_snack_notif_on">Oznámení pro %1$s jsou povolena</string>
<string name="su_snack_notif_off">Oznámení pro %1$s jsou zakázána</string>
<string name="su_snack_log_on">Logování %1$s je povoleno</string>
<string name="su_snack_log_off">Logování %1$s je zakázáno</string>
<string name="su_revoke_title">Smazat?</string>
<string name="su_revoke_msg">Smazat záznam ohledně oprávnění pro %1$s?</string>
<string name="toast">Informační text</string>
<string name="none">Žádný</string>
<!--Notifications-->
<string name="update_channel">Aktualizace Magisk</string>
<string name="progress_channel">Oznámení o průběhu</string>
<string name="download_complete">Stahování dokončeno</string>
<string name="download_file_error">Chyba při stahování souboru</string>
<string name="magisk_update_title">Aktualizace Magisk je dostupná!</string>
<!--Superuser logs-->
<string name="pid">PID: %1$d</string>
<string name="target_uid">Cílové UID: %1$d</string>
<!-- MagiskHide -->
<string name="show_system_app">Zobrazit systémové aplikace</string>
<!--Toasts, Dialogs-->
<string name="yes">ANO</string>
<string name="no">NE</string>
<string name="repo_install_title">Instalovat %1$s</string>
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
<string name="download">Stáhnout</string>
<string name="reboot">Restartovat</string>
<string name="release_notes">Poznámky k vydání</string>
<string name="repo_cache_cleared">Mezipaměť smazána</string>
<string name="flashing">Instalování…</string>
<string name="done">Hotovo!</string>
<string name="failure">Selhalo!</string>
<string name="open_link_failed_toast">Nebyla nalezena žádná aplikace pro otevření odkazu.</string>
<string name="complete_uninstall">Odinstalovat</string>
<string name="restore_img">Obnovit obrazy</string>
<string name="restore_img_msg">Obnovování…</string>
<string name="restore_done">Obnovení bylo provedeno!</string>
<string name="restore_fail">Výchozí záloha neexistuje!</string>
<string name="proprietary_title">Stáhnout kód</string>
<string name="setup_fail">Nastavení selhalo</string>
<string name="env_fix_title">Vyžaduje se další nastavení</string>
<string name="setup_msg">Nastavení je spuštěno…</string>
<string name="authenticate">Ověřit</string>
<string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string>
<string name="external_rw_permission_denied">Udělte oprávnění pro povolení této funkce.</string>
<string name="add_shortcut_title">Přidat odkaz na domovskou obrazovku</string>
<string name="app_not_found">Nelze nalézt žádnou aplikaci, která dokáže provést tuto akci.</string>
</resources>

View File

@@ -14,25 +14,23 @@
<!--Home-->
<string name="no_connection">Keine Verbindung verfügbar</string>
<string name="app_changelog">Änderungen</string>
<string name="manager">Manager</string>
<string name="loading">Lädt…</string>
<string name="update">Update</string>
<string name="loading">Laden…</string>
<string name="update">Aktualisieren</string>
<string name="not_available">N/A</string>
<string name="hide">Verstecken</string>
<string name="status">Status</string>
<string name="home_package">Paket</string>
<string name="home_app_title">App</string>
<string name="home_notice_content">Benutzen Sie nur den quelloffenen Magisk Manager. Magisk von unbekannten Quellen kann zu schädlichen Aktionen führen.</string>
<string name="home_notice_content">Laden sie Magisk NUR von der offiziellen Github Seite herunter. Dateien aus unbekannten Quellen können bösartig sein!</string>
<string name="home_support_title">Unterstützen Sie uns</string>
<string name="home_item_source">Quelle</string>
<string name="home_support_content">Magisk ist und wird immer frei und quelloffen sein. Sie können uns jedoch jederzeit mit einer kleinen Spende unterstützen.</string>
<string name="home_status_normal">Normal</string>
<string name="home_status_stub">Stub</string>
<string name="home_installed_version">Installiert</string>
<string name="home_latest_version">Neueste</string>
<string name="invalid_update_channel">Ungültiger Update-Kanal</string>
<string name="uninstall_magisk_title">Deinstalliere Magisk</string>
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt!\nRoot wird entfernt!\nIhre Daten werden potentiell verschlüsselt, wenn sie nicht bereits verschlüsselt sind!</string>
<string name="home_check_safetynet">SafetyNet überprüfen</string>
<!--Install-->
<string name="keep_force_encryption">Erzwungene Verschlüsselung beibehalten</string>
@@ -40,10 +38,9 @@
<string name="recovery_mode">Recovery Modus</string>
<string name="install_options_title">Optionen</string>
<string name="install_method_title">Methode</string>
<string name="install_next">Nächste</string>
<string name="install_next">Nächster Schritt</string>
<string name="install_start">Los geht\'s</string>
<string name="manager_download_install">Tippen zum Herunterladen und Installieren</string>
<string name="download_zip_only">Nur Zip herunterladen</string>
<string name="direct_install">Direkte Installation (empfohlen)</string>
<string name="install_inactive_slot">Installiere in inaktiven Slot (Nach OTA)</string>
<string name="install_inactive_slot_msg">Ihr Gerät wird gezwungen, nach einem Neustart in den inaktiven Slot zu booten! Verwenden Sie diese Option nur, wenn OTA abgeschlossen ist.\nWeiter?</string>
@@ -51,9 +48,11 @@
<string name="select_patch_file">Eine Datei auswählen und patchen</string>
<string name="patch_file_msg">Raw Image (*.img) oder ein ODIN tarfile (*.tar) auswählen</string>
<string name="reboot_delay_toast">Neustart in 5 Sekunden…</string>
<string name="flash_screen_title">Installieren</string>
<!--Superuser-->
<string name="su_request_title">Superuser-Anfrage</string>
<string name="touch_filtered_warning">Weil eine App eine Superuser-Anfrage verdeckt, kann Magisk Ihre Antwort nicht verifizieren</string>
<string name="deny">Verweigern</string>
<string name="prompt">Fragen</string>
<string name="grant">Gewähren</string>
@@ -68,10 +67,10 @@
<string name="su_deny_toast">%1$s wurden Superuser-Rechte verweigert</string>
<string name="su_snack_grant">Superuser-Rechte für %1$s gewährt</string>
<string name="su_snack_deny">Superuser-Rechte für %1$s verweigert</string>
<string name="su_snack_notif_on">Benachrichtigungen von %1$s sind aktiviert</string>
<string name="su_snack_notif_off">Benachrichtigungen von %1$s sind deaktiviert</string>
<string name="su_snack_log_on">Protokollierung von %1$s ist aktiviert</string>
<string name="su_snack_log_off">Protokollierung von %1$s ist deaktiviert</string>
<string name="su_snack_notif_on">Benachrichtigungen für %1$s sind an</string>
<string name="su_snack_notif_off">Benachrichtigungen für %1$s sind aus</string>
<string name="su_snack_log_on">Protokollierung für %1$s ist an</string>
<string name="su_snack_log_off">Protokollierung für %1$s ist aus</string>
<string name="su_revoke_title">Widerrufen?</string>
<string name="su_revoke_msg">Bestätigen, dass %1$s-Rechte widerufen werden?</string>
<string name="toast">Toast</string>
@@ -100,6 +99,7 @@
<!-- MagiskHide -->
<string name="show_system_app">System-Apps anzeigen</string>
<string name="show_os_app">OS-Apps anzeigen</string>
<string name="hide_filter_hint">Nach Namen filtern</string>
<string name="hide_scroll_up">Nach oben scrollen</string>
<string name="hide_filters">Filter</string>
@@ -107,6 +107,7 @@
<!--Module Fragment-->
<string name="no_info_provided">(Keine Informationen verfügbar)</string>
<string name="reboot_userspace">Sanfter Neustart</string>
<string name="reboot_recovery">Neustart zum Recovery</string>
<string name="reboot_bootloader">Neustart zum Bootloader</string>
<string name="reboot_download">Neustart zum Download</string>
@@ -119,8 +120,8 @@
<string name="module_action_install_external">Aus dem Speicher installieren</string>
<string name="update_available">Update verfügbar</string>
<string name="module_installed">@string/home_installed_version</string>
<string name="module_section_online">Online</string>
<string name="sorting_order">Sortierreihenfolge</string>
<string name="external_rw_permission_denied">Gewähre den Speicher-Zugriff, um diese Funtion zu aktivieren.</string>
<!--Settings -->
<string name="settings_dark_mode_title">Themen-Modus</string>
@@ -132,12 +133,12 @@
<string name="settings_download_path_message">Dateien werden in %1$s gespeichert</string>
<string name="settings_clear_cache_title">Repo-Cache leeren</string>
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informationen des Online-Repos. Erzwingt eine Aktualisierung</string>
<string name="settings_hide_manager_title">Magisk Manager verbergen</string>
<string name="settings_hide_manager_summary">Magisk Manager mit zufälligem Paketnamen neu packen</string>
<string name="settings_restore_manager_title">Magisk Manager wiederherstellen</string>
<string name="settings_restore_manager_summary">Magisk Manager mit ursprünglichem Paketnamen wiederherstellen</string>
<string name="settings_hide_app_title">Verstecke die Magisk App</string>
<string name="settings_hide_app_summary">Installiere eine Proxy-App mit zufälliger Paket-ID und benutzerdefiniertem App-Label</string>
<string name="settings_restore_app_title">Stelle die Magisk App wiederher</string>
<string name="settings_restore_app_summary">Blende die App wieder ein und stelle die original APK wieder her</string>
<string name="language">Sprache</string>
<string name="system_default">(Systemstandard)</string>
<string name="system_default">Systemstandard</string>
<string name="settings_check_update_title">Auf Aktualisierungen prüfen</string>
<string name="settings_check_update_summary">Regelmäßig im Hintergrund auf Aktualisierungen prüfen</string>
<string name="settings_update_channel_title">Aktualisierungs-Kanal</string>
@@ -166,12 +167,17 @@
<string name="auto_response">Automatisch beantworten</string>
<string name="request_timeout">Zeitlimit für Anfrage</string>
<string name="superuser_notification">Superuser-Benachrichtigung</string>
<string name="settings_su_reauth_title">Nach Aktualisierung erneut authentifizieren</string>
<string name="settings_su_reauth_summary">Superuser-Zugriff nach App-Aktualisierung erneut abfragen</string>
<string name="settings_su_reauth_title">Nach Aktualisierung erneut zertifizieren</string>
<string name="settings_su_reauth_summary">Superuser-Berechtigungen nach App-Aktualisierung erneut authentifizieren</string>
<string name="settings_su_tapjack_title">Tapjacking-Schutz aktivieren</string>
<string name="settings_su_tapjack_summary">Das Dialogfeld der Superuser-Eingabeaufforderung reagiert nicht auf Eingaben, wenn es durch ein anderes Fenster oder Überlagerung verdeckt wird</string>
<string name="settings_su_biometric_title">Biometrische Authentifizierung aktivieren</string>
<string name="settings_su_biometric_summary">Biometrische Authentifizierung verwenden, um Superuser-Anfragen zu ermöglichen</string>
<string name="no_biometric">Nicht unterstütztes Gerät oder keine biometrischen Einstellungen aktiviert</string>
<string name="settings_su_biometric_summary">Biometrie verwenden, um Superuser-Anfragen zuzulassen</string>
<string name="no_biometric">Gerät unterstützt keine biometrischen Daten oder ist nicht mit diesen konfiguriert</string>
<string name="settings_customization">Personalisierung</string>
<string name="setting_add_shortcut_summary">Hinzufügen einer hübschen Startbildschirm-Verknüpfung, falls der Name und das Symbol nach dem Ausblenden der App schwer zu erkennen sind</string>
<string name="settings_doh_title">DNS über HTTPS</string>
<string name="settings_doh_description">Umgehung des DNS-Poisoning in einigen Ländern</string>
<string name="multiuser_mode">Mehrbenutzermodus</string>
<string name="settings_owner_only">Nur der Gerätebesitzer</string>
@@ -194,10 +200,7 @@
<string name="progress_channel">Fortschrittsmitteilungen</string>
<string name="download_complete">Download abgeschlossen</string>
<string name="download_file_error">Fehler beim herunterladen der Datei</string>
<string name="download_open_parent">Im übergeordneten Ordner anzeigen</string>
<string name="download_open_self">Datei anzeigen</string>
<string name="magisk_update_title">Magisk Update verfügbar!</string>
<string name="manager_update_title">Magisk Manager Update verfügbar!</string>
<!--Toasts, Dialogs-->
<string name="yes">Ja</string>
@@ -211,8 +214,7 @@
<string name="flashing">Flashen…</string>
<string name="done">Fertig!</string>
<string name="failure">Fehlgeschlagen</string>
<string name="hide_manager_title">Magisk Manager ausblenden</string>
<string name="hide_manager_fail_toast">Magisk Manager ausblenden fehlgeschlagen.</string>
<string name="hide_app_title">Verstecke die Magisk App</string>
<string name="open_link_failed_toast">Keine App zum öffnen des Links gefunden</string>
<string name="complete_uninstall">Vollständig deinstallieren</string>
<string name="restore_img">Image wiederherstellen</string>
@@ -220,13 +222,17 @@
<string name="restore_done">Wiederherstellen fertig!</string>
<string name="restore_fail">Stock-Sicherung existiert nicht!</string>
<string name="proprietary_title">Proprietären Code herunterladen</string>
<string name="proprietary_notice">Magisk Manager ist FOSS und enthält nicht den proprietären SafetyNet-API-Code von Google.\n\nWollen Sie Magisk Manager erlauben, eine Erweiterung (enthält GoogleApiClient) für SafetyNet-Prüfungen herunterzuladen?</string>
<string name="proprietary_notice">Magisk ist FOSS und enthält keinen Code für Google\'s proprietäre SafetyNet API.\n\nWillst du eine proprietäre Erweiterung für Safetynet Überprüfungen herunterladen?</string>
<string name="setup_fail">Setup fehlgeschlagen</string>
<string name="env_fix_title">Zusätzliche Einrichtung erforderlich</string>
<string name="env_fix_msg">Ihr Gerät muss zusätzlich eingerichtet werden, damit Magisk ordnungsgemäß funktioniert. Es wird das Magisk Setup-Zip herunterladen, möchten Sie jetzt fortfahren?</string>
<string name="env_fix_msg">Magisk braucht ein zusätzliches Setup, um auf deinem Gerät zu funktionieren. Willst du fortfahren und dein Gerät neustarten?</string>
<string name="setup_msg">Laufzeitumgebung einrichten…</string>
<string name="authenticate">Authentifizieren</string>
<string name="unsupport_magisk_title">Nicht unterstützte Magisk-Version</string>
<string name="unsupport_magisk_msg">Diese Version von Magisk Manager unterstützt keine Magisk-Version kleiner als %1$s.\n\nDie App verhält sich, als ob kein Magisk installiert wäre. Bitte Magisk so schnell wie möglich aktualisieren.</string>
<string name="unsupport_magisk_msg">Diese Version der App unterstütz keine Magisk version niedriger, als %1$s.\n\nDie App wird sich verhalten, als wäre Magisk nicht installiert, bitte aktualisiere Magisk so schnell, wie möglich.</string>
<string name="external_rw_permission_denied">Speichererlaubnis erteilen, um diese Funktion zu aktivieren</string>
<string name="add_shortcut_title">Verknüpfung zum Startbildschirm hinzufügen</string>
<string name="add_shortcut_msg">Nachdem du diese App versteckst, wird der Name und das Symbol eventuell schwer zu erkennen sein. Möchten sie zum Startbildschirm eine schöne Verknüpfung hinzufügen?</string>
<string name="app_not_found">Keine App gefunden, die diese Aktion verarbeitet</string>
</resources>

View File

@@ -1,123 +1,63 @@
<resources>
<!--Welcome Activity-->
<string name="modules">Modules</string>
<!--Sections-->
<string name="modules">Επεκτάσεις</string>
<string name="superuser">Υπερχρήστης</string>
<string name="logs">Αρχείο Καταγραφής</string>
<string name="settings">Ρυθμίσεις</string>
<string name="refresh">Ανανέωση τοπικών δεδομένων</string>
<string name="install">Εγκατάσταση</string>
<string name="section_home">Αρχική</string>
<string name="section_theme">Θέματα</string>
<string name="safetynet">SafetyNet</string>
<!--Status Fragment-->
<string name="invalid_update_channel">Λανθασμένο κανάλι ενημέρωσης</string>
<string name="safetynet_api_error">Σφάλμα του SafetyNet API</string>
<string name="safetynet_res_invalid">Η απόκριση είναι άκυρη</string>
<!--Install Fragment-->
<string name="keep_force_encryption">Διατήρηση επιβεβλημένης κρυπτογράφησης</string>
<string name="keep_dm_verity">Διατήρηση dm-verity</string>
<string name="uninstall_magisk_title">Απεγκατάσταση Magisk</string>
<string name="uninstall_magisk_msg">Όλα τα modules θα απενεργοποιηθούν/αφαιρεθούν. Το root θα αφαιρεθεί και ενδέχεται να κρυπτογραφηθούν τα δεδομένα σας, εάν δεν είναι κρυπτογραφημένα</string>
<string name="update">Ενημέρωση</string>
<!--Module Fragment-->
<string name="no_info_provided">(Δεν δόθηκαν πληροφορίες)</string>
<string name="reboot_recovery">Επανεκκίνηση στο Recovery</string>
<string name="reboot_bootloader">Επανεκκίνηση στο Bootloader</string>
<string name="reboot_download">Επανεκκίνηση για λήψη</string>
<!--Repo Fragment-->
<string name="update_available">Διαθέσιμη Ενημέρωση</string>
<string name="home_installed_version">Εγκαταστάθηκε</string>
<string name="sorting_order">Ταξινόμηση κατά</string>
<!--Log Fragment-->
<string name="menuSaveLog">"Αποθήκευση καταγραφής "</string>
<string name="menuClearLog">Εκκαθάριση αρχείου καταγραφής τώρα</string>
<string name="logs_cleared">Το αρχείο καταγραφής εκκαθαρίστηκε επιτυχώς</string>
<!--About Activity-->
<!--Home-->
<string name="no_connection">Δεν υπάρχει διαθέσιμη σύνδεση</string>
<string name="app_changelog">Καταγραφή αλλαγών εφαρμογής</string>
<string name="loading">Φόρτωση…</string>
<string name="update">Ενημέρωση</string>
<string name="not_available">Μη διαθέσιμο</string>
<string name="hide">Απόκρυψη</string>
<string name="home_package">Πακέτο</string>
<string name="home_app_title">Εφαρμογή</string>
<!--Toasts, Dialogs-->
<string name="repo_install_title">Εγκατάσταση %1$s</string>
<string name="repo_install_msg">Θέλετε να εγκαταστήσετε το %1$s τώρα;</string>
<string name="download">Λήψη</string>
<string name="reboot">Επανεκκίνηση</string>
<string name="magisk_update_title">Νέα Ενημέρωση Magisk Διαθέσιμη!</string>
<string name="release_notes">Σημειώσεις έκδοσης</string>
<string name="repo_cache_cleared">Η Repo cache καθαρίστηκε</string>
<string name="manager_update_title">Νέα Ενημέρωση Magisk Manager Διαθέσιμη!</string>
<string name="home_notice_content">Κάντε λήψη του Magisk ΜΟΝΟ από την επίσημη σελίδα του GitHub. Τα αρχεία από άγνωστες πηγές μπορεί να είναι κακόβουλα!</string>
<string name="home_support_title">Υποστήριξη</string>
<string name="home_item_source">Πηγή</string>
<string name="home_support_content">Το Magisk είναι, και θα είναι για πάντα, δωρεάν και ανοιχτού κώδικα. Μπορείτε ωστόσο να μας δείξετε ότι ενδιαφέρεστε στέλνοντας μια μικρή δωρεά.</string>
<string name="home_installed_version">Τρέχουσα έκδοση</string>
<string name="home_latest_version">Διαθέσιμη έκδοση</string>
<string name="invalid_update_channel">Λανθασμένο κανάλι ενημέρωσης</string>
<string name="uninstall_magisk_title">Απεγκατάσταση Magisk</string>
<string name="uninstall_magisk_msg">Όλα τα modules θα απενεργοποιηθούν/αφαιρεθούν. Το root θα αφαιρεθεί και ενδέχεται να κρυπτογραφηθούν τα δεδομένα σας, εάν δεν είναι κρυπτογραφημένα!</string>
<string name="home_check_safetynet">Έλεγχος SafetyNet</string>
<!--Install-->
<string name="keep_force_encryption">Διατήρηση επιβεβλημένης κρυπτογράφησης</string>
<string name="keep_dm_verity">Διατήρηση AVB 2.0/dm-verity</string>
<string name="recovery_mode">Μέσω Recovery mode</string>
<string name="install_options_title">Επιλογές</string>
<string name="install_method_title">Μέθοδος</string>
<string name="install_next">Επόμενο</string>
<string name="install_start">Εκκίνηση</string>
<string name="manager_download_install">Πιέστε για λήψη και εγκατάσταση</string>
<string name="update_channel">Ενημερώσεις Magisk</string>
<string name="flashing">Γίνεται flash</string>
<string name="hide_manager_title">Κρύβοντας το Magisk Manager…</string>
<string name="hide_manager_fail_toast">Η απόκρυψη του Magisk Manager απέτυχε…</string>
<string name="download_zip_only">Λήψη Zip Μόνο</string>
<string name="direct_install">Απευθείας Εγκατάσταση (Προτείνεται)</string>
<string name="complete_uninstall">Πλήρης απεγκατάσταση</string>
<string name="restore_done">Η ανάκτηση έγινε!</string>
<string name="restore_fail">Δεν υπάρχει αντίγραφο ασφαλείας!</string>
<string name="proprietary_title">Λήψη Ιδιόκτητου Κώδικα</string>
<string name="proprietary_notice">Το Magisk Manager είναι FOSS οπότε δεν περιέχει της Google τον ιδιόκτητο κώδικα του SafetyNet API.\n\nΕπιτρέπετε στο Magisk Manager να κατεβάσει μια επέκταση (περιέχει το GoogleApiClient) για ελέγχους του SafetyNet?</string>
<string name="download_file_error">Σφάλμα στη λήψη του αρχείου</string>
<string name="install_inactive_slot">Εγκατάσταση στο ανενεργό Slot (Μετά από OTA)</string>
<string name="install_inactive_slot_msg">Η συσκευή σας θα αναγκαστεί να εκκινήσει στο τρέχον ανενεργό Slot μετά από επανεκκίνηση!\nΧρησιμοποιήστε αυτήν την επιλογή μόνο αφού ολοκληρωθεί το OTA.\nΣυνέχεια;</string>
<string name="setup_title">Πρόσθετη εγκατάσταση</string>
<string name="select_patch_file">Επέλεξε και κάνε Patch ένα αρχείο</string>
<string name="patch_file_msg">Επέλεξε μία raw εικόνα (*.img) ή ένα ODIN tarfile (*.tar)</string>
<string name="reboot_delay_toast">Επανεκκίνηση σε 5 δευτερόλεπτα…</string>
<string name="flash_screen_title">Εγκατάσταση</string>
<!--Settings Activity -->
<string name="settings_clear_cache_title">Εκκαθάριση προσωρινής μνήμης αποθετηρίων</string>
<string name="settings_clear_cache_summary">Καθαρίζει τις κρυφές πληροφορίες για απευθείας συνδεδεμένα αποθετήρια, αναγκάζει την εφαρμογή να κάνει ανανέωση σε απευθείας σύνδεση</string>
<string name="settings_hide_manager_title">Απόκρυψη του Magisk Manager</string>
<string name="settings_hide_manager_summary">Ανασυγκρότηση του Magisk Manager με τυχαίο όνομα πακέτου</string>
<string name="language">Γλώσσα</string>
<string name="system_default">(Προεπιλογή Συστήματος)</string>
<string name="settings_update_channel_title">Κανάλι Ενημερώσεων</string>
<string name="settings_update_stable">Σταθερό</string>
<string name="settings_update_beta">Δοκιμαστικό</string>
<string name="settings_update_custom">Custom</string>
<string name="settings_update_custom_msg">Εισαγωγή ενός custom URL</string>
<string name="settings_magiskhide_summary">Κρύβει το Magisk από διάφορες ανιχνεύσεις</string>
<string name="settings_hosts_title">Systemless hosts</string>
<string name="settings_hosts_summary">Υποστήριξη Systemless hosts για εφαρμογές Adblock</string>
<string name="settings_su_app_adb">Εφαρμογές και ADB</string>
<string name="settings_su_app">Εφαρμογές μόνο</string>
<string name="settings_su_adb">ADB μόνο</string>
<string name="settings_su_disable">Απενεργοποιημένο</string>
<string name="settings_su_request_10">10 δευτερόλεπτα</string>
<string name="settings_su_request_15">15 δευτερόλεπτα</string>
<string name="settings_su_request_20">20 δευτερόλεπτα</string>
<string name="settings_su_request_30">30 δευτερόλεπτα</string>
<string name="settings_su_request_45">45 δευτερόλεπτα</string>
<string name="settings_su_request_60">60 δευτερόλεπτα</string>
<string name="superuser_access">Πρόσβαση Υπερχρήστη</string>
<string name="auto_response">Αυτόματη Απόκριση</string>
<string name="request_timeout">Χρονικό όριο Αιτήματος</string>
<string name="superuser_notification">Ειδοποίηση Υπερχρήστη</string>
<string name="settings_su_reauth_title">Επαναπιστοποίηση μετά από αναβάθμιση</string>
<string name="settings_su_reauth_summary">Επαναπιστοποίηση αδειών υπερχρήστη μετά την αναβάθμιση μίας εφαρμογής</string>
<string name="multiuser_mode">Λειτουργία Πολλών Χρηστών</string>
<string name="settings_owner_only">Μόνο Ιδιοκτήτης Συσκευής</string>
<string name="settings_owner_manage">Διαχειριζόμενη από τον Ιδιοκτήτη</string>
<string name="settings_user_independent">Ανεξάρτητη από τον χρήστη</string>
<string name="owner_only_summary">Μόνο ο ιδιοκτήτης έχει πρόσβαση root</string>
<string name="owner_manage_summary">Μόνο ο ιδιοκτήτης μπορεί να διαχειριστεί την πρόσβαση root και να δεχτεί προτροπές αίτημάτων</string>
<string name="user_indepenent_summary">Κάθε χρήστης έχει τους δικούς του ξεχωριστούς κανόνες root</string>
<string name="mount_namespace_mode">Λειτουργία προσάρτησης χώρου ονομάτων</string>
<string name="settings_ns_global">Καθολικός Χώρος Ονομάτων</string>
<string name="settings_ns_requester">Κληρονόμησε Χώρο Ονομάτων</string>
<string name="settings_ns_isolate">Απομονωμένος Χώρος Ονομάτων</string>
<string name="global_summary">Όλες οι συνεδρίες root χρησιμοποιούν τον καθολικό χώρο oνομάτων προσάρτησης</string>
<string name="requester_summary">Οι συνεδρίες root θα κληρονομούν το χώρο ονομάτων του αιτούντα τους</string>
<string name="isolate_summary">Κάθε συνεδρία root θα έχει το δικό της απομονωμένο χώρο ονομάτων</string>
<!--Superuser-->
<!--Superuser-->
<string name="su_request_title">Αίτημα υπερχρήστη</string>
<string name="touch_filtered_warning">Επειδή μια εφαρμογή αποκρύπτει ένα αίτημα υπερχρήστη, το Magisk δεν μπορεί να επαληθεύσει την απάντησή σας</string>
<string name="deny">Άρνηση</string>
<string name="prompt">Προτροπή</string>
<string name="grant">Αποδοχή</string>
<string name="su_warning">Δίνει πλήρη πρόσβαση στη συσκευή σας.\nΑρνηθείτε αν δεν είστε σίγουρος/η!</string>
<string name="forever">Πάντα</string>
<string name="forever">Μόνιμα</string>
<string name="once">Μία φορά</string>
<string name="tenmin">10 λεπτά</string>
<string name="twentymin">20 λεπτά</string>
@@ -136,6 +76,163 @@
<string name="toast">Αναδυόμενο παράθυρο</string>
<string name="none">Κανένα</string>
<!--Superuser logs-->
<string name="superuser_toggle_notification">Ειδοποιήσεις</string>
<string name="superuser_toggle_revoke">Ανακάλεσε</string>
<string name="superuser_policy_none">Καμία εφαρμογή δεν έχει ζητήσει άδεια υπερχρήστη.</string>
<!--Logs-->
<string name="log_data_none">Δεν υπάρχουν αρχεία καταγραφής, δοκιμάστε να χρησιμοποιήσετε περισσότερο τις εφαρμογές με δυνατότητα SU</string>
<string name="log_data_magisk_none">Τα αρχεία καταγραφής του Magisk είναι κενά, αυτό είναι περίεργο</string>
<string name="menuSaveLog">Αποθήκευση καταγραφής</string>
<string name="menuClearLog">Εκκαθάριση αρχείου καταγραφής</string>
<string name="logs_cleared">Το αρχείο καταγραφής εκκαθαρίστηκε επιτυχώς</string>
<string name="pid">PID: %1$d</string>
<string name="target_uid">UID Στόχος: %1$d</string>
</resources>
<!--SafetyNet-->
<string name="safetynet_api_error">Σφάλμα του SafetyNet API</string>
<string name="safetynet_res_invalid">Η απόκριση είναι άκυρη</string>
<string name="safetynet_attest_success">Η πιστοποίηση ήταν επιτυχής!</string>
<string name="safetynet_attest_failure">Η πιστοποίηση απέτυχε!</string>
<string name="safetynet_attest_loading">Μισό λεπτό…</string>
<string name="safetynet_attest_restart">Προσπάθησε ξανά</string>
<!--MagiskHide-->
<string name="show_system_app">Εμφάνιση εφαρμογών συστήματος</string>
<string name="show_os_app">Εμφάνιση εφαρμογών OS</string>
<string name="hide_filter_hint">Φιλτράρισμα</string>
<string name="hide_scroll_up">Μετακίνηση</string>
<string name="hide_filters">Φίλτρα</string>
<string name="hide_search">Αναζήτηση</string>
<!--Module-->
<string name="no_info_provided">(Δεν δόθηκαν πληροφορίες)</string>
<string name="reboot_userspace">Soft επανεκκίνηση</string>
<string name="reboot_recovery">Επανεκκίνηση στο Recovery</string>
<string name="reboot_bootloader">Επανεκκίνηση στο Bootloader</string>
<string name="reboot_download">Επανεκκίνηση για λήψη</string>
<string name="reboot_edl">Επανεκκίνηση σε EDL</string>
<string name="module_version_author">%1$s από %2$s</string>
<string name="module_section_pending">Ενημερώσεις</string>
<string name="module_section_pending_action">Ενημέρωση όλων</string>
<string name="module_state_remove">Αφαίρεση</string>
<string name="module_state_restore">Επαναφορά</string>
<string name="module_action_install_external">Εγκατάσταση από χώρο αποθήκευσης</string>
<string name="update_available">Διαθέσιμη Ενημέρωση</string>
<string name="module_installed">@string/home_installed_version</string>
<string name="module_section_online">Online</string>
<string name="sorting_order">Ταξινόμηση κατά</string>
<!--Settings-->
<string name="settings_dark_mode_title">Λειτουργία θέματος</string>
<string name="settings_dark_mode_message">Επιλέξτε τη λειτουργία που ταιριάζει καλύτερα στο στυλ σας!</string>
<string name="settings_dark_mode_light">Πάντα Light</string>
<string name="settings_dark_mode_system">Ακολούθησε το σύστημα</string>
<string name="settings_dark_mode_dark">Πάντα Dark</string>
<string name="settings_download_path_title">Διαδρομή λήψης</string>
<string name="settings_download_path_message">Τα αρχεία θα αποθηκευτούν στο %1$s</string>
<string name="settings_clear_cache_title">Εκκαθάριση προσωρινής μνήμης αποθετηρίων</string>
<string name="settings_clear_cache_summary">Καθαρίζει τις κρυφές πληροφορίες για απευθείας συνδεδεμένα αποθετήρια, αναγκάζει την εφαρμογή να κάνει ανανέωση σε απευθείας σύνδεση</string>
<string name="settings_hide_app_title">Απόκρυψη της εφαρμογής Magisk</string>
<string name="settings_hide_app_summary">Εγκαταστήστε μια εφαρμογή με τυχαίο αναγνωριστικό πακέτου και προσαρμοσμένη ετικέτα εφαρμογής</string>
<string name="settings_restore_app_title">Επαναφέρετε την εφαρμογή Magisk</string>
<string name="settings_restore_app_summary">Καταργήστε την απόκρυψη της εφαρμογής και επαναφέρετέ την στο αρχικό APK</string>
<string name="language">Γλώσσα</string>
<string name="system_default">(Προεπιλογή Συστήματος)</string>
<string name="settings_check_update_title">Έλεγχος Ενημερώσεων</string>
<string name="settings_check_update_summary">Περιοδικά ελέγχετε για ενημερώσεις στο παρασκήνιο</string>
<string name="settings_update_channel_title">Κανάλι Ενημερώσεων</string>
<string name="settings_update_stable">Σταθερό</string>
<string name="settings_update_beta">Δοκιμαστικό</string>
<string name="settings_update_custom">Προσαρμοσμένο</string>
<string name="settings_update_custom_msg">Εισαγωγή ενός custom URL</string>
<string name="settings_magiskhide_summary">Κρύβει το Magisk από διάφορες ανιχνεύσεις</string>
<string name="settings_hosts_title">Systemless hosts</string>
<string name="settings_hosts_summary">Υποστήριξη Systemless hosts για εφαρμογές Adblock</string>
<string name="settings_hosts_toast">Προσθήκη αρθρώματος systemless hosts</string>
<string name="settings_app_name_hint">Νέο όνομα</string>
<string name="settings_app_name_helper">Η εφαρμογή θα επανασυσκευαστεί με αυτό το όνομα</string>
<string name="settings_app_name_error">Μη έγκυρη μορφή</string>
<string name="settings_su_app_adb">Εφαρμογές και ADB</string>
<string name="settings_su_app">Εφαρμογές μόνο</string>
<string name="settings_su_adb">ADB μόνο</string>
<string name="settings_su_disable">Απενεργοποιημένο</string>
<string name="settings_su_request_10">10 δευτερόλεπτα</string>
<string name="settings_su_request_15">15 δευτερόλεπτα</string>
<string name="settings_su_request_20">20 δευτερόλεπτα</string>
<string name="settings_su_request_30">30 δευτερόλεπτα</string>
<string name="settings_su_request_45">45 δευτερόλεπτα</string>
<string name="settings_su_request_60">60 δευτερόλεπτα</string>
<string name="superuser_access">Πρόσβαση Υπερχρήστη</string>
<string name="auto_response">Αυτόματη Απόκριση</string>
<string name="request_timeout">Χρονικό όριο Αιτήματος</string>
<string name="superuser_notification">Ειδοποίηση Υπερχρήστη</string>
<string name="settings_su_reauth_title">Επαναπιστοποίηση μετά από αναβάθμιση</string>
<string name="settings_su_reauth_summary">Επαναπιστοποίηση αδειών υπερχρήστη μετά την αναβάθμιση μίας εφαρμογής</string>
<string name="settings_su_tapjack_title">Ενεργοποίηση προστασίας Tapjacking</string>
<string name="settings_su_tapjack_summary">Το παράθυρο διαλόγου προτροπής του υπερχρήστη δεν αποκρίνεται στην είσοδο ενώ κρύβεται από οποιοδήποτε άλλο παράθυρο</string>
<string name="settings_su_biometric_title">Ενεργοποίηση βιομετρικού ελέγχου ταυτότητας</string>
<string name="settings_su_biometric_summary">Χρησιμοποιήστε βιομετρικό έλεγχο ταυτότητας για να επιτρέψετε αιτήματα υπερχρηστών</string>
<string name="no_biometric">Δεν υποστηρίζεται η συσκευή ή δεν υπάρχει καμία βιομετρική ρύθμιση</string>
<string name="settings_customization">Προσαρμογή</string>
<string name="setting_add_shortcut_summary">Προσθέστε μια όμορφη συντόμευση στην αρχική οθόνη σε περίπτωση που το όνομα και το εικονίδιο είναι δύσκολο να αναγνωριστούν αφού κρύψετε την εφαρμογή</string>
<string name="settings_doh_title">DNS μέσω HTTPS</string>
<string name="settings_doh_description">Λύση DNS poisoning για μερικά έθνη</string>
<string name="multiuser_mode">Λειτουργία Πολλών Χρηστών</string>
<string name="settings_owner_only">Μόνο Ιδιοκτήτης Συσκευής</string>
<string name="settings_owner_manage">Διαχειριζόμενη από τον Ιδιοκτήτη</string>
<string name="settings_user_independent">Ανεξάρτητη από τον χρήστη</string>
<string name="owner_only_summary">Μόνο ο ιδιοκτήτης έχει πρόσβαση root</string>
<string name="owner_manage_summary">Μόνο ο ιδιοκτήτης μπορεί να διαχειριστεί την πρόσβαση root και να δεχτεί προτροπές αίτημάτων</string>
<string name="user_indepenent_summary">Κάθε χρήστης έχει τους δικούς του ξεχωριστούς κανόνες root</string>
<string name="mount_namespace_mode">Λειτουργία προσάρτησης χώρου ονομάτων</string>
<string name="settings_ns_global">Καθολικός Χώρος Ονομάτων</string>
<string name="settings_ns_requester">Κληρονόμησε Χώρο Ονομάτων</string>
<string name="settings_ns_isolate">Απομονωμένος Χώρος Ονομάτων</string>
<string name="global_summary">Όλες οι συνεδρίες root χρησιμοποιούν τον καθολικό χώρο oνομάτων προσάρτησης</string>
<string name="requester_summary">Οι συνεδρίες root θα κληρονομούν το χώρο ονομάτων του αιτούντα τους</string>
<string name="isolate_summary">Κάθε συνεδρία root θα έχει το δικό της απομονωμένο χώρο ονομάτων</string>
<!--Notifications-->
<string name="update_channel">Ενημερώσεις Magisk</string>
<string name="progress_channel">Ειδοποιήσεις προόδου</string>
<string name="download_complete">Η λήψη ολοκληρώθηκε</string>
<string name="download_file_error">Σφάλμα στη λήψη του αρχείου</string>
<string name="magisk_update_title">Νέα Ενημέρωση Magisk Διαθέσιμη!</string>
<!--Toasts, Dialogs-->
<string name="yes">Ναι</string>
<string name="no">Όχι</string>
<string name="repo_install_title">Εγκατάσταση %1$s</string>
<string name="repo_install_msg">Θέλετε να εγκαταστήσετε το %1$s τώρα;</string>
<string name="download">Λήψη</string>
<string name="reboot">Επανεκκίνηση</string>
<string name="release_notes">Σημειώσεις έκδοσης</string>
<string name="repo_cache_cleared">Πραγματοποιήθηκε εκκαθάριση της προσωρινής μνήμης του Repository</string>
<string name="flashing">Προγραμματισμός σε εξέλιξη</string>
<string name="done">Ολοκληρώθηκε</string>
<string name="failure">Αποτυχία</string>
<string name="hide_app_title">Απόκρυψη της εφαρμογής Magisk…</string>
<string name="open_link_failed_toast">Δεν βρέθηκε εφαρμογή για άνοιγμα του συνδέσμου</string>
<string name="complete_uninstall">Πλήρης απεγκατάσταση</string>
<string name="restore_img">Επαναφορά Images</string>
<string name="restore_img_msg">Επαναφορά…</string>
<string name="restore_done">Η επαναφορά πραγματοποιήθηκε</string>
<string name="restore_fail">Δεν υπάρχει αντίγραφο ασφαλείας!</string>
<string name="proprietary_title">Λήψη Ιδιόκτητου Κώδικα</string>
<string name="proprietary_notice">Το Magisk είναι FOSS και δεν περιλαμβάνει κώδικα για το ιδιόκτητο SafetyNet API της Google.\n\nΘέλετε να κατεβάσετε μια ιδιόκτητη επέκταση για ελέγχους SafetyNet;</string>
<string name="setup_fail">Η εγκατάσταση απέτυχε</string>
<string name="env_fix_title">Απαιτείται πρόσθετη εγκατάσταση</string>
<string name="env_fix_msg">Η συσκευή σας χρειάζεται πρόσθετη ρύθμιση για να λειτουργεί σωστά το Magisk. Θέλετε να συνεχίσετε και να επανεκκινήσετε;</string>
<string name="setup_msg">Τρέχει η εγκατάσταση περιβάλλοντος…</string>
<string name="authenticate">Αυθεντικοποίηση</string>
<string name="unsupport_magisk_title">Μη υποστηριζόμενη έκδοση Magisk</string>
<string name="unsupport_magisk_msg">Αυτή η έκδοση της εφαρμογής δεν υποστηρίζει έκδοση Magisk χαμηλότερη από %1$s.\n\nΗ εφαρμογή θα συμπεριφέρεται σαν να μην έχει εγκατασταθεί το Magisk, αναβαθμίστε το Magisk το συντομότερο δυνατό.</string>
<string name="external_rw_permission_denied">Παραχωρήστε άδεια αποθήκευσης για να ενεργοποιήσετε αυτήν τη λειτουργία</string>
<string name="add_shortcut_title">Προσθέστε συντόμευση στην αρχική οθόνη</string>
<string name="add_shortcut_msg">Μετά την απόκρυψη αυτής της εφαρμογής, το όνομα και το εικονίδιο της ενδέχεται να είναι δύσκολο να αναγνωριστούν. Θέλετε να προσθέσετε μια όμορφη συντόμευση στην αρχική οθόνη;</string>
<string name="app_not_found">Δεν βρέθηκε εφαρμογή που να χειρίζεται αυτήν την ενέργεια</string>
</resources>

View File

@@ -14,20 +14,17 @@
<!--Home-->
<string name="no_connection">Conexión no disponible</string>
<string name="app_changelog">Registro de cambios</string>
<string name="manager">Manager</string>
<string name="loading">Cargando…</string>
<string name="update">Actualizar</string>
<string name="not_available">No disponible</string>
<string name="hide">Ocultar</string>
<string name="status">Estado</string>
<string name="home_package">Paquete</string>
<string name="home_app_title">App</string>
<string name="home_notice_content">Siempre asegúrate de que estás usando el software de código abierto Magisk Manager. Un Manager de fuente desconocida puede realizar acciones maliciosas.</string>
<string name="home_notice_content">Descarga Magisk SÓLO de la página de GitHub oficial. Archivos de fuentes desconocidas pueden resultar maliciosos!</string>
<string name="home_support_title">Apóyenos</string>
<string name="home_item_source">Código fuente</string>
<string name="home_support_content">Magisk es, y siempre será gratuito y de código abierto. Sin embargo, puede apoyarnos enviando una pequeña donación.</string>
<string name="home_status_normal">Normal</string>
<string name="home_status_stub">Stub</string>
<string name="home_installed_version">Instalado</string>
<string name="home_latest_version">Última</string>
<string name="invalid_update_channel">Canal de actualización inválido</string>
@@ -44,7 +41,6 @@
<string name="install_next">Siguiente</string>
<string name="install_start">Comencemos</string>
<string name="manager_download_install">Pulse para descargar e instalar</string>
<string name="download_zip_only">Descargar sólo el archivo ZIP</string>
<string name="direct_install">Instalación directa (recomendado)</string>
<string name="install_inactive_slot">Instalar en ranura inactiva (después de OTA)</string>
<string name="install_inactive_slot_msg">¡Su dispositivo será forzado a arrancar en la ranura inactiva actual después de un reinicio!\nSólo use esta opción después de que OTA haya terminado.\n¿Continuar?</string>
@@ -56,6 +52,7 @@
<!--Superuser-->
<string name="su_request_title">Petición de superusuario</string>
<string name="touch_filtered_warning">Como otra app está escondiendo la solicitud de superusuario, Magisk no puede verificar tu respuesta</string>
<string name="deny">Denegar</string>
<string name="prompt">Preguntar</string>
<string name="grant">Permitir</string>
@@ -101,7 +98,8 @@
<string name="safetynet_attest_restart">Inténtelo de nuevo</string>
<!-- MagiskHide -->
<string name="show_system_app">Mostrar sistema</string>
<string name="show_system_app">Mostrar apps del sistema</string>
<string name="show_os_app">Mostrar apps del OS</string>
<string name="hide_filter_hint">Filtrar por nombre</string>
<string name="hide_scroll_up">Subir</string>
<string name="hide_filters">Filtros</string>
@@ -109,6 +107,7 @@
<!--Module -->
<string name="no_info_provided">(No hay información)</string>
<string name="reboot_userspace">Reinicio suave</string>
<string name="reboot_recovery">Reiniciar en modo recovery</string>
<string name="reboot_bootloader">Reiniciar en modo bootloader</string>
<string name="reboot_download">Reiniciar en modo download</string>
@@ -134,10 +133,10 @@
<string name="settings_download_path_message">Los archivos se guardarán en %1$s</string>
<string name="settings_clear_cache_title">Limpiar caché del repositorio</string>
<string name="settings_clear_cache_summary">Limpiar la información en caché para los repositorios en línea, fuerza a la aplicación a actualizar en línea</string>
<string name="settings_hide_manager_title">Ocultar Magisk Manager</string>
<string name="settings_hide_manager_summary">Re-empaquetar Magisk Manager con un nombre de paquete al azar</string>
<string name="settings_restore_manager_title">Restaurar Magisk Manager</string>
<string name="settings_restore_manager_summary">Restaura Magisk Manager con el paquete original</string>
<string name="settings_hide_app_title">Esconder la app de Magisk</string>
<string name="settings_hide_app_summary">Instalar una nueva app proxy con una ID de paquete aleatoria y una etiqueta personalizada</string>
<string name="settings_restore_app_title">Restaurar la app de Magisk</string>
<string name="settings_restore_app_summary">Descubrir la app y restaurarla al APK original</string>
<string name="language">Idioma</string>
<string name="system_default">(Idioma del sistema)</string>
<string name="settings_check_update_title">Comprobar Actualizaciones</string>
@@ -201,10 +200,7 @@
<string name="progress_channel">Notificaciones de progreso</string>
<string name="download_complete">Descarga Completa</string>
<string name="download_file_error">Error descargando archivo</string>
<string name="download_open_parent">Mostrar en carpeta principal</string>
<string name="download_open_self">Mostrar archivo</string>
<string name="magisk_update_title">¡Actualización de Magisk disponible!</string>
<string name="manager_update_title">¡Actualización de Magisk Manager disponible!</string>
<!--Toasts, Dialogs-->
<string name="yes"></string>
@@ -218,8 +214,7 @@
<string name="flashing">Flasheando…</string>
<string name="done">¡Hecho!</string>
<string name="failure">Ha fallado</string>
<string name="hide_manager_title">Ocultando Magisk Manager</string>
<string name="hide_manager_fail_toast">La ocultación de Magisk Manager ha fallado…</string>
<string name="hide_app_title">Escondiendo la app de Magisk</string>
<string name="open_link_failed_toast">No se encontró ninguna aplicación para abrir el enlace…</string>
<string name="complete_uninstall">Desinstalación completa</string>
<string name="restore_img">Restaurar imágenes</string>
@@ -227,17 +222,17 @@
<string name="restore_done">¡Restauración Terminada!</string>
<string name="restore_fail">¡El respaldo de la imagen boot Stock no existe!</string>
<string name="proprietary_title">Descargar código propietario</string>
<string name="proprietary_notice">Magisk Manager es un software libre por lo que no contiene el código API de SafetyNet (código propietario de Google).\n\n ¿Desea permitir que Magisk Manager descargue una extensión (que contiene GoogleApiClient) para comprobar el estado de SafetyNet?</string>
<string name="proprietary_notice">Magisk es FOSS y no incluye código de la API de SafetyNet, propiedad de Google.\n\n¿Quieres descargar una extensión propietaria para las comprobaciones de SafetyNet?</string>
<string name="setup_fail">Instalación fallida</string>
<string name="env_fix_title">Se requiere una instalación adicional</string>
<string name="env_fix_msg">Su dispositivo requiere una instalación adicional para que Magisk funcione correctamente. Se descargará el zip de instalación de Magisk, ¿desea continuar ahora?</string>
<string name="env_fix_msg">Tu dispositivo necesita configuración adicional para el correcto funcionamiento de Magisk. ¿Quieres proceder y reiniciar?</string>
<string name="setup_msg">Ejecutando configuración de entorno…</string>
<string name="authenticate">Autenticar</string>
<string name="unsupport_magisk_title">Versión de Magisk no soportada</string>
<string name="unsupport_magisk_msg">Esta versión de Magisk Manager no admite versiones de Magisk inferiores a %1$s.\n\nLa aplicación se comportará como si no estuviera instalado Magisk, actualícelo tan pronto como sea posible.</string>
<string name="unsupport_magisk_msg">Esta versión de la app no soporta versiones de Magisk inferiores a la %1$s.\n\nLa aplicación se comportará como si no tuvieses Magisk instalado, por favor actualiza Magisk tan pronto como sea posible.</string>
<string name="external_rw_permission_denied">Conceder permiso de almacenamiento para habilitar esta funcionalidad</string>
<string name="add_shortcut_title">Añadir un atajo a la pantalla de inicio</string>
<string name="add_shortcut_msg">Después de esconder el Magisk Manager, su nombre e icono podría ser difícil de reconocer. ¿Quieres añadir un bonito atajo a la pantalla de inicio?</string>
<string name="add_shortcut_msg">Tras esconder la app, su nombre e icono pueden resultar difíciles de reconocer. ¿Quieres añadir un acceso directo más bonito a la pantalla principal?</string>
<string name="app_not_found">No se ha encontrado ninguna app para manejar esta acción</string>
</resources>

View File

@@ -14,20 +14,15 @@
<!--Home-->
<string name="no_connection">Ühendus puudub</string>
<string name="app_changelog">Muudatuste logi</string>
<string name="manager">Manager</string>
<string name="loading">Laadimine…</string>
<string name="update">Uuenda</string>
<string name="not_available">puudub</string>
<string name="hide">Peida</string>
<string name="status">Olek</string>
<string name="home_package">Pakett</string>
<string name="home_notice_content">Veendu alati, et kasutad avatud lähtekoodiga Magisk Manageri. Tundmatust allikast pärinev Manager võib teha pahatahtlikke toiminguid.</string>
<string name="home_support_title">Toeta meid</string>
<string name="home_item_source">Lähtekood</string>
<string name="home_support_content">Magisk on ja jääb alati tasuta ning avatud lähtekoodiga kättesaadavaks. Siiski, sa võid meile väikese annetuse näol toetust üles näidata.</string>
<string name="home_status_normal">Tavaline</string>
<string name="home_status_stub">Makett</string>
<string name="home_installed_version">Installitud</string>
<string name="home_latest_version">Viimatine</string>
<string name="invalid_update_channel">Sobimatu uuenduste kanal</string>
@@ -44,7 +39,6 @@
<string name="install_next">Edasi</string>
<string name="install_start">Läksime</string>
<string name="manager_download_install">Vajuta allalaadimiseks ja installimiseks.</string>
<string name="download_zip_only">Laadi ainult ZIP alla</string>
<string name="direct_install">Otsene install (soovitatud)</string>
<string name="install_inactive_slot">Installi ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust)</string>
<string name="install_inactive_slot_msg">Sinu seade SUNNITAKSE peale taaskäivitust käivituma praegusesse ebaaktiivsesse lahtrisse!\nKasuta seda valikut vaid peale üle-õhu uuenduse teostamist.\nJätkad?</string>
@@ -134,10 +128,6 @@
<string name="settings_download_path_message">Failid salvestatakse kausta %1$s</string>
<string name="settings_clear_cache_title">Tühjenda hoidla vahemälu</string>
<string name="settings_clear_cache_summary">Tühjenda vahemälus olev teave võrgus olevate hoidlate kohta. See sunnib rakendust võrgust värskendama</string>
<string name="settings_hide_manager_title">Peida Magisk Manager</string>
<string name="settings_hide_manager_summary">Paki Magisk Manager juhusliku paketi- ja rakenduse nimega ümber</string>
<string name="settings_restore_manager_title">Taasta Magisk Manager</string>
<string name="settings_restore_manager_summary">Taasta Magisk Manageri originaalpakett</string>
<string name="language">Keel</string>
<string name="system_default">(Süsteemi vaikesäte)</string>
<string name="settings_check_update_title">Kontrolli uuendusi</string>
@@ -199,10 +189,7 @@
<string name="progress_channel">Edenemise teated</string>
<string name="download_complete">Allalaadimine valmis</string>
<string name="download_file_error">Faili allalaadimisel esines viga</string>
<string name="download_open_parent">Kuva ülemkaustas</string>
<string name="download_open_self">Kuva fail</string>
<string name="magisk_update_title">Magiski uuendus on saadaval!</string>
<string name="manager_update_title">Magisk Manageri uuendus on saadaval!</string>
<!--Toasts, Dialogs-->
<string name="yes">Jah</string>
@@ -216,8 +203,6 @@
<string name="flashing">Välgutamine…</string>
<string name="done">Valmis!</string>
<string name="failure">Ebaõnnestus</string>
<string name="hide_manager_title">Magisk Manageri peitmine…</string>
<string name="hide_manager_fail_toast">Magisk Manageri peitmine ebaõnnestus.</string>
<string name="open_link_failed_toast">Lingi avamiseks sobivat rakendust ei leitud.</string>
<string name="complete_uninstall">Täielik eemaldus</string>
<string name="restore_img">Taasta tõmmised</string>
@@ -225,17 +210,13 @@
<string name="restore_done">Taastamine valmis!</string>
<string name="restore_fail">Originaalne varundus puudub!</string>
<string name="proprietary_title">Laadi alla suletud koodi</string>
<string name="proprietary_notice">Magisk Manager on vaba ja avatud lähtekoodiga ning ei sisalda Google\'i suletud SafetyNeti API koodi.\n\nKas lubad Magisk Manageril SafetyNeti kontrollide jaoks laadida alla laiendus (sisaldab GoogleApiClienti)?</string>
<string name="setup_fail">Seadistus ebaõnnnestus</string>
<string name="env_fix_title">Vajab lisaseadistust</string>
<string name="env_fix_msg">Sinu seade vajab Magiski korralikuks toimimiseks lisaseadistust. Laadime alla Magiski seadistus-ZIPi, kas soovid kohe jätkata?</string>
<string name="setup_msg">Käivitan keskkonnaseadistust…</string>
<string name="authenticate">Autendi</string>
<string name="unsupport_magisk_title">Mittetoetatud Magiski versioon</string>
<string name="unsupport_magisk_msg">See Magisk Manageri versioon ei toeta Magiski versiooni, mis on vanem kui %1$s.\n\nRakendus käitub nii, nagu Magisk ei oleks paigaldatud, palun uuenda Magisk nii ruttu kui võimalik.</string>
<string name="external_rw_permission_denied">Selle funktsionaalsuse lubamiseks anna mäluruumi haldamise luba</string>
<string name="add_shortcut_title">Lisa avakuvale otsetee</string>
<string name="add_shortcut_msg">Pärast Magisk Manageri peitmist võib selle nimi ja ikoon muutuda raskelt tuvastatavaks. Kas soovid avakuvale ilusa otsetee lisada?</string>
<string name="app_not_found">Selle tegevuse teostamiseks ei leitud ühtegi rakendust</string>
</resources>

View File

@@ -14,20 +14,15 @@
<!--Home-->
<string name="no_connection">هیچ اتصالی وجود ندارد</string>
<string name="app_changelog">تغییرات</string>
<string name="manager">مدیریت</string>
<string name="loading">در حال بارگذاری…</string>
<string name="update">بروزرسانی کردن</string>
<string name="not_available">غیر/قابل دسترسی</string>
<string name="hide">پنهان کردن</string>
<string name="status">وضعیت</string>
<string name="home_package">پکیج</string>
<string name="home_notice_content">همیشه اطمینان حاصل کنید که از نسخه متن باز Magisk Manager استفاده می کنید. توجه داشته باشد نصب این نرم افزار از منبع ناشناخته میتواند به دستگاه شما آسیب برساند و اطلاعات شما را در معرض خطر قرار دهد.</string>
<string name="home_support_title">حمایت ما</string>
<string name="home_item_source">منبع</string>
<string name="home_support_content">این برنامه (Magisk) رایگان و متن باز است و همیشه خواهد ماند. اگرچه شما میتواند با دونیت خود نشان دهد که به ما اهمیت می دهید.</string>
<string name="home_status_normal">وضعیت عادی</string>
<string name="home_status_stub">وضعیت Stub</string>
<string name="home_installed_version">نصب شده</string>
<string name="home_latest_version">آخرین نسخه</string>
<string name="invalid_update_channel">کانال بروزرسانی نامعتبر است</string>
@@ -44,7 +39,6 @@
<string name="install_next">بعدی</string>
<string name="install_start">بزن بریم</string>
<string name="manager_download_install">برای دانلود و نصب فشار دهید</string>
<string name="download_zip_only">فقط zip را دانلود شود</string>
<string name="direct_install">نصب مستقیم (توصیه می شود)</string>
<string name="install_inactive_slot">نصب در حافظه غیر فعال (بعد OTA)</string>
<string name="install_inactive_slot_msg">بعد از راه اندازی مجدد دستگاه شما مجبور به راه اندازی در حافظه غیرفعال فعلی می شود! \nفقط پس از انجام OTA از این گزینه استفاده کنید. \nادامه می دهید؟</string>
@@ -131,10 +125,6 @@
<string name="settings_download_path_message">فایل ها در %1$s ذخیره خواهند شد.</string>
<string name="settings_clear_cache_title">پاک کردن حافظه پنهان</string>
<string name="settings_clear_cache_summary">پاک کردن اطلاعات ذخیره شده برای ریپوسیتوری های آنلاین. این کار برنامه را مجبور به دریافت دوباره اطللاعات هنگام آنلاین شدن میکند</string>
<string name="settings_hide_manager_title">پنهان کردن Magisk Manager</string>
<string name="settings_hide_manager_summary">پکیج بندی مجدد Magisk Manager با نام تصادفی برای پکیج ها و برنامه ها</string>
<string name="settings_restore_manager_title">بازگرداندن Magisk Manager</string>
<string name="settings_restore_manager_summary">بازگرداندن Magisk Manager نام اصلی پکیج ها و برنامه ها</string>
<string name="language">زبان</string>
<string name="system_default">(پیش فرض سیستم)</string>
<string name="settings_check_update_title">چک کردن بروز رسانی ها</string>
@@ -194,10 +184,7 @@
<string name="progress_channel">Progress اعلان</string>
<string name="download_complete">دانلود کامل شد</string>
<string name="download_file_error">خطا در دانلود فایل</string>
<string name="download_open_parent">نمایش پوشه</string>
<string name="download_open_self">نمایش فایل</string>
<string name="magisk_update_title">بروزرسانی Magisk در دسترس است!</string>
<string name="manager_update_title">بروزرسانی Magisk Manager در دسترس است!</string>
<!--Toasts, Dialogs-->
<string name="yes">بله</string>
@@ -211,8 +198,6 @@
<string name="flashing">ر حال فلش کردن…</string>
<string name="done">تمام!</string>
<string name="failure">ناموفق</string>
<string name="hide_manager_title">قایم شدن Magisk Manager…</string>
<string name="hide_manager_fail_toast">پنهان کردن Magisk Manager ناموفق بود.</string>
<string name="open_link_failed_toast">هیچ برنامه ای برای باز کردن لینک یافت نشد</string>
<string name="complete_uninstall">کامل کردن حذف</string>
<string name="restore_img">بازیابی تصاویر</string>
@@ -220,16 +205,12 @@
<string name="restore_done">بازیابی انجام شد!</string>
<string name="restore_fail">نسخه پشتیبان استک موجود نیست!</string>
<string name="proprietary_title">دانلود کد اختصاصی</string>
<string name="proprietary_notice">برنامه Magisk Manager رایگان و متن باز است و حاوی کد اختصاصی Google\'s SafetyNet API نیست.\n\nآیا به Magisk Manager اجازه برای بررسی اکستنشن SafetyNet (شامل GoogleApiClient) می دهید؟</string>
<string name="setup_fail">نصب انجام نشد</string>
<string name="env_fix_title">به تنظیمات اضافی نیاز دارد</string>
<string name="env_fix_msg">دستگاه شما برای کار صحیح Magisk به تنظیمات اضافی نیاز دارد. این کار نصب Magisk zip را بارگیری می کند. میخوای الان ادامه بدی؟</string>
<string name="setup_msg">راه اندازی محیط نصب…</string>
<string name="authenticate">اعتبار دادن</string>
<string name="unsupport_magisk_title">نسخه پشتیبانی نشده Magisk</string>
<string name="unsupport_magisk_msg">این نسخه از Magisk Manager از نسخه Magisk کمتر از پشتیبانی نمی کند %1$s.\n\nاین برنامه به گونه ای رفتار می کند که گویی هیچ Magisk نصب نشده است ، لطفاً در اسرع وقت Magisk را ارتقا دهید.</string>
<string name="external_rw_permission_denied">برای فعال کردن این قابلیت ، اجازه دسترسی به حافظه بدهید</string>
<string name="add_shortcut_title">اضافه کردن میانبر را به صفحه</string>
<string name="add_shortcut_msg">پس از پنهان کردن ManagerMagisk ، ممکن است تشخیص نام و نماد آن دشوار باشد. آیا می خواهید یک میانبر زیبا به صفحه اصلی اضافه کنید؟</string>
</resources>

View File

@@ -14,20 +14,16 @@
<!--Home-->
<string name="no_connection">Aucune connexion disponible</string>
<string name="app_changelog">Journal des modifications</string>
<string name="manager">Manager</string>
<string name="loading">Chargement…</string>
<string name="update">Mettre à jour</string>
<string name="not_available">non disponible</string>
<string name="hide">Masquer</string>
<string name="status">État</string>
<string name="home_package">Paquet</string>
<string name="home_notice_content">Assurezvous de toujours utiliser la version libre de Magisk Manager. Une version provenant dune source inconnue peut effectuer des actions malveillantes.</string>
<string name="home_notice_content">Téléchargez Magisk SEULEMENT depuis la page Github officielle. Les fichiers provenant de sources inconnues peuvent être malveillants !</string>
<string name="home_support_title">Souteneznous</string>
<string name="home_item_source">Sources</string>
<string name="home_support_content">Magisk est, et sera toujours, libre et open source. Vous pouvez cependant nous témoigner de votre soutien en envoyant un petit don.</string>
<string name="home_status_normal">Normal</string>
<string name="home_status_stub">Stub</string>
<string name="home_installed_version">Installé</string>
<string name="home_latest_version">Dernière</string>
<string name="invalid_update_channel">Canal de mise à jour invalide</string>
@@ -44,7 +40,6 @@
<string name="install_next">Suivant</string>
<string name="install_start">Cest parti</string>
<string name="manager_download_install">Appuyez pour le télécharger et linstaller</string>
<string name="download_zip_only">Télécharger le ZIP sans linstaller</string>
<string name="direct_install">Installation directe (recommandée)</string>
<string name="install_inactive_slot">Installer dans lespace inactif (après mise à jour OTA)</string>
<string name="install_inactive_slot_msg">Votre appareil sera obligatoirement réamorcé à partir de lespace (slot) actuellement inactif après son redémarrage!\nNutilisez uniquement cette option quaprès que la mise à jour OTA a été effectuée.\nVoulezvous continuer?</string>
@@ -137,10 +132,10 @@
<string name="settings_download_path_message">Les fichiers seront enregistrés sous %1$s</string>
<string name="settings_clear_cache_title">Vider le cache des dépôts</string>
<string name="settings_clear_cache_summary">Effacer les informations en cache concerant les dépôts en ligne. Ceci force lapplication à télécharger des informations à jour.</string>
<string name="settings_hide_manager_title">Masquer Magisk Manager</string>
<string name="settings_hide_manager_summary">Réempaqueter Magisk Manager avec des noms de paquet et dapplication aléatoires.</string>
<string name="settings_restore_manager_title">Restaurer Magisk Manager</string>
<string name="settings_restore_manager_summary">Restaurer Magisk Manager avec ses noms de paquet et dapplication dorigine.</string>
<string name="settings_hide_app_title">Masquer lapplication Magisk</string>
<string name="settings_hide_app_summary">Installer une application intermédiaire avec un ID de package aléatoire et un nom personnalisé</string>
<string name="settings_restore_app_title">Restorer lapplication Magisk</string>
<string name="settings_restore_app_summary">Démasquer lapplication et la restaurer à son APK dorigine</string>
<string name="language">Langue</string>
<string name="system_default">(langue par défaut du système)</string>
<string name="settings_check_update_title">Vérifier les mises à jour</string>
@@ -204,10 +199,7 @@
<string name="progress_channel">Notifications de progression</string>
<string name="download_complete">Téléchargement terminé</string>
<string name="download_file_error">Erreur lors du téléchargement du fichier</string>
<string name="download_open_parent">Afficher le dossier parent</string>
<string name="download_open_self">Afficher le fichier</string>
<string name="magisk_update_title">Une mise à jour de Magisk est disponible!</string>
<string name="manager_update_title">Une mise à jour de Magisk Manager est disponible!</string>
<!--Toasts, Dialogs-->
<string name="yes">Oui</string>
@@ -221,27 +213,25 @@
<string name="flashing">Écriture dans la mémoire Flash…</string>
<string name="done">Terminé!</string>
<string name="failure">Erreur</string>
<string name="hide_manager_title">Masquage de Magisk Manager</string>
<string name="hide_manager_fail_toast">Le masquage de Magisk Manager a échoué.</string>
<string name="hide_app_title">Masquage de lapplication Magisk</string>
<string name="open_link_failed_toast">Aucune application permettant douvrir le lien na été trouvée</string>
<string name="restore_manager_fail_toast">Échec de la restauration de Magisk Manager</string>
<string name="complete_uninstall">Désinstallation complète</string>
<string name="restore_img">Restauration des images</string>
<string name="restore_img_msg">Restauration…</string>
<string name="restore_done">Restauration effectuée!</string>
<string name="restore_fail">La sauvegarde par défaut nexiste pas!</string>
<string name="proprietary_title">Téléchargement de code propriétaire</string>
<string name="proprietary_notice">Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de lAPI SafetyNet de Google. Autorisezvous Magisk Manager à télécharger une extension (contenant GoogleApiClient) permettant deffectuer les contrôles SafetyNet?</string>
<string name="proprietary_notice">Lapplication Magisk est un logiciel libre et ne contient pas de code propriétaire de lAPI SafetyNet de Google. Souhaitez-vous télécharger une extension permettant deffectuer les contrôles SafetyNet?</string>
<string name="setup_fail">Échec de linstallation</string>
<string name="env_fix_title">Installation supplémentaire requise</string>
<string name="env_fix_msg">Votre appareil a besoin dune installation supplémentaire afin que Magisk fonctionne correctement. Un fichier ZIP dinstallation pour Magisk doit être téléchargé. Voulezvous effectuer cette installation maintenant?</string>
<string name="env_fix_msg">Votre appareil a besoin dune installation supplémentaire afin que Magisk fonctionne correctement. Voulez-vous effectuer cette installation maintenant et redémarrer?</string>
<string name="setup_msg">Configuration de lenvironnement en cours…</string>
<string name="authenticate">Authentification</string>
<string name="unsupport_magisk_title">Version de Magisk non prise en charge</string>
<string name="unsupport_magisk_msg">Cette version de Magisk Manager ne prend pas en charge les versions de Magisk inférieures à %1$s.\n\nLapplication se comportera comme si Magisk nétait pas installé. Veuillez mettre à jour Magisk aussi vite que possible.</string>
<string name="unsupport_magisk_msg">Cette version de Magisk ne prend pas en charge les versions de Magisk inférieures à %1$s.\n\nLapplication se comportera comme si Magisk nétait pas installé. Veuillez mettre à jour Magisk aussi vite que possible.</string>
<string name="external_rw_permission_denied">Permettre laccès au stockage pour activer cette fonctionnalité</string>
<string name="add_shortcut_title">Ajouter un raccourci à lécran daccueil</string>
<string name="add_shortcut_msg">Après avoir caché Magisk Manager, son nom et son icône peuvent devenir difficiles à reconnaître. Voulezvous ajouter un joli raccourci vers lécran daccueil?</string>
<string name="add_shortcut_msg">Après avoir masqué Magisk, son nom et son icône peuvent devenir difficiles à reconnaître. Voulezvous ajouter un joli raccourci vers lécran daccueil?</string>
<string name="app_not_found">Aucune application trouvée pour traiter cette action</string>
</resources>

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